chore: backup session 2026-06-01b — JupyterHub fix, Hermes Dashboard, OR mbtiles, Trino node.properties

Summary of changes:
- JupyterHub: fix DB path (absolute), Dockerfile cleanup, SimpleLocalProcessSpawner
- JupyterHub: user eric created as admin
- Hermes Dashboard WebUI + TUI chat service (systemd, localhost:9119, auto-boot)
- OR mbtiles: generated Martinique PNG tiles (5690 tiles, 10.9MB) — needs PBF for OR
- OR mbtiles: restored original PBF with corrected metadata (world bounds, Martinique center)
- OR mapsettings: verified center=[-61,14.5], bounds=Martinique, minZoom=0
- Trino: added node.properties (node.environment=production) — needs restart
- TODO.md: updated with current state
- session_resume_consolide.md: created (per-session summary)
This commit is contained in:
Eric FELIXINE
2026-05-30 08:14:47 -04:00
parent 008f1679ce
commit acdf250470
7 changed files with 248 additions and 46 deletions

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Generate mbtiles for Martinique by downloading OSM raster tiles.
Usage: python3 gen_mbtiles_martinique.py
Output: /tmp/mapdata_martinique.mbtiles
"""
import sqlite3, os, math, time, gzip, io, struct, sys
import urllib.request, urllib.error
# Martinique bounds (with buffer)
MIN_LON, MIN_LAT = -61.7, 13.9
MAX_LON, MAX_LAT = -60.3, 15.1
MIN_Z, MAX_Z = 0, 14
# Number of tiles per zoom level (Marti◆ique is small)
# zoom 0: 1, zoom 1: 1, zoom 2: 1, zoom 3: 1, zoom 4: 1
# zoom 5: 1, zoom 6: 2, zoom 7: 4, zoom 8: 6, zoom 9: 12
# zoom 10: 24, zoom 11: 48, zoom 12: 96, zoom 13: 192, zoom 14: 384
TMP_DIR = "/tmp/osm_tiles"
OUTPUT = "/tmp/mapdata_martinique.mbtiles"
OSM_URL = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
DELAY = 0.15 # OSM usage policy: max 2req/s
UA = "SmartCityMartinique/1.0 (eric@digitribe.fr)"
os.makedirs(TMP_DIR, exist_ok=True)
def deg2tile(lat, lon, z):
n = 1 << z
x = int((lon + 180.0) / 360.0 * n)
y = int((1.0 - math.asinh(math.tan(math.radians(lat))) / math.pi) / 2.0 * n)
return max(0, min(n-1, x)), max(0, min(n-1, y))
def tile_exists_in_mbtiles(c, z, x, y_tms):
c.execute("SELECT 1 FROM map WHERE zoom_level=? AND tile_column=? AND tile_row=?", (z, x, y_tms))
return c.fetchone() is not None
def download_tile(z, x, y):
"""Download raster tile from OSM. Returns PNG bytes or None."""
url = OSM_URL.format(z=z, x=x, y=y)
try:
req = urllib.request.Request(url, headers={"User-Agent": UA})
resp = urllib.request.urlopen(req, timeout=10)
data = resp.read()
if len(data) > 100:
return data
except urllib.error.HTTPError as e:
if e.code == 404:
return None
print(f" HTTP {e.code} for {z}/{x}/{y}")
except Exception as e:
print(f" Error {z}/{x}/{y}: {e}")
return None
def create_mbtiles():
if os.path.exists(OUTPUT):
os.remove(OUTPUT)
conn = sqlite3.connect(OUTPUT)
c = conn.cursor()
c.execute("CREATE TABLE map (zoom_level INTEGER, tile_column INTEGER, tile_row INTEGER, tile_id TEXT)")
c.execute("CREATE TABLE images (tile_data BLOB, tile_id TEXT)")
c.execute("CREATE TABLE metadata (name TEXT PRIMARY KEY, value TEXT)")
meta = [
("name", "Martinique"),
("type", "overlay"),
("version", "1.0"),
("description", "OSM raster tiles for Martinique"),
("format", "png"),
("tilejson", "2.1.0"),
("scheme", "tms"),
("minzoom", str(MIN_Z)),
("maxzoom", str(MAX_Z)),
("bounds", f"{MIN_LON},{MIN_LAT},{MAX_LON},{MAX_LAT}"),
("center", "-61.0,14.5,10"),
("attribution", "© OpenStreetMap contributors"),
]
c.executemany("INSERT INTO metadata VALUES (?, ?)", meta)
return conn, c
def main():
conn, c = create_mbtiles()
total = 0
downloaded = 0
skipped = 0
start = time.time()
for z in range(MIN_Z, MAX_Z + 1):
x1, y1 = deg2tile(MIN_LAT, MIN_LON, z)
x2, y2 = deg2tile(MAX_LAT, MAX_LON, z)
xm, xM = min(x1, x2)-1, max(x1, x2)+1
ym, yM = min(y1, y2)-1, max(y1, y2)+1
xm = max(0, xm)
xM = min((1 << z) - 1, xM)
ym = max(0, ym)
yM = min((1 << z) - 1, yM)
z_downloaded = 0
for x in range(xm, xM + 1):
for y in range(ym, yM + 1):
total += 1
tms_y = (1 << z) - 1 - y
png = download_tile(z, x, y)
if png:
tid = f"osm_{z}_{x}_{tms_y}"
c.execute("INSERT INTO images VALUES (?, ?)", (sqlite3.Binary(png), tid))
c.execute("INSERT INTO map VALUES (?, ?, ?, ?)", (z, x, tms_y, tid))
downloaded += 1
z_downloaded += 1
else:
skipped += 1
time.sleep(DELAY)
conn.commit()
print(f"Zoom {z}: {z_downloaded} tiles (total: {downloaded})", flush=True)
c.execute("CREATE INDEX idx_map ON map (zoom_level, tile_column, tile_row)")
conn.commit()
size_mb = os.path.getsize(OUTPUT) / (1024*1024)
elapsed = time.time() - start
print(f"\nDone! {downloaded}/{total} tiles downloaded, {skipped} empty. {size_mb:.1f} MB in {elapsed:.0f}s")
conn.close()
if __name__ == "__main__":
main()

View File

@@ -12,7 +12,7 @@ from datetime import datetime
# Configuration
CRITICAL_CONTAINERS = [
"openremote_manager_1", "openremote_keycloak_1", "smart-city-simulator",
"openremote-manager", "openremote-keycloak", "smart-city-simulator",
"emqx_emqx_1", "mainfluxlabs-broker", "stellio-api-gateway",
"smart-city-influxdb", "smart-city-grafana", "traefik",
"smart-city-prometheus-brokers"