Skip to content
This repository has been archived by the owner on Jun 16, 2021. It is now read-only.
/ senex Public archive

Elm/Haskell webapp to draw natal charts and horoscopes, using Swiss Ephemeris and Google Maps APIs

License

Notifications You must be signed in to change notification settings

lfborjas/senex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Senex

Uses Astrodientst's Swiss Ephemeris C library in Haskell, exposing it as a Module and then using it for a Servant-based API that powers a tiny Elm SPA, which renders a Placidus chart in SVG.

Progress as of 12/15/2019

image

Development

Been using VSCode with elm, Haskell Language Server and Haskell Language as extensions; it's been quite alright except for the weird memory leak in HIE (!)

To run the backend server, simply run stack run. Currently, it doesn't serve the Elm app: I'm using elm-live for live development, so I'm simply running both processes on separate terminals and visiting localhost:8000 to see the Elm SPA.

To run elm-live:

elm-live src/Main.elm -- --output=main.js

To run the Haskell server

GOOGLE_API_KEY=<GET_YOUR_OWN_DANG_KEY> stack run

Notes:

  • Consider packaging the C bindings, and adding the appropriate support in the cabal file.
  • Considered using servant-elm, but ended up defining my JSON types and functions by hand.
  • Even though cabal auto-generates the hs files from hsc sources when building/linking, VSCode seems confounded about hsc files and one needs to run stack build by hand to get them to be compiled -- the hie process picks them up afterwards, it seems.

Haskell bindings to the SwissEphemeris library

Compiling the C libs

I'm currently ignoring the C sources, as they're downloaded as-is from the official site. The version I'm using is the sources for 2.08 as found in the downloads page -- I put them in a csrc folder.

➜  csrc git:(master) ✗ make libswe.so
cc	 -c -g -Wall -fPIC  	   swedate.c     
cc	 -c -g -Wall -fPIC  	   swehouse.c     
cc	 -c -g -Wall -fPIC  	   swejpl.c     
cc	 -c -g -Wall -fPIC  	   swemmoon.c     
cc	 -c -g -Wall -fPIC  	   swemplan.c     
cc	 -c -g -Wall -fPIC  	   swepcalc.c     
cc	 -c -g -Wall -fPIC  	   sweph.c     
cc	 -c -g -Wall -fPIC  	   swepdate.c     
cc	 -c -g -Wall -fPIC  	   swephlib.c     
cc	 -c -g -Wall -fPIC  	   swecl.c     
cc	 -c -g -Wall -fPIC  	   swehel.c     
cc	 -shared -o libswe.so swedate.o swehouse.o swejpl.o swemmoon.o swemplan.o swepcalc.o sweph.o swepdate.o swephlib.o swecl.o swehel.o

➜  csrc git:(master) ✗ make libswe.a
ar r libswe.a	swedate.o swehouse.o swejpl.o swemmoon.o swemplan.o swepcalc.o sweph.o swepdate.o swephlib.o swecl.o swehel.o
ar: creating archive libswe.a

➜  csrc git:(master) ✗ make libswe.dylib

➜  csrc git:(master) ✗ cp libswe.* /usr/local/lib/

Using the C Bindings

