Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
codebox committed Nov 7, 2016
1 parent 6b9c01e commit 855545e
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.DS_Store
*.iml
.idea
*.pyc
*.svg
stardata/
95 changes: 95 additions & 0 deletions diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from __future__ import division
from svg import Svg
import codecs

MARGIN=20
MAGNIFICATION = 500

MIN_D = 1
MAX_D = 4

DIMMEST_MAG = 6
BRIGHTEST_MAG = -1.5

LABEL_OFFSET_X = 4
LABEL_OFFSET_Y = 3
FONT_SIZE=16
FONT_COLOUR='#167ac6'

STAR_COLOUR='#000'

CURVE_WIDTH = 0.1
CURVE_COLOUR = '#000'

class Diagram:
def __init__(self):
self.points = []
self.curves = []
self.border_min_x = self.border_min_y = self.border_max_x = self.border_max_y = None

def add_point(self, x, y, m, l):
self.points.append((x, y, m, l))

def add_curve(self, curve_points):
self.curves.append(curve_points)

def _mag_to_d(self, m):
mag_range = DIMMEST_MAG - BRIGHTEST_MAG
m_score = (DIMMEST_MAG - m) / mag_range
r_range = MAX_D - MIN_D
return MIN_D + m_score * r_range

def _normalise_coords(self):
self.min_x = min(map(lambda p: p[0], self.points))
self.min_y = min(map(lambda p: p[1], self.points))
self.max_x = max(map(lambda p: p[0], self.points))
self.max_y = max(map(lambda p: p[1], self.points))

range_x = self.max_x - self.min_x
range_y = self.max_y - self.min_y

self.largest_range = max(range_x, range_y)

nc = []
for p in self.points:
x, y = self._convert_coord(p[0], p[1])
nc.append((x, y, p[2], p[3]))
return nc

def _convert_coord(self, ra, dec):
return (ra - self.min_x) / self.largest_range, (self.max_y - dec) / self.largest_range

def render_svg(self, outfile):
svg = Svg()

normalised_coords = self._normalise_coords()

# add stars first
for point in normalised_coords:
_x, _y, m, _ = point
d = self._mag_to_d(m)
x = MARGIN + _x * MAGNIFICATION
y = MARGIN + _y * MAGNIFICATION

svg.circle(x, y, d, STAR_COLOUR)

# next add labels
for point in self._normalise_coords():
_x, _y, m, l = point

if l:
d = self._mag_to_d(m)
x = MARGIN + _x * MAGNIFICATION
y = MARGIN + _y * MAGNIFICATION
svg.text(x + LABEL_OFFSET_X + d/2, y + LABEL_OFFSET_Y, l, FONT_COLOUR, FONT_SIZE)

# next add curves
for curve_points in self.curves:
normalised_curve_points = []
for cp in curve_points:
ccx, ccy = self._convert_coord(cp[0], cp[1])
normalised_curve_points.append((MARGIN + ccx * MAGNIFICATION, MARGIN + ccy * MAGNIFICATION))

svg.curve(normalised_curve_points, CURVE_WIDTH, CURVE_COLOUR)

codecs.open(outfile, 'w', 'utf-8').writelines(svg.to_list())
30 changes: 30 additions & 0 deletions input_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import codecs

class InputFile:
def __init__(self, file_path):
self.file_path = file_path

def get_stars(self, ra_range, dec_range, min_mag):
"""
Expected line format:
<RA>,<DEC>,<MAG>
RA: 0 -> 24
DEC: +90 -> -90
MAG: any floating-point value
"""
matches = []
for line in codecs.open(self.file_path, 'r', 'utf-8').readlines():
parts = line.split(',')
ra, dec, mag = map(float, parts[:3])
label = '' if len(parts) < 4 else parts[3]
if mag > min_mag: #because smaller mag values mean brighter stars
continue
if not (ra_range[0] <= ra <= ra_range[1]):
continue
if not (dec_range[0] <= dec <= dec_range[1]):
continue

matches.append((ra, dec, mag, label))

return matches
100 changes: 100 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from __future__ import division
from math import sin, cos, degrees, radians, pi
from input_file import InputFile
from diagram import Diagram

IN_FILE = 'stardata/in.csv'
MIN_MAG=10

'''
#Ursa-Major
RA_RANGE=(11, 14)
DEC_RANGE=(45, 65)
# Orion
RA_RANGE=(5, 6)
DEC_RANGE=(-10, 10)
#Ursa-Minor
RA_RANGE=(0, 24)
DEC_RANGE=(-90, 0)
RA_RANGE=(4, 6)
DEC_RANGE=(10, 30)
'''
# Orion
RA_RANGE=(5, 6)
DEC_RANGE=(-10, 10)

RA_STEP=1
DEC_STEP=10

def _calc(ra0, dec0, ra, dec):
# http://www.projectpluto.com/project.htm
delta_ra = ra - ra0;
x1 = cos(dec) * sin(delta_ra)
y1 = sin(dec) * cos(dec0) - cos(dec) * cos(delta_ra) * sin(dec0)
return x1, y1

