Session 2026-05-17: GeoServer, PostGIS dédié, MapStore, ChirpStack
- GeoServer: workspace Digitribe + Data Store PostGIS dédié - PostGIS dédié: conteneur postgis-smartcity (PostGIS 3.4) - Couche sensors: 55 capteurs IoT importés depuis OpenRemote - MapStore: GeoServer WMS ajouté au CORS - ChirpStack: credentials réinitialisés (admin/admin1234) - BunkerM: DNS corrigé (underscores → hyphens) - Ditto: config MongoDB et auth devops - Documentation: session_resume + TODO.md
This commit is contained in:
137
geojson-proxy/geojson_proxy.py
Normal file
137
geojson-proxy/geojson_proxy.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GeoJSON proxy service for OpenRemote assets map display."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
OR_URL = os.environ.get("OR_URL", "http://openremote_manager_1:8080")
|
||||
OR_ADMIN_USER = os.environ.get("OR_ADMIN_USER", "admin")
|
||||
OR_ADMIN_PASS = os.environ.get("OR_ADMIN_PASS", "")
|
||||
OR_TOKEN = os.environ.get("OR_TOKEN", "")
|
||||
|
||||
|
||||
def get_token():
|
||||
"""Fetch an OpenRemote access token using admin credentials."""
|
||||
if OR_TOKEN:
|
||||
return OR_TOKEN
|
||||
data = json.dumps({
|
||||
"username": OR_ADMIN_USER,
|
||||
"password": OR_ADMIN_PASS,
|
||||
"grant_type": "password",
|
||||
"client_id": "openremote"
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
f"{OR_URL}/auth/realms/master/protocol/openid-connect/token",
|
||||
data=data,
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST"
|
||||
)
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
body = json.loads(resp.read())
|
||||
return body["access_token"]
|
||||
|
||||
|
||||
def fetch_assets(token):
|
||||
"""Fetch IoT sensor assets from OpenRemote."""
|
||||
url = f"{OR_URL}/api/master/assets?type=IOTSensor"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Accept": "application/json"
|
||||
},
|
||||
method="GET"
|
||||
)
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
return json.loads(resp.read())
|
||||
|
||||
|
||||
def to_geojson(assets):
|
||||
"""Convert OpenRemote assets to a GeoJSON FeatureCollection."""
|
||||
features = []
|
||||
for asset in assets:
|
||||
attrs = asset.get("attributes", {})
|
||||
location = attrs.get("location", {})
|
||||
if not location:
|
||||
continue
|
||||
value = location.get("value")
|
||||
if not value:
|
||||
continue
|
||||
coords = value.get("coordinates")
|
||||
if not coords or len(coords) < 2:
|
||||
continue
|
||||
|
||||
# Build properties from asset attributes
|
||||
properties = {}
|
||||
for attr_name, attr_val in attrs.items():
|
||||
v = attr_val.get("value")
|
||||
if v is not None:
|
||||
properties[attr_name] = v
|
||||
# Ensure key fields are at top level for Mapbox filters
|
||||
properties.setdefault("id", asset.get("id"))
|
||||
properties.setdefault("name", asset.get("name", ""))
|
||||
properties.setdefault("type", asset.get("type", ""))
|
||||
properties.setdefault("sensorType", attrs.get("sensorType", {}).get("value", ""))
|
||||
|
||||
features.append({
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [coords[0], coords[1]]
|
||||
},
|
||||
"properties": properties
|
||||
})
|
||||
|
||||
return {
|
||||
"type": "FeatureCollection",
|
||||
"features": features
|
||||
}
|
||||
|
||||
|
||||
class GeoJSONHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
path = self.path.split("?")[0]
|
||||
if path == "/geojson":
|
||||
try:
|
||||
token = get_token()
|
||||
assets = fetch_assets(token)
|
||||
geojson = to_geojson(assets)
|
||||
body = json.dumps(geojson).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||
self.send_header("Access-Control-Allow-Headers", "*")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
except Exception as e:
|
||||
error_body = json.dumps({"error": str(e)}).encode()
|
||||
self.send_response(500)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Content-Length", str(len(error_body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(error_body)
|
||||
elif path == "/health":
|
||||
body = json.dumps({"status": "ok"}).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
print(f"[geojson-proxy] {args[0]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = HTTPServer(("0.0.0.0", 8080), GeoJSONHandler)
|
||||
print("[geojson-proxy] Listening on 0.0.0.0:8080")
|
||||
server.serve_forever()
|
||||
Reference in New Issue
Block a user