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); + } + }); + } + }); + } + };