umap-project 3.0.4__py3-none-any.whl → 3.0.5__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 +136 -56
- umap/locale/en/LC_MESSAGES/django.po +18 -18
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +136 -56
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +1 -1
- umap/models.py +1 -0
- umap/settings/base.py +1 -0
- umap/static/umap/css/bar.css +1 -1
- umap/static/umap/css/tooltip.css +13 -0
- umap/static/umap/js/modules/autocomplete.js +7 -8
- umap/static/umap/js/modules/browser.js +89 -94
- umap/static/umap/js/modules/caption.js +6 -4
- umap/static/umap/js/modules/data/features.js +1 -19
- umap/static/umap/js/modules/data/layer.js +100 -61
- umap/static/umap/js/modules/facets.js +1 -1
- umap/static/umap/js/modules/form/fields.js +1 -1
- umap/static/umap/js/modules/importer.js +1 -1
- umap/static/umap/js/modules/managers.js +46 -0
- umap/static/umap/js/modules/permissions.js +1 -1
- umap/static/umap/js/modules/rendering/controls.js +251 -0
- umap/static/umap/js/modules/rendering/layers/heat.js +5 -0
- umap/static/umap/js/modules/rendering/map.js +21 -10
- umap/static/umap/js/modules/rendering/ui.js +0 -1
- umap/static/umap/js/modules/rules.js +56 -46
- umap/static/umap/js/modules/schema.js +5 -1
- umap/static/umap/js/modules/share.js +2 -2
- umap/static/umap/js/modules/slideshow.js +1 -1
- umap/static/umap/js/modules/sync/engine.js +23 -9
- umap/static/umap/js/modules/ui/bar.js +2 -2
- umap/static/umap/js/modules/ui/base.js +13 -0
- umap/static/umap/js/modules/umap.js +69 -111
- umap/static/umap/js/umap.controls.js +0 -310
- umap/static/umap/js/umap.core.js +0 -40
- umap/static/umap/locale/am_ET.js +8 -3
- umap/static/umap/locale/am_ET.json +8 -3
- umap/static/umap/locale/ar.js +8 -3
- umap/static/umap/locale/ar.json +8 -3
- umap/static/umap/locale/ast.js +8 -3
- umap/static/umap/locale/ast.json +8 -3
- umap/static/umap/locale/bg.js +8 -3
- umap/static/umap/locale/bg.json +8 -3
- umap/static/umap/locale/br.js +8 -3
- umap/static/umap/locale/br.json +8 -3
- umap/static/umap/locale/ca.js +20 -17
- umap/static/umap/locale/ca.json +20 -17
- umap/static/umap/locale/cs_CZ.js +5 -2
- umap/static/umap/locale/cs_CZ.json +5 -2
- umap/static/umap/locale/da.js +8 -3
- umap/static/umap/locale/da.json +8 -3
- umap/static/umap/locale/de.js +5 -2
- umap/static/umap/locale/de.json +5 -2
- umap/static/umap/locale/el.js +92 -87
- umap/static/umap/locale/el.json +92 -87
- umap/static/umap/locale/en.js +5 -2
- umap/static/umap/locale/en.json +5 -2
- umap/static/umap/locale/en_US.json +8 -3
- umap/static/umap/locale/es.js +18 -15
- umap/static/umap/locale/es.json +18 -15
- umap/static/umap/locale/et.js +8 -3
- umap/static/umap/locale/et.json +8 -3
- umap/static/umap/locale/eu.js +5 -2
- umap/static/umap/locale/eu.json +5 -2
- umap/static/umap/locale/fa_IR.js +5 -2
- umap/static/umap/locale/fa_IR.json +5 -2
- umap/static/umap/locale/fi.js +8 -3
- umap/static/umap/locale/fi.json +8 -3
- umap/static/umap/locale/fr.js +5 -2
- umap/static/umap/locale/fr.json +5 -2
- umap/static/umap/locale/gl.js +5 -2
- umap/static/umap/locale/gl.json +5 -2
- umap/static/umap/locale/he.js +8 -3
- umap/static/umap/locale/he.json +8 -3
- umap/static/umap/locale/hr.js +8 -3
- umap/static/umap/locale/hr.json +8 -3
- umap/static/umap/locale/hu.js +5 -2
- umap/static/umap/locale/hu.json +5 -2
- umap/static/umap/locale/id.js +8 -3
- umap/static/umap/locale/id.json +8 -3
- umap/static/umap/locale/is.js +8 -3
- umap/static/umap/locale/is.json +8 -3
- umap/static/umap/locale/it.js +5 -2
- umap/static/umap/locale/it.json +5 -2
- umap/static/umap/locale/ja.js +8 -3
- umap/static/umap/locale/ja.json +8 -3
- umap/static/umap/locale/ko.js +8 -3
- umap/static/umap/locale/ko.json +8 -3
- umap/static/umap/locale/lt.js +8 -3
- umap/static/umap/locale/lt.json +8 -3
- umap/static/umap/locale/ms.js +8 -3
- umap/static/umap/locale/ms.json +8 -3
- umap/static/umap/locale/nl.js +7 -4
- umap/static/umap/locale/nl.json +7 -4
- umap/static/umap/locale/no.js +8 -3
- umap/static/umap/locale/no.json +8 -3
- umap/static/umap/locale/pl.js +8 -3
- umap/static/umap/locale/pl.json +8 -3
- umap/static/umap/locale/pl_PL.json +8 -3
- umap/static/umap/locale/pt.js +5 -2
- umap/static/umap/locale/pt.json +5 -2
- umap/static/umap/locale/pt_BR.js +8 -3
- umap/static/umap/locale/pt_BR.json +8 -3
- umap/static/umap/locale/pt_PT.js +5 -2
- umap/static/umap/locale/pt_PT.json +5 -2
- umap/static/umap/locale/ro.js +8 -3
- umap/static/umap/locale/ro.json +8 -3
- umap/static/umap/locale/ru.js +8 -3
- umap/static/umap/locale/ru.json +8 -3
- umap/static/umap/locale/sk_SK.js +8 -3
- umap/static/umap/locale/sk_SK.json +8 -3
- umap/static/umap/locale/sl.js +8 -3
- umap/static/umap/locale/sl.json +8 -3
- umap/static/umap/locale/sr.js +8 -3
- umap/static/umap/locale/sr.json +8 -3
- umap/static/umap/locale/sv.js +8 -3
- umap/static/umap/locale/sv.json +8 -3
- umap/static/umap/locale/th_TH.js +8 -3
- umap/static/umap/locale/th_TH.json +8 -3
- umap/static/umap/locale/tr.js +8 -3
- umap/static/umap/locale/tr.json +8 -3
- umap/static/umap/locale/uk_UA.js +8 -3
- umap/static/umap/locale/uk_UA.json +8 -3
- umap/static/umap/locale/vi.js +8 -3
- umap/static/umap/locale/vi.json +8 -3
- umap/static/umap/locale/vi_VN.json +8 -3
- umap/static/umap/locale/zh.js +8 -3
- umap/static/umap/locale/zh.json +8 -3
- umap/static/umap/locale/zh_CN.json +8 -3
- umap/static/umap/locale/zh_TW.Big5.json +8 -3
- umap/static/umap/locale/zh_TW.js +5 -2
- umap/static/umap/locale/zh_TW.json +5 -2
- umap/static/umap/map.css +9 -31
- umap/static/umap/vendors/togeojson/togeojson.es.js +350 -177
- umap/static/umap/vendors/togeojson/togeojson.es.mjs.map +1 -1
- umap/templates/umap/design_system.html +355 -0
- umap/tests/base.py +2 -2
- umap/tests/fixtures/heatmap_data.json +1044 -0
- umap/tests/integration/test_browser.py +3 -3
- umap/tests/integration/test_conditional_rules.py +2 -2
- umap/tests/integration/test_datalayer.py +0 -1
- umap/tests/integration/test_edit_map.py +7 -7
- umap/tests/integration/test_facets_browser.py +2 -2
- umap/tests/integration/test_heatmap.py +41 -0
- umap/tests/integration/test_import.py +58 -1
- umap/tests/integration/test_map.py +7 -8
- umap/tests/integration/test_optimistic_merge.py +12 -4
- umap/tests/integration/test_querystring.py +1 -1
- umap/tests/integration/test_remote_data.py +79 -0
- umap/tests/integration/test_websocket_sync.py +2 -2
- umap/urls.py +1 -0
- umap/views.py +7 -0
- {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/METADATA +8 -8
- {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/RECORD +158 -152
- {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/WHEEL +0 -0
- {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/entry_points.txt +0 -0
- {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -64,6 +64,9 @@ export class DataLayer {
|
|
|
64
64
|
|
|
65
65
|
this.setOptions(data)
|
|
66
66
|
this.pane.dataset.id = this.id
|
|
67
|
+
if (this.options.rank === undefined) {
|
|
68
|
+
this.options.rank = this._umap.datalayers.count()
|
|
69
|
+
}
|
|
67
70
|
|
|
68
71
|
if (!Utils.isObject(this.options.remoteData)) {
|
|
69
72
|
this.options.remoteData = {}
|
|
@@ -122,6 +125,20 @@ export class DataLayer {
|
|
|
122
125
|
return `datalayer-${stamp(this)}`
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
get rank() {
|
|
129
|
+
// Make sure we always have a valid rank. Undefined rank may happen
|
|
130
|
+
// after importing an old umap backup, and not touching the layers
|
|
131
|
+
// after that.
|
|
132
|
+
if (this.options.rank === undefined) {
|
|
133
|
+
this.options.rank = this.getDOMOrder()
|
|
134
|
+
}
|
|
135
|
+
return this.options.rank
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
set rank(value) {
|
|
139
|
+
this.options.rank = value
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
getSyncMetadata() {
|
|
126
143
|
return {
|
|
127
144
|
subject: 'datalayer',
|
|
@@ -130,6 +147,10 @@ export class DataLayer {
|
|
|
130
147
|
}
|
|
131
148
|
|
|
132
149
|
render(fields, builder) {
|
|
150
|
+
// Propagate will remove the fields it has already
|
|
151
|
+
// processed
|
|
152
|
+
fields = this.propagate(fields)
|
|
153
|
+
|
|
133
154
|
const impacts = Utils.getImpactsFromSchema(fields)
|
|
134
155
|
|
|
135
156
|
for (const impact of impacts) {
|
|
@@ -149,8 +170,34 @@ export class DataLayer {
|
|
|
149
170
|
case 'remote-data':
|
|
150
171
|
this.fetchRemoteData()
|
|
151
172
|
break
|
|
173
|
+
case 'datalayer-rank':
|
|
174
|
+
this._umap.reorderDataLayers()
|
|
175
|
+
break
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// This method does a targeted update of the UI,
|
|
181
|
+
// it whould be merged with `render`` method and the
|
|
182
|
+
// SCHEMA at some point
|
|
183
|
+
propagate(fields = []) {
|
|
184
|
+
const impacts = {
|
|
185
|
+
'properties.name': () => {
|
|
186
|
+
Utils.eachElement('.datalayer-name', (el) => {
|
|
187
|
+
if (el.dataset.id === this.id) {
|
|
188
|
+
el.textContent = this.getName()
|
|
189
|
+
el.title = this.getName()
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
for (const [field, impact] of Object.entries(impacts)) {
|
|
195
|
+
if (!fields.length || fields.includes(field)) {
|
|
196
|
+
impact()
|
|
197
|
+
fields = fields.filter((item) => item !== field)
|
|
152
198
|
}
|
|
153
199
|
}
|
|
200
|
+
return fields
|
|
154
201
|
}
|
|
155
202
|
|
|
156
203
|
showAtLoad() {
|
|
@@ -222,16 +269,9 @@ export class DataLayer {
|
|
|
222
269
|
if (!error) {
|
|
223
270
|
this._umap.modifiedAt = response.headers.get('last-modified')
|
|
224
271
|
this.setReferenceVersion({ response, sync: false })
|
|
225
|
-
|
|
226
|
-
// And thus it's not in the geojson file in the server
|
|
227
|
-
// So do not let all options to be reset
|
|
228
|
-
// Fix is a proper migration so all datalayers settings are
|
|
229
|
-
// in DB, and we remove it from geojson flat files.
|
|
230
|
-
if (geojson._umap_options) {
|
|
231
|
-
geojson._umap_options.editMode = this.options.editMode
|
|
232
|
-
}
|
|
272
|
+
delete geojson._umap_options
|
|
233
273
|
// In case of maps pre 1.0 still around
|
|
234
|
-
|
|
274
|
+
delete geojson._storage
|
|
235
275
|
await this.fromUmapGeoJSON(geojson)
|
|
236
276
|
this.backupOptions()
|
|
237
277
|
this._loading = false
|
|
@@ -260,7 +300,6 @@ export class DataLayer {
|
|
|
260
300
|
|
|
261
301
|
async fromUmapGeoJSON(geojson) {
|
|
262
302
|
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
|
|
263
|
-
geojson._umap_options.id = this.id
|
|
264
303
|
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
|
265
304
|
if (this.isRemoteLayer()) {
|
|
266
305
|
await this.fetchRemoteData()
|
|
@@ -269,15 +308,6 @@ export class DataLayer {
|
|
|
269
308
|
}
|
|
270
309
|
}
|
|
271
310
|
|
|
272
|
-
clear() {
|
|
273
|
-
this.sync.startBatch()
|
|
274
|
-
for (const feature of Object.values(this._features)) {
|
|
275
|
-
feature.del()
|
|
276
|
-
}
|
|
277
|
-
this.sync.commitBatch()
|
|
278
|
-
this.dataChanged()
|
|
279
|
-
}
|
|
280
|
-
|
|
281
311
|
backupData() {
|
|
282
312
|
if (this._geojson) {
|
|
283
313
|
this._geojson_bk = Utils.CopyJSON(this._geojson)
|
|
@@ -328,7 +358,7 @@ export class DataLayer {
|
|
|
328
358
|
url = this._umap.proxyUrl(url, this.options.remoteData.ttl)
|
|
329
359
|
}
|
|
330
360
|
return await this.getUrl(url, remoteUrl).then((raw) => {
|
|
331
|
-
this.clear()
|
|
361
|
+
this.clear(false)
|
|
332
362
|
return this._umap.formatter
|
|
333
363
|
.parse(raw, this.options.remoteData.format)
|
|
334
364
|
.then((geojson) => this.fromGeoJSON(geojson, false))
|
|
@@ -368,12 +398,7 @@ export class DataLayer {
|
|
|
368
398
|
}
|
|
369
399
|
|
|
370
400
|
connectToMap() {
|
|
371
|
-
|
|
372
|
-
this._umap.datalayers[this.id] = this
|
|
373
|
-
}
|
|
374
|
-
if (!this._umap.datalayersIndex.includes(this)) {
|
|
375
|
-
this._umap.datalayersIndex.push(this)
|
|
376
|
-
}
|
|
401
|
+
this._umap.datalayers.add(this)
|
|
377
402
|
this._umap.onDataLayersChanged()
|
|
378
403
|
}
|
|
379
404
|
|
|
@@ -492,8 +517,19 @@ export class DataLayer {
|
|
|
492
517
|
const features = []
|
|
493
518
|
this.sortFeatures(collection)
|
|
494
519
|
for (const featureJson of collection) {
|
|
495
|
-
|
|
496
|
-
|
|
520
|
+
if (featureJson.geometry?.type === 'GeometryCollection') {
|
|
521
|
+
for (const geometry of featureJson.geometry.geometries) {
|
|
522
|
+
const feature = this.makeFeature({
|
|
523
|
+
type: 'Feature',
|
|
524
|
+
geometry,
|
|
525
|
+
properties: featureJson.properties,
|
|
526
|
+
})
|
|
527
|
+
if (feature) features.push(feature)
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
const feature = this.makeFeature(featureJson, sync)
|
|
531
|
+
if (feature) features.push(feature)
|
|
532
|
+
}
|
|
497
533
|
}
|
|
498
534
|
return features
|
|
499
535
|
}
|
|
@@ -606,16 +642,33 @@ export class DataLayer {
|
|
|
606
642
|
|
|
607
643
|
del(sync = true) {
|
|
608
644
|
const oldValue = Utils.CopyJSON(this.umapGeoJSON())
|
|
609
|
-
|
|
645
|
+
// TODO merge datalayer del and features del in same
|
|
646
|
+
// batch
|
|
647
|
+
this.clear()
|
|
610
648
|
if (sync) {
|
|
611
649
|
this.isDeleted = true
|
|
612
650
|
this.sync.delete(oldValue)
|
|
613
651
|
}
|
|
652
|
+
this.hide()
|
|
653
|
+
this.parentPane.removeChild(this.pane)
|
|
654
|
+
this._umap.onDataLayersChanged()
|
|
655
|
+
this.layer.onDelete(this._leafletMap)
|
|
656
|
+
this.propagateDelete()
|
|
657
|
+
this._leaflet_events_bk = this._leaflet_events
|
|
614
658
|
}
|
|
615
659
|
|
|
616
660
|
empty() {
|
|
617
661
|
if (this.isRemoteLayer()) return
|
|
662
|
+
this.sync.startBatch()
|
|
618
663
|
this.clear()
|
|
664
|
+
this.sync.commitBatch()
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
clear(sync = true) {
|
|
668
|
+
for (const feature of Object.values(this._features)) {
|
|
669
|
+
feature.del(sync)
|
|
670
|
+
}
|
|
671
|
+
this.dataChanged()
|
|
619
672
|
}
|
|
620
673
|
|
|
621
674
|
clone() {
|
|
@@ -628,24 +681,13 @@ export class DataLayer {
|
|
|
628
681
|
return datalayer
|
|
629
682
|
}
|
|
630
683
|
|
|
631
|
-
erase() {
|
|
632
|
-
this.hide()
|
|
633
|
-
this._umap.datalayersIndex.splice(this.getRank(), 1)
|
|
634
|
-
this.parentPane.removeChild(this.pane)
|
|
635
|
-
this._umap.onDataLayersChanged()
|
|
636
|
-
this.layer.onDelete(this._leafletMap)
|
|
637
|
-
this.propagateDelete()
|
|
638
|
-
this._leaflet_events_bk = this._leaflet_events
|
|
639
|
-
this.clear()
|
|
640
|
-
}
|
|
641
|
-
|
|
642
684
|
redraw() {
|
|
643
685
|
if (!this.isVisible()) return
|
|
644
686
|
this.eachFeature((feature) => feature.redraw())
|
|
645
687
|
}
|
|
646
688
|
|
|
647
689
|
edit() {
|
|
648
|
-
if (!this._umap.editEnabled
|
|
690
|
+
if (!this._umap.editEnabled) {
|
|
649
691
|
return
|
|
650
692
|
}
|
|
651
693
|
const container = DomUtil.create('div', 'umap-layer-properties-container')
|
|
@@ -1053,23 +1095,11 @@ export class DataLayer {
|
|
|
1053
1095
|
}
|
|
1054
1096
|
|
|
1055
1097
|
getPreviousBrowsable() {
|
|
1056
|
-
|
|
1057
|
-
let next
|
|
1058
|
-
const index = this._umap.datalayersIndex
|
|
1059
|
-
while (((id = index[++id] ? id : 0), (next = index[id]))) {
|
|
1060
|
-
if (next === this || next.canBrowse()) break
|
|
1061
|
-
}
|
|
1062
|
-
return next
|
|
1098
|
+
return this._umap.datalayers.prev(this)
|
|
1063
1099
|
}
|
|
1064
1100
|
|
|
1065
1101
|
getNextBrowsable() {
|
|
1066
|
-
|
|
1067
|
-
let prev
|
|
1068
|
-
const index = this._umap.datalayersIndex
|
|
1069
|
-
while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
|
|
1070
|
-
if (prev === this || prev.canBrowse()) break
|
|
1071
|
-
}
|
|
1072
|
-
return prev
|
|
1102
|
+
return this._umap.datalayers.next(this)
|
|
1073
1103
|
}
|
|
1074
1104
|
|
|
1075
1105
|
umapGeoJSON() {
|
|
@@ -1080,8 +1110,8 @@ export class DataLayer {
|
|
|
1080
1110
|
}
|
|
1081
1111
|
}
|
|
1082
1112
|
|
|
1083
|
-
|
|
1084
|
-
return this.
|
|
1113
|
+
getDOMOrder() {
|
|
1114
|
+
return Array.from(this.parentPane.children).indexOf(this.pane)
|
|
1085
1115
|
}
|
|
1086
1116
|
|
|
1087
1117
|
isReadOnly() {
|
|
@@ -1103,15 +1133,21 @@ export class DataLayer {
|
|
|
1103
1133
|
}
|
|
1104
1134
|
}
|
|
1105
1135
|
|
|
1136
|
+
prepareOptions() {
|
|
1137
|
+
const options = Utils.CopyJSON(this.options)
|
|
1138
|
+
delete options.permissions
|
|
1139
|
+
return JSON.stringify(options)
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1106
1142
|
async save() {
|
|
1107
1143
|
if (this.isDeleted) return await this.saveDelete()
|
|
1108
|
-
if (!this.isLoaded()) return
|
|
1144
|
+
if (!this.isRemoteLayer() && !this.isLoaded()) return
|
|
1109
1145
|
const geojson = this.umapGeoJSON()
|
|
1110
1146
|
const formData = new FormData()
|
|
1111
1147
|
formData.append('name', this.options.name)
|
|
1112
1148
|
formData.append('display_on_load', !!this.options.displayOnLoad)
|
|
1113
|
-
formData.append('rank', this.
|
|
1114
|
-
formData.append('settings',
|
|
1149
|
+
formData.append('rank', this.rank)
|
|
1150
|
+
formData.append('settings', this.prepareOptions())
|
|
1115
1151
|
// Filename support is shaky, don't do it for now.
|
|
1116
1152
|
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
|
1117
1153
|
formData.append('geojson', blob)
|
|
@@ -1146,12 +1182,15 @@ export class DataLayer {
|
|
|
1146
1182
|
await this._umap.saveAll()
|
|
1147
1183
|
}
|
|
1148
1184
|
)
|
|
1185
|
+
} else {
|
|
1186
|
+
console.debug(error)
|
|
1187
|
+
Alert.error(translate('Cannot save layer, please try again in a few minutes.'))
|
|
1149
1188
|
}
|
|
1150
1189
|
} else {
|
|
1151
1190
|
// Response contains geojson only if save has conflicted and conflicts have
|
|
1152
1191
|
// been resolved. So we need to reload to get extra data (added by someone else)
|
|
1153
1192
|
if (data.geojson) {
|
|
1154
|
-
this.clear()
|
|
1193
|
+
this.clear(false)
|
|
1155
1194
|
this.fromGeoJSON(data.geojson)
|
|
1156
1195
|
delete data.geojson
|
|
1157
1196
|
}
|
|
@@ -24,7 +24,7 @@ export default class Facets {
|
|
|
24
24
|
this.selected[name] = selected
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
this._umap.
|
|
27
|
+
this._umap.datalayers.browsable().map((datalayer) => {
|
|
28
28
|
datalayer.eachFeature((feature) => {
|
|
29
29
|
for (const name of names) {
|
|
30
30
|
let value = feature.properties[name]
|
|
@@ -560,7 +560,7 @@ Fields.SlideshowDelay = class extends Fields.IntSelect {
|
|
|
560
560
|
Fields.DataLayerSwitcher = class extends Fields.Select {
|
|
561
561
|
getOptions() {
|
|
562
562
|
const options = []
|
|
563
|
-
this.builder._umap.
|
|
563
|
+
this.builder._umap.datalayers.reverse().map((datalayer) => {
|
|
564
564
|
if (
|
|
565
565
|
datalayer.isLoaded() &&
|
|
566
566
|
!datalayer.isDataReadOnly() &&
|
|
@@ -243,7 +243,7 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
243
243
|
this.raw = null
|
|
244
244
|
const layerSelect = this.qs('[name="layer-id"]')
|
|
245
245
|
layerSelect.innerHTML = ''
|
|
246
|
-
this._umap.
|
|
246
|
+
this._umap.datalayers.reverse().map((datalayer) => {
|
|
247
247
|
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
|
248
248
|
DomUtil.element({
|
|
249
249
|
tagName: 'option',
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export class DataLayerManager extends Object {
|
|
2
|
+
add(datalayer) {
|
|
3
|
+
this[datalayer.id] = datalayer
|
|
4
|
+
}
|
|
5
|
+
active() {
|
|
6
|
+
return Object.values(this)
|
|
7
|
+
.filter((datalayer) => !datalayer.isDeleted)
|
|
8
|
+
.sort((a, b) => a.rank > b.rank)
|
|
9
|
+
}
|
|
10
|
+
reverse() {
|
|
11
|
+
return this.active().reverse()
|
|
12
|
+
}
|
|
13
|
+
count() {
|
|
14
|
+
return this.active().length
|
|
15
|
+
}
|
|
16
|
+
find(func) {
|
|
17
|
+
for (const datalayer of this.reverse()) {
|
|
18
|
+
if (func.call(datalayer, datalayer)) {
|
|
19
|
+
return datalayer
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
filter(func) {
|
|
24
|
+
return this.active().filter(func)
|
|
25
|
+
}
|
|
26
|
+
visible() {
|
|
27
|
+
return this.filter((datalayer) => datalayer.isVisible())
|
|
28
|
+
}
|
|
29
|
+
browsable() {
|
|
30
|
+
return this.reverse().filter((datalayer) => datalayer.allowBrowse())
|
|
31
|
+
}
|
|
32
|
+
prev(datalayer) {
|
|
33
|
+
const browsable = this.browsable()
|
|
34
|
+
const current = browsable.indexOf(datalayer)
|
|
35
|
+
const prev = browsable[current - 1] || browsable[browsable.length - 1]
|
|
36
|
+
if (!prev.canBrowse()) return this.prev(prev)
|
|
37
|
+
return prev
|
|
38
|
+
}
|
|
39
|
+
next(datalayer) {
|
|
40
|
+
const browsable = this.browsable()
|
|
41
|
+
const current = browsable.indexOf(datalayer)
|
|
42
|
+
const next = browsable[current + 1] || browsable[0]
|
|
43
|
+
if (!next.canBrowse()) return this.next(next)
|
|
44
|
+
return next
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -159,7 +159,7 @@ export class MapPermissions {
|
|
|
159
159
|
`<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
|
|
160
160
|
)
|
|
161
161
|
container.appendChild(fieldset)
|
|
162
|
-
this._umap.
|
|
162
|
+
this._umap.datalayers.active().map((datalayer) => {
|
|
163
163
|
datalayer.permissions.edit(fieldset)
|
|
164
164
|
})
|
|
165
165
|
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Control } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import * as Utils from '../utils.js'
|
|
3
|
+
import { translate } from '../i18n.js'
|
|
4
|
+
|
|
5
|
+
export const HomeControl = Control.extend({
|
|
6
|
+
options: {
|
|
7
|
+
position: 'topleft',
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
onAdd: (map) => {
|
|
11
|
+
const path = map._umap.getStaticPathFor('home.svg')
|
|
12
|
+
const container = Utils.loadTemplate(
|
|
13
|
+
`<a href="/" class="home-button" title="${translate('Back to home')}"><img src="${path}" alt="${translate('Home logo')}" width="38px" height="38px" /></a>`
|
|
14
|
+
)
|
|
15
|
+
return container
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const EditControl = Control.extend({
|
|
20
|
+
options: {
|
|
21
|
+
position: 'topright',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
onAdd: (map) => {
|
|
25
|
+
const template = `
|
|
26
|
+
<div class="edit-enable">
|
|
27
|
+
<button type="button" data-ref="button">${translate('Edit')}</button>
|
|
28
|
+
</div>
|
|
29
|
+
`
|
|
30
|
+
const [container, { button }] = Utils.loadTemplateWithRefs(template)
|
|
31
|
+
button.addEventListener('click', () => map._umap.enableEdit())
|
|
32
|
+
button.addEventListener('mouseover', () => {
|
|
33
|
+
map._umap.tooltip.open({
|
|
34
|
+
content: map._umap.help.displayLabel('TOGGLE_EDIT'),
|
|
35
|
+
anchor: button,
|
|
36
|
+
position: 'bottom',
|
|
37
|
+
delay: 750,
|
|
38
|
+
duration: 5000,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
return container
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const MoreControl = Control.extend({
|
|
46
|
+
options: {
|
|
47
|
+
position: 'topleft',
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
onAdd: function (map) {
|
|
51
|
+
const pos = this.getPosition()
|
|
52
|
+
const corner = map._controlCorners[pos]
|
|
53
|
+
const className = 'umap-more-controls'
|
|
54
|
+
const template = `
|
|
55
|
+
<div class="umap-control-text">
|
|
56
|
+
<button class="umap-control-more" type="button" data-ref="button"></button>
|
|
57
|
+
</div>
|
|
58
|
+
`
|
|
59
|
+
const [container, { button }] = Utils.loadTemplateWithRefs(template)
|
|
60
|
+
button.addEventListener('click', () => corner.classList.toggle(className))
|
|
61
|
+
button.addEventListener('mouseover', () => {
|
|
62
|
+
const extended = corner.classList.contains(className)
|
|
63
|
+
map._umap.tooltip.open({
|
|
64
|
+
content: extended ? translate('Hide controls') : translate('More controls'),
|
|
65
|
+
anchor: button,
|
|
66
|
+
position: 'right',
|
|
67
|
+
delay: 750,
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
return container
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const PermanentCreditsControl = Control.extend({
|
|
75
|
+
options: {
|
|
76
|
+
position: 'bottomleft',
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
onAdd: (map) => {
|
|
80
|
+
const container = Utils.loadTemplate(
|
|
81
|
+
`<div class="umap-permanent-credits-container text">${Utils.toHTML(map.options.permanentCredit)}</div>`
|
|
82
|
+
)
|
|
83
|
+
const background = map.options.permanentCreditBackground ? '#FFFFFFB0' : ''
|
|
84
|
+
container.style.backgroundColor = background
|
|
85
|
+
return container
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const BaseButton = Control.extend({
|
|
90
|
+
initialize: function (umap, options) {
|
|
91
|
+
this._umap = umap
|
|
92
|
+
Control.prototype.initialize.call(this, options)
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
onAdd: function (map) {
|
|
96
|
+
const template = `
|
|
97
|
+
<div class="${this.options.className} umap-control">
|
|
98
|
+
<button type="button" title="${this.options.title}" data-ref="button"></button>
|
|
99
|
+
</div>
|
|
100
|
+
`
|
|
101
|
+
const [container, { button }] = Utils.loadTemplateWithRefs(template)
|
|
102
|
+
button.addEventListener('click', (event) => {
|
|
103
|
+
event.stopPropagation()
|
|
104
|
+
this.onClick()
|
|
105
|
+
})
|
|
106
|
+
button.addEventListener('dblclick', (event) => {
|
|
107
|
+
event.stopPropagation()
|
|
108
|
+
})
|
|
109
|
+
this.afterAdd(container)
|
|
110
|
+
return container
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
afterAdd: (container) => {},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
export const DataLayersControl = BaseButton.extend({
|
|
117
|
+
options: {
|
|
118
|
+
position: 'topleft',
|
|
119
|
+
className: 'umap-control-browse',
|
|
120
|
+
title: translate('Open browser'),
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
afterAdd: function (container) {
|
|
124
|
+
Utils.toggleBadge(container, this._umap.browser?.hasFilters())
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
onClick: function () {
|
|
128
|
+
this._umap.openBrowser()
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
export const CaptionControl = BaseButton.extend({
|
|
133
|
+
options: {
|
|
134
|
+
position: 'topleft',
|
|
135
|
+
className: 'umap-control-caption',
|
|
136
|
+
title: translate('About'),
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
onClick: function () {
|
|
140
|
+
this._umap.openCaption()
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
export const EmbedControl = BaseButton.extend({
|
|
145
|
+
options: {
|
|
146
|
+
position: 'topleft',
|
|
147
|
+
title: translate('Share and download'),
|
|
148
|
+
className: 'leaflet-control-embed',
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
onClick: function () {
|
|
152
|
+
this._umap.share.open()
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
export const AttributionControl = Control.Attribution.extend({
|
|
157
|
+
options: {
|
|
158
|
+
prefix: '',
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
_update: function () {
|
|
162
|
+
// Layer is no more on the map
|
|
163
|
+
if (!this._map) return
|
|
164
|
+
Control.Attribution.prototype._update.call(this)
|
|
165
|
+
const shortCredit = this._map._umap.getProperty('shortCredit')
|
|
166
|
+
const captionMenus = this._map._umap.getProperty('captionMenus')
|
|
167
|
+
// Use our own container, so we can hide/show on small screens
|
|
168
|
+
const originalCredits = this._container.innerHTML
|
|
169
|
+
this._container.innerHTML = ''
|
|
170
|
+
const template = `
|
|
171
|
+
<div class="attribution-container">
|
|
172
|
+
${originalCredits}
|
|
173
|
+
<span data-ref="short"> — ${Utils.toHTML(shortCredit)}</span>
|
|
174
|
+
<a href="#" data-ref="caption"> — ${translate('Open caption')}</a>
|
|
175
|
+
<a href="/" data-ref="home"> — ${translate('Home')}</a>
|
|
176
|
+
<a href="https://umap-project.org/" data-ref="site"> — ${translate('Powered by uMap')}</a>
|
|
177
|
+
<a href="#" class="attribution-toggle"></a>
|
|
178
|
+
</div>
|
|
179
|
+
`
|
|
180
|
+
const [container, { short, caption, home, site }] =
|
|
181
|
+
Utils.loadTemplateWithRefs(template)
|
|
182
|
+
caption.addEventListener('click', () => this._map._umap.openCaption())
|
|
183
|
+
this._container.appendChild(container)
|
|
184
|
+
short.hidden = !shortCredit
|
|
185
|
+
caption.hidden = !captionMenus
|
|
186
|
+
site.hidden = !captionMenus
|
|
187
|
+
home.hidden = this._map._umap.isEmbed || !captionMenus
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
/* Used in edit mode to define the default tilelayer */
|
|
192
|
+
export const TileLayerChooser = BaseButton.extend({
|
|
193
|
+
options: {
|
|
194
|
+
position: 'topleft',
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
onClick: function () {
|
|
198
|
+
this.openSwitcher({ edit: true })
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
openSwitcher: function (options = {}) {
|
|
202
|
+
const template = `
|
|
203
|
+
<div class="umap-edit-tilelayers">
|
|
204
|
+
<h3><i class="icon icon-16 icon-tilelayer" title=""></i><span class="">${translate('Change tilelayers')}</span></h3>
|
|
205
|
+
<ul data-ref="tileContainer"></ul>
|
|
206
|
+
</div>
|
|
207
|
+
`
|
|
208
|
+
const [container, { tileContainer }] = Utils.loadTemplateWithRefs(template)
|
|
209
|
+
this.buildList(tileContainer, options)
|
|
210
|
+
const panel = options.edit ? this._umap.editPanel : this._umap.panel
|
|
211
|
+
panel.open({ content: container, highlight: 'tilelayers' })
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
buildList: function (container, options) {
|
|
215
|
+
this._umap._leafletMap.eachTileLayer((tilelayer) => {
|
|
216
|
+
const browserIsHttps = window.location.protocol === 'https:'
|
|
217
|
+
const tileLayerIsHttp = tilelayer.options.url_template.indexOf('http:') === 0
|
|
218
|
+
if (browserIsHttps && tileLayerIsHttp) return
|
|
219
|
+
container.appendChild(this.addTileLayerElement(tilelayer, options))
|
|
220
|
+
})
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
addTileLayerElement: function (tilelayer, options) {
|
|
224
|
+
const selectedClass = this._umap._leafletMap.hasLayer(tilelayer) ? 'selected' : ''
|
|
225
|
+
const src = Utils.template(
|
|
226
|
+
tilelayer.options.url_template,
|
|
227
|
+
this._umap._leafletMap.options.demoTileInfos
|
|
228
|
+
)
|
|
229
|
+
const template = `
|
|
230
|
+
<li>
|
|
231
|
+
<img src="${src}" loading="lazy" />
|
|
232
|
+
<div>${tilelayer.options.name}</div>
|
|
233
|
+
</li>
|
|
234
|
+
`
|
|
235
|
+
const li = Utils.loadTemplate(template)
|
|
236
|
+
li.addEventListener('click', () => {
|
|
237
|
+
const oldTileLayer = this._umap.properties.tilelayer
|
|
238
|
+
this._umap._leafletMap.selectTileLayer(tilelayer)
|
|
239
|
+
this._umap._leafletMap._controls.tilelayers.setLayers()
|
|
240
|
+
if (options?.edit) {
|
|
241
|
+
this._umap.properties.tilelayer = tilelayer.toJSON()
|
|
242
|
+
this._umap.sync.update(
|
|
243
|
+
'properties.tilelayer',
|
|
244
|
+
this._umap.properties.tilelayer,
|
|
245
|
+
oldTileLayer
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
return li
|
|
250
|
+
},
|
|
251
|
+
})
|
|
@@ -41,6 +41,11 @@ export const Heat = L.HeatLayer.extend({
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
|
|
44
|
+
removeLayer: (layer) => {
|
|
45
|
+
// No op, there is no "removeLatLng" in Leaflet.heat
|
|
46
|
+
// but this method is expected by DataLayer
|
|
47
|
+
},
|
|
48
|
+
|
|
44
49
|
onAdd: function (map) {
|
|
45
50
|
LayerMixin.onAdd.call(this, map)
|
|
46
51
|
return L.HeatLayer.prototype.onAdd.call(this, map)
|