support satellite imagery map layer from esri
This commit is contained in:
@ -27,6 +27,10 @@
|
|||||||
<link rel="stylesheet" href="plugins/leaflet.markercluster/MarkerCluster.css"/>
|
<link rel="stylesheet" href="plugins/leaflet.markercluster/MarkerCluster.css"/>
|
||||||
<link rel="stylesheet" href="plugins/leaflet.markercluster/MarkerCluster.Default.css"/>
|
<link rel="stylesheet" href="plugins/leaflet.markercluster/MarkerCluster.Default.css"/>
|
||||||
|
|
||||||
|
<!-- leaflet groupedlayercontrol -->
|
||||||
|
<script src="plugins/leaflet.groupedlayercontrol/leaflet.groupedlayercontrol.js"></script>
|
||||||
|
<link rel="stylesheet" href="plugins/leaflet.groupedlayercontrol/leaflet.groupedlayercontrol.css"/>
|
||||||
|
|
||||||
<!-- moment -->
|
<!-- moment -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js"></script>
|
||||||
|
|
||||||
@ -2025,9 +2029,14 @@
|
|||||||
|
|
||||||
var openStreetMap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
var openStreetMap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 22, // increase from 18 to 22
|
maxZoom: 22, // increase from 18 to 22
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> | Data from <a target="_blank" href="https://meshtastic.org/docs/software/integrations/mqtt/">Meshtastic</a>',
|
attribution: 'Tiles © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> | Data from <a target="_blank" href="https://meshtastic.org/docs/software/integrations/mqtt/">Meshtastic</a>',
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
|
var esriWorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
||||||
|
maxZoom: 21, // esri doesn't have tiles closer than this
|
||||||
|
attribution: 'Tiles © <a href="https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/">Esri</a> | Data from <a target="_blank" href="https://meshtastic.org/docs/software/integrations/mqtt/">Meshtastic</a>'
|
||||||
|
});
|
||||||
|
|
||||||
// create layer groups
|
// create layer groups
|
||||||
var nodesLayerGroup = new L.LayerGroup();
|
var nodesLayerGroup = new L.LayerGroup();
|
||||||
var neighboursLayerGroup = new L.LayerGroup();
|
var neighboursLayerGroup = new L.LayerGroup();
|
||||||
@ -2068,16 +2077,9 @@
|
|||||||
return div;
|
return div;
|
||||||
};
|
};
|
||||||
|
|
||||||
var baseLayers = {
|
var tileLayers = {
|
||||||
"Nodes (All)": nodesLayerGroup,
|
"Map": openStreetMap,
|
||||||
"Nodes (Clustered)": nodesClusteredLayerGroup,
|
"Satellite": esriWorldImagery,
|
||||||
"Nodes (Hide All)": new L.LayerGroup(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var overlays = {
|
|
||||||
"Legend": legendLayerGroup,
|
|
||||||
"Neighbours": neighboursLayerGroup,
|
|
||||||
"Waypoints": waypointsLayerGroup,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle adding/remove legend on map (can't use L.Control as an overlay, so we toggle an empty L.LayerGroup)
|
// handle adding/remove legend on map (can't use L.Control as an overlay, so we toggle an empty L.LayerGroup)
|
||||||
@ -2092,7 +2094,21 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// add layers to control ui
|
// add layers to control ui
|
||||||
L.control.layers(baseLayers, overlays).addTo(map);
|
L.control.groupedLayers(tileLayers, {
|
||||||
|
"Nodes": {
|
||||||
|
"All": nodesLayerGroup,
|
||||||
|
"Clustered": nodesClusteredLayerGroup,
|
||||||
|
"None": new L.LayerGroup(),
|
||||||
|
},
|
||||||
|
"Overlays": {
|
||||||
|
"Legend": legendLayerGroup,
|
||||||
|
"Neighbours": neighboursLayerGroup,
|
||||||
|
"Waypoints": waypointsLayerGroup,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
// make the "Nodes" group exclusive (use radio inputs instead of checkbox)
|
||||||
|
exclusiveGroups: ["Nodes"],
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
// enable base layers
|
// enable base layers
|
||||||
nodesClusteredLayerGroup.addTo(map);
|
nodesClusteredLayerGroup.addTo(map);
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
.leaflet-control-layers-group-name {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: .2em;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-control-layers-group {
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-control-layers-scrollbar {
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
@ -0,0 +1,374 @@
|
|||||||
|
/* global L */
|
||||||
|
|
||||||
|
// A layer control which provides for layer groupings.
|
||||||
|
// Author: Ishmael Smyrnow
|
||||||
|
L.Control.GroupedLayers = L.Control.extend({
|
||||||
|
|
||||||
|
options: {
|
||||||
|
collapsed: true,
|
||||||
|
position: 'topright',
|
||||||
|
autoZIndex: true,
|
||||||
|
exclusiveGroups: [],
|
||||||
|
groupCheckboxes: false
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (baseLayers, groupedOverlays, options) {
|
||||||
|
var i, j;
|
||||||
|
L.Util.setOptions(this, options);
|
||||||
|
|
||||||
|
this._layers = [];
|
||||||
|
this._lastZIndex = 0;
|
||||||
|
this._handlingClick = false;
|
||||||
|
this._groupList = [];
|
||||||
|
this._domGroups = [];
|
||||||
|
|
||||||
|
for (i in baseLayers) {
|
||||||
|
this._addLayer(baseLayers[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in groupedOverlays) {
|
||||||
|
for (j in groupedOverlays[i]) {
|
||||||
|
this._addLayer(groupedOverlays[i][j], j, i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function (map) {
|
||||||
|
this._initLayout();
|
||||||
|
this._update();
|
||||||
|
|
||||||
|
map
|
||||||
|
.on('layeradd', this._onLayerChange, this)
|
||||||
|
.on('layerremove', this._onLayerChange, this);
|
||||||
|
|
||||||
|
return this._container;
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemove: function (map) {
|
||||||
|
map
|
||||||
|
.off('layeradd', this._onLayerChange, this)
|
||||||
|
.off('layerremove', this._onLayerChange, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
addBaseLayer: function (layer, name) {
|
||||||
|
this._addLayer(layer, name);
|
||||||
|
this._update();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
addOverlay: function (layer, name, group) {
|
||||||
|
this._addLayer(layer, name, group, true);
|
||||||
|
this._update();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeLayer: function (layer) {
|
||||||
|
var id = L.Util.stamp(layer);
|
||||||
|
var _layer = this._getLayer(id);
|
||||||
|
if (_layer) {
|
||||||
|
delete this._layers[this._layers.indexOf(_layer)];
|
||||||
|
}
|
||||||
|
this._update();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getLayer: function (id) {
|
||||||
|
for (var i = 0; i < this._layers.length; i++) {
|
||||||
|
if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
|
||||||
|
return this._layers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_initLayout: function () {
|
||||||
|
var className = 'leaflet-control-layers',
|
||||||
|
container = this._container = L.DomUtil.create('div', className);
|
||||||
|
|
||||||
|
// Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
|
||||||
|
container.setAttribute('aria-haspopup', true);
|
||||||
|
|
||||||
|
if (L.Browser.touch) {
|
||||||
|
L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
|
||||||
|
} else {
|
||||||
|
L.DomEvent.disableClickPropagation(container);
|
||||||
|
L.DomEvent.on(container, 'wheel', L.DomEvent.stopPropagation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var form = this._form = L.DomUtil.create('form', className + '-list');
|
||||||
|
|
||||||
|
if (this.options.collapsed) {
|
||||||
|
if (!L.Browser.android) {
|
||||||
|
L.DomEvent
|
||||||
|
.on(container, 'mouseover', this._expand, this)
|
||||||
|
.on(container, 'mouseout', this._collapse, this);
|
||||||
|
}
|
||||||
|
var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
|
||||||
|
link.href = '#';
|
||||||
|
link.title = 'Layers';
|
||||||
|
|
||||||
|
if (L.Browser.touch) {
|
||||||
|
L.DomEvent
|
||||||
|
.on(link, 'click', L.DomEvent.stop)
|
||||||
|
.on(link, 'click', this._expand, this);
|
||||||
|
} else {
|
||||||
|
L.DomEvent.on(link, 'focus', this._expand, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._map.on('click', this._collapse, this);
|
||||||
|
// TODO keyboard accessibility
|
||||||
|
} else {
|
||||||
|
this._expand();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
|
||||||
|
this._separator = L.DomUtil.create('div', className + '-separator', form);
|
||||||
|
this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
|
||||||
|
|
||||||
|
container.appendChild(form);
|
||||||
|
},
|
||||||
|
|
||||||
|
_addLayer: function (layer, name, group, overlay) {
|
||||||
|
var id = L.Util.stamp(layer);
|
||||||
|
|
||||||
|
var _layer = {
|
||||||
|
layer: layer,
|
||||||
|
name: name,
|
||||||
|
overlay: overlay
|
||||||
|
};
|
||||||
|
this._layers.push(_layer);
|
||||||
|
|
||||||
|
group = group || '';
|
||||||
|
var groupId = this._indexOf(this._groupList, group);
|
||||||
|
|
||||||
|
if (groupId === -1) {
|
||||||
|
groupId = this._groupList.push(group) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1);
|
||||||
|
|
||||||
|
_layer.group = {
|
||||||
|
name: group,
|
||||||
|
id: groupId,
|
||||||
|
exclusive: exclusive
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.options.autoZIndex && layer.setZIndex) {
|
||||||
|
this._lastZIndex++;
|
||||||
|
layer.setZIndex(this._lastZIndex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function () {
|
||||||
|
if (!this._container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._baseLayersList.innerHTML = '';
|
||||||
|
this._overlaysList.innerHTML = '';
|
||||||
|
this._domGroups.length = 0;
|
||||||
|
|
||||||
|
var baseLayersPresent = false,
|
||||||
|
overlaysPresent = false,
|
||||||
|
i, obj;
|
||||||
|
|
||||||
|
for (var i = 0; i < this._layers.length; i++) {
|
||||||
|
obj = this._layers[i];
|
||||||
|
this._addItem(obj);
|
||||||
|
overlaysPresent = overlaysPresent || obj.overlay;
|
||||||
|
baseLayersPresent = baseLayersPresent || !obj.overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLayerChange: function (e) {
|
||||||
|
var obj = this._getLayer(L.Util.stamp(e.layer)),
|
||||||
|
type;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._handlingClick) {
|
||||||
|
this._update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.overlay) {
|
||||||
|
type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove';
|
||||||
|
} else {
|
||||||
|
type = e.type === 'layeradd' ? 'baselayerchange' : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
this._map.fire(type, obj);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
|
||||||
|
_createRadioElement: function (name, checked) {
|
||||||
|
var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + name + '"';
|
||||||
|
if (checked) {
|
||||||
|
radioHtml += ' checked="checked"';
|
||||||
|
}
|
||||||
|
radioHtml += '/>';
|
||||||
|
|
||||||
|
var radioFragment = document.createElement('div');
|
||||||
|
radioFragment.innerHTML = radioHtml;
|
||||||
|
|
||||||
|
return radioFragment.firstChild;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addItem: function (obj) {
|
||||||
|
var label = document.createElement('label'),
|
||||||
|
input,
|
||||||
|
checked = this._map.hasLayer(obj.layer),
|
||||||
|
container,
|
||||||
|
groupRadioName;
|
||||||
|
|
||||||
|
if (obj.overlay) {
|
||||||
|
if (obj.group.exclusive) {
|
||||||
|
groupRadioName = 'leaflet-exclusive-group-layer-' + obj.group.id;
|
||||||
|
input = this._createRadioElement(groupRadioName, checked);
|
||||||
|
} else {
|
||||||
|
input = document.createElement('input');
|
||||||
|
input.type = 'checkbox';
|
||||||
|
input.className = 'leaflet-control-layers-selector';
|
||||||
|
input.defaultChecked = checked;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input = this._createRadioElement('leaflet-base-layers', checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
input.layerId = L.Util.stamp(obj.layer);
|
||||||
|
input.groupID = obj.group.id;
|
||||||
|
L.DomEvent.on(input, 'click', this._onInputClick, this);
|
||||||
|
|
||||||
|
var name = document.createElement('span');
|
||||||
|
name.innerHTML = ' ' + obj.name;
|
||||||
|
|
||||||
|
label.appendChild(input);
|
||||||
|
label.appendChild(name);
|
||||||
|
|
||||||
|
if (obj.overlay) {
|
||||||
|
container = this._overlaysList;
|
||||||
|
|
||||||
|
var groupContainer = this._domGroups[obj.group.id];
|
||||||
|
|
||||||
|
// Create the group container if it doesn't exist
|
||||||
|
if (!groupContainer) {
|
||||||
|
groupContainer = document.createElement('div');
|
||||||
|
groupContainer.className = 'leaflet-control-layers-group';
|
||||||
|
groupContainer.id = 'leaflet-control-layers-group-' + obj.group.id;
|
||||||
|
|
||||||
|
var groupLabel = document.createElement('label');
|
||||||
|
groupLabel.className = 'leaflet-control-layers-group-label';
|
||||||
|
|
||||||
|
if (obj.group.name !== '' && !obj.group.exclusive) {
|
||||||
|
// ------ add a group checkbox with an _onInputClickGroup function
|
||||||
|
if (this.options.groupCheckboxes) {
|
||||||
|
var groupInput = document.createElement('input');
|
||||||
|
groupInput.type = 'checkbox';
|
||||||
|
groupInput.className = 'leaflet-control-layers-group-selector';
|
||||||
|
groupInput.groupID = obj.group.id;
|
||||||
|
groupInput.legend = this;
|
||||||
|
L.DomEvent.on(groupInput, 'click', this._onGroupInputClick, groupInput);
|
||||||
|
groupLabel.appendChild(groupInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupName = document.createElement('span');
|
||||||
|
groupName.className = 'leaflet-control-layers-group-name';
|
||||||
|
groupName.innerHTML = obj.group.name;
|
||||||
|
groupLabel.appendChild(groupName);
|
||||||
|
|
||||||
|
groupContainer.appendChild(groupLabel);
|
||||||
|
container.appendChild(groupContainer);
|
||||||
|
|
||||||
|
this._domGroups[obj.group.id] = groupContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = groupContainer;
|
||||||
|
} else {
|
||||||
|
container = this._baseLayersList;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(label);
|
||||||
|
|
||||||
|
return label;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGroupInputClick: function () {
|
||||||
|
var i, input, obj;
|
||||||
|
|
||||||
|
var this_legend = this.legend;
|
||||||
|
this_legend._handlingClick = true;
|
||||||
|
|
||||||
|
var inputs = this_legend._form.getElementsByTagName('input');
|
||||||
|
var inputsLen = inputs.length;
|
||||||
|
|
||||||
|
for (i = 0; i < inputsLen; i++) {
|
||||||
|
input = inputs[i];
|
||||||
|
if (input.groupID === this.groupID && input.className === 'leaflet-control-layers-selector') {
|
||||||
|
input.checked = this.checked;
|
||||||
|
obj = this_legend._getLayer(input.layerId);
|
||||||
|
if (input.checked && !this_legend._map.hasLayer(obj.layer)) {
|
||||||
|
this_legend._map.addLayer(obj.layer);
|
||||||
|
} else if (!input.checked && this_legend._map.hasLayer(obj.layer)) {
|
||||||
|
this_legend._map.removeLayer(obj.layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this_legend._handlingClick = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInputClick: function () {
|
||||||
|
var i, input, obj,
|
||||||
|
inputs = this._form.getElementsByTagName('input'),
|
||||||
|
inputsLen = inputs.length;
|
||||||
|
|
||||||
|
this._handlingClick = true;
|
||||||
|
|
||||||
|
for (i = 0; i < inputsLen; i++) {
|
||||||
|
input = inputs[i];
|
||||||
|
if (input.className === 'leaflet-control-layers-selector') {
|
||||||
|
obj = this._getLayer(input.layerId);
|
||||||
|
|
||||||
|
if (input.checked && !this._map.hasLayer(obj.layer)) {
|
||||||
|
this._map.addLayer(obj.layer);
|
||||||
|
} else if (!input.checked && this._map.hasLayer(obj.layer)) {
|
||||||
|
this._map.removeLayer(obj.layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._handlingClick = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_expand: function () {
|
||||||
|
L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
|
||||||
|
// permits to have a scrollbar if overlays heighter than the map.
|
||||||
|
var acceptableHeight = this._map._size.y - (this._container.offsetTop * 4);
|
||||||
|
if (acceptableHeight < this._form.clientHeight) {
|
||||||
|
L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
|
||||||
|
this._form.style.height = acceptableHeight + 'px';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_collapse: function () {
|
||||||
|
this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
|
||||||
|
},
|
||||||
|
|
||||||
|
_indexOf: function (arr, obj) {
|
||||||
|
for (var i = 0, j = arr.length; i < j; i++) {
|
||||||
|
if (arr[i] === obj) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.control.groupedLayers = function (baseLayers, groupedOverlays, options) {
|
||||||
|
return new L.Control.GroupedLayers(baseLayers, groupedOverlays, options);
|
||||||
|
};
|
Reference in New Issue
Block a user