From 5af583c67539e381ccc00249451646ae50fabe34 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:51:49 +0200 Subject: [PATCH 1/6] refactor: create_gps_markers Also adds support for data fields containing coordinates. --- frappe/geo/utils.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/frappe/geo/utils.py b/frappe/geo/utils.py index 9b4a42179c..dd46af2ba2 100644 --- a/frappe/geo/utils.py +++ b/frappe/geo/utils.py @@ -2,6 +2,7 @@ # License: MIT. See LICENSE import frappe +from frappe.utils.data import flt @frappe.whitelist() @@ -46,14 +47,19 @@ def merge_location_features_in_one(coords): def create_gps_markers(coords): """Build Marker based on latitude and longitude.""" - geojson_dict = [] - for i in coords: - node = {"type": "Feature", "properties": {}, "geometry": {"type": "Point", "coordinates": None}} - node["properties"]["name"] = i.name - node["geometry"]["coordinates"] = [i.longitude, i.latitude] # geojson needs it reverse! - geojson_dict.append(node.copy()) - - return geojson_dict + return [ + { + "type": "Feature", + "properties": { + "name": i.name, + }, + "geometry": { + "type": "Point", + "coordinates": [flt(i.longitude), flt(i.latitude)], # geojson needs it reverse! + }, + } + for i in coords + ] def return_location(doctype, filters_sql): From 9bd06d79d31db96f3681f09763e4f41da27d9793 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:02:03 +0200 Subject: [PATCH 2/6] feat: show link to form --- frappe/public/js/frappe/views/map/map_view.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index b86faf3edc..aafed1b1ee 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -45,7 +45,11 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { L.control.scale().addTo(this.map); if (this.coords.features && this.coords.features.length) { this.coords.features.forEach((coords) => - L.geoJSON(coords).bindPopup(coords.properties.name).addTo(this.map) + L.geoJSON(coords) + .bindPopup( + frappe.utils.get_form_link(this.doctype, coords.properties.name, true) + ) + .addTo(this.map) ); let lastCoords = this.coords.features[0].geometry.coordinates.reverse(); this.map.panTo(lastCoords, 8); From d014f73632c2c10bc82a32f3c7c624f30dd4a02c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:12:37 +0200 Subject: [PATCH 3/6] refactor: remove redundant libs from map view These are already included in desk bundle. --- frappe/public/js/frappe/views/map/map_view.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index aafed1b1ee..aa1fe2c344 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -85,11 +85,4 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { this.coords = r.message; }); } - - get required_libs() { - return [ - "assets/frappe/js/lib/leaflet/leaflet.css", - "assets/frappe/js/lib/leaflet/leaflet.js", - ]; - } }; From 87a1538cef50b8e223b53207eb27072c6bfba1b0 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:38:02 +0200 Subject: [PATCH 4/6] fix(map view): hide sort selector --- frappe/public/js/frappe/views/map/map_view.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index aa1fe2c344..46741ca470 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -10,6 +10,7 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { } setup_defaults() { + this.hide_sort_selector = true; super.setup_defaults(); this.page_title = __("{0} Map", [this.page_title]); } From ffd04624cbf5471fb0a7d17241587ba5b0a01cb4 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:56:35 +0200 Subject: [PATCH 5/6] fix(map view): separate map creation and data rendering Map and controls are created during `setup_view`; data fetching and rendering happnes in `render`. This way we don't have to create a new map every time filters change or the view is refreshed. --- frappe/public/js/frappe/views/map/map_view.js | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index 46741ca470..72a5a365e7 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -15,22 +15,8 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { this.page_title = __("{0} Map", [this.page_title]); } - setup_view() {} - - on_filter_change() { - this.get_coords(); - } - - render() { - this.get_coords().then(() => { - this.render_map_view(); - }); - this.$paging_area.find(".level-left").append("
"); - } - - render_map_view() { + setup_view() { this.map_id = frappe.dom.get_unique_id(); - this.$result.html(`
`); L.Icon.Default.imagePath = frappe.utils.map_defaults.image_path; @@ -44,16 +30,35 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { ); L.control.scale().addTo(this.map); + } + + render() { + this.get_coords().then(() => { + this.render_map_data(); + }); + this.$paging_area.find(".level-left").append("
"); + } + + render_map_data() { + // Clear existing markers + if (this.markerLayer) { + this.map.removeLayer(this.markerLayer); + } + if (this.coords.features && this.coords.features.length) { - this.coords.features.forEach((coords) => - L.geoJSON(coords) - .bindPopup( - frappe.utils.get_form_link(this.doctype, coords.properties.name, true) - ) - .addTo(this.map) - ); - let lastCoords = this.coords.features[0].geometry.coordinates.reverse(); - this.map.panTo(lastCoords, 8); + this.markerLayer = L.featureGroup(); + + this.coords.features.forEach((coords) => { + const marker = L.geoJSON(coords).bindPopup( + frappe.utils.get_form_link(this.doctype, coords.properties.name, true) + ); + this.markerLayer.addLayer(marker); + }); + + this.markerLayer.addTo(this.map); + + // Fit bounds to show all markers + this.map.fitBounds(this.markerLayer.getBounds()); } } From 36d900adebe34c2b0eaf8de94996ebe1abbacd48 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:57:38 +0200 Subject: [PATCH 6/6] feat(map view): add locate control Let's users view entries in their proximity. --- frappe/public/js/frappe/views/map/map_view.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index 72a5a365e7..b253896dc4 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -29,6 +29,7 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { this.map ); + this.bind_leaflet_locate_control(); L.control.scale().addTo(this.map); } @@ -91,4 +92,10 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { this.coords = r.message; }); } + + bind_leaflet_locate_control() { + // To request location update and set location, sets current geolocation on load + this.locate_control = L.control.locate({ position: "topright" }); + this.locate_control.addTo(this.map); + } };