220 lines
No EOL
8.7 KiB
Python
220 lines
No EOL
8.7 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2020 Sublunar Space
|
|
|
|
---- MIT License ----
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
import math
|
|
import swisseph as swe
|
|
import drawSvg as svg
|
|
from openlocationcode import openlocationcode as olc
|
|
|
|
def astroChart(lat, lon, epoch, canvas, colors = ['violet', 'yellow', 'white', 'orange', 'green', 'red', 'royalblue', 'indigo']):
|
|
|
|
print("drawing astro chart for", epoch)
|
|
planets = [0,1,2,3,4,5,6]
|
|
tmp = time.gmtime(epoch) # convert epoch to time
|
|
hours = tmp.tm_hour + tmp.tm_min / 60 + tmp.tm_sec / 3600 # convert hh:mm:ss to decimal hours
|
|
jd = swe.julday(tmp.tm_year,tmp.tm_mon,tmp.tm_mday,hours,1) # convert to Julian day
|
|
te = jd + swe.deltat(jd) # add deltaT
|
|
|
|
# get ecliptic planet locations
|
|
for i in range(len(planets)):
|
|
planets[i] = swe.calc(te, i)
|
|
|
|
# clean up
|
|
for i in range(len(planets)):
|
|
planets[i] = planets[i][0][0]
|
|
|
|
cusps = swe.houses_ex(jd, lat, lon, b'P',0) # get ecliptic ascendant degree
|
|
planets.insert(0, cusps[0][0]) # add to list
|
|
|
|
# draw arcs for ascendant + planets
|
|
for i in range(len(planets)):
|
|
circleColor = colors[i]
|
|
degree = planets[i]
|
|
startArc = degree + 90 # 0° is at the 12 o'clock position
|
|
endArc = degree - 110 + 90
|
|
radius = 180 + i * 10
|
|
canvas.append(svg.ArcLine(0, 0, radius, startArc, endArc, stroke=circleColor, stroke_width=7, fill='none'))
|
|
|
|
def magicSquareSigil(plusCode): # create sigil coordinates based on OLC magic square
|
|
|
|
codeList = list(plusCode.replace('+', '')) # remove the + in the string
|
|
print("sigilizing", codeList)
|
|
coordList = []
|
|
grid = {
|
|
'R' : [-105, 112], 'V' : [-35, 112], 'W' : [35, 112], 'X' : [105, 112], # | R | V | W | X |
|
|
'J' : [-105, 56], 'M' : [-35, 56], 'P' : [35, 56], 'Q' : [105, 56], # | J | M | P | Q |
|
|
'C' : [-105, 0], 'F' : [-35, 0], 'G' : [35, 0], 'H' : [105, 0], # | C | F | G | H |
|
|
'6' : [-105, -56], '7' : [-35, -56], '8' : [35, -56], '9' : [105, -56], # | 6 | 7 | 8 | 9 |
|
|
'2' : [-105, -112], '3' : [-35, -112], '4' : [35, -112], '5' : [105, -112] # | 2 | 3 | 4 | 5 |
|
|
}
|
|
|
|
for char in codeList:
|
|
coordList.append(grid[char]) # write center coordinates to sigil coordinate list
|
|
return coordList
|
|
|
|
def radialSigil(plusCode): # create sigil coordinates based on concentric circle positions
|
|
|
|
codeList = list(plusCode.replace('+', '')) # remove the + in the string
|
|
print("sigilizing", codeList)
|
|
coordList = []
|
|
decoder = ['2', '3', '4', '5', '6', '7', '8', '9', 'C', 'F', 'G', 'H', 'J', 'M', 'P', 'Q', 'R', 'V', 'W', 'X']
|
|
|
|
count = 0
|
|
for char in codeList:
|
|
index = decoder.index(char)
|
|
# find coordinates on circle every 18° (360° / 20 digits)
|
|
radius = 145 - count * 20
|
|
angle = 18 * index
|
|
x = radius * math.cos(angle)
|
|
y = radius * math.sin(angle)
|
|
coordList.append([x, y]) # write center coordinates to sigil coordinate list
|
|
count = count + 1
|
|
|
|
return coordList
|
|
|
|
def nameToPlusCode(name): # turns an alphabetic string into a sigilizable plusCode
|
|
|
|
vowels = [ 'A', 'E', 'I', 'O', 'U']
|
|
translate = {
|
|
'B' : '2',
|
|
'D' : '3',
|
|
'K' : '4',
|
|
'L' : '5',
|
|
'N' : '6',
|
|
'S' : '7',
|
|
'T' : '8',
|
|
'Z' : '9',
|
|
'Y' : 'J'
|
|
}
|
|
newName = name.upper().replace(" ", "") # make all uppercase and get rid of spaces
|
|
for x in newName:
|
|
if x in vowels:
|
|
newName = newName.replace(x,"") # get rid of all vowels
|
|
if x in translate.keys():
|
|
newName = newName.replace(x, translate[x]) # translate consonant string into plusCode
|
|
|
|
return newName
|
|
|
|
def drawSigil(points, canvas, color='white'): # draw sigil from coordinate list
|
|
|
|
# setup sigil
|
|
dash = svg.Marker(-0.5, -0.5, 0.5, 0.5, scale=5, orient='auto') # define line to terminate the sigil
|
|
dash.append(svg.Line(-0., -0.5, 0., 0.5, stroke_width=0.2, stroke=color))
|
|
dot = svg.Marker(-0.8, -0.5, 0.5, 0.5, scale=5, orient='auto') # define circle to start the sigil
|
|
dot.append(svg.Circle(-0.3, 0.0, 0.3, stroke_width=0.2, stroke=color, fill='none'))
|
|
p = svg.Path(stroke_width=7, stroke=color, fill='none', marker_start=dot, marker_end=dash)
|
|
|
|
# draw sigil
|
|
for point in points:
|
|
if points.index(point) == 0:
|
|
originX = point[0]
|
|
originY = point[1]
|
|
p.M(originX, originY)
|
|
else:
|
|
x = point[0] - originX # abs. to rel. coords
|
|
y = point[1] - originY # abs. to rel. coords
|
|
p.l(x, y) # draw
|
|
originX = point[0]
|
|
originY = point[1]
|
|
|
|
# add to canvas
|
|
canvas.append(p)
|
|
|
|
def createSigil(lat, lon, epoch, squareSigilize, radialSigilize, fileName): # function called by main() according to CLI arguments
|
|
|
|
# set up SVG canvas
|
|
d = svg.Drawing(600, 600, origin='center') # define canvas
|
|
|
|
# Create gradients
|
|
gradient = svg.RadialGradient(0,0,260)
|
|
gradient.addStop(0, '#211d05', 1)
|
|
gradient.addStop(1, 'black', 1)
|
|
rusty = svg.RadialGradient(0,0,260)
|
|
rusty.addStop(0.5, '#d17b0a', 1)
|
|
rusty.addStop(1, '#453905', 1)
|
|
silver = svg.RadialGradient(0,0,260)
|
|
silver.addStop(0.5, 'white', 0.8)
|
|
silver.addStop(1, 'grey', 1)
|
|
|
|
# Background circle
|
|
c = svg.Circle(0, 0, 260, fill=gradient, stroke_width=0)
|
|
d.append(c)
|
|
|
|
# draw astro chart for epoch moment
|
|
astroChart(lat, lon, epoch, d, [rusty, rusty, rusty, rusty, rusty, rusty, rusty, rusty])
|
|
|
|
# draw sigils
|
|
drawSigil( radialSigil(radialSigilize), d, rusty)
|
|
drawSigil( magicSquareSigil(squareSigilize), d, silver)
|
|
|
|
# save sigil to SVG file
|
|
d.saveSvg(fileName)
|
|
|
|
def main():
|
|
|
|
argNo = len(sys.argv) - 1
|
|
|
|
if argNo == 5: # if coordinates, timestamp, and two strings (first + last name)are provided
|
|
|
|
lat = float(sys.argv[1])
|
|
lon = float(sys.argv[2])
|
|
epoch = float(sys.argv[3])
|
|
squareSigilize = nameToPlusCode(sys.argv[4]) # first name
|
|
radialSigilize = nameToPlusCode(sys.argv[5]) # last name
|
|
fileName = '%s_%s.svg' % (sys.argv[5], sys.argv[4])
|
|
createSigil(lat, lon, epoch, squareSigilize, radialSigilize, fileName)
|
|
|
|
if argNo == 3: # if coordinates, and timestamp are provided
|
|
|
|
lat = float(sys.argv[1])
|
|
lon = float(sys.argv[2])
|
|
epoch = float(sys.argv[3])
|
|
plusCode = olc.encode(lat,lon,10) # generate Open Location Code aka. plus code from coordinates
|
|
squareSigilize = plusCode[4:]
|
|
radialSigilize = plusCode[0:4]
|
|
fileName = '%s-%s.svg' % (plusCode, epoch)
|
|
createSigil(lat, lon, epoch, squareSigilize, radialSigilize, fileName)
|
|
|
|
|
|
if argNo == 2: # if just coordinates are provided
|
|
|
|
lat = float(sys.argv[1])
|
|
lon = float(sys.argv[2])
|
|
epoch = time.time()
|
|
plusCode = olc.encode(lat,lon,10) # generate Open Location Code aka. plus code from coordinates
|
|
squareSigilize = plusCode[4:]
|
|
radialSigilize = plusCode[0:4]
|
|
fileName = '%s-%s.svg' % (plusCode, epoch)
|
|
createSigil(lat, lon, epoch, squareSigilize, radialSigilize, fileName)
|
|
else:
|
|
print("--------------------------------------------")
|
|
print("Please provide arguments: <decimal latitude> <decimal longitude> (<epoch timestamp> <first name> <last name>)")
|
|
print("--------------------------------------------")
|
|
print("If you only provide latitude and longitude, the timestamp is calculated automatically for this moment.")
|
|
print("If you provice first- and last name of a person, please provide their birth-location coordinates and birth-time as geospatial arguments")
|
|
print("--------------------------------------------")
|
|
|
|
if __name__ == "__main__":
|
|
main() |