umap-project 3.2.0__py3-none-any.whl → 3.3.0__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/en/LC_MESSAGES/django.mo +0 -0
- umap/locale/en/LC_MESSAGES/django.po +15 -15
- 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 +317 -173
- umap/static/umap/js/modules/data/layer.js +17 -18
- 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 +199 -63
- 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 +98 -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 +67 -61
- 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 +39 -4
- umap/static/umap/locale/el.json +39 -4
- umap/static/umap/locale/en.js +39 -4
- umap/static/umap/locale/en.json +39 -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 +79 -44
- umap/static/umap/locale/eu.json +79 -44
- 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 +39 -4
- umap/static/umap/locale/hu.json +39 -4
- 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 +39 -4
- umap/static/umap/locale/ms.json +39 -4
- umap/static/umap/locale/nl.js +39 -4
- umap/static/umap/locale/nl.json +39 -4
- 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.0.dist-info}/METADATA +8 -8
- {umap_project-3.2.0.dist-info → umap_project-3.3.0.dist-info}/RECORD +169 -165
- 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.0.dist-info}/WHEEL +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.0.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,58 +103,132 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
27
103
|
|
|
28
104
|
initialize: function (datalayer) {
|
|
29
105
|
this.datalayer = datalayer
|
|
106
|
+
this._bucket = []
|
|
30
107
|
if (!Utils.isObject(this.datalayer.properties.cluster)) {
|
|
31
108
|
this.datalayer.properties.cluster = {}
|
|
32
109
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
110
|
+
FeatureGroup.prototype.initialize.call(this)
|
|
111
|
+
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
dataChanged: function () {
|
|
115
|
+
this.redraw()
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
removeClusters() {
|
|
119
|
+
this.hideCoverage()
|
|
120
|
+
if (this._map) {
|
|
121
|
+
for (const cluster of this._clusters) {
|
|
122
|
+
const layer = cluster._layers.length === 1 ? cluster._layers[0] : cluster
|
|
123
|
+
this._map.removeLayer(layer)
|
|
124
|
+
}
|
|
38
125
|
}
|
|
39
|
-
|
|
40
|
-
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
addClusters() {
|
|
129
|
+
if (this._map) {
|
|
130
|
+
for (const cluster of this._clusters) {
|
|
131
|
+
const layer = cluster._layers.length === 1 ? cluster._layers[0] : cluster
|
|
132
|
+
this._map.addLayer(layer)
|
|
133
|
+
}
|
|
41
134
|
}
|
|
42
|
-
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
|
43
|
-
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
44
|
-
this._markerCluster = MarkerCluster
|
|
45
|
-
this._layers = []
|
|
46
135
|
},
|
|
47
136
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
137
|
+
redraw: function () {
|
|
138
|
+
this.removeClusters()
|
|
139
|
+
this.compute()
|
|
140
|
+
this.addClusters()
|
|
51
141
|
},
|
|
52
142
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
143
|
+
compute() {
|
|
144
|
+
const radius = this.datalayer.properties.cluster?.radius || 80
|
|
145
|
+
this._clusters = []
|
|
146
|
+
const map = this.datalayer._umap._leafletMap
|
|
147
|
+
const CRS = map.options.crs
|
|
148
|
+
for (const layer of this._bucket) {
|
|
149
|
+
layer._xy = CRS.latLngToPoint(layer._latlng, map.getZoom())
|
|
150
|
+
let cluster = null
|
|
151
|
+
for (const candidate of this._clusters) {
|
|
152
|
+
if (candidate._xy.distanceTo(layer._xy) <= radius) {
|
|
153
|
+
cluster = candidate
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!cluster) {
|
|
158
|
+
const icon = new ClusterIcon({
|
|
159
|
+
color: this.datalayer.getColor(),
|
|
160
|
+
textColor: this.datalayer.properties.cluster?.textColor,
|
|
161
|
+
getCounter: () => cluster._layers.length,
|
|
162
|
+
})
|
|
163
|
+
cluster = new MarkerCluster(layer._latlng, { icon })
|
|
164
|
+
cluster.addEventParent(this)
|
|
165
|
+
cluster._xy ??= layer._xy
|
|
166
|
+
cluster._layers = []
|
|
167
|
+
this._clusters.push(cluster)
|
|
168
|
+
}
|
|
169
|
+
cluster._layers.push(layer)
|
|
170
|
+
layer._cluster = cluster
|
|
171
|
+
}
|
|
172
|
+
for (const cluster of this._clusters) {
|
|
173
|
+
cluster.computeCoverage()
|
|
174
|
+
}
|
|
62
175
|
},
|
|
63
176
|
|
|
64
177
|
addLayer: function (layer) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
178
|
+
if (!layer.getLatLng) return FeatureGroup.prototype.addLayer.call(this, layer)
|
|
179
|
+
// Do not add yet the layer to the map
|
|
180
|
+
// wait for datachanged event, so we can compute breaks only once
|
|
181
|
+
this._bucket.push(layer)
|
|
182
|
+
return this
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
onAdd: function (leafletMap) {
|
|
186
|
+
this.on('click', this.onClick)
|
|
187
|
+
this.on('mouseover', this.onMouseOver)
|
|
188
|
+
this.on('mouseout', this.onMouseOut)
|
|
189
|
+
this.compute()
|
|
190
|
+
LayerMixin.onAdd.call(this, leafletMap)
|
|
191
|
+
leafletMap.on('zoomend', this.redraw, this)
|
|
192
|
+
this.addClusters()
|
|
193
|
+
return FeatureGroup.prototype.onAdd.call(this, leafletMap)
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
onRemove: function (leafletMap) {
|
|
197
|
+
leafletMap.off('zoomend', this.redraw, this)
|
|
198
|
+
this.off('click', this.onClick)
|
|
199
|
+
this.off('mouseover', this.onMouseOver)
|
|
200
|
+
this.off('mouseout', this.onMouseOut)
|
|
201
|
+
LayerMixin.onRemove.call(this, leafletMap)
|
|
202
|
+
this.removeClusters()
|
|
203
|
+
return FeatureGroup.prototype.onRemove.call(this, leafletMap)
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
showCoverage(cluster) {
|
|
207
|
+
if (cluster._coverage) {
|
|
208
|
+
this._shownCoverage = cluster._coverage
|
|
209
|
+
this._map.addLayer(this._shownCoverage)
|
|
76
210
|
}
|
|
77
211
|
},
|
|
78
212
|
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
213
|
+
hideCoverage() {
|
|
214
|
+
if (this._shownCoverage) this._map.removeLayer(this._shownCoverage)
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
onMouseOver(event) {
|
|
218
|
+
event.layer?.computeCoverage?.()
|
|
219
|
+
this.showCoverage(event.layer)
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
onMouseOut(event) {
|
|
223
|
+
this.hideCoverage()
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
onClick(event) {
|
|
227
|
+
if (this._map.getZoom() === this._map.getMaxZoom()) {
|
|
228
|
+
event.layer.spiderfy?.()
|
|
229
|
+
} else {
|
|
230
|
+
event.layer.zoomToCoverage?.()
|
|
231
|
+
}
|
|
82
232
|
},
|
|
83
233
|
|
|
84
234
|
getEditableProperties: () => [
|
|
@@ -101,20 +251,6 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
101
251
|
],
|
|
102
252
|
|
|
103
253
|
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
|
|
254
|
+
if (field === 'properties.cluster.radius') this.redraw()
|
|
119
255
|
},
|
|
120
256
|
})
|
|
@@ -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
|
|
|
@@ -123,6 +127,7 @@ const PointMixin = {
|
|
|
123
127
|
},
|
|
124
128
|
|
|
125
129
|
_enableDragging: function () {
|
|
130
|
+
if (this._cluster) return
|
|
126
131
|
// TODO: start dragging after 1 second on mouse down
|
|
127
132
|
if (this._map._umap.editEnabled) {
|
|
128
133
|
if (!this.editEnabled()) this.enableEdit()
|
|
@@ -249,17 +254,22 @@ export const LeafletMarker = Marker.extend({
|
|
|
249
254
|
})
|
|
250
255
|
|
|
251
256
|
const PathMixin = {
|
|
252
|
-
|
|
257
|
+
maxVertex: 100,
|
|
258
|
+
onMouseOver: function () {
|
|
253
259
|
if (this._map.measureTools?.enabled()) {
|
|
254
260
|
this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this })
|
|
255
|
-
} else
|
|
256
|
-
|
|
257
|
-
content: translate('Click to edit'),
|
|
258
|
-
anchor: this,
|
|
259
|
-
})
|
|
261
|
+
} else {
|
|
262
|
+
FeatureMixin.onMouseOver.call(this)
|
|
260
263
|
}
|
|
261
264
|
},
|
|
262
265
|
|
|
266
|
+
shouldAllowGeometryEdit: function () {
|
|
267
|
+
const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
|
|
268
|
+
return (
|
|
269
|
+
pointsCount < this.maxVertex || this._map.getZoom() === this._map.getMaxZoom()
|
|
270
|
+
)
|
|
271
|
+
},
|
|
272
|
+
|
|
263
273
|
makeGeometryEditable: function () {
|
|
264
274
|
// Feature has been removed since then?
|
|
265
275
|
if (!this._map) return
|
|
@@ -268,20 +278,18 @@ const PathMixin = {
|
|
|
268
278
|
return
|
|
269
279
|
}
|
|
270
280
|
this._map.once('moveend', this.makeGeometryEditable, this)
|
|
271
|
-
|
|
272
|
-
|
|
281
|
+
if (this.shouldAllowGeometryEdit()) {
|
|
282
|
+
this.enableEdit()
|
|
283
|
+
} else {
|
|
273
284
|
this._map._umap.tooltip.open({
|
|
274
285
|
content: L._('Please zoom in to edit the geometry'),
|
|
275
286
|
})
|
|
276
287
|
this.disableEdit()
|
|
277
|
-
} else {
|
|
278
|
-
this.enableEdit()
|
|
279
288
|
}
|
|
280
289
|
},
|
|
281
290
|
|
|
282
291
|
addInteractions: function () {
|
|
283
292
|
FeatureMixin.addInteractions.call(this)
|
|
284
|
-
this.on('mouseover', this._onMouseOver)
|
|
285
293
|
this.on('drag editable:drag', this._onDrag)
|
|
286
294
|
this.on('popupopen', this.highlightPath)
|
|
287
295
|
this.on('popupclose', this._redraw)
|
|
@@ -293,6 +301,7 @@ const PathMixin = {
|
|
|
293
301
|
},
|
|
294
302
|
|
|
295
303
|
highlightPath: function () {
|
|
304
|
+
this.feature.activate()
|
|
296
305
|
this.parentClass.prototype.setStyle.call(this, {
|
|
297
306
|
fillOpacity: Math.sqrt(this.feature.getDynamicOption('fillOpacity', 1.0)),
|
|
298
307
|
opacity: 1.0,
|
|
@@ -336,17 +345,11 @@ const PathMixin = {
|
|
|
336
345
|
},
|
|
337
346
|
|
|
338
347
|
_redraw: function () {
|
|
348
|
+
this.feature.deactivate()
|
|
339
349
|
this.setStyle()
|
|
340
350
|
this.resetTooltip()
|
|
341
351
|
},
|
|
342
352
|
|
|
343
|
-
onVertexRawClick: function (event) {
|
|
344
|
-
this._map._umap.editContextmenu.open(
|
|
345
|
-
event.originalEvent,
|
|
346
|
-
this.feature.getInplaceEditVertexMenu(event)
|
|
347
|
-
)
|
|
348
|
-
},
|
|
349
|
-
|
|
350
353
|
isolateShape: function (atLatLng) {
|
|
351
354
|
if (!this.feature.isMulti()) return
|
|
352
355
|
const shape = this.enableEdit().deleteShapeAt(atLatLng)
|
|
@@ -457,6 +460,67 @@ export const LeafletPolyline = Polyline.extend({
|
|
|
457
460
|
},
|
|
458
461
|
})
|
|
459
462
|
|
|
463
|
+
export const RouteEditor = L.Editable.PolylineEditor.extend({
|
|
464
|
+
options: {
|
|
465
|
+
skipMiddleMarkers: true,
|
|
466
|
+
draggable: false,
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
getLatLngs: function () {
|
|
470
|
+
return this.feature._route
|
|
471
|
+
},
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
export const LeafletRoute = LeafletPolyline.extend({
|
|
475
|
+
initialize: function (feature, latlngs) {
|
|
476
|
+
this._route = GeoJSON.coordsToLatLngs(
|
|
477
|
+
feature.properties._umap_options.route?.coordinates
|
|
478
|
+
)
|
|
479
|
+
FeatureMixin.initialize.call(this, feature, latlngs)
|
|
480
|
+
delete this.dragging
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
addInteractions: function () {
|
|
484
|
+
PathMixin.addInteractions.call(this)
|
|
485
|
+
this.on('editable:drawing:clicked', this.onDrawingClick)
|
|
486
|
+
this.on('editable:vertex:dragend editable:vertex:deleted', this.onDrawingMoved)
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
getEditorClass: (tools) => {
|
|
490
|
+
return RouteEditor
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
getClass: () => LeafletRoute,
|
|
494
|
+
|
|
495
|
+
syncRoute() {
|
|
496
|
+
this.feature.properties._umap_options.route.coordinates = GeoJSON.latLngsToCoords(
|
|
497
|
+
this._route
|
|
498
|
+
)
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
onDrawingMoved: function (event) {
|
|
502
|
+
this.syncRoute()
|
|
503
|
+
if (this._route.length >= 2) {
|
|
504
|
+
this.feature.computeRoute()
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
onDrawingClick: function (event) {
|
|
509
|
+
this._route.push(event.latlng)
|
|
510
|
+
this.syncRoute()
|
|
511
|
+
if (this._route.length >= 2) {
|
|
512
|
+
this.feature.computeRoute()
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
shouldAllowGeometryEdit: function () {
|
|
517
|
+
return (
|
|
518
|
+
this._route.length < this.maxVertex ||
|
|
519
|
+
this._map.getZoom() === this._map.getMaxZoom()
|
|
520
|
+
)
|
|
521
|
+
},
|
|
522
|
+
})
|
|
523
|
+
|
|
460
524
|
export const LeafletPolygon = Polygon.extend({
|
|
461
525
|
parentClass: Polygon,
|
|
462
526
|
includes: [FeatureMixin, PathMixin],
|
|
@@ -218,6 +218,7 @@ export const SCHEMA = {
|
|
|
218
218
|
choices: [
|
|
219
219
|
['Default', translate('Default')],
|
|
220
220
|
['Circle', translate('Circle')],
|
|
221
|
+
['LargeCircle', translate('Large Circle')],
|
|
221
222
|
['Drop', translate('Drop')],
|
|
222
223
|
['Ball', translate('Ball')],
|
|
223
224
|
['Raw', translate('None')],
|
|
@@ -234,6 +235,17 @@ export const SCHEMA = {
|
|
|
234
235
|
inheritable: true,
|
|
235
236
|
default: 1,
|
|
236
237
|
},
|
|
238
|
+
iconSize: {
|
|
239
|
+
type: Number,
|
|
240
|
+
impacts: ['data'],
|
|
241
|
+
min: 12,
|
|
242
|
+
max: 64,
|
|
243
|
+
step: 4,
|
|
244
|
+
label: translate('Icon size'),
|
|
245
|
+
helpText: translate('Will only affect raw and large circle icons.'),
|
|
246
|
+
inheritable: true,
|
|
247
|
+
default: 24,
|
|
248
|
+
},
|
|
237
249
|
iconUrl: {
|
|
238
250
|
type: String,
|
|
239
251
|
impacts: ['data'],
|
|
@@ -438,9 +450,17 @@ export const SCHEMA = {
|
|
|
438
450
|
['GeoRSSLink', translate('GeoRSS (only link)')],
|
|
439
451
|
['OSM', translate('OpenStreetMap')],
|
|
440
452
|
['Wikipedia', translate('Wikipedia')],
|
|
453
|
+
['Route', translate('Route')],
|
|
441
454
|
],
|
|
442
455
|
default: 'Default',
|
|
443
456
|
},
|
|
457
|
+
printControl: {
|
|
458
|
+
type: Boolean,
|
|
459
|
+
impacts: ['ui'],
|
|
460
|
+
nullable: true,
|
|
461
|
+
label: translate('Display the print control'),
|
|
462
|
+
default: null,
|
|
463
|
+
},
|
|
444
464
|
rank: {
|
|
445
465
|
type: Number,
|
|
446
466
|
impacts: ['datalayer-rank'],
|
|
@@ -453,6 +473,10 @@ export const SCHEMA = {
|
|
|
453
473
|
type: Object,
|
|
454
474
|
impacts: ['data'],
|
|
455
475
|
},
|
|
476
|
+
route: {
|
|
477
|
+
type: Object,
|
|
478
|
+
impacts: ['data'],
|
|
479
|
+
},
|
|
456
480
|
scaleControl: {
|
|
457
481
|
type: Boolean,
|
|
458
482
|
impacts: ['ui'],
|