mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 10:51:36 -04:00
- Add MapLibre GL JS viewer with light/dark themes, terrain layer, and embedded static map page for coordinate sharing - Add map pin support with persistent map_pins.yaml config - Add map region subscriptions and downloader with progress tracking, sourcing pre-extracted PMTiles from a cold CDN - Add map export to PNG and GPS coordinate copy via context menu - Add MapViewer React component alongside reworked Map page - Add map catalog and region-building script (build_map_regions.py) - Remove OSM import/render scripts, leaflet files, Apache configs, renderd service, and osm-map-dumper Docker container - Update Caddyfile to serve PMTiles and map static assets - Update build/install/repair scripts for Protomaps dependencies - Drop map_file table, add/drop map_subscription table (alembic) - Adding button to skip primary drive mounting during onboarding.
100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
from http import HTTPStatus
|
|
|
|
from sanic import Request, response, Blueprint
|
|
from sanic_ext import validate
|
|
from sanic_ext.extensions.openapi import openapi
|
|
|
|
from modules.map import lib, schema
|
|
from modules.map.pins import get_map_pins_config
|
|
from wrolpi.api_utils import json_response
|
|
from wrolpi.common import wrol_mode_check
|
|
from wrolpi.events import Events
|
|
|
|
map_bp = Blueprint('Map', '/api/map')
|
|
|
|
|
|
@map_bp.get('/files')
|
|
@openapi.description('List available PMTiles map files')
|
|
def get_files(_: Request):
|
|
files = lib.get_pmtiles_files()
|
|
return json_response(dict(files=files), HTTPStatus.OK)
|
|
|
|
|
|
@map_bp.delete('/files/<filename:str>')
|
|
@openapi.description('Delete a PMTiles map file')
|
|
@wrol_mode_check
|
|
async def delete_file(_: Request, filename: str):
|
|
try:
|
|
deleted = lib.delete_pmtiles_file(filename)
|
|
except ValueError as e:
|
|
return response.json({'error': str(e)}, HTTPStatus.BAD_REQUEST)
|
|
|
|
if deleted:
|
|
return response.empty()
|
|
return response.json({'error': 'File not found'}, HTTPStatus.NOT_FOUND)
|
|
|
|
|
|
@map_bp.get('/subscribe')
|
|
@openapi.description('Get map catalog and current subscriptions')
|
|
def get_subscriptions(request: Request):
|
|
catalog = lib.get_map_catalog()
|
|
subscriptions = lib.get_map_subscriptions(request.ctx.session)
|
|
return json_response(dict(catalog=catalog, subscriptions=subscriptions), HTTPStatus.OK)
|
|
|
|
|
|
@map_bp.post('/subscribe')
|
|
@openapi.description('Subscribe to a map region for automatic downloads')
|
|
@validate(schema.MapSubscribeRequest)
|
|
@wrol_mode_check
|
|
async def post_subscribe(request: Request, body: schema.MapSubscribeRequest):
|
|
try:
|
|
await lib.subscribe(request.ctx.session, body.name, body.region)
|
|
except ValueError as e:
|
|
return response.json({'error': str(e)}, HTTPStatus.BAD_REQUEST)
|
|
|
|
Events.send_created(f'Map subscription created for {body.name}')
|
|
return response.empty(HTTPStatus.CREATED)
|
|
|
|
|
|
@map_bp.delete('/subscribe/<region:str>')
|
|
@openapi.description('Unsubscribe from a map region')
|
|
@wrol_mode_check
|
|
async def delete_subscription(request: Request, region: str):
|
|
try:
|
|
await lib.unsubscribe(request.ctx.session, region)
|
|
except ValueError as e:
|
|
return response.json({'error': str(e)}, HTTPStatus.NOT_FOUND)
|
|
|
|
return response.empty(HTTPStatus.NO_CONTENT)
|
|
|
|
|
|
@map_bp.get('/pins')
|
|
@openapi.description('Get all map pins')
|
|
def get_pins(_: Request):
|
|
pins = get_map_pins_config().pins
|
|
return json_response(dict(pins=pins), HTTPStatus.OK)
|
|
|
|
|
|
@map_bp.post('/pins')
|
|
@openapi.description('Add a map pin')
|
|
@validate(schema.MapPinRequest)
|
|
async def add_pin(_: Request, body: schema.MapPinRequest):
|
|
pin = get_map_pins_config().add_pin(body.lat, body.lon, body.label, body.color)
|
|
return json_response(dict(pin=pin), HTTPStatus.CREATED)
|
|
|
|
|
|
@map_bp.delete('/pins/<pin_id:int>')
|
|
@openapi.description('Delete a map pin by ID')
|
|
async def delete_pin(_: Request, pin_id: int):
|
|
if get_map_pins_config().delete_pin(pin_id):
|
|
return response.empty(HTTPStatus.NO_CONTENT)
|
|
return response.json({'error': 'Pin not found'}, HTTPStatus.NOT_FOUND)
|
|
|
|
|
|
@map_bp.put('/pins/<pin_id:int>')
|
|
@openapi.description('Update a map pin by ID')
|
|
@validate(schema.MapPinUpdateRequest)
|
|
async def update_pin(_: Request, pin_id: int, body: schema.MapPinUpdateRequest):
|
|
if get_map_pins_config().update_pin(pin_id, body.label, body.color):
|
|
return response.empty(HTTPStatus.NO_CONTENT)
|
|
return response.json({'error': 'Pin not found'}, HTTPStatus.NOT_FOUND)
|