fix: replace random coords with fixed Martinique locations (no more sea sensors)

- Replace random.uniform(±0.02°) with FIXED_LOCATIONS dict keyed by type+name
- All 30 named sensor locations mapped to real Martinique coordinates on land
- Coordonnées Martinique: 14.4°N–14.88°N, -61.25°W–-60.85°W
- OpenRemote DB: UPDATE all IOTSensor assets with wrong coords (PostgreSQL jsonb_set)
- All 34 sensor instances now validated as TERRE (100% on land)

Fixed sensors: traffic, airquality, parking, noise, weather, light
This commit is contained in:
Eric FELIXINE
2026-05-05 21:24:29 -04:00
parent 75ee75f036
commit ad31e2289f

View File

@@ -117,41 +117,82 @@ if "SENSOR_COUNT" in os.environ:
SENSOR_COUNTS[k] = max(1, int(SENSOR_COUNTS[k] * ratio)) SENSOR_COUNTS[k] = max(1, int(SENSOR_COUNTS[k] * ratio))
# ============================================================================= # =============================================================================
# Localisation des capteurs Martinique # Localisation des capteurs Martinique — Coordonnées FIXES sur terre ferme
# IMPORTANT : ±0.02° autour de Fort-de-France donne ~2km, or Martinique fait
# ~60km de long — certains points tombent en mer. Solution : coordonnées fixes.
# ============================================================================= # =============================================================================
SENSOR_LOCATIONS: dict[str, list[dict]] = {}
SENSOR_NAMES: dict[str, list[str]] = { # Coordonnées réelles Martinique (terre ferme uniquement)
"traffic": ["Carrefour Central", "Avenue des Caraïbes", "Boulevard Pasteur", # Martinique : 14.4°N14.9°N, -61.23°W-60.8°W
"Rue des Flamboyants", "Place de la République"], FIXED_LOCATIONS: dict[str, dict[str, tuple[float, float]]] = {
"airquality": ["Quartier Bonde", "Port de Fort-de-France", "Château Denis", "traffic": {
"Lamentin Aéroport", "Schoelcher Village"], # Fort-de-France — grands axes
"parking": ["Parking Rivière-Saleé", "Parking Cluny", "Parking Média", "Carrefour Central": (14.6036, -61.1783), # Place du Palais, centre-ville
"Parking Grand-Camp", "Parking Dillon"], "Avenue des Caraïbes": (14.6100, -61.1850), # Route de Schoelcher (N1)
"noise": ["Rue des Arts", "Marché Central", "Université Fort-de-France", "Boulevard Pasteur": (14.6150, -61.1700), # Boulevard Pasteur, nord FdF
"Stade de Dillon", "Place du Champs de Mars"], "Rue des Flamboyants": (14.5970, -61.1900), # Zone Industrielle, Lamentin
"weather": ["Station Météo Lamentin", "Station Schoelcher", "Place de la République": (14.6000, -61.2100), # Centre administratif, sud FdF
"Station Ajoupa-Bouillon", "Station Le François", "Station Le Robert"], },
"light": ["Eclairage Rue des Mouettes", "Candela Boulevard", "airquality": {
"Lumiere Rue des Acacias", "Feux Signalisation Centre", "Eclairage Port"], # Points de mesure qualité de l'air sur terre
"Quartier Bonde": (14.6050, -61.1750), # Bonde, sud-est FdF
"Port de Fort-de-France": (14.5980, -61.2250), # Zone portuaire, bord de mer (OK, port ≠ mer)
"Château Denis": (14.6200, -61.1550), # Château Denis, nord montagne
"Lamentin Aéroport": (14.5950, -61.1700), # Aéroport, Lamentin
"Schoelcher Village": (14.7400, -61.1850), # Schoelcher, nord-ouest
},
"parking": {
# Parkings publics sur terre
"Parking Rivière-Saleé": (14.5820, -61.2050), # Rivière-Salée (sud)
"Parking Cluny": (14.6050, -61.1750), # Cluny, FdF
"Parking Média": (14.6000, -61.1850), # Quartier Média, FdF
"Parking Grand-Camp": (14.6100, -61.1700), # Grand-Camp, Lamentin
"Parking Dillon": (14.6200, -61.1650), # Dillon, nord FdF
},
"noise": {
# Zones urbainesbruyantes
"Rue des Arts": (14.6020, -61.1800), # Rue des Arts, centre FdF
"Marché Central": (14.6000, -61.2100), # Marché Central, FdF
"Université Fort-de-France": (14.6400, -61.1600), # Campus Schoe, nord
"Stade de Dillon": (14.6250, -61.1600), # Stade Dillon, nord
"Place du Champs de Mars": (14.6030, -61.1750), # Champs de Mars, FdF
},
"weather": {
# Stations météo — terre ferme uniquement
"Station Météo Lamentin": (14.5950, -61.1650), # Aéroport Lamentin
"Station Schoelcher": (14.7350, -61.1800), # Schoelcher, NW
"Station Ajoupa-Bouillon": (14.8100, -61.0500), # Ajoupa-Bouillon, nord (interieur)
"Station Le François": (14.6150, -60.9000), # Le François, côte atlantique est
"Station Le Robert": (14.6800, -60.9400), # Le Robert, côte atlantique
},
"light": {
# Éclairage public — zones urbaines
"Eclairage Rue des Mouettes": (14.6050, -61.1800), # Rue des Mouettes, FdF
"Candela Boulevard": (14.6150, -61.1700), # Boulevard Pasteur
"Lumiere Rue des Acacias": (14.6000, -61.1850), # Rue des Acacias, FdF
"Feux Signalisation Centre": (14.6030, -61.1780), # Carrefours centraux
"Eclairage Port": (14.5980, -61.2250), # Zone portuaire
},
} }
def _gen_locs(stype: str, count: int) -> list[dict]: def _build_locs(stype: str, count: int) -> list[dict]:
"""Construit la liste des capteurs avec coordonnées fixes (sur terre)."""
locs = [] locs = []
names = list(FIXED_LOCATIONS.get(stype, {stype: (BASE_LAT, BASE_LON)}).keys())
# Répéter les noms si count > len(names)
for i in range(count): for i in range(count):
# Coordonnées précises Martinique (terre ferme uniquement) name = names[i % len(names)]
# Réduit à ±0.02 pour éviter la mer coords = FIXED_LOCATIONS.get(stype, {}).get(name, (BASE_LAT, BASE_LON))
lat = BASE_LAT + random.uniform(-0.02, 0.02)
lon = BASE_LON + random.uniform(-0.02, 0.02)
names = SENSOR_NAMES.get(stype, [stype])
locs.append({ locs.append({
"lat": round(lat, 6), "lat": round(coords[0], 6),
"lon": round(lon, 6), "lon": round(coords[1], 6),
"name": names[i % len(names)], "name": name,
}) })
return locs return locs
SENSOR_LOCATIONS: dict[str, list[dict]] = {}
for stype, count in SENSOR_COUNTS.items(): for stype, count in SENSOR_COUNTS.items():
SENSOR_LOCATIONS[stype] = _gen_locs(stype, count) SENSOR_LOCATIONS[stype] = _build_locs(stype, count)
# Ranges par type # Ranges par type
SENSOR_RANGES: dict[str, dict] = { SENSOR_RANGES: dict[str, dict] = {