Skip to content

Commit ef5d9e4

Browse files
add location info
1 parent 66869c3 commit ef5d9e4

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed

app/callbacks/display_callbacks.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import logging_config
1515
import pandas as pd
1616
from botocore.exceptions import ClientError # type: ignore
17-
from dash import Input, Output, State, ctx
17+
from dash import Input, Output, State, ctx, html
1818
from dash.dependencies import ALL
1919
from dash.exceptions import PreventUpdate
2020
from dateutil.relativedelta import relativedelta # type: ignore
@@ -27,6 +27,7 @@
2727
build_vision_polygon,
2828
create_sequence_list,
2929
filter_bboxes_dict,
30+
get_location_info,
3031
prepare_archive,
3132
)
3233

@@ -568,6 +569,64 @@ def update_fire_markers(smoke_location_str, api_sequences):
568569
return [pos, 1, coords_str, pos, 1, coords_str]
569570

570571

572+
@app.callback(
573+
Output("alert-location-parcel-info", "children"),
574+
Output("alert-location-parcel-info", "style"),
575+
Input("fire-location-marker", "opacity"),
576+
State("smoke-location-copy-content", "children"),
577+
State("language", "data"),
578+
)
579+
def update_location_and_parcel_info(opacity, smoke_location, lang):
580+
if opacity != 1 or not smoke_location:
581+
return dash.no_update, {"display": "none"} # hide but don't change content
582+
583+
try:
584+
lat, lon = map(float, smoke_location.split(","))
585+
except Exception:
586+
return [], {"display": "none"} # Invalid format, clear everything
587+
588+
result = get_location_info(lat, lon)
589+
city = result.get("city")
590+
parcel = result.get("parcel")
591+
592+
children = []
593+
594+
if city:
595+
children.append(
596+
html.Div(
597+
[
598+
html.Span(
599+
f"{translate('localisation', lang)} : ", style={"fontWeight": "bold", "marginRight": "4px"}
600+
),
601+
html.Span(city),
602+
],
603+
style={"margin": "0", "padding": "0", "lineHeight": "1.2"},
604+
)
605+
)
606+
607+
if parcel:
608+
label = parcel.get("llib_frt", "")
609+
code = parcel.get("ccod_cact", "")
610+
if label or code:
611+
parcel_label = f"{label} (ID: {code})" if code else label
612+
children.append(
613+
html.Div(
614+
[
615+
html.Span(
616+
f"{translate('onf_parcel', lang)} : ", style={"fontWeight": "bold", "marginRight": "4px"}
617+
),
618+
html.Span(parcel_label),
619+
],
620+
style={"margin": "0", "padding": "0", "lineHeight": "1.2"},
621+
)
622+
)
623+
624+
if not children:
625+
return [], {"display": "none"}
626+
627+
return children, {"display": "block", "marginTop": "8px", "padding": "6px 0"}
628+
629+
571630
@app.callback(
572631
[Output("confirmation-modal", "style"), Output("to_acknowledge", "data")],
573632
[

app/pages/homepage.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def homepage_layout(user_token, api_cameras, lang="fr", descending_order=True):
409409
children=[
410410
html.Span(
411411
id="smoke-location-copy-content",
412-
children="48.5595, 2.5468", # Or dynamically updated
412+
children="",
413413
style={"marginRight": "6px", "whiteSpace": "nowrap"},
414414
),
415415
dcc.Clipboard(
@@ -427,6 +427,15 @@ def homepage_layout(user_token, api_cameras, lang="fr", descending_order=True):
427427
),
428428
],
429429
),
430+
html.Div(
431+
id="alert-location-parcel-info",
432+
style={
433+
"display": "block",
434+
"margin": "0",
435+
"padding": "0",
436+
"lineHeight": "1",
437+
},
438+
),
430439
],
431440
),
432441
],

app/translations.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"unmatch-sequence": "Dissocier la séquence",
5050
"detections_to_fetch": "Afficher :",
5151
"fetch_order": "Afficher les plus récentes",
52+
"localisation": "Localisation",
53+
"onf_parcel": "Parcelle ONF",
5254
# live stream
5355
"move_speed": "Vitesse de déplacement",
5456
"zoom_level": "Niveau de zoom",
@@ -125,6 +127,8 @@
125127
"fetch_order": "Orden de recuperación",
126128
"detections_to_fetch": "Mostrar:",
127129
"fetch_order": "Mostrar las más recientes",
130+
"localisation": "Localización",
131+
"onf_parcel": "Parcela ONF",
128132
# live stream
129133
"move_speed": "Velocidad de movimiento",
130134
"zoom_level": "Nivel de zoom",
@@ -218,6 +222,8 @@
218222
"live_stream_warning": "⚠️ Verification in progress: detection inactive!",
219223
"loading_modal_title": "Stream is loading...",
220224
"loading_modal_body": "Please wait.",
225+
"localisation": "Location",
226+
"onf_parcel": "ONF Parcel",
221227
# login
222228
"username_placeholder": "USERNAME",
223229
"password_placeholder": "PASSWORD",

app/utils/display.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,41 @@ def prepare_archive(sequence_data, api_sequences, folder_name, camera_value):
413413

414414
# Create zip
415415
shutil.make_archive(os.path.join("zips", folder_name), "zip", base_dir)
416+
417+
418+
def get_location_info(lat, lon):
419+
# Step 1: Reverse geocode using Nominatim
420+
nominatim_url = "https://nominatim.openstreetmap.org/reverse"
421+
params = {"lat": lat, "lon": lon, "format": "json", "addressdetails": 1}
422+
headers = {"User-Agent": "YourAppName/1.0"}
423+
response = requests.get(nominatim_url, params=params, headers=headers)
424+
response.raise_for_status()
425+
data = response.json()
426+
427+
address = data.get("address", {})
428+
city = address.get("city") or address.get("town") or address.get("village") or None
429+
country = address.get("country")
430+
country_code = address.get("country_code")
431+
432+
result = {"city": city, "country": country, "country_code": country_code, "parcel": None}
433+
434+
# Step 2: If in France, check for forest parcel
435+
if country_code == "fr":
436+
parcel_url = "https://services1.arcgis.com/Y4HgaQpzkE7kenlE/arcgis/rest/services/Parcelles_foresti%C3%A8res_publiques_de_France_m%C3%A9tropolitaine/FeatureServer/11/query"
437+
parcel_params = {
438+
"geometry": f"{lon},{lat}",
439+
"geometryType": "esriGeometryPoint",
440+
"inSR": 4326,
441+
"spatialRel": "esriSpatialRelIntersects",
442+
"outFields": "*",
443+
"returnGeometry": "false",
444+
"f": "json",
445+
}
446+
parcel_response = requests.get(parcel_url, params=parcel_params)
447+
parcel_response.raise_for_status()
448+
parcel_data = parcel_response.json()
449+
450+
if parcel_data.get("features"):
451+
result["parcel"] = parcel_data["features"][0]["attributes"]
452+
453+
return result

0 commit comments

Comments
 (0)