Render country labels
The positions are still a bit off, because they are simply calculated as the average of all polygon coordinates. That causes a bias towards detailed borderlines. A better way would be to calculate the center of mass for each polygon, but that is not implemented yet.
This commit is contained in:
parent
60f59379e5
commit
e965ec9032
85
qsomap.py
85
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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue