-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.DS_Store | ||
*.iml | ||
.idea | ||
*.pyc | ||
*.svg | ||
stardata/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |