From ae264e252373071b8e3d2dfb2c3bdd0d8d642d73 Mon Sep 17 00:00:00 2001 From: muhammedirfan Desk Date: Fri, 23 May 2025 17:45:34 +0530 Subject: [PATCH] feat(geolocation): add satellite view to input field Added sattellite view on geolocation input field. Added labels option and tarrain lines option in sattellite view. move tail url and options in utils frappe.provide --- .../js/frappe/form/controls/geolocation.js | 76 ++++++++++++++- frappe/public/js/frappe/utils/utils.js | 93 +++++++++++-------- frappe/public/js/frappe/views/map/map_view.js | 79 +++++++++++++++- 3 files changed, 206 insertions(+), 42 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/geolocation.js b/frappe/public/js/frappe/form/controls/geolocation.js index 827a9be315..45d8046adf 100644 --- a/frappe/public/js/frappe/form/controls/geolocation.js +++ b/frappe/public/js/frappe/form/controls/geolocation.js @@ -47,6 +47,7 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f if (!this.map) { this.customize_draw_controls(); this.bind_leaflet_map(); + this.bind_leaflet_layers_control(); } if (this.disabled) { this.map.dragging.disable(); @@ -123,7 +124,7 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f * @param {Object} feature - The leaflet object representing a geojson feature. * @param {Object} layer - The leaflet layer object. */ - on_each_feature(feature, layer) {} + on_each_feature(feature, layer) { } customize_draw_controls() { const circleToGeoJSON = L.Circle.prototype.toGeoJSON; @@ -156,13 +157,47 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f this.map = L.map(this.map_id); this.map.setView(frappe.utils.map_defaults.center, frappe.utils.map_defaults.zoom); - L.tileLayer(frappe.utils.map_defaults.tiles, frappe.utils.map_defaults.options).addTo( - this.map + + this.streetLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.defaultTile.url, + frappe.utils.map_defaults.tiles.defaultTile.options + ); + this.satelliteLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.sattelliteTail.url, + frappe.utils.map_defaults.tiles.sattelliteTail.options + ); + this.labelsLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.labelsTail.url, + frappe.utils.map_defaults.tiles.labelsTail.options + ); + this.terrainLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.terrainLinesTail.url, + frappe.utils.map_defaults.tiles.terrainLinesTail.options ); + this.streetLayer.addTo(this.map) + this.editableLayers = new L.FeatureGroup(); } + bind_leaflet_layers_control() { + // Add layers control for switching between map types + // Define base and overlay layers as properties of the class instance for access in other methods + + const baseLayers = { + "Default": this.streetLayer, + "Satellite": this.satelliteLayer + }; + const overlays = { + "Labels": this.labelsLayer, + "Terrain": this.terrainLayer + }; + + L.control.layers(baseLayers, overlays).addTo(this.map); + this.display_leaflet_overlays_control('none') + + } + bind_leaflet_locate_control() { // To request location update and set location, sets current geolocation on load this.locate_control = L.control.locate({ position: "topright" }); @@ -214,6 +249,17 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f }); } + display_leaflet_overlays_control(display = '') { + const layerControlContainer = document.querySelector('.leaflet-control-layers-overlays'); + const separator = document.querySelector('.leaflet-control-layers-separator'); + if (layerControlContainer) { + layerControlContainer.style.display = display; + } + if (separator) { + separator.style.display = display; + } + } + bind_leaflet_event_listeners() { this.bound_event_listeners = true; this.map.on("draw:created", (e) => { @@ -231,6 +277,30 @@ frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.f this.editableLayers.removeLayer(layer); this.set_value(JSON.stringify(this.editableLayers.toGeoJSON())); }); + + // Remove overlays and overlays options when selecting the default view + this.map.on("baselayerchange", (e) => { + + if (e.name === "Satellite") { + // Show overlays options and separator only in Satellite view + this.display_leaflet_overlays_control() + } else { + // Hide overlays options and separator in other views + this.display_leaflet_overlays_control('none') + // Remove all overlays + Object.values(this.map._layers).forEach(layer => { + if ( + layer instanceof L.TileLayer && + ( + layer._url === frappe.utils.map_defaults.tiles.labelsTail.url || + layer._url === frappe.utils.map_defaults.tiles.terrainLinesTail.url + ) + ) { + this.map.removeLayer(layer); + } + }); + } + }); } add_non_group_layers(source_layer, target_group) { diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index f4df7a66d0..bfeac24004 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -713,14 +713,14 @@ Object.assign(frappe.utils, { var objPattern = new RegExp( // Delimiters. "(\\" + - strDelimiter + - "|\\r?\\n|\\r|^)" + - // Quoted fields. - '(?:"([^"]*(?:""[^"]*)*)"|' + - // Standard fields. - '([^"\\' + - strDelimiter + - "\\r\\n]*))", + strDelimiter + + "|\\r?\\n|\\r|^)" + + // Quoted fields. + '(?:"([^"]*(?:""[^"]*)*)"|' + + // Standard fields. + '([^"\\' + + strDelimiter + + "\\r\\n]*))", "gi" ); @@ -1185,10 +1185,31 @@ Object.assign(frappe.utils, { map_defaults: { center: [19.08, 72.8961], zoom: 13, - tiles: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - options: { - attribution: - '© OpenStreetMap contributors', + tiles: { + defaultTile: { + url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + options: { + attribution: '© OpenStreetMap contributors', + } + }, + sattelliteTail: { + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + options: { + attribution: '© Esri © OpenStreetMap Contributors', + } + }, + labelsTail: { + url: 'https://tiles.stadiamaps.com/tiles/stamen_toner_labels/{z}/{x}/{y}{r}.png', + options: { + attribution: '© Stadia Maps © Stamen Design © OpenMapTiles', + } + }, + terrainLinesTail: { + url: 'https://tiles.stadiamaps.com/tiles/stamen_terrain_lines/{z}/{x}/{y}{r}.png', + options: { + attribution: '© Stadia Maps © Stamen Design © OpenMapTiles', + } + } }, image_path: "/assets/frappe/images/leaflet/", }, @@ -1203,13 +1224,12 @@ Object.assign(frappe.utils, { } else { size_class = `icon-${size}`; } - return `
-
${ - summary.value - }
+
${summary.value + }
`); } let df = { fieldtype: summary.datatype }; @@ -1400,8 +1419,8 @@ Object.assign(frappe.utils, { let color = summary.indicator ? summary.indicator.toLowerCase() : summary.color - ? summary.color.toLowerCase() - : ""; + ? summary.color.toLowerCase() + : ""; return $(`
${__(summary.label)} @@ -1413,17 +1432,17 @@ Object.assign(frappe.utils, { let w = window.open( frappe.urllib.get_full_url( "/printview?doctype=" + - encodeURIComponent(doctype) + - "&name=" + - encodeURIComponent(docname) + - "&trigger_print=1" + - "&format=" + - encodeURIComponent(print_format) + - "&no_letterhead=" + - (letterhead ? "0" : "1") + - "&letterhead=" + - encodeURIComponent(letterhead) + - (lang_code ? "&_lang=" + lang_code : "") + encodeURIComponent(doctype) + + "&name=" + + encodeURIComponent(docname) + + "&trigger_print=1" + + "&format=" + + encodeURIComponent(print_format) + + "&no_letterhead=" + + (letterhead ? "0" : "1") + + "&letterhead=" + + encodeURIComponent(letterhead) + + (lang_code ? "&_lang=" + lang_code : "") ) ); @@ -1779,8 +1798,8 @@ Object.assign(frappe.utils, { frappe.msgprint( __("Tracking URL generated and copied to clipboard") + - ":
" + - `${url.bold()}`, + ":
" + + `${url.bold()}`, __("Here's your tracking URL") ); }, diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js index 4e9be64794..6be2d320a4 100644 --- a/frappe/public/js/frappe/views/map/map_view.js +++ b/frappe/public/js/frappe/views/map/map_view.js @@ -43,12 +43,31 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { frappe.utils.map_defaults.zoom ); - L.tileLayer(frappe.utils.map_defaults.tiles, frappe.utils.map_defaults.options).addTo( - this.map + this.streetLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.defaultTile.url, + frappe.utils.map_defaults.tiles.defaultTile.options + ); + this.satelliteLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.sattelliteTail.url, + frappe.utils.map_defaults.tiles.sattelliteTail.options + ); + this.labelsLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.labelsTail.url, + frappe.utils.map_defaults.tiles.labelsTail.options + ); + this.terrainLayer = L.tileLayer( + frappe.utils.map_defaults.tiles.terrainLinesTail.url, + frappe.utils.map_defaults.tiles.terrainLinesTail.options ); + this.streetLayer.addTo(this.map) + + this.bind_leaflet_layers_control() this.bind_leaflet_locate_control(); L.control.scale().addTo(this.map); + if (!this.bound_event_listeners) { + this.bind_leaflet_event_listeners(); + } } render() { @@ -140,9 +159,65 @@ frappe.views.MapView = class MapView extends frappe.views.ListView { } } + bind_leaflet_layers_control() { + // Add layers control for switching between map types + // Define base and overlay layers as properties of the class instance for access in other methods + + const baseLayers = { + "Default": this.streetLayer, + "Satellite": this.satelliteLayer + }; + const overlays = { + "Labels": this.labelsLayer, + "Terrain": this.terrainLayer + }; + + L.control.layers(baseLayers, overlays).addTo(this.map); + this.display_leaflet_overlays_control('none') + + } + 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); } + + display_leaflet_overlays_control(display = '') { + const layerControlContainer = document.querySelector('.leaflet-control-layers-overlays'); + const separator = document.querySelector('.leaflet-control-layers-separator'); + if (layerControlContainer) { + layerControlContainer.style.display = display; + } + if (separator) { + separator.style.display = display; + } + } + + bind_leaflet_event_listeners() { + this.bound_event_listeners = true; + // Remove overlays and overlays options when selecting the default view + this.map.on("baselayerchange", (e) => { + if (e.name === "Satellite") { + // Show overlays options and separator only in Satellite view + this.display_leaflet_overlays_control() + } else { + // Hide overlays options and separator in other views + this.display_leaflet_overlays_control('none') + // Remove all overlays + Object.values(this.map._layers).forEach(layer => { + if ( + layer instanceof L.TileLayer && + ( + layer._url === frappe.utils.map_defaults.tiles.labelsTail.url || + layer._url === frappe.utils.map_defaults.tiles.terrainLinesTail.url + ) + ) { + this.map.removeLayer(layer); + } + }); + } + }); + } + };