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