xmet/lib/xmet/map.py

105 lines
2.8 KiB
Python

import math
import cairo
import shapely
import gi
from xmet.city import City
gi.require_version('Rsvg', '2.0')
from gi.repository import Rsvg
MAP_BOUNDS = (
49.8, # North
-66.5, # East
24.2, # South
-125.5 # West
)
MAP_DIMENSIONS = (MAP_BOUNDS[0] - MAP_BOUNDS[2],
MAP_BOUNDS[1] - MAP_BOUNDS[3])
MAP_SCREEN_DIMENSIONS = (1859.0, 968.0)
class EquirectMap():
def __init__(self,
width: float,
height: float,
bounds: tuple[float, float, float, float]):
self.width = width
self.height = height
self.bounds = bounds
self.map_width = bounds[1] - bounds[3]
self.map_height = bounds[0] - bounds[2]
def draw_from_file(self,
cr: cairo.Context,
path: str,
x: float,
y: float,
width: float,
height: float):
svg = Rsvg.Handle.new_from_file(path)
rect = Rsvg.Rectangle()
rect.x = x
rect.y = y
rect.width = width
rect.height = height
svg.render_document(cr, rect)
def draw_base_map_from_file(self,
cr: cairo.Context,
path: str):
return self.draw_from_file(cr, path, 0, 0, self.width, self.height)
def screen_to_map(self, x: float, y: float) -> shapely.Point:
lon = (x / self.width) * self.map_width + self.bounds[3]
lat = self.height * self.map_height + self.bounds[2] + (y / self.height)
return shapely.Point(lon, lat)
def map_to_screen(self, point: shapely.Point) -> tuple[float, float]:
x = ((point.x - self.bounds[3]) / self.map_width) * self.width
y = self.height - ((point.y - self.bounds[2]) / self.map_height) * self.height
return x, y
def draw_polygon(self, cr: cairo.Context, poly: shapely.Polygon):
first = True
for point in poly.exterior.coords:
x, y = self.map_to_screen(shapely.Point(*point))
if first:
cr.move_to(x, y)
first = False
else:
cr.line_to(x, y)
def draw_city(self, cr: cairo.Context, city: City):
cr.save()
x, y = self.map_to_screen(city.location)
if city.population >= 1000000:
radius = 4
elif city.population >= 500000:
radius = 3
elif city.population >= 100000:
radius = 2.5
else:
radius = 1.3
extents = cr.text_extents(city.name)
cr.set_source_rgb(0.2, 0.2, 0.2)
cr.arc(x, y, radius, 0, 2*math.pi)
cr.fill()
cr.set_font_size(14)
cr.move_to(x + 8, y + extents.height / 2)
cr.show_text(city.name)
cr.restore()