diff --git a/qsomap.py b/qsomap.py index de1c955..1ec15f8 100755 --- a/qsomap.py +++ b/qsomap.py @@ -9,10 +9,7 @@ import json import random import argparse -REF_LATITUDE = 49.58666 -REF_LONGITUDE = 11.01250 -# REF_LATITUDE = -30 -# REF_LONGITUDE = 90 +LABEL_MIN_FONT_SIZE = 2 def map_azimuthal_equidistant(lat, lon, ref_lat, ref_lon, R=1): @@ -57,14 +54,6 @@ def map_azimuthal_equidistant(lat, lon, ref_lat, ref_lon, R=1): return x, y -def random_country_color(): - h = random.random() - s = 0.7 - v = 0.8 - r, g, b = [int(255.99*x) for x in hsv_to_rgb([h, s, v])] - return f"#{r:02x}{g:02x}{b:02x}" - - def is_point_in_polygon(point, polygon): # Idea: draw an infinite line from the test point along the x axis to the # right. Then check how many polygon edges this line intersects. If the @@ -203,6 +192,18 @@ def map_all_polygons(simplegeodata, ref_lat, ref_lon, map_radius): v['proj_coordinates'] = proj_polys +def hsv_to_svgstr(h, s, v): + r, g, b = [int(255.99*x) for x in hsv_to_rgb([h, s, v])] + return f"#{r:02x}{g:02x}{b:02x}" + + +def assign_country_colors(simplegeodata): + for k, v in simplegeodata.items(): + hue = random.random() + v['polygon_color'] = hsv_to_svgstr(hue, 0.7, 0.8) + v['label_color'] = hsv_to_svgstr(hue, 0.5, 0.4) + + def svg_add_countries(doc, simplegeodata, ref_lat, ref_lon, map_radius): antipodal_lat = -ref_lat antipodal_lon = ref_lon + np.pi @@ -211,9 +212,9 @@ def svg_add_countries(doc, simplegeodata, ref_lat, ref_lon, map_radius): antipodal_lon -= 2*np.pi for k, v in simplegeodata.items(): - print(f"Exporting {k}…", file=sys.stderr) + print(f"Mapping {k}…", file=sys.stderr) - color = random_country_color() + color = v['polygon_color'] group = doc.g() @@ -293,6 +294,55 @@ def svg_add_maidenhead_grid(doc, ref_lat, ref_lon, map_radius): doc.add(group) # Maidenhead grid +def svg_add_country_names(doc, simplegeodata, map_radius): + group = doc.g() + + for k, v in simplegeodata.items(): + print(f"Labeling {k} ", end='') + for poly in v['proj_coordinates']: + x = poly[0, :] + y = poly[1, :] + + w = np.max(x) - np.min(x) + h = np.max(y) - np.min(y) + + # FIXME: calculate center of mass + center_x = np.median(x) + map_radius + center_y = np.median(y) + map_radius + + kwargs = { + 'class': 'country_label', + 'fill': v['label_color'] + } + + # rotate text by 90 degrees if polygon is higher than wide + if h > w: + font_size = h / len(v['name']) + rotate = True + else: + font_size = w / len(v['name']) + rotate = False + + if font_size < LABEL_MIN_FONT_SIZE: + print('.', end='', flush=True) + continue # too small + else: + print('#', end='', flush=True) + + kwargs['font-size'] = font_size + + txt = doc.text(v['name'], (center_x, center_y), + **kwargs) + + if rotate: + txt.rotate(90, center=(center_x, center_y)) + group.add(txt) + + print() + + doc.add(group) + + def svg_add_distance_azimuth_lines(doc, ref_lat, ref_lon, map_radius): group = doc.g() @@ -388,6 +438,7 @@ def render(ref_lat, ref_lon, output_stream): """ map_all_polygons(simplegeodata, ref_lat, ref_lon, R) + assign_country_colors(simplegeodata) # generate the SVG @@ -418,10 +469,13 @@ def render(ref_lat, ref_lon, output_stream): opacity: 0.5; } - .maidenhead_label { + .maidenhead_label, .country_label { font-family: sans-serif; dominant-baseline: middle; text-anchor: middle; + } + + .maidenhead_label { fill: red; opacity: 0.25; } @@ -432,6 +486,7 @@ def render(ref_lat, ref_lon, output_stream): stroke_width=1, stroke='black')) svg_add_countries(doc, simplegeodata, ref_lat, ref_lon, R) + svg_add_country_names(doc, simplegeodata, R) svg_add_maidenhead_grid(doc, ref_lat, ref_lon, R) svg_add_distance_azimuth_lines(doc, ref_lat, ref_lon, R)