From 2d3ca459ea911d989ad6e4732c87e1272c28181b Mon Sep 17 00:00:00 2001 From: superbrom Date: Sat, 29 Nov 2025 20:40:12 -0500 Subject: [PATCH] Geo query polygons (#56) * allow declustering, update council page (#48) * option to disable clustering * update wins, add videos, clean up council page * improve grouping toggle * some cleanup * add polygon from geo query * use leaflet geo json type * store geo shape in url * improve malformed url handling. update nominatim client request * update readme for java version * use the query text in the url instead of encoded json * fix url persistence, toggle boundaries * style changes * update prefs on new search --------- Co-authored-by: Will Freeman --- README.md | 4 + .../main/scala/services/NominatimClient.scala | 2 +- webapp/src/components/LeafletMap.vue | 100 +++++++++++++++--- webapp/src/views/Map.vue | 66 ++++++++++-- 4 files changed, 143 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index dcd69b0..2fd39e8 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,10 @@ See photos of common ALPRs and learn about their capabilities. ### Running Backend +#### Prerequisites +* JDK 11 +* SBT + 1. `cd shotgun` 2. `sbt run` diff --git a/shotgun/src/main/scala/services/NominatimClient.scala b/shotgun/src/main/scala/services/NominatimClient.scala index 48ecf99..2ec6fcf 100644 --- a/shotgun/src/main/scala/services/NominatimClient.scala +++ b/shotgun/src/main/scala/services/NominatimClient.scala @@ -31,7 +31,7 @@ class NominatimClient(implicit val system: ActorSystem, implicit val executionCo case _ => println(s"Cache miss for $query") val request = HttpRequest( - uri = s"$baseUrl?q=$query&format=json", + uri = s"$baseUrl?q=$query&polygon_geojson=1&format=json", headers = List(headers.`User-Agent`("DeFlock/1.0")) ) diff --git a/webapp/src/components/LeafletMap.vue b/webapp/src/components/LeafletMap.vue index 2730ed8..cf3c4c9 100644 --- a/webapp/src/components/LeafletMap.vue +++ b/webapp/src/components/LeafletMap.vue @@ -8,23 +8,47 @@
- - - -
- mdi-chart-bubble - Grouping - -
-
-
+ +
+ + + +
+ + mdi-chart-bubble + Grouping + + +
+
+
+ + + + +
+ + mdi-map-outline + City Boundaries + + +
+
+
+
@@ -84,6 +108,9 @@ const clusteringEnabled = ref(true); const currentZoom = ref(0); const zoomWarningDismissed = ref(false); +// City Boundaries Control +const cityBoundariesVisible = ref(true); + // Computed property to determine if clustering should be active based on zoom and user preference const shouldCluster = computed(() => { // Force clustering ON when zoomed out (below zoom 12) regardless of user preference @@ -112,6 +139,10 @@ const props = defineProps({ type: Array as PropType, default: () => [], }, + geojson: { + type : Object as PropType, + default: null, + }, currentLocation: { type: Object as PropType<[number, number] | null>, default: null, @@ -316,6 +347,10 @@ function initializeMap() { map.addLayer(clusterLayer); registerMapEvents(); + if (props.geojson) { + updateGeoJson(props.geojson); + } + if (props.alprs.length) { updateMarkers(props.alprs); } else { @@ -347,6 +382,27 @@ function updateMarkers(newAlprs: ALPR[]): void { clusterLayer.addLayer(circlesLayer); } +function updateGeoJson(newGeoJson: GeoJSON.GeoJsonObject | null): void { + map.eachLayer((layer) => { + if (layer instanceof L.GeoJSON) { + map.removeLayer(layer); + } + }); + + if (newGeoJson && cityBoundariesVisible.value) { + const geoJsonLayer = L.geoJSON(newGeoJson, { + style: { + color: '#3388ff', + weight: 2, + opacity: 1, + fillOpacity: 0.2, + }, + interactive: false, // Make unclickable + }); + geoJsonLayer.addTo(map); + } +} + function updateCurrentLocation(): void { currentLocationLayer.clearLayers(); @@ -436,6 +492,16 @@ onMounted(() => { updateMarkers(newAlprs); }, { deep: true }); + watch(() => props.geojson, (newGeoJson) => { + updateGeoJson(newGeoJson); + cityBoundariesVisible.value = true; + }, { deep: true }); + + // Watch for city boundaries visibility changes + watch(() => cityBoundariesVisible.value, () => { + updateGeoJson(props.geojson); + }); + watch(() => props.currentLocation, () => { updateCurrentLocation(); }); diff --git a/webapp/src/views/Map.vue b/webapp/src/views/Map.vue index aa5f3db..80b13e3 100644 --- a/webapp/src/views/Map.vue +++ b/webapp/src/views/Map.vue @@ -8,6 +8,7 @@ :current-location="currentLocation" @update:bounds="updateBounds" :alprs + :geojson >