umap-project 3.2.0__py3-none-any.whl → 3.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +42 -38
- umap/locale/en/LC_MESSAGES/django.mo +0 -0
- umap/locale/en/LC_MESSAGES/django.po +15 -15
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +39 -35
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +31 -27
- umap/settings/base.py +2 -0
- umap/static/umap/css/contextmenu.css +58 -2
- umap/static/umap/css/form.css +175 -45
- umap/static/umap/css/icon.css +20 -0
- umap/static/umap/img/16-white.svg +21 -40
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24-white.svg +9 -9
- umap/static/umap/img/24.svg +23 -10
- umap/static/umap/img/source/16-white.svg +23 -41
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24-white.svg +11 -11
- umap/static/umap/img/source/24.svg +25 -12
- umap/static/umap/js/modules/caption.js +8 -0
- umap/static/umap/js/modules/data/features.js +318 -174
- umap/static/umap/js/modules/data/layer.js +27 -20
- umap/static/umap/js/modules/form/builder.js +11 -7
- umap/static/umap/js/modules/form/fields.js +10 -7
- umap/static/umap/js/modules/formatter.js +42 -20
- umap/static/umap/js/modules/importer.js +6 -1
- umap/static/umap/js/modules/importers/opendata.js +125 -37
- umap/static/umap/js/modules/importers/openrouteservice.js +140 -0
- umap/static/umap/js/modules/managers.js +12 -4
- umap/static/umap/js/modules/printer.js +107 -0
- umap/static/umap/js/modules/rendering/controls.js +78 -2
- umap/static/umap/js/modules/rendering/icon.js +113 -82
- umap/static/umap/js/modules/rendering/layers/cluster.js +220 -64
- umap/static/umap/js/modules/rendering/map.js +5 -1
- umap/static/umap/js/modules/rendering/template.js +71 -1
- umap/static/umap/js/modules/rendering/ui.js +101 -34
- umap/static/umap/js/modules/schema.js +24 -0
- umap/static/umap/js/modules/share.js +19 -12
- umap/static/umap/js/modules/ui/bar.js +6 -1
- umap/static/umap/js/modules/ui/base.js +24 -9
- umap/static/umap/js/modules/ui/contextmenu.js +17 -7
- umap/static/umap/js/modules/ui/dialog.js +7 -4
- umap/static/umap/js/modules/umap.js +68 -62
- umap/static/umap/js/umap.controls.js +22 -57
- umap/static/umap/locale/am_ET.js +39 -4
- umap/static/umap/locale/am_ET.json +39 -4
- umap/static/umap/locale/ar.js +39 -4
- umap/static/umap/locale/ar.json +39 -4
- umap/static/umap/locale/ast.js +39 -4
- umap/static/umap/locale/ast.json +39 -4
- umap/static/umap/locale/bg.js +39 -4
- umap/static/umap/locale/bg.json +39 -4
- umap/static/umap/locale/br.js +39 -4
- umap/static/umap/locale/br.json +39 -4
- umap/static/umap/locale/ca.js +39 -4
- umap/static/umap/locale/ca.json +39 -4
- umap/static/umap/locale/cs_CZ.js +39 -4
- umap/static/umap/locale/cs_CZ.json +39 -4
- umap/static/umap/locale/da.js +47 -12
- umap/static/umap/locale/da.json +47 -12
- umap/static/umap/locale/de.js +39 -4
- umap/static/umap/locale/de.json +39 -4
- umap/static/umap/locale/el.js +81 -46
- umap/static/umap/locale/el.json +81 -46
- umap/static/umap/locale/en.js +38 -4
- umap/static/umap/locale/en.json +38 -4
- umap/static/umap/locale/en_US.json +39 -4
- umap/static/umap/locale/es.js +47 -12
- umap/static/umap/locale/es.json +47 -12
- umap/static/umap/locale/et.js +39 -4
- umap/static/umap/locale/et.json +39 -4
- umap/static/umap/locale/eu.js +80 -45
- umap/static/umap/locale/eu.json +80 -45
- umap/static/umap/locale/fa_IR.js +39 -4
- umap/static/umap/locale/fa_IR.json +39 -4
- umap/static/umap/locale/fi.js +39 -4
- umap/static/umap/locale/fi.json +39 -4
- umap/static/umap/locale/fr.js +39 -4
- umap/static/umap/locale/fr.json +39 -4
- umap/static/umap/locale/gl.js +39 -4
- umap/static/umap/locale/gl.json +39 -4
- umap/static/umap/locale/he.js +39 -4
- umap/static/umap/locale/he.json +39 -4
- umap/static/umap/locale/hr.js +39 -4
- umap/static/umap/locale/hr.json +39 -4
- umap/static/umap/locale/hu.js +55 -20
- umap/static/umap/locale/hu.json +55 -20
- umap/static/umap/locale/id.js +39 -4
- umap/static/umap/locale/id.json +39 -4
- umap/static/umap/locale/is.js +39 -4
- umap/static/umap/locale/is.json +39 -4
- umap/static/umap/locale/it.js +39 -4
- umap/static/umap/locale/it.json +39 -4
- umap/static/umap/locale/ja.js +39 -4
- umap/static/umap/locale/ja.json +39 -4
- umap/static/umap/locale/ko.js +39 -4
- umap/static/umap/locale/ko.json +39 -4
- umap/static/umap/locale/lt.js +39 -4
- umap/static/umap/locale/lt.json +39 -4
- umap/static/umap/locale/ms.js +52 -17
- umap/static/umap/locale/ms.json +52 -17
- umap/static/umap/locale/nl.js +58 -23
- umap/static/umap/locale/nl.json +58 -23
- umap/static/umap/locale/no.js +39 -4
- umap/static/umap/locale/no.json +39 -4
- umap/static/umap/locale/pl.js +39 -4
- umap/static/umap/locale/pl.json +39 -4
- umap/static/umap/locale/pl_PL.json +39 -4
- umap/static/umap/locale/pt.js +39 -4
- umap/static/umap/locale/pt.json +39 -4
- umap/static/umap/locale/pt_BR.js +39 -4
- umap/static/umap/locale/pt_BR.json +39 -4
- umap/static/umap/locale/pt_PT.js +39 -4
- umap/static/umap/locale/pt_PT.json +39 -4
- umap/static/umap/locale/ro.js +39 -4
- umap/static/umap/locale/ro.json +39 -4
- umap/static/umap/locale/ru.js +39 -4
- umap/static/umap/locale/ru.json +39 -4
- umap/static/umap/locale/sk_SK.js +39 -4
- umap/static/umap/locale/sk_SK.json +39 -4
- umap/static/umap/locale/sl.js +39 -4
- umap/static/umap/locale/sl.json +39 -4
- umap/static/umap/locale/sr.js +39 -4
- umap/static/umap/locale/sr.json +39 -4
- umap/static/umap/locale/sv.js +39 -4
- umap/static/umap/locale/sv.json +39 -4
- umap/static/umap/locale/th_TH.js +39 -4
- umap/static/umap/locale/th_TH.json +39 -4
- umap/static/umap/locale/tr.js +39 -4
- umap/static/umap/locale/tr.json +39 -4
- umap/static/umap/locale/uk_UA.js +39 -4
- umap/static/umap/locale/uk_UA.json +39 -4
- umap/static/umap/locale/vi.js +39 -4
- umap/static/umap/locale/vi.json +39 -4
- umap/static/umap/locale/vi_VN.json +39 -4
- umap/static/umap/locale/zh.js +39 -4
- umap/static/umap/locale/zh.json +39 -4
- umap/static/umap/locale/zh_CN.json +39 -4
- umap/static/umap/locale/zh_TW.Big5.json +39 -4
- umap/static/umap/locale/zh_TW.js +98 -63
- umap/static/umap/locale/zh_TW.json +98 -63
- umap/static/umap/map.css +90 -41
- umap/static/umap/vars.css +1 -0
- umap/static/umap/vendors/editable/Leaflet.Editable.js +3 -1
- umap/static/umap/vendors/openrouteservice/ors-js-client.js +521 -0
- umap/static/umap/vendors/openrouteservice/ors-js-client.js.map +1 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.js +63 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.svg +8 -0
- umap/static/umap/vendors/snapdom/snapdom.min.mjs +3 -0
- umap/storage/staticfiles.py +12 -0
- umap/templates/umap/css.html +0 -4
- umap/templates/umap/js.html +1 -3
- umap/tests/integration/test_basics.py +2 -0
- umap/tests/integration/test_conditional_rules.py +17 -17
- umap/tests/integration/test_datalayer.py +1 -1
- umap/tests/integration/test_draw_polygon.py +3 -5
- umap/tests/integration/test_draw_polyline.py +4 -6
- umap/tests/integration/test_draw_route.py +178 -0
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_edit_marker.py +7 -7
- umap/tests/integration/test_edit_polygon.py +2 -2
- umap/tests/integration/test_export_map.py +74 -10
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_share.py +1 -1
- umap/tests/integration/test_tableeditor.py +4 -4
- umap/tests/integration/test_websocket_sync.py +4 -4
- umap/utils.py +5 -1
- umap/views.py +2 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/METADATA +9 -9
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/RECORD +175 -171
- umap/static/umap/vendors/markercluster/MarkerCluster.Default.css +0 -60
- umap/static/umap/vendors/markercluster/MarkerCluster.css +0 -14
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js +0 -2
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js.map +0 -1
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/WHEEL +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/entry_points.txt +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,24 +1,100 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
FeatureGroup,
|
|
3
|
+
LayerGroup,
|
|
4
|
+
Point,
|
|
5
|
+
Marker,
|
|
6
|
+
Rectangle,
|
|
7
|
+
Polyline,
|
|
8
|
+
DomUtil,
|
|
9
|
+
latLngBounds,
|
|
10
|
+
} from '../../../../vendors/leaflet/leaflet-src.esm.js'
|
|
4
11
|
import { translate } from '../../i18n.js'
|
|
5
12
|
import * as Utils from '../../utils.js'
|
|
6
13
|
import { Cluster as ClusterIcon } from '../icon.js'
|
|
7
14
|
import { LayerMixin } from './base.js'
|
|
8
15
|
|
|
9
|
-
const MarkerCluster =
|
|
10
|
-
// Custom class so we can call computeTextColor
|
|
11
|
-
// when element is already on the DOM.
|
|
12
|
-
|
|
16
|
+
const MarkerCluster = Marker.extend({
|
|
13
17
|
_initIcon: function () {
|
|
14
|
-
|
|
15
|
-
const
|
|
18
|
+
Marker.prototype._initIcon.call(this)
|
|
19
|
+
const counter = this._icon.querySelector('span')
|
|
16
20
|
// Compute text color only when icon is added to the DOM.
|
|
17
|
-
|
|
21
|
+
const bgColor = this.options.icon.options.color
|
|
22
|
+
const textColor = this.options.icon.options.textColor
|
|
23
|
+
counter.style.color =
|
|
24
|
+
textColor || DomUtil.TextColorFromBackgroundColor(counter, bgColor)
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
computeCoverage() {
|
|
28
|
+
if (this._layers.length < 2) return
|
|
29
|
+
if (!this._coverage) {
|
|
30
|
+
const latlngs = this._layers.map((layer) => layer._latlng)
|
|
31
|
+
const bounds = latLngBounds(latlngs)
|
|
32
|
+
this._coverage = new Rectangle(latlngs, {
|
|
33
|
+
color: this.options.icon.options.color,
|
|
34
|
+
stroke: false,
|
|
35
|
+
})
|
|
36
|
+
this._latlng = bounds.getCenter()
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
async zoomToCoverage() {
|
|
41
|
+
let resolve = undefined
|
|
42
|
+
const promise = new Promise((r) => {
|
|
43
|
+
resolve = r
|
|
44
|
+
})
|
|
45
|
+
if (this._map && this._coverage) {
|
|
46
|
+
this._map.once('moveend', () => resolve())
|
|
47
|
+
this._map.fitBounds(this._coverage.getBounds())
|
|
48
|
+
}
|
|
49
|
+
return promise
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
_spiderfyLatLng: function (center, index) {
|
|
53
|
+
const step = 20
|
|
54
|
+
const maxRadius = 150
|
|
55
|
+
const zoom = this._map.getZoom()
|
|
56
|
+
const angle = (index * step * Math.PI) / 180
|
|
57
|
+
const progress = index / this._layers.length
|
|
58
|
+
const radius = maxRadius * (1 - progress) ** 0.4
|
|
59
|
+
const x = radius * Math.cos(angle)
|
|
60
|
+
const y = radius * Math.sin(angle)
|
|
61
|
+
const point = this._map.project([center.lat, center.lng], zoom)
|
|
62
|
+
const latlng = this._map.unproject(new Point(point.x + x, point.y + y), zoom)
|
|
63
|
+
return latlng
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
spiderfy() {
|
|
67
|
+
if (!this._map) return
|
|
68
|
+
const crs = this._map.options.crs
|
|
69
|
+
if (this._spider && this._map.hasLayer(this._spider)) this.unspiderfy()
|
|
70
|
+
this._spider = new LayerGroup()
|
|
71
|
+
let i = 1
|
|
72
|
+
const center = this.getLatLng()
|
|
73
|
+
for (const layer of this._layers) {
|
|
74
|
+
const latlng = this._spiderfyLatLng(center, i++)
|
|
75
|
+
layer._originalLatLng = layer._latlng
|
|
76
|
+
layer.setLatLng(latlng)
|
|
77
|
+
this._spider.addLayer(layer)
|
|
78
|
+
const line = new Polyline([center, latlng], { color: 'black', weight: 1 })
|
|
79
|
+
this._spider.addLayer(line)
|
|
80
|
+
}
|
|
81
|
+
this._map.addLayer(this._spider)
|
|
82
|
+
this._icon.hidden = true
|
|
83
|
+
this._map.once('click zoomstart', this.unspiderfy, this)
|
|
84
|
+
this.once('remove', this.unspiderfy, this)
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
unspiderfy() {
|
|
88
|
+
if (this._icon) this._icon.hidden = false
|
|
89
|
+
if (this._spider) this._spider.remove()
|
|
90
|
+
for (const layer of this._layers) {
|
|
91
|
+
if (layer._originalLatLng) layer.setLatLng(layer._originalLatLng)
|
|
92
|
+
delete layer._originalLatLng
|
|
93
|
+
}
|
|
18
94
|
},
|
|
19
95
|
})
|
|
20
96
|
|
|
21
|
-
export const Cluster =
|
|
97
|
+
export const Cluster = FeatureGroup.extend({
|
|
22
98
|
statics: {
|
|
23
99
|
NAME: translate('Clustered'),
|
|
24
100
|
TYPE: 'Cluster',
|
|
@@ -27,65 +103,159 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
27
103
|
|
|
28
104
|
initialize: function (datalayer) {
|
|
29
105
|
this.datalayer = datalayer
|
|
106
|
+
this._bucket = []
|
|
107
|
+
this._group = new LayerGroup()
|
|
30
108
|
if (!Utils.isObject(this.datalayer.properties.cluster)) {
|
|
31
109
|
this.datalayer.properties.cluster = {}
|
|
32
110
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
111
|
+
FeatureGroup.prototype.initialize.call(this)
|
|
112
|
+
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
dataChanged: function () {
|
|
116
|
+
this.redraw()
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
removeClusters() {
|
|
120
|
+
this.hideCoverage()
|
|
121
|
+
for (const layer of this._bucket) {
|
|
122
|
+
delete layer._cluster
|
|
38
123
|
}
|
|
39
|
-
if (this.
|
|
40
|
-
|
|
124
|
+
if (this._map) {
|
|
125
|
+
this._group.clearLayers()
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
addClusters() {
|
|
130
|
+
if (this._map) {
|
|
131
|
+
for (const cluster of this._clusters) {
|
|
132
|
+
const layer = cluster._layers.length === 1 ? cluster._layers[0] : cluster
|
|
133
|
+
this._group.addLayer(layer)
|
|
134
|
+
}
|
|
41
135
|
}
|
|
42
|
-
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
|
43
|
-
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
44
|
-
this._markerCluster = MarkerCluster
|
|
45
|
-
this._layers = []
|
|
46
136
|
},
|
|
47
137
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
138
|
+
redraw: function () {
|
|
139
|
+
this.removeClusters()
|
|
140
|
+
this.compute()
|
|
141
|
+
this.addClusters()
|
|
51
142
|
},
|
|
52
143
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
144
|
+
compute() {
|
|
145
|
+
const radius = this.datalayer.properties.cluster?.radius || 80
|
|
146
|
+
this._clusters = []
|
|
147
|
+
const map = this.datalayer._umap._leafletMap
|
|
148
|
+
this._bounds = map.getBounds().pad(0.1)
|
|
149
|
+
const CRS = map.options.crs
|
|
150
|
+
for (const layer of this._bucket) {
|
|
151
|
+
if (layer._cluster) continue
|
|
152
|
+
if (!this._bounds.contains(layer._latlng)) continue
|
|
153
|
+
layer._xy = CRS.latLngToPoint(layer._latlng, map.getZoom())
|
|
154
|
+
let cluster = null
|
|
155
|
+
for (const candidate of this._clusters) {
|
|
156
|
+
if (candidate._xy.distanceTo(layer._xy) <= radius) {
|
|
157
|
+
cluster = candidate
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!cluster) {
|
|
162
|
+
const icon = new ClusterIcon({
|
|
163
|
+
color: this.datalayer.getColor(),
|
|
164
|
+
textColor: this.datalayer.properties.cluster?.textColor,
|
|
165
|
+
getCounter: () => cluster._layers.length,
|
|
166
|
+
})
|
|
167
|
+
cluster = new MarkerCluster(layer._latlng, { icon })
|
|
168
|
+
cluster.addEventParent(this)
|
|
169
|
+
cluster._xy ??= layer._xy
|
|
170
|
+
cluster._layers = []
|
|
171
|
+
this._clusters.push(cluster)
|
|
172
|
+
}
|
|
173
|
+
cluster._layers.push(layer)
|
|
174
|
+
layer._cluster = cluster
|
|
175
|
+
}
|
|
176
|
+
for (const cluster of this._clusters) {
|
|
177
|
+
cluster.computeCoverage()
|
|
178
|
+
}
|
|
62
179
|
},
|
|
63
180
|
|
|
64
181
|
addLayer: function (layer) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
182
|
+
if (!layer.getLatLng) return FeatureGroup.prototype.addLayer.call(this, layer)
|
|
183
|
+
// Do not add yet the layer to the map
|
|
184
|
+
// wait for datachanged event, so we can compute breaks only once
|
|
185
|
+
this._bucket.push(layer)
|
|
186
|
+
return this
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
onAdd: function (leafletMap) {
|
|
190
|
+
this.on('click', this.onClick)
|
|
191
|
+
this.on('mouseover', this.onMouseOver)
|
|
192
|
+
this.on('mouseout', this.onMouseOut)
|
|
193
|
+
this.compute()
|
|
194
|
+
LayerMixin.onAdd.call(this, leafletMap)
|
|
195
|
+
leafletMap.on('moveend', this.onMoveEnd, this)
|
|
196
|
+
leafletMap.on('zoomend', this.onZoomEnd, this)
|
|
197
|
+
this.addClusters()
|
|
198
|
+
leafletMap.addLayer(this._group)
|
|
199
|
+
return FeatureGroup.prototype.onAdd.call(this, leafletMap)
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
onRemove: function (leafletMap) {
|
|
203
|
+
leafletMap.off('zoomend', this.onZoomEnd, this)
|
|
204
|
+
leafletMap.off('moveend', this.onMoveEnd, this)
|
|
205
|
+
this.off('click', this.onClick)
|
|
206
|
+
this.off('mouseover', this.onMouseOver)
|
|
207
|
+
this.off('mouseout', this.onMouseOut)
|
|
208
|
+
LayerMixin.onRemove.call(this, leafletMap)
|
|
209
|
+
this.removeClusters()
|
|
210
|
+
leafletMap.removeLayer(this._group)
|
|
211
|
+
return FeatureGroup.prototype.onRemove.call(this, leafletMap)
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
onZoomEnd: function () {
|
|
215
|
+
this.removeClusters()
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
onMoveEnd: function () {
|
|
219
|
+
this.compute()
|
|
220
|
+
this.addClusters()
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
showCoverage(cluster) {
|
|
224
|
+
if (cluster._coverage) {
|
|
225
|
+
this._shownCoverage = cluster._coverage
|
|
226
|
+
this._map.addLayer(this._shownCoverage)
|
|
76
227
|
}
|
|
77
228
|
},
|
|
78
229
|
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
230
|
+
hideCoverage() {
|
|
231
|
+
if (this._shownCoverage) this._map.removeLayer(this._shownCoverage)
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
onMouseOver(event) {
|
|
235
|
+
event.layer?.computeCoverage?.()
|
|
236
|
+
this.showCoverage(event.layer)
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
onMouseOut(event) {
|
|
240
|
+
this.hideCoverage()
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
onClick(event) {
|
|
244
|
+
if (this._map.getZoom() === this._map.getMaxZoom()) {
|
|
245
|
+
event.layer.spiderfy?.()
|
|
246
|
+
} else {
|
|
247
|
+
event.layer.zoomToCoverage?.()
|
|
248
|
+
}
|
|
82
249
|
},
|
|
83
250
|
|
|
84
251
|
getEditableProperties: () => [
|
|
85
252
|
[
|
|
86
253
|
'properties.cluster.radius',
|
|
87
254
|
{
|
|
88
|
-
handler: '
|
|
255
|
+
handler: 'Range',
|
|
256
|
+
min: 40,
|
|
257
|
+
max: 200,
|
|
258
|
+
step: 10,
|
|
89
259
|
placeholder: translate('Clustering radius'),
|
|
90
260
|
helpText: translate('Override clustering radius (default 80)'),
|
|
91
261
|
},
|
|
@@ -101,20 +271,6 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
101
271
|
],
|
|
102
272
|
|
|
103
273
|
onEdit: function (field, builder) {
|
|
104
|
-
if (field === 'properties.cluster.radius')
|
|
105
|
-
// No way to reset radius of an already instanciated MarkerClusterGroup...
|
|
106
|
-
this.datalayer.resetLayer(true)
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
if (field === 'properties.color') {
|
|
110
|
-
this.options.polygonOptions.color = this.datalayer.getColor()
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
_moveChild: (layer, from, to) => {
|
|
115
|
-
// Extend parent method, so to remove remove/addLayer,
|
|
116
|
-
// to let our own dragend event listener be called
|
|
117
|
-
// cf https://github.com/umap-project/umap/issues/2749
|
|
118
|
-
layer._latlng = to
|
|
274
|
+
if (field === 'properties.cluster.radius') this.redraw()
|
|
119
275
|
},
|
|
120
276
|
})
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
PermanentCreditsControl,
|
|
23
23
|
TileLayerChooser,
|
|
24
24
|
LoadTemplateControl,
|
|
25
|
+
PrintControl,
|
|
26
|
+
SearchControl,
|
|
25
27
|
} from './controls.js'
|
|
26
28
|
import * as Utils from '../utils.js'
|
|
27
29
|
import * as Icon from './icon.js'
|
|
@@ -45,6 +47,7 @@ const ControlsMixin = {
|
|
|
45
47
|
'locate',
|
|
46
48
|
'measure',
|
|
47
49
|
'editinosm',
|
|
50
|
+
'print',
|
|
48
51
|
'tilelayers',
|
|
49
52
|
],
|
|
50
53
|
|
|
@@ -84,8 +87,9 @@ const ControlsMixin = {
|
|
|
84
87
|
true: translate('Exit Fullscreen'),
|
|
85
88
|
},
|
|
86
89
|
})
|
|
87
|
-
this._controls.search = new
|
|
90
|
+
this._controls.search = new SearchControl(this._umap)
|
|
88
91
|
this._controls.embed = new EmbedControl(this._umap)
|
|
92
|
+
this._controls.print = new PrintControl(this._umap)
|
|
89
93
|
this._controls.tilelayersChooser = new TileLayerChooser(this._umap)
|
|
90
94
|
this._controls.editinosm = new Control.EditInOSM({
|
|
91
95
|
position: 'topleft',
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DomEvent,
|
|
3
|
+
DomUtil,
|
|
4
|
+
CircleMarker,
|
|
5
|
+
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
6
|
import { getLocale, translate } from '../i18n.js'
|
|
3
7
|
import { Request } from '../request.js'
|
|
4
8
|
import * as Utils from '../utils.js'
|
|
@@ -22,6 +26,9 @@ export default async function loadTemplate(name, feature, container) {
|
|
|
22
26
|
case 'Wikipedia':
|
|
23
27
|
klass = Wikipedia
|
|
24
28
|
break
|
|
29
|
+
case 'Route':
|
|
30
|
+
klass = Route
|
|
31
|
+
break
|
|
25
32
|
}
|
|
26
33
|
const content = new klass()
|
|
27
34
|
return await content.render(feature, container)
|
|
@@ -282,3 +289,66 @@ class Wikipedia extends PopupTemplate {
|
|
|
282
289
|
return body
|
|
283
290
|
}
|
|
284
291
|
}
|
|
292
|
+
|
|
293
|
+
class Route extends TitleMixin(PopupTemplate) {
|
|
294
|
+
async renderBody(feature) {
|
|
295
|
+
if (feature.type !== 'LineString' || feature.isMulti()) {
|
|
296
|
+
return super.renderBody(feature)
|
|
297
|
+
}
|
|
298
|
+
let prev
|
|
299
|
+
let dist = 0
|
|
300
|
+
const data = []
|
|
301
|
+
const latlngs = feature.ui.getLatLngs()
|
|
302
|
+
const map = feature._umap._leafletMap
|
|
303
|
+
const properties = feature.extendedProperties()
|
|
304
|
+
for (const latlng of latlngs) {
|
|
305
|
+
if (!latlng.alt) {
|
|
306
|
+
continue
|
|
307
|
+
}
|
|
308
|
+
if (prev) {
|
|
309
|
+
dist = map.distance(latlng, prev)
|
|
310
|
+
}
|
|
311
|
+
data.push([latlng.alt, dist])
|
|
312
|
+
prev = latlng
|
|
313
|
+
}
|
|
314
|
+
const [root, { altitude, chart }] = Utils.loadTemplateWithRefs(`
|
|
315
|
+
<div>
|
|
316
|
+
<p>
|
|
317
|
+
${translate('Distance:')} ${properties.measure} •
|
|
318
|
+
${translate('Gain:')} ${properties.gain} m ↗ •
|
|
319
|
+
${translate('Loss:')} ${properties.loss} m ↘ •
|
|
320
|
+
${translate('Altitude:')} <span data-ref="altitude">—</span> m
|
|
321
|
+
</p>
|
|
322
|
+
<object width="100%"
|
|
323
|
+
data="${feature._umap.getStaticPathFor('../vendors/simple-elevation-chart/elevation.svg')}"
|
|
324
|
+
data-elevation="${JSON.stringify(data)}"
|
|
325
|
+
data-ref="chart"
|
|
326
|
+
type="image/svg+xml">
|
|
327
|
+
</div>
|
|
328
|
+
`)
|
|
329
|
+
let marker
|
|
330
|
+
function removeMarker() {
|
|
331
|
+
if (marker) {
|
|
332
|
+
marker.remove()
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
chart.addEventListener('mouseout', removeMarker)
|
|
336
|
+
map.on('popupclose', removeMarker)
|
|
337
|
+
chart.addEventListener('chart:over', (event) => {
|
|
338
|
+
const dataset = event.detail.element.dataset
|
|
339
|
+
if (dataset.ele) {
|
|
340
|
+
altitude.textContent = dataset.ele
|
|
341
|
+
}
|
|
342
|
+
removeMarker()
|
|
343
|
+
const latlng = latlngs[dataset.index]
|
|
344
|
+
if (!latlng) return
|
|
345
|
+
marker = new CircleMarker(latlng, {
|
|
346
|
+
radius: 8,
|
|
347
|
+
fillColor: 'white',
|
|
348
|
+
fillOpacity: 1,
|
|
349
|
+
color: 'orange',
|
|
350
|
+
}).addTo(map)
|
|
351
|
+
})
|
|
352
|
+
return root
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CircleMarker as BaseCircleMarker,
|
|
4
4
|
DomEvent,
|
|
5
5
|
DomUtil,
|
|
6
|
+
GeoJSON,
|
|
6
7
|
LatLng,
|
|
7
8
|
LatLngBounds,
|
|
8
9
|
LineUtil,
|
|
@@ -45,26 +46,29 @@ const FeatureMixin = {
|
|
|
45
46
|
this.on('contextmenu editable:vertex:contextmenu', this.onContextMenu)
|
|
46
47
|
this.on('click', this.onClick)
|
|
47
48
|
this.on('editable:edited', this.onCommit)
|
|
49
|
+
this.on('mouseover', this.onMouseOver)
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
onMouseOver: function () {
|
|
53
|
+
if (this._map._umap.editEnabled && !this._map._umap.editedFeature) {
|
|
54
|
+
this._map._umap.tooltip.open({
|
|
55
|
+
content: translate('Right-click to edit'),
|
|
56
|
+
anchor: this,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
48
59
|
},
|
|
49
60
|
|
|
50
61
|
onClick: function (event) {
|
|
51
62
|
if (this._map.measureTools?.enabled()) return
|
|
52
63
|
this._popupHandlersAdded = true // Prevent leaflet from managing event
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
this.feature.datalayer.edit(event)
|
|
59
|
-
} else {
|
|
60
|
-
this.feature.toggleEditing(event)
|
|
61
|
-
}
|
|
62
|
-
} else if (!this._map.editTools?.drawing()) {
|
|
63
|
-
this._map._umap.editContextmenu.open(
|
|
64
|
-
event.originalEvent,
|
|
65
|
-
this.feature.getInplaceEditMenu(event)
|
|
66
|
-
)
|
|
64
|
+
if (event.originalEvent.shiftKey) {
|
|
65
|
+
if (event.originalEvent.ctrlKey || event.originalEvent.metaKey) {
|
|
66
|
+
this.feature.datalayer.edit(event)
|
|
67
|
+
} else if (!this.feature.isReadOnly()) {
|
|
68
|
+
this.feature.toggleEditing(event)
|
|
67
69
|
}
|
|
70
|
+
} else if (!this._map.editTools?.drawing()) {
|
|
71
|
+
this.feature.view(event)
|
|
68
72
|
}
|
|
69
73
|
DomEvent.stop(event)
|
|
70
74
|
},
|
|
@@ -91,8 +95,8 @@ const FeatureMixin = {
|
|
|
91
95
|
onContextMenu: function (event) {
|
|
92
96
|
DomEvent.stop(event)
|
|
93
97
|
const items = this.feature
|
|
94
|
-
.
|
|
95
|
-
.concat(this._map._umap.
|
|
98
|
+
.getContextMenu(event)
|
|
99
|
+
.concat(this._map._umap.getSharedContextMenu(event))
|
|
96
100
|
this._map._umap.contextmenu.open(event.originalEvent, items)
|
|
97
101
|
},
|
|
98
102
|
|
|
@@ -110,6 +114,10 @@ const PointMixin = {
|
|
|
110
114
|
FeatureMixin.addInteractions.call(this)
|
|
111
115
|
this.on('dragend', (event) => {
|
|
112
116
|
this.feature.edit(event)
|
|
117
|
+
if (this._cluster) {
|
|
118
|
+
delete this._originalLatLng
|
|
119
|
+
this.feature.datalayer.dataChanged()
|
|
120
|
+
}
|
|
113
121
|
})
|
|
114
122
|
if (!this.feature.isReadOnly()) this.on('mouseover', this._enableDragging)
|
|
115
123
|
this.on('mouseout', this._onMouseOut)
|
|
@@ -249,17 +257,22 @@ export const LeafletMarker = Marker.extend({
|
|
|
249
257
|
})
|
|
250
258
|
|
|
251
259
|
const PathMixin = {
|
|
252
|
-
|
|
260
|
+
maxVertex: 100,
|
|
261
|
+
onMouseOver: function () {
|
|
253
262
|
if (this._map.measureTools?.enabled()) {
|
|
254
263
|
this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this })
|
|
255
|
-
} else
|
|
256
|
-
|
|
257
|
-
content: translate('Click to edit'),
|
|
258
|
-
anchor: this,
|
|
259
|
-
})
|
|
264
|
+
} else {
|
|
265
|
+
FeatureMixin.onMouseOver.call(this)
|
|
260
266
|
}
|
|
261
267
|
},
|
|
262
268
|
|
|
269
|
+
shouldAllowGeometryEdit: function () {
|
|
270
|
+
const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
|
|
271
|
+
return (
|
|
272
|
+
pointsCount < this.maxVertex || this._map.getZoom() === this._map.getMaxZoom()
|
|
273
|
+
)
|
|
274
|
+
},
|
|
275
|
+
|
|
263
276
|
makeGeometryEditable: function () {
|
|
264
277
|
// Feature has been removed since then?
|
|
265
278
|
if (!this._map) return
|
|
@@ -268,20 +281,18 @@ const PathMixin = {
|
|
|
268
281
|
return
|
|
269
282
|
}
|
|
270
283
|
this._map.once('moveend', this.makeGeometryEditable, this)
|
|
271
|
-
|
|
272
|
-
|
|
284
|
+
if (this.shouldAllowGeometryEdit()) {
|
|
285
|
+
this.enableEdit()
|
|
286
|
+
} else {
|
|
273
287
|
this._map._umap.tooltip.open({
|
|
274
288
|
content: L._('Please zoom in to edit the geometry'),
|
|
275
289
|
})
|
|
276
290
|
this.disableEdit()
|
|
277
|
-
} else {
|
|
278
|
-
this.enableEdit()
|
|
279
291
|
}
|
|
280
292
|
},
|
|
281
293
|
|
|
282
294
|
addInteractions: function () {
|
|
283
295
|
FeatureMixin.addInteractions.call(this)
|
|
284
|
-
this.on('mouseover', this._onMouseOver)
|
|
285
296
|
this.on('drag editable:drag', this._onDrag)
|
|
286
297
|
this.on('popupopen', this.highlightPath)
|
|
287
298
|
this.on('popupclose', this._redraw)
|
|
@@ -293,6 +304,7 @@ const PathMixin = {
|
|
|
293
304
|
},
|
|
294
305
|
|
|
295
306
|
highlightPath: function () {
|
|
307
|
+
this.feature.activate()
|
|
296
308
|
this.parentClass.prototype.setStyle.call(this, {
|
|
297
309
|
fillOpacity: Math.sqrt(this.feature.getDynamicOption('fillOpacity', 1.0)),
|
|
298
310
|
opacity: 1.0,
|
|
@@ -336,17 +348,11 @@ const PathMixin = {
|
|
|
336
348
|
},
|
|
337
349
|
|
|
338
350
|
_redraw: function () {
|
|
351
|
+
this.feature.deactivate()
|
|
339
352
|
this.setStyle()
|
|
340
353
|
this.resetTooltip()
|
|
341
354
|
},
|
|
342
355
|
|
|
343
|
-
onVertexRawClick: function (event) {
|
|
344
|
-
this._map._umap.editContextmenu.open(
|
|
345
|
-
event.originalEvent,
|
|
346
|
-
this.feature.getInplaceEditVertexMenu(event)
|
|
347
|
-
)
|
|
348
|
-
},
|
|
349
|
-
|
|
350
356
|
isolateShape: function (atLatLng) {
|
|
351
357
|
if (!this.feature.isMulti()) return
|
|
352
358
|
const shape = this.enableEdit().deleteShapeAt(atLatLng)
|
|
@@ -457,6 +463,67 @@ export const LeafletPolyline = Polyline.extend({
|
|
|
457
463
|
},
|
|
458
464
|
})
|
|
459
465
|
|
|
466
|
+
export const RouteEditor = L.Editable.PolylineEditor.extend({
|
|
467
|
+
options: {
|
|
468
|
+
skipMiddleMarkers: true,
|
|
469
|
+
draggable: false,
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
getLatLngs: function () {
|
|
473
|
+
return this.feature._route
|
|
474
|
+
},
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
export const LeafletRoute = LeafletPolyline.extend({
|
|
478
|
+
initialize: function (feature, latlngs) {
|
|
479
|
+
this._route = GeoJSON.coordsToLatLngs(
|
|
480
|
+
feature.properties._umap_options.route?.coordinates
|
|
481
|
+
)
|
|
482
|
+
FeatureMixin.initialize.call(this, feature, latlngs)
|
|
483
|
+
delete this.dragging
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
addInteractions: function () {
|
|
487
|
+
PathMixin.addInteractions.call(this)
|
|
488
|
+
this.on('editable:drawing:clicked', this.onDrawingClick)
|
|
489
|
+
this.on('editable:vertex:dragend editable:vertex:deleted', this.onDrawingMoved)
|
|
490
|
+
},
|
|
491
|
+
|
|
492
|
+
getEditorClass: (tools) => {
|
|
493
|
+
return RouteEditor
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
getClass: () => LeafletRoute,
|
|
497
|
+
|
|
498
|
+
syncRoute() {
|
|
499
|
+
this.feature.properties._umap_options.route.coordinates = GeoJSON.latLngsToCoords(
|
|
500
|
+
this._route
|
|
501
|
+
)
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
onDrawingMoved: function (event) {
|
|
505
|
+
this.syncRoute()
|
|
506
|
+
if (this._route.length >= 2) {
|
|
507
|
+
this.feature.computeRoute()
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
onDrawingClick: function (event) {
|
|
512
|
+
this._route.push(event.latlng)
|
|
513
|
+
this.syncRoute()
|
|
514
|
+
if (this._route.length >= 2) {
|
|
515
|
+
this.feature.computeRoute()
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
shouldAllowGeometryEdit: function () {
|
|
520
|
+
return (
|
|
521
|
+
this._route.length < this.maxVertex ||
|
|
522
|
+
this._map.getZoom() === this._map.getMaxZoom()
|
|
523
|
+
)
|
|
524
|
+
},
|
|
525
|
+
})
|
|
526
|
+
|
|
460
527
|
export const LeafletPolygon = Polygon.extend({
|
|
461
528
|
parentClass: Polygon,
|
|
462
529
|
includes: [FeatureMixin, PathMixin],
|