77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
"""Location quad: a static OpenStreetMap image centred on the device's IP-geolocated
|
|
position, with a marker.
|
|
|
|
We render a static map (backend/staticmap.py) rather than an interactive libshumate
|
|
map: Shumate does not paint tiles in this environment (the official shumate-demo
|
|
shows the same blank map), while tile downloads themselves work fine. Consumes the
|
|
shared LocationService, which the Weather quad also uses.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
from gi.repository import Gtk # noqa: E402
|
|
|
|
from lib.proc import run_text
|
|
from module_base import Feature, ModuleContext, ModuleInstance, ModuleSpec
|
|
from paths import BACKEND_DIR, CACHE_DIR
|
|
|
|
_SCRIPT = str(BACKEND_DIR / "staticmap.py")
|
|
|
|
|
|
class _MapView(Gtk.Box):
|
|
def __init__(self, ctx: ModuleContext, zoom: int, size: tuple[int, int],
|
|
tag: str, show_info: bool):
|
|
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
|
self.add_css_class("map-view")
|
|
self.ctx = ctx
|
|
self.zoom = zoom
|
|
self.size = size
|
|
self._out = str(CACHE_DIR / f"map_{tag}.png")
|
|
|
|
self.picture = Gtk.Picture(content_fit=Gtk.ContentFit.COVER, vexpand=True)
|
|
self.picture.add_css_class("map-picture")
|
|
self.append(self.picture)
|
|
|
|
self._info = None
|
|
if show_info:
|
|
self._info = Gtk.Label(label="Locating…", xalign=0.0)
|
|
self._info.add_css_class("map-info")
|
|
self.append(self._info)
|
|
|
|
ctx.services.location.subscribe(self._on_location)
|
|
|
|
def _on_location(self, data: dict) -> None:
|
|
lat, lon = data["lat"], data["lon"]
|
|
w, h = self.size
|
|
run_text([sys.executable, _SCRIPT, str(lat), str(lon), str(self.zoom),
|
|
str(w), str(h), self._out], self._on_rendered)
|
|
if self._info is not None:
|
|
city = data.get("city") or "Unknown"
|
|
country = data.get("country") or ""
|
|
self._info.set_text(f"{city}, {country} · {lat:.3f}, {lon:.3f}")
|
|
|
|
def _on_rendered(self, ok: bool, out: str, err: str) -> None:
|
|
if ok:
|
|
self.picture.set_filename(self._out)
|
|
|
|
|
|
def build(ctx: ModuleContext) -> ModuleInstance:
|
|
ctx.services.location.get() # kick off geolocation if not started
|
|
compact = _MapView(ctx, zoom=12, size=(640, 340), tag="compact", show_info=False)
|
|
expanded = _MapView(ctx, zoom=13, size=(1100, 620), tag="expanded", show_info=True)
|
|
return ModuleInstance(compact=compact, expanded=expanded, scroll_expanded=False)
|
|
|
|
|
|
SPEC = ModuleSpec(
|
|
id="location",
|
|
title="Location",
|
|
icon="", # nf-fa-map_marker
|
|
build=build,
|
|
features=[Feature("ip_locate", "Locate via IP", True)],
|
|
)
|