diff --git a/frappe/public/js/frappe/form/controls/geolocation.js b/frappe/public/js/frappe/form/controls/geolocation.js
index 827a9be315..045863ce42 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();
@@ -156,13 +157,45 @@ 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.default_tile.url,
+ frappe.utils.map_defaults.tiles.default_tile.options
+ );
+ this.satelliteLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.satellite_tile.url,
+ frappe.utils.map_defaults.tiles.satellite_tile.options
+ );
+ this.labelsLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.labels_tail.url,
+ frappe.utils.map_defaults.tiles.labels_tail.options
+ );
+ this.terrainLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.terrain_lines_tail.url,
+ frappe.utils.map_defaults.tiles.terrain_lines_tail.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 +247,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 +275,27 @@ 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.labels_tail.url ||
+ layer._url === frappe.utils.map_defaults.tiles.terrain_lines_tail.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 4c0787bdff..5a5b75bdeb 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -1203,10 +1203,34 @@ 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: {
+ default_tile: {
+ url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
+ options: {
+ attribution:
+ '© OpenStreetMap contributors',
+ },
+ },
+ satellite_tile: {
+ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
+ options: {
+ attribution: "© Esri © OpenStreetMap Contributors",
+ },
+ },
+ labels_tail: {
+ url: "https://tiles.stadiamaps.com/tiles/stamen_toner_labels/{z}/{x}/{y}{r}.png",
+ options: {
+ attribution:
+ '© Stadia Maps © Stamen Design © OpenMapTiles',
+ },
+ },
+ terrain_lines_tail: {
+ 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/",
},
diff --git a/frappe/public/js/frappe/views/map/map_view.js b/frappe/public/js/frappe/views/map/map_view.js
index 4e9be64794..4ecc71796f 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.default_tile.url,
+ frappe.utils.map_defaults.tiles.default_tile.options
+ );
+ this.satelliteLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.satellite_tile.url,
+ frappe.utils.map_defaults.tiles.satellite_tile.options
+ );
+ this.labelsLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.labels_tail.url,
+ frappe.utils.map_defaults.tiles.labels_tail.options
+ );
+ this.terrainLayer = L.tileLayer(
+ frappe.utils.map_defaults.tiles.terrain_lines_tail.url,
+ frappe.utils.map_defaults.tiles.terrain_lines_tail.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,61 @@ 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.labels_tail.url ||
+ layer._url === frappe.utils.map_defaults.tiles.terrain_lines_tail.url)
+ ) {
+ this.map.removeLayer(layer);
+ }
+ });
+ }
+ });
+ }
};