def _ra_to_angle(ra):
return pi * 2 * (1 - ra / 24)

def _dec_to_angle(dec):
return radians(dec)

# expects ra: 0 -> 24, dec: +90 -> -90
def calc(ra, dec):
centre_ra = sum(RA_RANGE) / 2 #TODO this isn't quite right, eg RA of (0,24) it should center on 0 not 12
centre_dec = sum(DEC_RANGE) / 2
return _calc(_ra_to_angle(centre_ra), _dec_to_angle(centre_dec), _ra_to_angle(ra), _dec_to_angle(dec))

f = InputFile(IN_FILE)

d = Diagram()

for s in f.get_stars(RA_RANGE, DEC_RANGE, MIN_MAG):
ra, dec, mag, l = s
x,y = calc(ra, dec)
d.add_point(x, y, mag, l)

def add_ra_dec_curves(d, min_ra, max_ra, ra_step, min_dec, max_dec, dec_step):
first_ra_line = min_ra if min_ra % ra_step == 0 else min_ra + ra_step - min_ra % ra_step
last_ra_line = max_ra if max_ra % ra_step == 0 else max_ra - max_ra % ra_step
first_dec_line = min_dec if min_dec % dec_step == 0 else min_dec + dec_step - min_dec % dec_step
last_dec_line = max_dec if max_dec % dec_step == 0 else max_dec - max_dec % dec_step

ra_lines = []
ra = first_ra_line
while ra < last_ra_line:
ra_lines.append(ra)
ra += ra_step
ra_lines.append(last_ra_line)

dec_lines = []
dec = first_dec_line
while dec < last_dec_line:
dec_lines.append(dec)
dec += dec_step
dec_lines.append(last_dec_line)

#TODO need more points on curves to make shape better
for ra in ra_lines:
p = [calc(ra, min_dec)]
for dec in dec_lines:
p.append(calc(ra, dec))
p.append(calc(ra, max_dec))

d.add_curve(p)

for dec in dec_lines:
p = [calc(min_ra, dec)]
for ra in ra_lines:
p.append(calc(ra, dec))
p.append(calc(max_ra, dec))

d.add_curve(p)


add_ra_dec_curves(d, RA_RANGE[0], RA_RANGE[1], RA_STEP, DEC_RANGE[0], DEC_RANGE[1], DEC_STEP)

d.render_svg('star-chart.svg')
56 changes: 56 additions & 0 deletions svg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
SVG_HEADER = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
SVG_FOOTER = '</svg>'

class Svg:
def __init__(self):
self.elements = []

def line(self, x1, y1, x2, y2, width, colour):
self.elements.append('<line x1="{}" y1="{}" x2="{}" y2="{}" stroke-width="{}" stroke="{}"/>'.format(x1, y1, x2, y2, width, colour))

def text(self, x, y, l, colour, size):
self.elements.append(u'<text x="{}" y="{}" style="fill: {}; font-size: {}px;">{}</text>'.format(x, y, colour, size, l))

def circle(self, x, y, d, colour):
self.elements.append('<circle cx="{}" cy="{}" r="{}" fill="{}" />'.format(x, y, d, colour))

def curve(self, _points, width, colour):
points = sum(_points, ())

# http://schepers.cc/getting-to-the-point
d = 'M {} {} '.format(points[0], points[1])
i = 0
iLen = len(points)
while iLen - 2 > i:
p = []
if i == 0:
p.append((points[i], points[i + 1]))
p.append((points[i], points[i + 1]))
p.append((points[i + 2], points[i + 3]))
p.append((points[i + 4], points[i + 5]))
elif iLen - 4 == i:
p.append((points[i - 2], points[i - 1]))
p.append((points[i], points[i + 1]))
p.append((points[i + 2], points[i + 3]))
p.append((points[i + 2], points[i + 3]))
else:
p.append((points[i - 2], points[i - 1]))
p.append((points[i], points[i + 1]))
p.append((points[i + 2], points[i + 3]))
p.append((points[i + 4], points[i + 5]))

i += 2

bp = []
bp.append((p[1][0], p[1][1]))
bp.append((((-(p[0][0]) + 6*p[1][0] + p[2][0]) / 6), (-(p[0][1]) + 6*p[1][1] + p[2][1]) / 6))
bp.append(((( p[1][0] + 6*p[2][0] - p[3][0]) / 6), ( p[1][1] + 6*p[2][1] - p[3][1]) / 6))
bp.append((p[2][0], p[2][1]))

d += 'C {} {},{} {},{} {} '.format(bp[1][0], bp[1][1], bp[2][0], bp[2][1], bp[3][0], bp[3][1])

self.elements.append('<path d="{}" stroke="{}" stroke-width="{}" fill="transparent"/>'.format(d, colour, width))

def to_list(self):
return [XML_HEADER, SVG_HEADER] + self.elements + [SVG_FOOTER]

0 comments on commit 855545e

Please sign in to comment.