feat(dashboard): console phase 2 - self-hosted basemap, basin stations, markers
- Basemap: src/static/basemap/ping-basin.pmtiles, a Protomaps extract of the Ping basin (zoom 0-12, 15 MB, OpenStreetMap data), drawn by protomaps-leaflet 5.1.0 (unpkg, SRI) in its light/dark style with labels in the page language. It replaces the dimmed OSM raster tiles, whose baked-in labels were the noisiest part of the screen; OSM stays as the fallback. The browser reads the file with range requests (ServeDir answers 206; dev_proxy.py now does too). The map stays within the basin. - Basin stations: a second list next to the RID gauges and a map layer of diamonds, from /api/hii/waterlevel/latest (falling back to /sensors/thaiwater), coloured by ThaiWater's bank-height classes (over the bank = danger, 70-100 % = watch). Gauges RID already lists appear once. The panel shows level (m MSL), % of bank height, distance to and height of the bank top, gauge reading and agency; selection in the URL (#hii-<id>). - custom-markers.json as pins from zoom 12, with the flood zone they are in. Refresh instructions for the basemap in src/static/basemap/README.md.
This commit is contained in:
1 parent
301a629b42
commit
71cc1b0ca7
6 files changed
+408
-34
No files matched your search
+16
-2
@@ -8,6 +8,7 @@ console preview at /static/console.html), everything else the live server."""
|
||||
import http.server
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
@@ -38,7 +39,17 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
)
|
||||
if ctype.startswith("text/"):
|
||||
ctype += "; charset=utf-8"
|
||||
self._send(200, ctype, f.read_bytes())
|
||||
body = f.read_bytes()
|
||||
# Byte ranges, like the server's ServeDir: PMTiles reads the
|
||||
# basemap in pieces
|
||||
m = re.fullmatch(r"bytes=(\d+)-(\d*)", self.headers.get("Range", ""))
|
||||
if m:
|
||||
start = int(m.group(1))
|
||||
end = min(int(m.group(2) or len(body) - 1), len(body) - 1)
|
||||
extra = {"Content-Range": f"bytes {start}-{end}/{len(body)}"}
|
||||
self._send(206, ctype, body[start : end + 1], extra)
|
||||
return
|
||||
self._send(200, ctype, body)
|
||||
return
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
@@ -57,9 +68,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
except urllib.error.HTTPError as e:
|
||||
self._send(e.code, "application/json", e.read())
|
||||
|
||||
def _send(self, code, ctype, body):
|
||||
def _send(self, code, ctype, body, extra=None):
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", ctype)
|
||||
self.send_header("Accept-Ranges", "bytes")
|
||||
for name, value in (extra or {}).items():
|
||||
self.send_header(name, value)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.end_headers()
|
||||
|
||||
Reference in new issue
Block a user