cleanup of obsolote leaflet components
This commit is contained in:
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 8.5 8.5"><defs><filter id="shadow"><feDropShadow dx="0" dy="0" stdDeviation="0.3" flood-color="#000000"/></filter></defs><circle cx="4.2" cy="4.2" r="2.2" fill="#ffffff" fill-opacity="1" filter="url(#shadow)"/><circle cx="4.2" cy="4.2" r="1.85" fill="none" stroke="#333333" stroke-linejoin="round" stroke-width="0.45"/><circle cx="4.2" cy="4.2" r="1.25" fill="none" stroke="#333333" stroke-linejoin="round" stroke-width="0.25"/><circle cx="4.2" cy="4.2" r="0.55" fill="#333333"/></svg>
|
||||
|
Before Width: | Height: | Size: 563 B |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1,405 +0,0 @@
|
||||
/*
|
||||
* https://github.com/adoroszlai/joebed/tree/gh-pages
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014- Doroszlai Attila, 2019- Raruto
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
L.Mixin.Selectable = {
|
||||
includes: L.Mixin.Events,
|
||||
|
||||
setSelected: function (s) {
|
||||
var selected = !!s;
|
||||
if (this._selected !== selected) {
|
||||
this._selected = selected;
|
||||
this.fire('selected');
|
||||
}
|
||||
},
|
||||
|
||||
isSelected: function () {
|
||||
return !!this._selected;
|
||||
},
|
||||
};
|
||||
|
||||
L.Mixin.Selection = {
|
||||
includes: L.Mixin.Events,
|
||||
|
||||
getSelection: function () {
|
||||
return this._selected;
|
||||
},
|
||||
|
||||
setSelection: function (item) {
|
||||
if (this._selected === item) {
|
||||
if (item !== null) {
|
||||
item.setSelected(!item.isSelected());
|
||||
if (!item.isSelected()) {
|
||||
this._selected = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this._selected) {
|
||||
this._selected.setSelected(false);
|
||||
}
|
||||
this._selected = item;
|
||||
if (this._selected) {
|
||||
this._selected.setSelected(true);
|
||||
}
|
||||
}
|
||||
this.fire('selection_changed', { polyline: item });
|
||||
},
|
||||
};
|
||||
|
||||
L.GeoJSON.include(L.Mixin.Selectable);
|
||||
|
||||
export const GpxGroup = L.GpxGroup = L.Class.extend({
|
||||
options: {
|
||||
highlight: {
|
||||
opacity: 1,
|
||||
weight: 6,
|
||||
},
|
||||
points: [],
|
||||
points_options: {
|
||||
icon: {
|
||||
iconUrl: '../images/elevation-poi.png',
|
||||
iconSize: [12, 12],
|
||||
}
|
||||
},
|
||||
flyToBounds: true,
|
||||
elevation: true,
|
||||
elevation_options: {
|
||||
theme: 'lightblue-theme',
|
||||
detached: true,
|
||||
elevationDiv: '#elevation',
|
||||
},
|
||||
distanceMarkers: true,
|
||||
distanceMarkers_options: {
|
||||
lazy: true,
|
||||
distance: false,
|
||||
direction: true,
|
||||
offset: 1000,
|
||||
},
|
||||
},
|
||||
|
||||
initialize: function (tracks, options) {
|
||||
|
||||
L.Util.setOptions(this, options);
|
||||
|
||||
this._count = 0;
|
||||
this._loadedCount = 0;
|
||||
this._tracks = tracks;
|
||||
this._layers = L.featureGroup();
|
||||
this._markers = L.featureGroup();
|
||||
this._hotline = L.featureGroup();
|
||||
this._elevation = L.control.elevation(this.options.elevation_options);
|
||||
|
||||
this._routes = [];
|
||||
|
||||
this.options.points.forEach((poi) =>
|
||||
L
|
||||
.marker(poi.latlng, { icon: L.icon(this.options.points_options.icon) })
|
||||
.bindTooltip(poi.name, { direction: 'auto' }).addTo(this._markers)
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
getBounds: function () {
|
||||
return this._layers.getBounds();
|
||||
},
|
||||
|
||||
addTo: function (map) {
|
||||
this._hotline.addTo(map);
|
||||
this._layers.addTo(map);
|
||||
this._markers.addTo(map);
|
||||
|
||||
this._map = map;
|
||||
|
||||
this.on('selection_changed', this._onSelectionChanged, this);
|
||||
this.addTracks();
|
||||
},
|
||||
|
||||
addTracks() {
|
||||
this._tracks.forEach(this._addTrack, this);
|
||||
|
||||
},
|
||||
|
||||
highlightTrack(id) {
|
||||
const route = this._routes.find(r => r.options.id == id)
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
route.eachLayer((l) => {
|
||||
this.highlight(route, l)
|
||||
})
|
||||
},
|
||||
|
||||
unHighlightTrack(id) {
|
||||
const route = this._routes.find(r => r.options.id == id)
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
route.eachLayer((l) => {
|
||||
this.unhighlight(route, l)
|
||||
})
|
||||
},
|
||||
|
||||
select(id) {
|
||||
const route = this._routes.find(r => r.options.id == id)
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
this.setSelection(route)
|
||||
},
|
||||
|
||||
resetSelection() {
|
||||
this.setSelection(this._selected)
|
||||
if (this.options.flyToBounds) {
|
||||
this._map.flyToBounds(this.getBounds(), { duration: 0.25, easeLinearity: 0.25, noMoveStart: true });
|
||||
}
|
||||
},
|
||||
|
||||
openPopup(id) {
|
||||
const route = this._routes.find(r => r.options.id == id)
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
route.openPopup();
|
||||
},
|
||||
|
||||
closePopup(id) {
|
||||
const route = this._routes.find(r => r.options.id == id)
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
route.closePopup();
|
||||
},
|
||||
|
||||
_addTrack: function (track) {
|
||||
this._elevation._parseFromString(track.gpx)
|
||||
.then(geojson => this._loadGeoJSON(geojson, track.id, track.gpx.split('/').pop().split('#')[0].split('?')[0]))
|
||||
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._elevation.clear()
|
||||
this._elevation.remove();
|
||||
this._clearLayers();
|
||||
this._clearLayers(this._markers);
|
||||
this._clearLayers(this._hotline)
|
||||
this._count = 0;
|
||||
this._loadedCount = 0;
|
||||
this._tracks = []
|
||||
this._routes = []
|
||||
|
||||
this.fire('clear')
|
||||
},
|
||||
|
||||
_clearLayers(l) {
|
||||
l = l || this._layers;
|
||||
if (l && l.eachLayer) {
|
||||
l.eachLayer(f => f.remove())
|
||||
l.clearLayers();
|
||||
}
|
||||
},
|
||||
|
||||
_loadGeoJSON: function (geojson, id, fallbackName) {
|
||||
if (geojson) {
|
||||
geojson.id = id
|
||||
geojson.name = geojson.name || (geojson[0] && geojson[0].properties.name) || fallbackName;
|
||||
this._loadRoute(geojson);
|
||||
}
|
||||
},
|
||||
|
||||
_loadRoute: function (data) {
|
||||
if (!data) return;
|
||||
var line_style = {
|
||||
color: this._stringToColor(data.id),
|
||||
opacity: 0.75,
|
||||
weight: 5,
|
||||
distanceMarkers: this.options.distanceMarkers_options,
|
||||
};
|
||||
|
||||
var route = L.geoJson(data, {
|
||||
name: data.name || '',
|
||||
style: (feature) => line_style,
|
||||
distanceMarkers: line_style.distanceMarkers,
|
||||
originalStyle: line_style,
|
||||
isGroupLayer: true,
|
||||
id: data.id,
|
||||
filter: feature => feature.geometry.type != "Point",
|
||||
});
|
||||
|
||||
|
||||
this._elevation.import([this._elevation.__LGEOMUTIL, this._elevation.__LDISTANCEM]).then(() => {
|
||||
route.addTo(this._layers);
|
||||
|
||||
route.eachLayer((layer) => this._onEachRouteLayer(route, layer));
|
||||
|
||||
|
||||
this._onEachRouteLoaded(route);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_onEachRouteLayer: function (route, layer) {
|
||||
var polyline = layer;
|
||||
|
||||
this._routes.push(route)
|
||||
|
||||
route.on('selected', L.bind(this._onRouteSelected, this, route, polyline));
|
||||
|
||||
polyline.on('mouseover', L.bind(this._onRouteMouseOver, this, route, polyline));
|
||||
polyline.on('mouseout', L.bind(this._onRouteMouseOut, this, route, polyline));
|
||||
polyline.on('click', L.bind(this._onRouteClick, this, route, polyline));
|
||||
|
||||
const startIcon = L.divIcon({
|
||||
html: '<i class="p-2 text-white bg-gray-500 rounded-full fa fa-bullseye -translate-x-1/2"></i>',
|
||||
className: 'start-icon'
|
||||
});
|
||||
const endIcon = L.divIcon({
|
||||
html: '<i class="p-2 text-white bg-gray-500 rounded-full fa fa-flag-checkered -translate-x-1/2"></i>',
|
||||
className: 'end-icon'
|
||||
});
|
||||
const latlngs = polyline.getLatLngs().flat(1);
|
||||
|
||||
polyline.setLatLngs(latlngs);
|
||||
|
||||
if (this._loadedCount == 0 || !this.options.itinerary) {
|
||||
L.marker(latlngs[0], { icon: startIcon }).addTo(this._markers)
|
||||
}
|
||||
|
||||
L.marker(latlngs[latlngs.length - 1], { icon: endIcon }).addTo(this._markers)
|
||||
},
|
||||
|
||||
_onEachRouteLoaded: function (route) {
|
||||
this.fire('route_loaded', { route: route });
|
||||
|
||||
if (++this._loadedCount === this._tracks.length) {
|
||||
this.fire('loaded');
|
||||
if (this.options.flyToBounds) {
|
||||
this._map.flyToBounds(this.getBounds(), { duration: 0.25, easeLinearity: 0.25, noMoveStart: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
highlight: function (route, polyline) {
|
||||
polyline.setStyle(this.options.highlight);
|
||||
polyline.options.highlighted = true
|
||||
if (this.options.distanceMarkers) {
|
||||
polyline.addDistanceMarkers();
|
||||
}
|
||||
},
|
||||
|
||||
unhighlight: function (route, polyline) {
|
||||
polyline.setStyle(route.options.originalStyle);
|
||||
polyline.options.highlighted = false
|
||||
if (this.options.distanceMarkers) {
|
||||
polyline.removeDistanceMarkers();
|
||||
}
|
||||
},
|
||||
|
||||
_onRouteMouseOver: function (route, polyline) {
|
||||
if (!route.isSelected()) {
|
||||
this.highlight(route, polyline);
|
||||
}
|
||||
this.fire('route_mouseover', { route: route, polyline: polyline });
|
||||
},
|
||||
|
||||
_onRouteMouseOut: function (route, polyline) {
|
||||
if (!route.isSelected()) {
|
||||
this.unhighlight(route, polyline);
|
||||
}
|
||||
this.fire('route_mouseout', { route: route, polyline: polyline });
|
||||
},
|
||||
|
||||
_onRouteClick: function (route, polyline) {
|
||||
this.highlight(route, polyline)
|
||||
this.setSelection(route);
|
||||
},
|
||||
|
||||
_onRouteSelected: function (route, polyline) {
|
||||
if (!route.isSelected()) {
|
||||
this.unhighlight(route, polyline);
|
||||
}
|
||||
},
|
||||
|
||||
_onSelectionChanged: async function (e) {
|
||||
var elevation = this._elevation;
|
||||
var eleDiv = elevation.getContainer();
|
||||
var route = this.getSelection();
|
||||
var hotline = this._hotline;
|
||||
|
||||
hotline.clearLayers();
|
||||
|
||||
if (route && route.isSelected()) {
|
||||
elevation.clear();
|
||||
|
||||
if (!eleDiv) {
|
||||
elevation.addTo(this._map);
|
||||
this._map.invalidateSize()
|
||||
}
|
||||
for (const layer of route.getLayers()) {
|
||||
if (layer instanceof L.Polyline) {
|
||||
await elevation.addData(layer, false);
|
||||
}
|
||||
}
|
||||
await elevation._initHotLine(route, this._hotline)
|
||||
|
||||
|
||||
this._map.flyToBounds(route.getBounds(), { duration: 0.25, easeLinearity: 0.25, noMoveStart: true });
|
||||
|
||||
|
||||
} else {
|
||||
if (eleDiv) {
|
||||
elevation.remove();
|
||||
this._map.invalidateSize()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_stringToColor(input) {
|
||||
// Define the maximum brightness to ensure the color is not too light
|
||||
const maxBrightness = 200;
|
||||
|
||||
// Divide the string into 3 parts (5 characters each)
|
||||
const redPart = input.slice(0, 5);
|
||||
const greenPart = input.slice(5, 10);
|
||||
const bluePart = input.slice(10, 15);
|
||||
|
||||
// Calculate the red, green, and blue values by summing the char codes of the parts
|
||||
const red = Math.floor((redPart.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0) % 256) * (maxBrightness / 255));
|
||||
const greenRaw = greenPart.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0) % 256;
|
||||
const green = Math.floor((greenRaw * 0.5) * (maxBrightness / 255)); // Reduce green to avoid greenish colors
|
||||
const blue = Math.floor((bluePart.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0) % 256) * (maxBrightness / 255));
|
||||
|
||||
// Format as HEX color
|
||||
return `#${(1 << 24 | red << 16 | green << 8 | blue).toString(16).slice(1).toUpperCase()}`;
|
||||
},
|
||||
|
||||
removeFrom: function (map) {
|
||||
this._layers.removeFrom(map);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
L.GpxGroup.include(L.Mixin.Events);
|
||||
L.GpxGroup.include(L.Mixin.Selection);
|
||||
|
||||
L.gpxGroup = (tracks, options) => new L.GpxGroup(tracks, options);
|
||||
1347
web/src/lib/vendor/leaflet-elevation/src/control.js
vendored
1347
web/src/lib/vendor/leaflet-elevation/src/control.js
vendored
File diff suppressed because it is too large
Load Diff
350
web/src/lib/vendor/leaflet-elevation/src/index.css
vendored
350
web/src/lib/vendor/leaflet-elevation/src/index.css
vendored
@@ -1,350 +0,0 @@
|
||||
.leaflet-hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.legend {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.leaflet-container {
|
||||
z-index: 0;
|
||||
/* prevent overlapping the .elevation-detached chart */
|
||||
}
|
||||
|
||||
.elevation-control .background {
|
||||
background-color: var(--ele-bg, rgba(70, 130, 180, 0.2));
|
||||
border-radius: 5px;
|
||||
overflow: visible;
|
||||
display: block;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.elevation-control .grid,
|
||||
.elevation-control .area>foreignObject,
|
||||
.elevation-control .axis,
|
||||
.elevation-control .tooltip,
|
||||
.height-focus.line {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.elevation-control .axis line,
|
||||
.elevation-control .axis path {
|
||||
stroke: var(--ele-axis, #2D1130);
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.elevation-control .grid .tick line {
|
||||
stroke: var(--ele-grid, #EEE);
|
||||
stroke-width: 1px;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.elevation-control .grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.elevation-control .axis text,
|
||||
.elevation-control .legend text,
|
||||
.elevation-control .point text {
|
||||
fill: #fff;
|
||||
font-weight: 700;
|
||||
paint-order: stroke fill;
|
||||
stroke: #000;
|
||||
stroke-width: 2px
|
||||
}
|
||||
|
||||
.elevation-control .legend .legend-switcher-symbol {
|
||||
fill: rgb(135, 135, 135);
|
||||
}
|
||||
|
||||
.elevation-control .y.axis text {
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.elevation-control .area {
|
||||
fill: var(--ele-area, #4682B4);
|
||||
stroke: var(--ele-stroke, #000);
|
||||
stroke-width: 1.2;
|
||||
paint-order: stroke fill;
|
||||
}
|
||||
|
||||
.elevation-control .horizontal-drag-line {
|
||||
cursor: row-resize;
|
||||
stroke: transparent;
|
||||
stroke-dasharray: 5;
|
||||
stroke-width: 1.1;
|
||||
}
|
||||
|
||||
.elevation-control .active .horizontal-drag-line {
|
||||
stroke: #000;
|
||||
}
|
||||
|
||||
.elevation-control .horizontal-drag-label {
|
||||
fill: #000;
|
||||
font-weight: 700;
|
||||
paint-order: stroke;
|
||||
stroke: #FFF;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.elevation-control .ruler {
|
||||
color: #000;
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.elevation-control .mouse-focus-line {
|
||||
stroke: #000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.elevation-control .mouse-focus-label-rect {
|
||||
fill: #000;
|
||||
fill-opacity: 0.75;
|
||||
stroke-width: 1;
|
||||
stroke: #444;
|
||||
}
|
||||
|
||||
.elevation-control .mouse-focus-label-text {
|
||||
fill: #FFF;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.elevation-control .brush .overlay {
|
||||
cursor: unset;
|
||||
}
|
||||
|
||||
.elevation-control .brush .selection {
|
||||
fill: var(--ele-brush, rgba(23, 74, 117, 0.4));
|
||||
stroke: none;
|
||||
fill-opacity: unset;
|
||||
}
|
||||
|
||||
.elevation-summary {
|
||||
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
margin: var(--ele-sum-margin, 0 auto);
|
||||
text-shadow: var(--ele-sum-shadow, 1px 0 0 #FFF, -1px 0 0 #FFF, 0 1px 0 #FFF, 0 -1px 0 #FFF, 1px 1px #FFF, -1px -1px 0 #FFF, 1px -1px 0 #FFF, -1px 1px 0 #FFF);
|
||||
}
|
||||
|
||||
.elevation-summary>span:not(:last-child):after {
|
||||
content: var(--ele-sum-sep, '');
|
||||
}
|
||||
|
||||
.multiline-summary>span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.multiline-summary .download {
|
||||
float: right;
|
||||
margin-top: -3em;
|
||||
margin-right: 2em;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.elevation-summary .summaryvalue {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.elevation-toggle-icon {
|
||||
background-color: #fff;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
height: var(--ele-toggle-size, 36px);
|
||||
width: var(--ele-toggle-size, 36px);
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 7px rgba(0, 0, 0, 0.4);
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
position: var(--ele-toggle-pos, relative);
|
||||
}
|
||||
|
||||
.elevation-toggle-icon:before {
|
||||
content: '\2716';
|
||||
display: var(--ele-close-btn, none);
|
||||
color: #000;
|
||||
width: 100%;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.leaflet-elevation-pane .height-focus,
|
||||
.leaflet-overlay-pane .height-focus {
|
||||
stroke: #000;
|
||||
fill: var(--ele-circle, var(--ele-area, #FFF));
|
||||
}
|
||||
|
||||
.leaflet-elevation-pane .height-focus.line,
|
||||
.leaflet-overlay-pane .height-focus.line {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.leaflet-elevation-pane .height-focus-label,
|
||||
.leaflet-overlay-pane .height-focus-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
fill: #000;
|
||||
paint-order: stroke;
|
||||
stroke: #FFF;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.elevation-waypoint-icon:before,
|
||||
.elevation-position-icon:before {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
background: var(--ele-marker) no-repeat center center / contain;
|
||||
}
|
||||
|
||||
.elevation-polyline {
|
||||
stroke: var(--ele-poly, var(--ele-area, #000));
|
||||
filter: drop-shadow(1px 1px 0 #FFF) drop-shadow(-1px -1px 0 #FFF) drop-shadow(1px -1px 0 #FFF) drop-shadow(-1px 1px 0 #FFF);
|
||||
}
|
||||
|
||||
/* CHART STATES /////////////////////////////////////////////////// */
|
||||
|
||||
.elevation-detached {
|
||||
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.elevation-detached .area {
|
||||
fill-opacity: var(--ele-alpha, 0.8);
|
||||
}
|
||||
|
||||
.elevation-detached.elevation-collapsed .elevation-summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.elevation-detached.elevation-collapsed .elevation-toggle-icon {
|
||||
top: 5px;
|
||||
right: 9px;
|
||||
bottom: 5px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.elevation-control.elevation-collapsed>* {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.elevation-control.elevation-collapsed>.elevation-toggle-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.elevation-detached {
|
||||
--ele-sum-margin: 12px 35px;
|
||||
--ele-sum-shadow: none;
|
||||
--ele-toggle-pos: absolute;
|
||||
}
|
||||
|
||||
.elevation-expanded {
|
||||
--ele-close-btn: inline-block;
|
||||
--ele-toggle-bg: none;
|
||||
--ele-toggle-pos: absolute;
|
||||
--ele-toggle-size: 20px;
|
||||
}
|
||||
|
||||
.inline-summary {
|
||||
--ele-sum-sep: "\0020\2014\0020";
|
||||
}
|
||||
|
||||
.elevation-waypoint-icon {
|
||||
--ele-marker: url(../images/elevation-pushpin.svg);
|
||||
}
|
||||
|
||||
.elevation-position-icon {
|
||||
--ele-marker: url(../images/elevation-position.svg);
|
||||
}
|
||||
|
||||
/* LIME THEME ///////////////////////////////////////////////////// */
|
||||
.lime-theme {
|
||||
--ele-bg: rgba(156, 194, 34, 0.2);
|
||||
--ele-axis: #566B13;
|
||||
--ele-area: #9CC222;
|
||||
--ele-grid: #CCC;
|
||||
--ele-brush: rgba(99, 126, 11, 0.4);
|
||||
--ele-poly: #566B13;
|
||||
--ele-line: #70ab00;
|
||||
}
|
||||
|
||||
/* STEELBLUE THEME //////////////////////////////////////////////// */
|
||||
.steelblue-theme {
|
||||
--ele-axis: #0D1821;
|
||||
--ele-area: #4682B4;
|
||||
--ele-brush: rgba(23, 74, 117, 0.4);
|
||||
--ele-line: #174A75;
|
||||
}
|
||||
|
||||
/* PURPLE THEME /////////////////////////////////////////////////// */
|
||||
.purple-theme {
|
||||
--ele-bg: rgba(115, 44, 123, 0.2);
|
||||
--ele-area: #732C7B;
|
||||
--ele-brush: rgba(74, 14, 80, 0.4);
|
||||
--ele-line: #732c7b;
|
||||
}
|
||||
|
||||
/* YELLOW THEME /////////////////////////////////////////////////// */
|
||||
.yellow-theme {
|
||||
--ele-area: #FF0;
|
||||
}
|
||||
|
||||
/* RED THEME ////////////////////////////////////////////////////// */
|
||||
.red-theme {
|
||||
--ele-area: #F00;
|
||||
}
|
||||
|
||||
/* MAGENTA THEME ////////////////////////////////////////////////// */
|
||||
.magenta-theme {
|
||||
--ele-bg: rgba(255, 255, 255, 0.47);
|
||||
--ele-area: #FF005E;
|
||||
}
|
||||
|
||||
/* LIGHTBLUE THEME //////////////////////////////////////////////// */
|
||||
.lightblue-theme {
|
||||
--ele-area: #3366CC;
|
||||
--ele-alpha: 0.45;
|
||||
--ele-stroke: #4682B4;
|
||||
--ele-circle: #fff;
|
||||
--ele-line: #000;
|
||||
--ele-grid: rgba(var(--input-border));
|
||||
}
|
||||
|
||||
/* GRAY THEME //////////////////////////////////////////////// */
|
||||
.gray-theme {
|
||||
--ele-bg: #ffffff;
|
||||
--ele-area: #242734;
|
||||
--ele-alpha: 0.45;
|
||||
--ele-stroke: #7b7b7b;
|
||||
--ele-circle: #fff;
|
||||
--ele-line: #000;
|
||||
--ele-grid: #d1d5db;
|
||||
}
|
||||
|
||||
.elevation-detached.lightblue-theme .area {
|
||||
stroke: #3366CC;
|
||||
}
|
||||
|
||||
/* leaflet-distance-markers */
|
||||
.dist-marker {
|
||||
font-size: 0.5rem;
|
||||
border: 1px solid #777;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.leaflet-popup-content {
|
||||
width: max-content !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, GPL-3.0+ Project, Raruto
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see .
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright (c) 2013-2016, MIT License, Felix “MrMufflon” Bache
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software
|
||||
* for any purpose with or without fee is hereby granted, provided
|
||||
* that the above copyright notice and this permission notice appear
|
||||
* in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as _ from './utils';
|
||||
import { Elevation } from './control';
|
||||
|
||||
Elevation.Utils = _;
|
||||
|
||||
L.control.elevation = (options) => new Elevation(options);
|
||||
@@ -1,90 +0,0 @@
|
||||
import * as _ from './utils';
|
||||
|
||||
export var Options = {
|
||||
autofitBounds: true,
|
||||
autohide: false,
|
||||
autohideMarker: true,
|
||||
almostover: true,
|
||||
altitude: true,
|
||||
closeBtn: true,
|
||||
collapsed: false,
|
||||
detached: true,
|
||||
distance: true,
|
||||
distanceMarkers: { lazy: true, distance: true, direction: true },
|
||||
dragging: !L.Browser.mobile,
|
||||
downloadLink: 'link',
|
||||
elevationDiv: "#elevation-div",
|
||||
edgeScale: { bar: true, icon: false, coords: false },
|
||||
followMarker: true,
|
||||
imperial: false,
|
||||
legend: true,
|
||||
handlers: ["Distance", "Time", "Altitude", "Slope", "Speed", "Acceleration"],
|
||||
hotline: 'elevation',
|
||||
marker: 'elevation-line',
|
||||
markerIcon: L.divIcon({
|
||||
className: 'elevation-position-marker',
|
||||
html: '<i class="elevation-position-icon"></i>',
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 16],
|
||||
}),
|
||||
position: "topright",
|
||||
polyline: {
|
||||
className: 'elevation-polyline',
|
||||
color: '#000',
|
||||
opacity: 0.75,
|
||||
weight: 5,
|
||||
lineCap: 'round'
|
||||
},
|
||||
polylineSegments: {
|
||||
className: 'elevation-polyline-segments',
|
||||
color: '#F00',
|
||||
interactive: false,
|
||||
},
|
||||
preferCanvas: false,
|
||||
reverseCoords: false,
|
||||
ruler: true,
|
||||
theme: "lightblue-theme",
|
||||
summary: 'inline',
|
||||
slope: false,
|
||||
speed: false,
|
||||
time: true,
|
||||
timeFactor: 3600,
|
||||
timestamps: false,
|
||||
showStartEnd: true,
|
||||
mapTooltip: true,
|
||||
trkStart: { className: 'start-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#00d800', fillOpacity: 1, interactive: false },
|
||||
trkEnd: { className: 'end-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#ff0606', fillOpacity: 1, interactive: false },
|
||||
waypoints: true,
|
||||
wptIcons: {
|
||||
'': L.divIcon({
|
||||
className: 'elevation-waypoint-marker',
|
||||
html: '<i class="elevation-waypoint-icon default"></i>',
|
||||
iconSize: [30, 30],
|
||||
iconAnchor: [8, 30],
|
||||
}),
|
||||
},
|
||||
wptLabels: true,
|
||||
xAttr: "dist",
|
||||
xLabel: "km",
|
||||
yAttr: "z",
|
||||
yLabel: "m",
|
||||
zFollow: false,
|
||||
zooming: !L.Browser.Mobile,
|
||||
|
||||
// Quite uncommon and undocumented options
|
||||
margins: { top: 30, right: 30, bottom: 30, left: 40 },
|
||||
height: (screen.height * 0.3) || 200,
|
||||
width: (screen.width * 0.6) || 600,
|
||||
xTicks: undefined,
|
||||
yTicks: undefined,
|
||||
|
||||
decimalsX: 2,
|
||||
decimalsY: 0,
|
||||
forceAxisBounds: false,
|
||||
interpolation: "curveLinear",
|
||||
yAxisMax: undefined,
|
||||
yAxisMin: undefined,
|
||||
|
||||
// Prevent CORS issues for relative locations (dynamic import)
|
||||
srcFolder: ((document.currentScript && document.currentScript.src) || (import.meta && import.meta.url)).split("/").slice(0,-1).join("/") + '/',
|
||||
};
|
||||
214
web/src/lib/vendor/leaflet-elevation/src/utils.js
vendored
214
web/src/lib/vendor/leaflet-elevation/src/utils.js
vendored
@@ -1,214 +0,0 @@
|
||||
/**
|
||||
* TODO: exget computed styles of theese values from actual "CSS vars"
|
||||
**/
|
||||
export const Colors = {
|
||||
'lightblue': { area: '#3366CC', alpha: 0.45, stroke: '#3366CC' },
|
||||
'magenta': { area: '#FF005E' },
|
||||
'yellow': { area: '#FF0' },
|
||||
'purple': { area: '#732C7B' },
|
||||
'steelblue': { area: '#4682B4' },
|
||||
'red': { area: '#F00' },
|
||||
'lime': { area: '#9CC222', line: '#566B13' },
|
||||
'gray': { area: '#000000', line: '#3366CC', alpha: 0.00001, stroke: '#000000' }
|
||||
};
|
||||
|
||||
export const LineColors = [
|
||||
'#0058ca',
|
||||
'#E36414',
|
||||
'5f0f40' ,
|
||||
'#9A031E',
|
||||
'#fb8b24',
|
||||
];
|
||||
|
||||
|
||||
const SEC = 1000;
|
||||
const MIN = SEC * 60;
|
||||
const HOUR = MIN * 60;
|
||||
const DAY = HOUR * 24;
|
||||
|
||||
export function resolveURL(src, baseUrl) {
|
||||
console.log(baseUrl, src);
|
||||
return (new URL(src, (src.startsWith('../') || src.startsWith('./')) ? baseUrl : undefined)).toString()
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a time (millis) to a human readable duration string (%Dd %H:%M'%S")
|
||||
*/
|
||||
export function formatTime(t) {
|
||||
let d = Math.floor(t / DAY);
|
||||
let h = Math.floor((t - d * DAY) / HOUR);
|
||||
let m = Math.floor((t - d * DAY - h * HOUR) / MIN);
|
||||
let s = Math.round((t - d * DAY - h * HOUR - m * MIN) / SEC);
|
||||
if (s === 60) { m++; s = 0; }
|
||||
if (m === 60) { h++; m = 0; }
|
||||
if (h === 24) { d++; h = 0; }
|
||||
return (d ? d + "d " : '') + h.toString().padStart(2, 0) + ':' + m.toString().padStart(2, 0) + "'" + s.toString().padStart(2, 0) + '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a time (millis) to human readable date string (dd-mm-yyyy hh:mm:ss)
|
||||
*/
|
||||
export function formatDate(format) {
|
||||
if (!format) {
|
||||
return (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' ');
|
||||
} else if (format == 'time') {
|
||||
return (time) => (new Date(time)).toLocaleTimeString();
|
||||
} else if (format == 'date') {
|
||||
return (time) => (new Date(time)).toLocaleDateString();
|
||||
}
|
||||
return (time) => format(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate download data event.
|
||||
*/
|
||||
export function saveFile(dataURI, fileName) {
|
||||
let a = create('a', '', { href: dataURI, target: '_new', download: fileName || "", style: "display:none;" });
|
||||
let b = document.body;
|
||||
b.appendChild(a);
|
||||
a.click();
|
||||
b.removeChild(a);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert SVG Path into Path2D and then update canvas
|
||||
*/
|
||||
export function drawCanvas(ctx, path) {
|
||||
path.classed('canvas-path', true);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
let p = new Path2D(path.attr('d'));
|
||||
|
||||
ctx.strokeStyle = path.__strokeStyle || path.attr('stroke');
|
||||
ctx.fillStyle = path.__fillStyle || path.attr('fill');
|
||||
ctx.lineWidth = 1.25;
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
|
||||
// stroke opacity
|
||||
ctx.globalAlpha = path.attr('stroke-opacity') || 0.3;
|
||||
ctx.stroke(p);
|
||||
|
||||
// fill opacity
|
||||
ctx.globalAlpha = path.attr('fill-opacity') || 0.45;
|
||||
ctx.fill(p);
|
||||
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop and extract GPX Extensions handled by "@tmcw/toGeoJSON" (eg. "coordinateProperties" > "times")
|
||||
*/
|
||||
export function coordPropsToMeta(coordProps, name, parser) {
|
||||
return coordProps && (({ props, point, id, isMulti }) => {
|
||||
if (props) {
|
||||
for (const key of coordProps) {
|
||||
if (key in props) {
|
||||
point.meta[name] = (parser || parseNumeric).call(this, (isMulti ? props[key][isMulti] : props[key]), id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract numeric property (id) from GeoJSON object
|
||||
*/
|
||||
export const parseNumeric = (property, id) => parseInt((typeof property === 'object' ? property[id] : property));
|
||||
|
||||
/**
|
||||
* Extract datetime property (id) from GeoJSON object
|
||||
*/
|
||||
export const parseDate = (property, id) => new Date(Date.parse((typeof property === 'object' ? property[id] : property)));
|
||||
|
||||
/**
|
||||
* A little bit shorter than L.DomUtil
|
||||
*/
|
||||
export const addClass = (n, str) => n && str.split(" ").every(s => s && L.DomUtil.addClass(n, s));
|
||||
export const removeClass = (n, str) => n && str.split(" ").every(s => s && L.DomUtil.removeClass(n, s));
|
||||
export const toggleClass = (n, str, cond) => (cond ? addClass : removeClass)(n, str);
|
||||
export const replaceClass = (n, rem, add) => (rem && removeClass(n, rem)) || (add && addClass(n, add));
|
||||
export const style = (n, k, v) => (typeof v === "undefined" && L.DomUtil.getStyle(n, k)) || n.style.setProperty(k, v);
|
||||
export const toggleStyle = (n, k, v, cond) => style(n, k, cond ? v : '');
|
||||
export const setAttributes = (n, attrs) => { for (let k in attrs) { n.setAttribute(k, attrs[k]); } };
|
||||
export const toggleEvent = (el, e, fn, cond) => el[cond ? 'on' : 'off'](e, fn);
|
||||
export const create = (tag, str, attrs, n) => { let elem = L.DomUtil.create(tag, str || ""); if (attrs) setAttributes(elem, attrs); if (n) append(n, elem); return elem; };
|
||||
export const append = (n, c) => n.appendChild(c);
|
||||
export const insert = (n, c, pos) => n.insertAdjacentElement(pos, c);
|
||||
export const select = (str, n) => (n || document).querySelector(str);
|
||||
export const each = (obj, fn) => { for (let i in obj) fn(obj[i], i); };
|
||||
export const randomId = () => Math.random().toString(36).substr(2, 9);
|
||||
|
||||
/**
|
||||
* TODO: use generators instead? (ie. "yield")
|
||||
*/
|
||||
export const iMax = (iVal, max = -Infinity) => (iVal > max ? iVal : max);
|
||||
export const iMin = (iVal, min = +Infinity) => (iVal < min ? iVal : min);
|
||||
export const iAvg = (iVal, avg = 0, idx = 1) => (iVal + avg * (idx - 1)) / idx;
|
||||
export const iSum = (iVal, sum = 0) => iVal + sum;
|
||||
|
||||
/**
|
||||
* Alias for some leaflet core functions
|
||||
*/
|
||||
export const { on, off } = L.DomEvent;
|
||||
export const { throttle, wrapNum } = L.Util;
|
||||
export const { hasClass } = L.DomUtil;
|
||||
|
||||
/**
|
||||
* Limit floating point precision
|
||||
*/
|
||||
export const round = L.Util.formatNum;
|
||||
|
||||
/**
|
||||
* Limit a number between min / max values
|
||||
*/
|
||||
export const clamp = (val, range) => range ? (val < range[0] ? range[0] : val > range[1] ? range[1] : val) : val;
|
||||
|
||||
/**
|
||||
* Limit a delta difference between two values
|
||||
*/
|
||||
export const wrapDelta = (curr, prev, deltaMax) => Math.abs(curr - prev) > deltaMax ? prev + deltaMax * Math.sign(curr - prev) : curr;
|
||||
|
||||
/**
|
||||
* A deep copy implementation that takes care of correct prototype chain and cycles, references
|
||||
*
|
||||
* @see https://web.dev/structured-clone/#features-and-limitations
|
||||
*/
|
||||
export function cloneDeep(o, skipProps = [], cache = []) {
|
||||
switch (!o || typeof o) {
|
||||
case 'object':
|
||||
const hit = cache.filter(c => o === c.original)[0];
|
||||
if (hit) return hit.copy; // handle circular structures
|
||||
const copy = Array.isArray(o) ? [] : Object.create(Object.getPrototypeOf(o));
|
||||
cache.push({ original: o, copy });
|
||||
Object
|
||||
.getOwnPropertyNames(o)
|
||||
.forEach(function (prop) {
|
||||
const propdesc = Object.getOwnPropertyDescriptor(o, prop);
|
||||
Object.defineProperty(
|
||||
copy,
|
||||
prop,
|
||||
propdesc.get || propdesc.set
|
||||
? propdesc // just copy accessor properties
|
||||
: { // deep copy data properties
|
||||
writable: propdesc.writable,
|
||||
configurable: propdesc.configurable,
|
||||
enumerable: propdesc.enumerable,
|
||||
value: skipProps.includes(prop) ? propdesc.value : cloneDeep(propdesc.value, skipProps, cache),
|
||||
}
|
||||
);
|
||||
});
|
||||
return copy;
|
||||
case 'function':
|
||||
case 'symbol':
|
||||
console.warn('cloneDeep: ' + typeof o + 's not fully supported:', o);
|
||||
case true:
|
||||
// null, undefined or falsy primitive
|
||||
default:
|
||||
return o;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.leaflet-grid-label .gridlabel-vert {
|
||||
margin-left: 8px;
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.leaflet-grid-label .gridlabel-vert,
|
||||
.leaflet-grid-label .gridlabel-horiz {
|
||||
padding-left:2px;
|
||||
text-shadow: -2px 0 #FFFFFF, 0 2px #FFFFFF, 2px 0 #FFFFFF, 0 -2px #FFFFFF;
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
import { LatLngBounds, type LatLngExpression, type LayerOptions, type LeafletEventHandlerFnMap, type PolylineOptions, Map, LayerGroup, Util, Polyline, LatLng, marker, divIcon, latLngBounds } from 'leaflet';
|
||||
import './leaflet-auto-graticule.css';
|
||||
|
||||
export interface AutoGraticuleOptions extends LayerOptions {
|
||||
redraw: keyof LeafletEventHandlerFnMap,
|
||||
|
||||
/** Minimum distance between two lines in pixels */
|
||||
minDistance: number
|
||||
}
|
||||
|
||||
export default class AutoGraticule extends LayerGroup {
|
||||
|
||||
options: AutoGraticuleOptions = {
|
||||
redraw: 'moveend',
|
||||
minDistance: 100 // Minimum distance between two lines in pixels
|
||||
};
|
||||
|
||||
lineStyle: PolylineOptions = {
|
||||
stroke: true,
|
||||
color: '#333',
|
||||
opacity: 0.6,
|
||||
weight: 1,
|
||||
interactive: false
|
||||
};
|
||||
|
||||
_bounds!: LatLngBounds;
|
||||
|
||||
|
||||
constructor(options?: Partial<AutoGraticuleOptions>) {
|
||||
super();
|
||||
Util.setOptions(this, options);
|
||||
}
|
||||
|
||||
|
||||
onAdd(map: Map) {
|
||||
this._map = map;
|
||||
|
||||
this.redraw();
|
||||
this._map.on('viewreset ' + this.options.redraw, this.redraw, this);
|
||||
|
||||
this.eachLayer(map.addLayer, map);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
onRemove(map: Map) {
|
||||
map.off('viewreset ' + this.options.redraw, this.redraw, this);
|
||||
this.eachLayer(this.removeLayer, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
redraw() {
|
||||
this._bounds = this._map.getBounds().pad(0.5);
|
||||
|
||||
this.clearLayers();
|
||||
|
||||
this.constructLines();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
constructLines() {
|
||||
const bounds = this._map.getBounds();
|
||||
const zoom = this._map.getZoom();
|
||||
|
||||
// Fix drawing of lines outside of bounds
|
||||
this._bounds = AutoGraticule.bboxIntersect(bounds, [[-85, -180], [85, 180]]);
|
||||
|
||||
// Fix drawing of labels outside of bounds
|
||||
const getBoundsBkp = this._map.getBounds;
|
||||
try {
|
||||
this._map.getBounds = function () {
|
||||
return AutoGraticule.bboxIntersect(getBoundsBkp.apply(this), [[-85, -180], [85, 180]])
|
||||
};
|
||||
|
||||
// Longitude: Draw vartical lines with a fixed distance between each other
|
||||
const center = this._map.project(bounds.getCenter(), zoom);
|
||||
const divisor = AutoGraticule.getGridDivisor(this._map.unproject(center.add([this.options.minDistance / 2, 0]), zoom).lng - this._map.unproject(center.subtract([this.options.minDistance / 2, 0]), zoom).lng, false);
|
||||
const west = Math.max(bounds.getWest(), -180);
|
||||
const east = Math.min(bounds.getEast(), 180);
|
||||
for (let lng = AutoGraticule.fixFloatingPoint(Math.ceil(west / divisor) * divisor); lng <= east; lng += divisor) {
|
||||
this.addLayer(this.buildXLine(lng));
|
||||
this.addLayer(this.buildLabel('gridlabel-horiz', AutoGraticule.fixFloatingPoint(lng)));
|
||||
}
|
||||
|
||||
// Latitude: Draw horizontal lines with a variable distance between each other (as in Mercator projection, the distance
|
||||
// between latitudes gets bigger towards the poles). Calculate a divisor that all latitude line coordinates must be
|
||||
// dividable by, but then only draw those coordinates that keep the minimum distance to their neighbour lines.
|
||||
// Draw lines north of the equator separately from lines south of the equator (to keep the grid symmetical in
|
||||
// relation to the equator).
|
||||
if (bounds.getNorth() > 0) {
|
||||
let lat = Math.max(0, bounds.getSouth());
|
||||
let first = true;
|
||||
while (lat < bounds.getNorth() && lat < 85) {
|
||||
const point = this._map.project([lat, bounds.getCenter().lng], zoom);
|
||||
const point2LatLng = this._map.unproject(point.subtract([0, this.options.minDistance]), zoom);
|
||||
|
||||
const divisor = AutoGraticule.getGridDivisor(point2LatLng.lat - lat, true);
|
||||
lat = AutoGraticule.fixFloatingPoint(first ? Math.ceil(lat / divisor) * divisor : Math.ceil(point2LatLng.lat / divisor) * divisor);
|
||||
|
||||
first = false;
|
||||
|
||||
this.addLayer(this.buildYLine(lat));
|
||||
this.addLayer(this.buildLabel('gridlabel-vert', lat));
|
||||
}
|
||||
}
|
||||
if (bounds.getSouth() < 0) {
|
||||
let lat = Math.min(0, bounds.getNorth());
|
||||
let first = true;
|
||||
while (lat > bounds.getSouth() && lat > -85) {
|
||||
const point = this._map.project([lat, bounds.getCenter().lng], zoom);
|
||||
const point2LatLng = this._map.unproject(point.add([0, this.options.minDistance]), zoom);
|
||||
|
||||
const divisor = AutoGraticule.getGridDivisor(AutoGraticule.fixFloatingPoint(lat - point2LatLng.lat), true);
|
||||
lat = AutoGraticule.fixFloatingPoint(first ? Math.floor(lat / divisor) * divisor : Math.floor(point2LatLng.lat / divisor) * divisor);
|
||||
|
||||
first = false;
|
||||
|
||||
this.addLayer(this.buildYLine(lat));
|
||||
this.addLayer(this.buildLabel('gridlabel-vert', lat));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this._map.getBounds = getBoundsBkp;
|
||||
}
|
||||
}
|
||||
|
||||
buildXLine(x: number): Polyline {
|
||||
const bottomLL = new LatLng(this._bounds.getSouth(), x);
|
||||
const topLL = new LatLng(this._bounds.getNorth(), x);
|
||||
|
||||
return new Polyline([bottomLL, topLL], this.lineStyle);
|
||||
}
|
||||
|
||||
buildYLine(y: number): L.Polyline {
|
||||
const leftLL = new LatLng(y, this._bounds.getWest());
|
||||
const rightLL = new LatLng(y, this._bounds.getEast());
|
||||
|
||||
return new Polyline([leftLL, rightLL], this.lineStyle);
|
||||
}
|
||||
|
||||
buildLabel(axis: 'gridlabel-horiz' | 'gridlabel-vert', val: number) {
|
||||
const bounds = this._map.getBounds().pad(-0.003);
|
||||
let latLng: LatLng;
|
||||
if (axis == 'gridlabel-horiz') {
|
||||
latLng = new LatLng(bounds.getNorth(), val);
|
||||
} else {
|
||||
latLng = new LatLng(val, bounds.getWest());
|
||||
}
|
||||
|
||||
return marker(latLng, {
|
||||
interactive: false,
|
||||
icon: divIcon({
|
||||
iconSize: [0, 0],
|
||||
className: 'leaflet-grid-label',
|
||||
html: '<div class="' + axis + '">' + val + ' °</div>'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given number to a fixed number of decimals in order to avoid floating point inaccuracies
|
||||
* (for example to make 0.1 + 0.2 = 0.3 instead of 0.30000000000000004).
|
||||
*/
|
||||
static fixFloatingPoint(number: number): number {
|
||||
return AutoGraticule.round(number, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given number to the given number of decimals.
|
||||
*/
|
||||
static round(number: number, digits: number) {
|
||||
const fac = Math.pow(10, digits);
|
||||
return Math.round(number * fac) / fac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the distance between two coordinates, floors this distance to 90, 60, 45, 30 or
|
||||
* 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01, ...
|
||||
* This will define the distance between two grid lines.
|
||||
* @param variableDistance This should be true when the distance between the grid lines will be variable, that is for the latitude
|
||||
* lines (because in Meractor projection the distance between two latitutes gets larger towards the poles, but the distance
|
||||
* between two longitudes is constant throughout the globe).
|
||||
* For constant distance lines (longitude), a line should be shown for every coordinate dividable by the result of this
|
||||
* function. For example, if the result is 45, a line should be shown for longitudes -180, -135, -90, -45, 0, 45, 90, 135, 180.
|
||||
* For variable distance lines (latitude), a line should be shown for coordinates dividable by a result of this function, but
|
||||
* not for every single coordinate but only those that have a minimum distance towards their neighbour coordinate line. For
|
||||
* variable distance lines, the maximum number returned by this function is 5, meaning at low zoom levels, all latitude lines
|
||||
* are dividable by 5 (but not every latitude dividable by 5 will get a line).
|
||||
*/
|
||||
static getGridDivisor(number: number, variableDistance: boolean) {
|
||||
if (number <= 0 || !isFinite(number))
|
||||
throw new Error("Invalid number " + number);
|
||||
else {
|
||||
if (variableDistance && number >= 5)
|
||||
return 5;
|
||||
if (number <= 10) {
|
||||
let fac = 1;
|
||||
while (number > 1) { fac *= 10; number /= 10; }
|
||||
while (number <= 0.1) { fac /= 10; number *= 10; }
|
||||
|
||||
// Dist is now some number between 0.1 and 1, so we can round it conveniently and then multiply it again by fac to get back to the original dist
|
||||
|
||||
if (number == 0.1)
|
||||
return AutoGraticule.fixFloatingPoint(0.1 * fac);
|
||||
else if (number <= 0.2)
|
||||
return AutoGraticule.fixFloatingPoint(0.2 * fac);
|
||||
else if (number <= 0.5)
|
||||
return AutoGraticule.fixFloatingPoint(0.5 * fac);
|
||||
else
|
||||
return fac;
|
||||
} else if (number <= 30)
|
||||
return 30;
|
||||
else if (number <= 45)
|
||||
return 45;
|
||||
else if (number <= 60)
|
||||
return 60;
|
||||
else
|
||||
return 90;
|
||||
}
|
||||
}
|
||||
// Backwards compatibility
|
||||
static niceRound = AutoGraticule.getGridDivisor;
|
||||
|
||||
static bboxIntersect(bbox1: LatLngBounds | LatLngExpression[], bbox2: LatLngBounds | LatLngExpression[]) {
|
||||
const bounds1 = bbox1 instanceof LatLngBounds ? bbox1 : latLngBounds(bbox1);
|
||||
const bounds2 = bbox2 instanceof LatLngBounds ? bbox2 : latLngBounds(bbox2);
|
||||
return latLngBounds([
|
||||
[Math.max(bounds1.getSouth(), bounds2.getSouth()), Math.max(bounds1.getWest(), bounds2.getWest())],
|
||||
[Math.min(bounds1.getNorth(), bounds2.getNorth()), Math.min(bounds1.getEast(), bounds2.getEast())]
|
||||
]);
|
||||
}
|
||||
}
|
||||
280
web/src/lib/vendor/leaflet-image/leaflet-image.js
vendored
280
web/src/lib/vendor/leaflet-image/leaflet-image.js
vendored
@@ -1,280 +0,0 @@
|
||||
import queue from "./queue";
|
||||
|
||||
var cacheBusterDate = +new Date();
|
||||
|
||||
// leaflet-image
|
||||
export default function leafletImage(map, callback) {
|
||||
|
||||
var dimensions = map.getSize(),
|
||||
layerQueue = new queue(1);
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = dimensions.x;
|
||||
canvas.height = dimensions.y;
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// dummy canvas image when loadTile get 404 error
|
||||
// and layer don't have errorTileUrl
|
||||
var dummycanvas = document.createElement('canvas');
|
||||
dummycanvas.width = 1;
|
||||
dummycanvas.height = 1;
|
||||
var dummyctx = dummycanvas.getContext('2d');
|
||||
dummyctx.fillStyle = 'rgba(0,0,0,0)';
|
||||
dummyctx.fillRect(0, 0, 1, 1);
|
||||
|
||||
// layers are drawn in the same order as they are composed in the DOM:
|
||||
// tiles, paths, and then markers
|
||||
map.eachLayer(drawTileLayer);
|
||||
map.eachLayer(drawEsriDynamicLayer);
|
||||
|
||||
if (map._pathRoot) {
|
||||
layerQueue.defer(handlePathRoot, map._pathRoot);
|
||||
} else if (map._panes) {
|
||||
var firstCanvas = map._panes.overlayPane.getElementsByTagName('canvas').item(0);
|
||||
if (firstCanvas) {
|
||||
layerQueue.defer(handlePathRoot, firstCanvas);
|
||||
}
|
||||
}
|
||||
map.eachLayer(drawMarkerLayer);
|
||||
layerQueue.awaitAll(layersDone);
|
||||
|
||||
function drawTileLayer(l) {
|
||||
if (l instanceof L.TileLayer) layerQueue.defer(handleTileLayer, l);
|
||||
else if (l._heat) layerQueue.defer(handlePathRoot, l._canvas);
|
||||
}
|
||||
|
||||
function drawMarkerLayer(l) {
|
||||
if (l instanceof L.Marker && l.options.icon instanceof L.Icon) {
|
||||
layerQueue.defer(handleMarkerLayer, l);
|
||||
}
|
||||
}
|
||||
|
||||
function drawEsriDynamicLayer(l) {
|
||||
if (!L.esri) return;
|
||||
|
||||
if (l instanceof L.esri.DynamicMapLayer) {
|
||||
layerQueue.defer(handleEsriDymamicLayer, l);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
callback(null, canvas);
|
||||
}
|
||||
|
||||
function layersDone(err, layers) {
|
||||
if (err) throw err;
|
||||
layers.forEach(function (layer) {
|
||||
if (layer && layer.canvas) {
|
||||
ctx.drawImage(layer.canvas, 0, 0);
|
||||
}
|
||||
});
|
||||
done();
|
||||
}
|
||||
|
||||
function handleTileLayer(layer, callback) {
|
||||
// `L.TileLayer.Canvas` was removed in leaflet 1.0
|
||||
var isCanvasLayer = (L.TileLayer.Canvas && layer instanceof L.TileLayer.Canvas),
|
||||
canvas = document.createElement('canvas');
|
||||
|
||||
canvas.width = dimensions.x;
|
||||
canvas.height = dimensions.y;
|
||||
|
||||
var ctx = canvas.getContext('2d'),
|
||||
bounds = map.getPixelBounds(),
|
||||
zoom = map.getZoom(),
|
||||
tileSize = layer.options.tileSize;
|
||||
|
||||
if (zoom > layer.options.maxZoom ||
|
||||
zoom < layer.options.minZoom
|
||||
) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
var tileBounds = L.bounds(
|
||||
bounds.min.divideBy(tileSize)._floor(),
|
||||
bounds.max.divideBy(tileSize)._floor()),
|
||||
tiles = [],
|
||||
j, i,
|
||||
tileQueue = new queue(1);
|
||||
|
||||
for (j = tileBounds.min.y; j <= tileBounds.max.y; j++) {
|
||||
for (i = tileBounds.min.x; i <= tileBounds.max.x; i++) {
|
||||
tiles.push(new L.Point(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
tiles.forEach(function (tilePoint) {
|
||||
var originalTilePoint = tilePoint.clone();
|
||||
|
||||
if (layer._adjustTilePoint) {
|
||||
layer._adjustTilePoint(tilePoint);
|
||||
}
|
||||
|
||||
var tilePos = originalTilePoint
|
||||
.scaleBy(new L.Point(tileSize, tileSize))
|
||||
.subtract(bounds.min);
|
||||
|
||||
if (tilePoint.y >= 0) {
|
||||
if (isCanvasLayer) {
|
||||
var tile = layer._tiles[tilePoint.x + ':' + tilePoint.y];
|
||||
tileQueue.defer(canvasTile, tile, tilePos, tileSize);
|
||||
} else {
|
||||
var url = addCacheString(layer.getTileUrl(tilePoint));
|
||||
tileQueue.defer(loadTile, url, tilePos, tileSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tileQueue.awaitAll(tileQueueFinish);
|
||||
|
||||
function canvasTile(tile, tilePos, tileSize, callback) {
|
||||
callback(null, {
|
||||
img: tile,
|
||||
pos: tilePos,
|
||||
size: tileSize
|
||||
});
|
||||
}
|
||||
|
||||
function loadTile(url, tilePos, tileSize, callback) {
|
||||
var im = new Image();
|
||||
im.crossOrigin = '';
|
||||
im.onload = function () {
|
||||
callback(null, {
|
||||
img: this,
|
||||
pos: tilePos,
|
||||
size: tileSize
|
||||
});
|
||||
};
|
||||
im.onerror = function (e) {
|
||||
// use canvas instead of errorTileUrl if errorTileUrl get 404
|
||||
if (layer.options.errorTileUrl != '' && e.target.errorCheck === undefined) {
|
||||
e.target.errorCheck = true;
|
||||
e.target.src = layer.options.errorTileUrl;
|
||||
} else {
|
||||
callback(null, {
|
||||
img: dummycanvas,
|
||||
pos: tilePos,
|
||||
size: tileSize
|
||||
});
|
||||
}
|
||||
};
|
||||
im.src = url;
|
||||
}
|
||||
|
||||
function tileQueueFinish(err, data) {
|
||||
data.forEach(drawTile);
|
||||
callback(null, { canvas: canvas });
|
||||
}
|
||||
|
||||
function drawTile(d) {
|
||||
ctx.drawImage(d.img, Math.floor(d.pos.x), Math.floor(d.pos.y),
|
||||
d.size, d.size);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePathRoot(root, callback) {
|
||||
var bounds = map.getPixelBounds(),
|
||||
origin = map.getPixelOrigin(),
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = dimensions.x;
|
||||
canvas.height = dimensions.y;
|
||||
var ctx = canvas.getContext('2d');
|
||||
var pos = L.DomUtil.getPosition(root).subtract(bounds.min).add(origin);
|
||||
try {
|
||||
ctx.drawImage(root, pos.x, pos.y, canvas.width - (pos.x * 2), canvas.height - (pos.y * 2));
|
||||
callback(null, {
|
||||
canvas: canvas
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Element could not be drawn on canvas', root); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
function handleMarkerLayer(marker, callback) {
|
||||
var canvas = document.createElement('canvas'),
|
||||
ctx = canvas.getContext('2d'),
|
||||
pixelBounds = map.getPixelBounds(),
|
||||
minPoint = new L.Point(pixelBounds.min.x, pixelBounds.min.y),
|
||||
pixelPoint = map.project(marker.getLatLng()),
|
||||
url = "/vendor/leaflet-image/marker.png",
|
||||
im = new Image(),
|
||||
options = marker.options.icon.options,
|
||||
size = options.iconSize,
|
||||
pos = pixelPoint.subtract(minPoint),
|
||||
anchor = L.point(options.iconAnchor || size && { x: size[0], y: size[1] });
|
||||
|
||||
if (size instanceof L.Point) size = [size.x, size.y];
|
||||
|
||||
var x = Math.round(pos.x - size[0] + anchor.x),
|
||||
y = Math.round(pos.y - anchor.y);
|
||||
|
||||
canvas.width = dimensions.x;
|
||||
canvas.height = dimensions.y;
|
||||
im.crossOrigin = '';
|
||||
|
||||
im.onload = function () {
|
||||
if (marker._icon.classList.contains("leaflet-grid-label")) {
|
||||
const label = marker._icon.children[0].textContent;
|
||||
ctx.font = '600 10px "IBMPlexSans"';
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.fillText(label, x + 4, y + 12);
|
||||
} else {
|
||||
const waypointName = marker.options.meta?.waypointName;
|
||||
const icon = getComputedStyle(marker._icon.children[0], ':before').getPropertyValue('content').substring(1, 2) ?? "\uf111";
|
||||
ctx.drawImage(this, x, y, size[0], size[1]);
|
||||
ctx.font = '600 14px "Font Awesome 6 Free"';
|
||||
ctx.fillStyle = "#ffffff";
|
||||
const iconWidth = ctx.measureText(icon).width
|
||||
ctx.fillText(icon, x + size[0] / 2 - iconWidth / 2, y + 24);
|
||||
if (waypointName) {
|
||||
ctx.fillStyle = "rgba(49, 49, 49, 0.5)"
|
||||
ctx.font = '600 12px "IBMPlexSans"';
|
||||
const textWidth = ctx.measureText(waypointName).width
|
||||
ctx.roundRect(size[0] / 2 + x - (textWidth + 16) / 2, y - 18, textWidth + 16, 20, 5)
|
||||
ctx.fill();
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fillText(waypointName, size[0] / 2 + x - (textWidth + 16) / 2 + 8, y - 4);
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
canvas: canvas
|
||||
});
|
||||
};
|
||||
|
||||
im.src = url;
|
||||
}
|
||||
|
||||
function handleEsriDymamicLayer(dynamicLayer, callback) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = dimensions.x;
|
||||
canvas.height = dimensions.y;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
var im = new Image();
|
||||
im.crossOrigin = '';
|
||||
im.src = addCacheString(dynamicLayer._currentImage._image.src);
|
||||
|
||||
im.onload = function () {
|
||||
ctx.drawImage(im, 0, 0);
|
||||
callback(null, {
|
||||
canvas: canvas
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function addCacheString(url) {
|
||||
// If it's a data URL we don't want to touch this.
|
||||
if (isDataURL(url)) {
|
||||
return url;
|
||||
}
|
||||
return url + ((url.match(/\?/)) ? '&' : '?') + 'cache=' + cacheBusterDate;
|
||||
}
|
||||
|
||||
function isDataURL(url) {
|
||||
var dataURLRegex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
|
||||
return !!url.match(dataURLRegex);
|
||||
}
|
||||
|
||||
};
|
||||
120
web/src/lib/vendor/leaflet-image/queue.js
vendored
120
web/src/lib/vendor/leaflet-image/queue.js
vendored
@@ -1,120 +0,0 @@
|
||||
var noabort = {};
|
||||
const slice = [].slice;
|
||||
|
||||
function Queue(size) {
|
||||
this._size = size;
|
||||
this._call =
|
||||
this._error = null;
|
||||
this._tasks = [];
|
||||
this._data = [];
|
||||
this._waiting =
|
||||
this._active =
|
||||
this._ended =
|
||||
this._start = 0; // inside a synchronous task callback?
|
||||
}
|
||||
|
||||
Queue.prototype = queue.prototype = {
|
||||
constructor: Queue,
|
||||
defer: function(callback) {
|
||||
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||
if (this._call) throw new Error("defer after await");
|
||||
if (this._error != null) return this;
|
||||
var t = slice.call(arguments, 1);
|
||||
t.push(callback);
|
||||
++this._waiting, this._tasks.push(t);
|
||||
poke(this);
|
||||
return this;
|
||||
},
|
||||
abort: function() {
|
||||
if (this._error == null) abort(this, new Error("abort"));
|
||||
return this;
|
||||
},
|
||||
await: function(callback) {
|
||||
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||
if (this._call) throw new Error("multiple await");
|
||||
this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
|
||||
maybeNotify(this);
|
||||
return this;
|
||||
},
|
||||
awaitAll: function(callback) {
|
||||
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||
if (this._call) throw new Error("multiple await");
|
||||
this._call = callback;
|
||||
maybeNotify(this);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
function poke(q) {
|
||||
if (!q._start) {
|
||||
try { start(q); } // let the current task complete
|
||||
catch (e) {
|
||||
if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
|
||||
else if (!q._data) throw e; // await callback errored synchronously
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function start(q) {
|
||||
while (q._start = q._waiting && q._active < q._size) {
|
||||
var i = q._ended + q._active,
|
||||
t = q._tasks[i],
|
||||
j = t.length - 1,
|
||||
c = t[j];
|
||||
t[j] = end(q, i);
|
||||
--q._waiting, ++q._active;
|
||||
t = c.apply(null, t);
|
||||
if (!q._tasks[i]) continue; // task finished synchronously
|
||||
q._tasks[i] = t || noabort;
|
||||
}
|
||||
}
|
||||
|
||||
function end(q, i) {
|
||||
return function(e, r) {
|
||||
if (!q._tasks[i]) return; // ignore multiple callbacks
|
||||
--q._active, ++q._ended;
|
||||
q._tasks[i] = null;
|
||||
if (q._error != null) return; // ignore secondary errors
|
||||
if (e != null) {
|
||||
abort(q, e);
|
||||
} else {
|
||||
q._data[i] = r;
|
||||
if (q._waiting) poke(q);
|
||||
else maybeNotify(q);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function abort(q, e) {
|
||||
var i = q._tasks.length, t;
|
||||
q._error = e; // ignore active callbacks
|
||||
q._data = undefined; // allow gc
|
||||
q._waiting = NaN; // prevent starting
|
||||
|
||||
while (--i >= 0) {
|
||||
if (t = q._tasks[i]) {
|
||||
q._tasks[i] = null;
|
||||
if (t.abort) {
|
||||
try { t.abort(); }
|
||||
catch (e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q._active = NaN; // allow notification
|
||||
maybeNotify(q);
|
||||
}
|
||||
|
||||
function maybeNotify(q) {
|
||||
if (!q._active && q._call) {
|
||||
var d = q._data;
|
||||
q._data = undefined; // allow gc
|
||||
q._call(q._error, d);
|
||||
}
|
||||
}
|
||||
|
||||
export default function queue(concurrency) {
|
||||
if (concurrency == null) concurrency = Infinity;
|
||||
else if (!((concurrency = +concurrency) >= 1)) throw new Error("invalid concurrency");
|
||||
return new Queue(concurrency);
|
||||
}
|
||||
42
web/src/lib/vendor/maplibre-fullscreen/fullscreen-control.ts
vendored
Normal file
42
web/src/lib/vendor/maplibre-fullscreen/fullscreen-control.ts
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import * as M from "maplibre-gl";
|
||||
import { type ControlPosition, type IControl } from "maplibre-gl";
|
||||
|
||||
export class FullscreenControl implements IControl {
|
||||
private buttonContainer?: HTMLDivElement;
|
||||
private fullscreenButton?: HTMLButtonElement;
|
||||
private iconSpan?: HTMLSpanElement;
|
||||
|
||||
private callback: (e: MouseEvent) => void;
|
||||
|
||||
constructor(callback: (e: MouseEvent) => void) {
|
||||
this.callback = callback;
|
||||
|
||||
}
|
||||
|
||||
|
||||
onAdd(map: M.Map): HTMLElement {
|
||||
this.buttonContainer = document.createElement("div");
|
||||
|
||||
this.buttonContainer.classList.add(
|
||||
"maplibregl-ctrl",
|
||||
"maplibregl-ctrl-group"
|
||||
);
|
||||
this.fullscreenButton = document.createElement("button");
|
||||
this.buttonContainer.appendChild(this.fullscreenButton);
|
||||
this.iconSpan = document.createElement("i");
|
||||
this.iconSpan.classList.add("fa", "fa-maximize", "text-black");
|
||||
this.fullscreenButton.appendChild(this.iconSpan);
|
||||
this.fullscreenButton.addEventListener("click", this.callback);
|
||||
|
||||
return this.buttonContainer;
|
||||
}
|
||||
|
||||
onRemove(): void {
|
||||
// remove button
|
||||
if (this.buttonContainer?.parentNode) {
|
||||
this.buttonContainer.parentNode.removeChild(this.buttonContainer);
|
||||
}
|
||||
this.buttonContainer = undefined;
|
||||
this.fullscreenButton = undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user