Example main (based on https://www.astro.com/swisseph/swephprg.htm#_Toc19111155):

main :: IO
main = do 
  -- location of your ephemeris _folder_
  setEphemeridesPath "/Users/luis/code/senex/csrc/sweph_18"
  let time = julianDay 1989 1 6 0.0
  let coords = map (\p -> (p, (calculateCoordinates time p))) [Sun .. Chiron]
  let cusps  = calculateCusps time (basicCoords (14.0839053, -87.2750137)) Placidus
  forM_ coords $ \(planet, coord)->
    putStrLn $ show planet ++ ": " ++ show coord
  putStrLn $ "Cusps: " ++ show cusps

Which outputs:

Sun: Right (Coords {lat = 285.64724200024165, long = -8.254238068673002e-5, distance = 0.983344884137739, longSpeed = 1.0196526213625938, latSpeed = 1.4968387810319695e-5, distSpeed = 1.734078975098347e-5})
Moon: Right (Coords {lat = 262.48117294528356, long = -4.905353383440304, distance = 2.541059357627873e-3, longSpeed = 13.539284298718682, latSpeed = 0.3392091965109866, distSpeed = -3.335582471922629e-5})
Mercury: Right (Coords {lat = 304.31440617188355, long = -1.3440800529425412, distance = 1.0631760110344726, longSpeed = 1.2740014806904785, latSpeed = 0.15124350747576998, distSpeed = -2.4544384076849012e-2})
Venus: Right (Coords {lat = 264.04869233523164, long = 0.611405334065098, distance = 1.541582583770699, longSpeed = 1.2513022022944864, latSpeed = -4.242629234528706e-2, distSpeed = 3.7838225239450584e-3})
Mars: Right (Coords {lat = 22.78483261142441, long = 0.6472654073363234, distance = 1.0224116051096686, longSpeed = 0.5238430038342773, latSpeed = 1.942622907667852e-2, distSpeed = 9.240031315557963e-3})
Jupiter: Right (Coords {lat = 56.44155800097809, long = -0.8785523476115656, distance = 4.335644198505431, longSpeed = -4.88842084035685e-2, latSpeed = 4.367591705532038e-3, distSpeed = 1.235220405072288e-2})
Saturn: Right (Coords {lat = 276.1820087613432, long = 0.7124661106433279, distance = 11.011939358359793, longSpeed = 0.11736930636883483, latSpeed = -9.50953203038811e-4, distSpeed = -2.7734883523371427e-3})
Uranus: Right (Coords {lat = 272.0517155140088, long = -0.22004079326191983, distance = 20.269982255822082, longSpeed = 5.9199702638179044e-2, latSpeed = -1.8348838403496113e-4, distSpeed = -3.835650269219664e-3})
Neptune: Right (Coords {lat = 280.1110438430756, long = 0.9024310257851641, distance = 31.197894367235328, longSpeed = 3.7822803787784465e-2, latSpeed = -1.0376379525413769e-4, distSpeed = -1.6316454570516235e-3})
Pluto: Right (Coords {lat = 224.68172926588147, long = 15.629616292066745, distance = 30.10503346247479, longSpeed = 2.3906567127179156e-2, latSpeed = 7.006203197180026e-3, distSpeed = -1.4619215232707367e-2})
MeanNode: Right (Coords {lat = 337.5234813280144, long = 0.0, distance = 2.5695552897999894e-3, longSpeed = -5.29028695601468e-2, latSpeed = 0.0, distSpeed = 0.0})
TrueNode: Right (Coords {lat = 336.0939772852687, long = 0.0, distance = 2.431002861595713e-3, longSpeed = -0.15458670098362634, latSpeed = 0.0, distSpeed = 9.461379851655086e-6})
MeanApog: Right (Coords {lat = 176.27796853267614, long = -1.658313456511187, distance = 2.7106251317225464e-3, longSpeed = 0.11092946668995039, latSpeed = -1.3957593707984951e-2, distSpeed = 0.0})
OscuApog: Right (Coords {lat = 160.76672907240018, long = -0.4175464087960888, distance = 2.7280104068905034e-3, longSpeed = -3.219056080555324, latSpeed = 0.2728854201125149, distSpeed = 4.0357015237500586e-6})
Earth: Right (Coords {lat = 0.0, long = 0.0, distance = 0.0, longSpeed = 0.0, latSpeed = 0.0, distSpeed = 0.0})
Chiron: Right (Coords {lat = 93.53727572747667, long = -6.849325566420532, distance = 11.045971701732345, longSpeed = -6.391339610156536e-2, latSpeed = 8.213606290819226e-4, distSpeed = 1.6210560093203594e-3})
Cusps: (Cusps {i = 112.20189657163523, ii = 138.4658382335878, iii = 167.69682489058204, iv = 199.79861981778183, v = 232.2797046698429, vi = 263.0249102802477, vii = 292.20189657163525, viii = 318.46583823358776, ix = 347.69682489058204, x = 19.798619817781823, xi = 52.27970466984291, xii = 83.02491028024768},Angles {ascendant = 112.20189657163523, mc = 19.798619817781823, armc = 18.277351820745423, vertex = 216.1872418365295, equatorialAscendant = 106.85773516967282, coAscendantKoch = 101.19442735316477, coAscendantMunkasey = 153.1221838791593, polarAscendant = 281.19442735316477})

Note that the calculateCoordinates function is currently using the default calculations from Swiss Ephemeris, which return the ecliptic degree numbers for position. One could also obtain equatorial. To compare the output of the above main to the <astro.com> raw data, you can use this page: https://www.astro.com/swisseph/swetest.htm

Using the API

If you execute stack run, you'll launch the little web server. Right now there's only one endpoint, /api/horoscope, which outputs all the necessary data to draw a chart using the Placidus system (with geocentric, ecliptic planetary coordinates).

E.g.

curl -H "Content-Type: application/json" -vd '{"dob": "2019-11-29T20:52:39.230Z", "loc": [40.7128, 74.0060]}' http://localhost:3030/api/horoscope | jq

Will return:

{
  "angles": {
    "coAscendantMunkasey": 184.07155823288545,
    "armc": 95.61800714416292,
    "vertex": 12.249319507635633,
    "coAscendantKoch": 189.73116677377803,
    "polarAscendant": 9.731166773778025,
    "ascendant": 184.45905828112743,
    "equatorialAscendant": 186.11946559553854,
    "mc": 95.15715438291743
  },
  "cusps": {
    "viii": 30.857771867801034,
    "iv": 275.15715438291744,
    "xii": 158.7976013458591,
    "vii": 4.459058281127454,
    "iii": 241.49309969070742,
    "xi": 128.63707423591143,
    "vi": 338.7976013458591,
    "x": 95.15715438291743,
    "ii": 210.857771867801,
    "v": 308.6370742359114,
    "ix": 61.493099690707425,
    "i": 184.45905828112743
  },
  "planets": [
    {
      "planet": "Sun",
      "coords": {
        "long": -3.2442303790940265e-05,
        "distSpeed": -0.00017325688712833964,
        "distance": 0.9863760524073597,
        "lat": 247.33264880176225,
        "longSpeed": 1.0133084132855246,
        "latSpeed": -3.863831321763737e-05
      }
    },
    {
      "planet": "Moon",
      "coords": {
        "long": -0.8193364655556776,
        "distSpeed": 3.4199571717112877e-05,
        "distance": 0.002595173044398994,
        "lat": 287.65707267611856,
        "longSpeed": 12.85711522794824,
        "latSpeed": -1.1591077157393737
      }
    },
    {
      "planet": "Mercury",
      "coords": {
        "long": 2.2840468202086095,
        "distSpeed": 0.023643862939218642,
        "distance": 1.0394771384346215,
        "lat": 227.4720925435879,
        "longSpeed": 1.1103955965543209,
        "latSpeed": -0.07381439176273445
      }
    },
    {
      "planet": "Venus",
      "coords": {
        "long": -1.4298689406579366,
        "distSpeed": -0.004673349588300221,
        "distance": 1.4436347832633516,
        "lat": 274.7729229195149,
        "longSpeed": 1.2393033430774942,
        "latSpeed": -0.03062141107869374
      }
    },
    {
      "planet": "Mars",
      "coords": {
        "long": 0.6389925032221768,
        "distSpeed": -0.005799817007780383,
        "distance": 2.394266061807406,
        "lat": 216.9582906658545,
        "longSpeed": 0.6613181108727035,
        "latSpeed": -0.007476121429545853
      }
    },
    {
      "planet": "Jupiter",
      "coords": {
        "long": 0.14065496787551218,
        "distSpeed": 0.0055021301641855185,
        "distance": 6.140564456540776,
        "lat": 269.35796286622525,
        "longSpeed": 0.22093221925582224,
        "latSpeed": -0.0017197586772309318
      }
    },
    {
      "planet": "Saturn",
      "coords": {
        "long": 0.09219558630909697,
        "distSpeed": 0.010786603076421411,
        "distance": 10.766634669694042,
        "lat": 287.87005793978847,
        "longSpeed": 0.09923907956042938,
        "latSpeed": -0.0013210873063737077
      }
    },
    {
      "planet": "Uranus",
      "coords": {
        "long": -0.5086037140772992,
        "distSpeed": 0.009634594204334702,
        "distance": 18.996900475321056,
        "lat": 33.3730578798885,
        "longSpeed": -0.03189501312360131,
        "latSpeed": 0.0003803078778294534
      }
    },
    {
      "planet": "Neptune",
      "coords": {
        "long": -1.0404667767161933,
        "distSpeed": 0.01714450411055644,
        "distance": 29.77022690830857,
        "lat": 345.9277533857561,
        "longSpeed": 0.0013585201191201608,
        "latSpeed": 0.00044575928526611444
      }
    },
    {
      "planet": "Pluto",
      "coords": {
        "long": -0.6093798128580833,
        "distSpeed": 0.012651161944830309,
        "distance": 34.630835428945375,
        "lat": 291.42159066580865,
        "longSpeed": 0.02585129866720648,
        "latSpeed": -0.0012984194145876868
      }
    },
    {
      "planet": "MeanNode",
      "coords": {
        "long": 0,
        "distSpeed": 0,
        "distance": 0.0025695552897999903,
        "lat": 99.940245204816,
        "longSpeed": -0.0529198493423801,
        "latSpeed": 0
      }
    },
    {
      "planet": "TrueNode",
      "coords": {
        "long": 0,
        "distSpeed": -2.000108152646491e-05,
        "distance": 0.0025454732460946884,
        "lat": 98.64506720104299,
        "longSpeed": 0.014766990832911874,
        "latSpeed": 0
      }
    },
    {
      "planet": "MeanApog",
      "coords": {
        "long": -4.934892324024697,
        "distSpeed": 0,
        "distance": 0.0027106251317225464,
        "lat": 353.45270582739926,
        "longSpeed": 0.11199567205597004,
        "latSpeed": -0.004183727585189979
      }
    },
    {
      "planet": "OscuApog",
      "coords": {
        "long": -5.19812275167748,
        "distSpeed": -5.769891521104746e-06,
        "distance": 0.0027194482077314954,
        "lat": 3.8151605129556345,
        "longSpeed": -3.557246823636227,
        "latSpeed": 0.01887116086156039
      }
    },
    {
      "planet": "Earth",
      "coords": {
        "long": 0,
        "distSpeed": 0,
        "distance": 0,
        "lat": 0,
        "longSpeed": 0,
        "latSpeed": 0
      }
    },
    {
      "planet": "Chiron",
      "coords": {
        "long": 3.0425800620614827,
        "distSpeed": 0.01597359808865773,
        "distance": 18.396872395030705,
        "lat": 1.5149084336742755,
        "longSpeed": -0.012095695862155824,
        "latSpeed": -0.0036824990895765585
      }
    }
  ]
}

Google Places Proxy

Due to CORS restrictions, we can't call the google places APIs from Elm. Fortunately, it's quite straightforward to build a proxy! Here's an example interaction:

➜  senex git:(google-apis) ✗ curl -v "http://localhost:3030/api/proxy/autocomplete?input=tegucigalpa&token=12345"
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 3030 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET /api/proxy/autocomplete?input=tegucigalpa&token=12345 HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Date: Sun, 15 Dec 2019 20:19:14 GMT
< Server: Warp/3.2.28
< Content-Type: application/json;charset=utf-8
< 
* Connection #0 to host localhost left intact
{"status":"OK","predictions":[{"place_id":"ChIJUT10v7qib48R08lqIDgiz2g","description":"Tegucigalpa, Honduras"}]}⏎                                                                                                                                       ➜  senex git:(google-apis) ✗ curl -v "http://localhost:3030/api/proxy/placeDetails?place_id=ChIJUT10v7qib48R08lqIDgiz2g&token=12345"
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 3030 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET /api/proxy/placeDetails?place_id=ChIJUT10v7qib48R08lqIDgiz2g&token=12345 HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Date: Sun, 15 Dec 2019 20:19:56 GMT
< Server: Warp/3.2.28
< Content-Type: application/json;charset=utf-8
< 
* Connection #0 to host localhost left intact
{"status":"OK","result":{"formatted_address":"Tegucigalpa, Honduras","name":"Tegucigalpa","geometry":{"location":{"lat":14.065049,"lng":-87.1715002}}}}⏎   

About

Elm/Haskell webapp to draw natal charts and horoscopes, using Swiss Ephemeris and Google Maps APIs

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published