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:
Thomas Kolb 2021-06-10 22:52:49 +02:00
parent 60f59379e5
commit e965ec9032
1 changed files with 70 additions and 15 deletions

View File

@ -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)