umap-project 3.4.0b1__py3-none-any.whl → 3.4.2__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.
- umap/__init__.py +1 -1
- umap/context_processors.py +1 -1
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +20 -16
- umap/locale/en/LC_MESSAGES/django.po +18 -14
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +20 -16
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +18 -14
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +72 -71
- umap/migrations/0018_datalayer_uuid.py +1 -1
- umap/models.py +7 -3
- umap/settings/local.py.sample +1 -1
- umap/static/umap/content.css +0 -3
- umap/static/umap/css/bar.css +9 -6
- umap/static/umap/css/form.css +27 -11
- umap/static/umap/css/popup.css +1 -0
- umap/static/umap/js/components/base.js +1 -1
- umap/static/umap/js/components/copiable.js +47 -0
- umap/static/umap/js/modules/autocomplete.js +31 -58
- umap/static/umap/js/modules/browser.js +8 -8
- umap/static/umap/js/modules/data/features.js +33 -36
- umap/static/umap/js/modules/data/fields.js +446 -0
- umap/static/umap/js/modules/data/layer.js +76 -93
- umap/static/umap/js/modules/domutils.js +24 -4
- umap/static/umap/js/modules/filters.js +20 -47
- umap/static/umap/js/modules/form/fields.js +4 -4
- umap/static/umap/js/modules/formatter.js +9 -1
- umap/static/umap/js/modules/help.js +13 -14
- umap/static/umap/js/modules/i18n.js +1 -1
- umap/static/umap/js/modules/importer.js +18 -27
- umap/static/umap/js/modules/importers/banfr.js +0 -1
- umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
- umap/static/umap/js/modules/importers/communesfr.js +7 -8
- umap/static/umap/js/modules/importers/datasets.js +14 -14
- umap/static/umap/js/modules/importers/geodatamine.js +20 -22
- umap/static/umap/js/modules/importers/opendata.js +10 -0
- umap/static/umap/js/modules/importers/overpass.js +19 -18
- umap/static/umap/js/modules/managers.js +1 -265
- umap/static/umap/js/modules/permissions.js +5 -3
- umap/static/umap/js/modules/rendering/controls.js +6 -4
- umap/static/umap/js/modules/rendering/icon.js +5 -9
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +16 -11
- umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
- umap/static/umap/js/modules/rendering/map.js +22 -22
- umap/static/umap/js/modules/rendering/popup.js +6 -3
- umap/static/umap/js/modules/rendering/template.js +31 -37
- umap/static/umap/js/modules/rendering/ui.js +1 -2
- umap/static/umap/js/modules/rules.js +34 -41
- umap/static/umap/js/modules/schema.js +0 -7
- umap/static/umap/js/modules/share.js +36 -69
- umap/static/umap/js/modules/slideshow.js +3 -3
- umap/static/umap/js/modules/tableeditor.js +0 -1
- umap/static/umap/js/modules/ui/bar.js +51 -32
- umap/static/umap/js/modules/ui/dialog.js +10 -1
- umap/static/umap/js/modules/ui/panel.js +28 -23
- umap/static/umap/js/modules/ui/tooltip.js +1 -1
- umap/static/umap/js/modules/umap.js +84 -84
- umap/static/umap/js/modules/utils.js +13 -4
- umap/static/umap/js/umap.controls.js +33 -14
- umap/static/umap/locale/am_ET.js +19 -8
- umap/static/umap/locale/am_ET.json +19 -8
- umap/static/umap/locale/ar.js +19 -8
- umap/static/umap/locale/ar.json +19 -8
- umap/static/umap/locale/ast.js +19 -8
- umap/static/umap/locale/ast.json +19 -8
- umap/static/umap/locale/bg.js +19 -8
- umap/static/umap/locale/bg.json +19 -8
- umap/static/umap/locale/br.js +20 -9
- umap/static/umap/locale/br.json +20 -9
- umap/static/umap/locale/ca.js +19 -8
- umap/static/umap/locale/ca.json +19 -8
- umap/static/umap/locale/cs_CZ.js +20 -9
- umap/static/umap/locale/cs_CZ.json +20 -9
- umap/static/umap/locale/da.js +54 -43
- umap/static/umap/locale/da.json +54 -43
- umap/static/umap/locale/de.js +44 -33
- umap/static/umap/locale/de.json +44 -33
- umap/static/umap/locale/el.js +20 -9
- umap/static/umap/locale/el.json +20 -9
- umap/static/umap/locale/en.js +20 -9
- umap/static/umap/locale/en.json +20 -9
- umap/static/umap/locale/en_US.json +19 -8
- umap/static/umap/locale/es.js +55 -44
- umap/static/umap/locale/es.json +55 -44
- umap/static/umap/locale/et.js +20 -9
- umap/static/umap/locale/et.json +20 -9
- umap/static/umap/locale/eu.js +25 -14
- umap/static/umap/locale/eu.json +25 -14
- umap/static/umap/locale/fa_IR.js +20 -9
- umap/static/umap/locale/fa_IR.json +20 -9
- umap/static/umap/locale/fi.js +19 -8
- umap/static/umap/locale/fi.json +19 -8
- umap/static/umap/locale/fr.js +21 -10
- umap/static/umap/locale/fr.json +21 -10
- umap/static/umap/locale/gl.js +147 -136
- umap/static/umap/locale/gl.json +147 -136
- umap/static/umap/locale/he.js +19 -8
- umap/static/umap/locale/he.json +19 -8
- umap/static/umap/locale/hr.js +19 -8
- umap/static/umap/locale/hr.json +19 -8
- umap/static/umap/locale/hu.js +62 -51
- umap/static/umap/locale/hu.json +62 -51
- umap/static/umap/locale/id.js +19 -8
- umap/static/umap/locale/id.json +19 -8
- umap/static/umap/locale/is.js +20 -9
- umap/static/umap/locale/is.json +20 -9
- umap/static/umap/locale/it.js +20 -9
- umap/static/umap/locale/it.json +20 -9
- umap/static/umap/locale/ja.js +19 -8
- umap/static/umap/locale/ja.json +19 -8
- umap/static/umap/locale/ko.js +19 -8
- umap/static/umap/locale/ko.json +19 -8
- umap/static/umap/locale/lt.js +19 -8
- umap/static/umap/locale/lt.json +19 -8
- umap/static/umap/locale/ms.js +20 -9
- umap/static/umap/locale/ms.json +20 -9
- umap/static/umap/locale/nl.js +20 -9
- umap/static/umap/locale/nl.json +20 -9
- umap/static/umap/locale/no.js +19 -8
- umap/static/umap/locale/no.json +19 -8
- umap/static/umap/locale/pl.js +56 -45
- umap/static/umap/locale/pl.json +56 -45
- umap/static/umap/locale/pl_PL.json +19 -8
- umap/static/umap/locale/pt.js +20 -9
- umap/static/umap/locale/pt.json +20 -9
- umap/static/umap/locale/pt_BR.js +19 -8
- umap/static/umap/locale/pt_BR.json +19 -8
- umap/static/umap/locale/pt_PT.js +19 -8
- umap/static/umap/locale/pt_PT.json +19 -8
- umap/static/umap/locale/ro.js +19 -8
- umap/static/umap/locale/ro.json +19 -8
- umap/static/umap/locale/ru.js +19 -8
- umap/static/umap/locale/ru.json +19 -8
- umap/static/umap/locale/si.js +1 -1
- umap/static/umap/locale/si.json +1 -1
- umap/static/umap/locale/sk_SK.js +19 -8
- umap/static/umap/locale/sk_SK.json +19 -8
- umap/static/umap/locale/sl.js +19 -8
- umap/static/umap/locale/sl.json +19 -8
- umap/static/umap/locale/sr.js +19 -8
- umap/static/umap/locale/sr.json +19 -8
- umap/static/umap/locale/sv.js +19 -8
- umap/static/umap/locale/sv.json +19 -8
- umap/static/umap/locale/th_TH.js +19 -8
- umap/static/umap/locale/th_TH.json +19 -8
- umap/static/umap/locale/tr.js +19 -8
- umap/static/umap/locale/tr.json +19 -8
- umap/static/umap/locale/uk_UA.js +19 -8
- umap/static/umap/locale/uk_UA.json +19 -8
- umap/static/umap/locale/vi.js +19 -8
- umap/static/umap/locale/vi.json +19 -8
- umap/static/umap/locale/vi_VN.json +19 -8
- umap/static/umap/locale/zh.js +19 -8
- umap/static/umap/locale/zh.json +19 -8
- umap/static/umap/locale/zh_CN.json +19 -8
- umap/static/umap/locale/zh_TW.Big5.json +19 -8
- umap/static/umap/locale/zh_TW.js +53 -42
- umap/static/umap/locale/zh_TW.json +53 -42
- umap/static/umap/map.css +8 -7
- umap/static/umap/unittests/utils.js +7 -7
- umap/templates/umap/content_footer.html +1 -0
- umap/templates/umap/css.html +0 -2
- umap/templates/umap/js.html +1 -3
- umap/templates/umap/login_popup_end.html +2 -2
- umap/tests/integration/conftest.py +11 -2
- umap/tests/integration/test_anonymous_owned_map.py +2 -2
- umap/tests/integration/test_conditional_rules.py +107 -52
- umap/tests/integration/test_draw_polygon.py +4 -0
- umap/tests/integration/test_draw_polyline.py +11 -0
- umap/tests/integration/test_edit_datalayer.py +1 -1
- umap/tests/integration/test_fields.py +19 -0
- umap/tests/integration/test_filters.py +6 -7
- umap/tests/integration/test_iframe.py +1 -1
- umap/tests/integration/test_import.py +23 -0
- umap/tests/integration/test_map.py +2 -2
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_owned_map.py +2 -2
- umap/tests/integration/test_picto.py +1 -1
- umap/tests/integration/test_popup.py +31 -0
- umap/tests/integration/test_remote_data.py +4 -4
- umap/tests/integration/test_save.py +1 -1
- umap/tests/integration/test_search.py +41 -0
- umap/tests/integration/test_share.py +2 -2
- umap/tests/integration/test_team.py +1 -1
- umap/tests/integration/test_websocket_sync.py +69 -20
- umap/tests/test_dashboard.py +1 -1
- umap/tests/test_statics.py +2 -2
- umap/tests/test_utils.py +4 -1
- umap/tests/test_views.py +1 -1
- umap/utils.py +3 -2
- {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/METADATA +17 -17
- {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/RECORD +198 -199
- umap/static/umap/js/umap.core.js +0 -93
- umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
- umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
- umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
- {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/WHEEL +0 -0
- {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/entry_points.txt +0 -0
- {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// FIXME: this module should not depend on Leaflet
|
|
2
2
|
import {
|
|
3
3
|
DomEvent,
|
|
4
|
-
DomUtil,
|
|
5
4
|
GeoJSON,
|
|
6
5
|
stamp,
|
|
6
|
+
SVG,
|
|
7
7
|
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
8
8
|
import {
|
|
9
9
|
uMapAlert as Alert,
|
|
@@ -19,10 +19,12 @@ import { Heat } from '../rendering/layers/heat.js'
|
|
|
19
19
|
import * as Schema from '../schema.js'
|
|
20
20
|
import TableEditor from '../tableeditor.js'
|
|
21
21
|
import * as Utils from '../utils.js'
|
|
22
|
+
import * as DOMUtils from '../domutils.js'
|
|
22
23
|
import { LineString, Point, Polygon } from './features.js'
|
|
23
24
|
import Rules from '../rules.js'
|
|
24
|
-
import { FeatureManager
|
|
25
|
+
import { FeatureManager } from '../managers.js'
|
|
25
26
|
import { Filters } from '../filters.js'
|
|
27
|
+
import { Fields, getDefaultFields } from './fields.js'
|
|
26
28
|
|
|
27
29
|
export const LAYER_TYPES = [
|
|
28
30
|
DefaultLayer,
|
|
@@ -49,7 +51,7 @@ export class DataLayer {
|
|
|
49
51
|
this.parentPane = this._leafletMap.getPane('overlayPane')
|
|
50
52
|
this.pane = this._leafletMap.createPane(`datalayer${stamp(this)}`, this.parentPane)
|
|
51
53
|
// FIXME: should be on layer
|
|
52
|
-
this.renderer =
|
|
54
|
+
this.renderer = new SVG({ pane: this.pane })
|
|
53
55
|
this.defaultProperties = {
|
|
54
56
|
displayOnLoad: true,
|
|
55
57
|
inCaption: true,
|
|
@@ -83,20 +85,15 @@ export class DataLayer {
|
|
|
83
85
|
}
|
|
84
86
|
this.connectToMap()
|
|
85
87
|
this.permissions = new DataLayerPermissions(this._umap, this)
|
|
86
|
-
this.rules = new Rules(umap, this)
|
|
87
88
|
|
|
88
89
|
this._needsFetch = this.createdOnServer || this.isRemoteLayer()
|
|
90
|
+
this.fields = new Fields(this, this._umap.dialog)
|
|
91
|
+
this.filters = new Filters(this, this._umap)
|
|
92
|
+
this.rules = new Rules(umap, this)
|
|
93
|
+
|
|
89
94
|
if (!this.createdOnServer) {
|
|
90
95
|
if (this.showAtLoad()) this.show()
|
|
91
96
|
}
|
|
92
|
-
if (!this._needsFetch && !this._umap.fields.size) {
|
|
93
|
-
this.properties.fields = [
|
|
94
|
-
{ key: U.DEFAULT_LABEL_KEY, type: 'String' },
|
|
95
|
-
{ key: 'description', type: 'Text' },
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
this.fields = new FieldManager(this, this._umap.dialog)
|
|
99
|
-
this.filters = new Filters(this, this._umap)
|
|
100
97
|
|
|
101
98
|
// Only layers that are displayed on load must be hidden/shown
|
|
102
99
|
// Automatically, others will be shown manually, and thus will
|
|
@@ -148,12 +145,6 @@ export class DataLayer {
|
|
|
148
145
|
this.properties.rank = value
|
|
149
146
|
}
|
|
150
147
|
|
|
151
|
-
get fieldKeys() {
|
|
152
|
-
// Needed to get a similar API from layer and uMap, but
|
|
153
|
-
// uMap whould return concat of all datalayers fields
|
|
154
|
-
return Array.from(this.fields.keys())
|
|
155
|
-
}
|
|
156
|
-
|
|
157
148
|
get sortKey() {
|
|
158
149
|
return this.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
|
|
159
150
|
}
|
|
@@ -209,7 +200,7 @@ export class DataLayer {
|
|
|
209
200
|
}
|
|
210
201
|
|
|
211
202
|
// This method does a targeted update of the UI,
|
|
212
|
-
// it
|
|
203
|
+
// it would be merged with `render`` method and the
|
|
213
204
|
// SCHEMA at some point
|
|
214
205
|
propagate(fields = []) {
|
|
215
206
|
const impacts = {
|
|
@@ -232,27 +223,28 @@ export class DataLayer {
|
|
|
232
223
|
}
|
|
233
224
|
|
|
234
225
|
showAtLoad() {
|
|
235
|
-
return this.
|
|
226
|
+
return this.autoVisibility && this.showAtZoom()
|
|
236
227
|
}
|
|
237
228
|
|
|
238
|
-
get
|
|
239
|
-
if (this.
|
|
229
|
+
get autoVisibility() {
|
|
230
|
+
if (this._autoVisibility === undefined) {
|
|
240
231
|
if (this._umap.datalayersFromQueryString) {
|
|
241
232
|
const datalayerIds = this._umap.datalayersFromQueryString
|
|
242
|
-
this.
|
|
233
|
+
this._autoVisibility = datalayerIds.includes(this.id.toString())
|
|
243
234
|
if (this.properties.old_id) {
|
|
244
|
-
this.
|
|
245
|
-
this.
|
|
235
|
+
this._autoVisibility =
|
|
236
|
+
this._autoVisibility ||
|
|
237
|
+
datalayerIds.includes(this.properties.old_id.toString())
|
|
246
238
|
}
|
|
247
239
|
} else {
|
|
248
|
-
this.
|
|
240
|
+
this._autoVisibility = this.properties.displayOnLoad
|
|
249
241
|
}
|
|
250
242
|
}
|
|
251
|
-
return this.
|
|
243
|
+
return this._autoVisibility
|
|
252
244
|
}
|
|
253
245
|
|
|
254
|
-
set
|
|
255
|
-
this.
|
|
246
|
+
set autoVisibility(value) {
|
|
247
|
+
this._autoVisibility = value
|
|
256
248
|
}
|
|
257
249
|
|
|
258
250
|
insertBefore(other) {
|
|
@@ -453,6 +445,10 @@ export class DataLayer {
|
|
|
453
445
|
this._umap.featuresIndex[feature.getSlug()] = feature
|
|
454
446
|
// TODO: quid for remote data ?
|
|
455
447
|
this.inferFields(feature)
|
|
448
|
+
if (!this.fields.size && !this._umap.fields.size) {
|
|
449
|
+
this.properties.fields = getDefaultFields()
|
|
450
|
+
this.fields.pull()
|
|
451
|
+
}
|
|
456
452
|
try {
|
|
457
453
|
this.showFeature(feature)
|
|
458
454
|
} catch (error) {
|
|
@@ -491,7 +487,7 @@ export class DataLayer {
|
|
|
491
487
|
|
|
492
488
|
inferFields(feature) {
|
|
493
489
|
for (const key in feature.properties) {
|
|
494
|
-
if (typeof feature.properties[key] !== 'object') {
|
|
490
|
+
if (key && typeof feature.properties[key] !== 'object') {
|
|
495
491
|
if (key.indexOf('_') === 0) continue
|
|
496
492
|
if (this.fields.has(key)) continue
|
|
497
493
|
if (this._umap.fields.has(key)) continue
|
|
@@ -529,12 +525,10 @@ export class DataLayer {
|
|
|
529
525
|
this.features.forEach((feature) => callback(feature))
|
|
530
526
|
}
|
|
531
527
|
|
|
532
|
-
sortedValues(
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
537
|
-
.sort(Utils.naturalSort)
|
|
528
|
+
sortedValues(key) {
|
|
529
|
+
const field = this.fields.get(key) || this._umap.fields.get(key)
|
|
530
|
+
if (!field) return []
|
|
531
|
+
return field.values(this.features.all()).sort(Utils.naturalSort)
|
|
538
532
|
}
|
|
539
533
|
|
|
540
534
|
addData(geojson, sync) {
|
|
@@ -779,7 +773,11 @@ export class DataLayer {
|
|
|
779
773
|
},
|
|
780
774
|
],
|
|
781
775
|
]
|
|
782
|
-
|
|
776
|
+
container.appendChild(
|
|
777
|
+
DOMUtils.loadTemplate(`
|
|
778
|
+
<h3><i class="icon icon-16 icon-layers"></i>${translate('Layer properties')}</h3>
|
|
779
|
+
`)
|
|
780
|
+
)
|
|
783
781
|
const builder = new MutatingForm(this, metadataFields)
|
|
784
782
|
builder.on('set', ({ detail }) => {
|
|
785
783
|
this._umap.onDataLayersChanged()
|
|
@@ -825,7 +823,7 @@ export class DataLayer {
|
|
|
825
823
|
const builder = new MutatingForm(this, fields, {
|
|
826
824
|
id: 'datalayer-advanced-properties',
|
|
827
825
|
})
|
|
828
|
-
const shapeFieldset =
|
|
826
|
+
const shapeFieldset = DOMUtils.createFieldset(
|
|
829
827
|
container,
|
|
830
828
|
translate('Shape properties')
|
|
831
829
|
)
|
|
@@ -850,7 +848,7 @@ export class DataLayer {
|
|
|
850
848
|
this.reindex()
|
|
851
849
|
}
|
|
852
850
|
})
|
|
853
|
-
const advancedFieldset =
|
|
851
|
+
const advancedFieldset = DOMUtils.createFieldset(
|
|
854
852
|
container,
|
|
855
853
|
translate('Advanced properties')
|
|
856
854
|
)
|
|
@@ -869,7 +867,7 @@ export class DataLayer {
|
|
|
869
867
|
'properties.interactive',
|
|
870
868
|
]
|
|
871
869
|
const builder = new MutatingForm(this, fields)
|
|
872
|
-
const popupFieldset =
|
|
870
|
+
const popupFieldset = DOMUtils.createFieldset(
|
|
873
871
|
container,
|
|
874
872
|
translate('Interaction options')
|
|
875
873
|
)
|
|
@@ -887,7 +885,7 @@ export class DataLayer {
|
|
|
887
885
|
'properties.textPathPosition',
|
|
888
886
|
]
|
|
889
887
|
const builder = new MutatingForm(this, fields)
|
|
890
|
-
const fieldset =
|
|
888
|
+
const fieldset = DOMUtils.createFieldset(container, translate('Line decoration'))
|
|
891
889
|
fieldset.appendChild(builder.build())
|
|
892
890
|
}
|
|
893
891
|
|
|
@@ -937,23 +935,21 @@ export class DataLayer {
|
|
|
937
935
|
fields.push('properties.remoteData.ttl')
|
|
938
936
|
}
|
|
939
937
|
|
|
940
|
-
const remoteDataContainer =
|
|
938
|
+
const remoteDataContainer = DOMUtils.createFieldset(
|
|
941
939
|
container,
|
|
942
940
|
translate('Remote data')
|
|
943
941
|
)
|
|
944
942
|
const builder = new MutatingForm(this, fields)
|
|
945
943
|
remoteDataContainer.appendChild(builder.build())
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
this
|
|
952
|
-
)
|
|
944
|
+
const button = DOMUtils.loadTemplate(`
|
|
945
|
+
<button class="umap-verify" type="button">${translate('Verify remote URL')}</button>
|
|
946
|
+
`)
|
|
947
|
+
button.addEventListener('click', () => this.fetchRemoteData(true))
|
|
948
|
+
remoteDataContainer.appendChild(button)
|
|
953
949
|
}
|
|
954
950
|
|
|
955
951
|
_buildAdvancedActions(container) {
|
|
956
|
-
const advancedActions =
|
|
952
|
+
const advancedActions = DOMUtils.createFieldset(
|
|
957
953
|
container,
|
|
958
954
|
translate('Advanced actions')
|
|
959
955
|
)
|
|
@@ -993,7 +989,7 @@ export class DataLayer {
|
|
|
993
989
|
if (!this._umap.editEnabled) {
|
|
994
990
|
return
|
|
995
991
|
}
|
|
996
|
-
const container =
|
|
992
|
+
const container = document.createElement('div')
|
|
997
993
|
this._editMetadata(container)
|
|
998
994
|
this._editLayerProperties(container)
|
|
999
995
|
this._editShapeProperties(container)
|
|
@@ -1010,15 +1006,13 @@ export class DataLayer {
|
|
|
1010
1006
|
|
|
1011
1007
|
this._buildAdvancedActions(container)
|
|
1012
1008
|
|
|
1013
|
-
const backButton =
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
translate('Back to layers')
|
|
1017
|
-
)
|
|
1009
|
+
const backButton = DOMUtils.loadTemplate(`
|
|
1010
|
+
<button class="icon icon-16 icon-back" type="button" title="${translate('Back to layers')}"></button>
|
|
1011
|
+
`)
|
|
1018
1012
|
// Fixme: remove me when this is merged and released
|
|
1019
1013
|
// https://github.com/Leaflet/Leaflet/pull/9052
|
|
1020
1014
|
DomEvent.disableClickPropagation(backButton)
|
|
1021
|
-
|
|
1015
|
+
backButton.addEventListener('click', () => this._umap.editDatalayers())
|
|
1022
1016
|
|
|
1023
1017
|
return this._umap.editPanel.open({
|
|
1024
1018
|
content: container,
|
|
@@ -1069,14 +1063,14 @@ export class DataLayer {
|
|
|
1069
1063
|
button.addEventListener('click', () => this.restore(data.ref))
|
|
1070
1064
|
}
|
|
1071
1065
|
|
|
1072
|
-
const versionsContainer =
|
|
1073
|
-
|
|
1066
|
+
const versionsContainer = DOMUtils.createFieldset(container, translate('Versions'))
|
|
1067
|
+
versionsContainer.closest('details').addEventListener('toggle', async (event) => {
|
|
1068
|
+
if (event.target.open) {
|
|
1074
1069
|
const [{ versions }, response, error] = await this._umap.server.get(
|
|
1075
1070
|
this.getVersionsUrl()
|
|
1076
1071
|
)
|
|
1077
1072
|
if (!error) versions.forEach(appendVersion)
|
|
1078
|
-
}
|
|
1079
|
-
context: this,
|
|
1073
|
+
}
|
|
1080
1074
|
})
|
|
1081
1075
|
}
|
|
1082
1076
|
|
|
@@ -1126,7 +1120,7 @@ export class DataLayer {
|
|
|
1126
1120
|
// From now on, do not try to how/hide
|
|
1127
1121
|
// automatically this layer, as user
|
|
1128
1122
|
// has taken control on this.
|
|
1129
|
-
this.
|
|
1123
|
+
this.autoVisibility = false
|
|
1130
1124
|
let display = force
|
|
1131
1125
|
if (force === undefined) {
|
|
1132
1126
|
if (!this.isVisible()) display = true
|
|
@@ -1347,7 +1341,10 @@ export class DataLayer {
|
|
|
1347
1341
|
rules.set(rule.condition, rule)
|
|
1348
1342
|
}
|
|
1349
1343
|
for (const rule of this._umap.rules) {
|
|
1350
|
-
if (
|
|
1344
|
+
if (
|
|
1345
|
+
!rules.has(rule.condition) &&
|
|
1346
|
+
(this.fields.has(rule.field.key) || this._umap.fields.has(rule.field.key))
|
|
1347
|
+
) {
|
|
1351
1348
|
rules.set(rule.condition, rule)
|
|
1352
1349
|
}
|
|
1353
1350
|
}
|
|
@@ -1366,31 +1363,17 @@ export class DataLayer {
|
|
|
1366
1363
|
}
|
|
1367
1364
|
|
|
1368
1365
|
renderToolbox(container) {
|
|
1369
|
-
const toggle =
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
)
|
|
1379
|
-
|
|
1380
|
-
container,
|
|
1381
|
-
'icon-zoom',
|
|
1382
|
-
translate('Zoom to layer extent')
|
|
1383
|
-
)
|
|
1384
|
-
const edit = DomUtil.createButtonIcon(
|
|
1385
|
-
container,
|
|
1386
|
-
'icon-edit show-on-edit',
|
|
1387
|
-
translate('Edit')
|
|
1388
|
-
)
|
|
1389
|
-
const remove = DomUtil.createButtonIcon(
|
|
1390
|
-
container,
|
|
1391
|
-
'icon-delete show-on-edit',
|
|
1392
|
-
translate('Delete layer')
|
|
1393
|
-
)
|
|
1366
|
+
const [span, { toggle, table, zoomTo, edit, remove }] =
|
|
1367
|
+
DOMUtils.loadTemplateWithRefs(`
|
|
1368
|
+
<span>
|
|
1369
|
+
<button class="icon icon-16 icon-eye" title="${translate('Show/hide layer')}" type="button" data-ref="toggle"></button>
|
|
1370
|
+
<button class="icon icon-16 icon-table show-on-edit" title="${translate('Edit properties in a table')}" type="button" data-ref="table"></button>
|
|
1371
|
+
<button class="icon icon-16 icon-zoom" title="${translate('Zoom to layer extent')}" type="button" data-ref="zoomTo"></button>
|
|
1372
|
+
<button class="icon icon-16 icon-edit show-on-edit" title="${translate('Edit')}" type="button" data-ref="edit"></button>
|
|
1373
|
+
<button class="icon icon-16 icon-delete show-on-edit" title="${translate('Delete layer')}" type="button" data-ref="remove"></button>
|
|
1374
|
+
</span>
|
|
1375
|
+
`)
|
|
1376
|
+
container.appendChild(span)
|
|
1394
1377
|
if (this.isReadOnly()) {
|
|
1395
1378
|
container.classList.add('readonly')
|
|
1396
1379
|
} else {
|
|
@@ -1414,7 +1397,7 @@ export class DataLayer {
|
|
|
1414
1397
|
propagateDelete() {
|
|
1415
1398
|
const els = this.getHidableElements()
|
|
1416
1399
|
for (const el of els) {
|
|
1417
|
-
|
|
1400
|
+
el.remove()
|
|
1418
1401
|
}
|
|
1419
1402
|
}
|
|
1420
1403
|
|
|
@@ -1427,15 +1410,15 @@ export class DataLayer {
|
|
|
1427
1410
|
|
|
1428
1411
|
propagateHide() {
|
|
1429
1412
|
const els = this.getHidableElements()
|
|
1430
|
-
for (
|
|
1431
|
-
|
|
1413
|
+
for (const el of els) {
|
|
1414
|
+
el.classList.add('off')
|
|
1432
1415
|
}
|
|
1433
1416
|
}
|
|
1434
1417
|
|
|
1435
1418
|
propagateShow() {
|
|
1436
1419
|
const els = this.getHidableElements()
|
|
1437
|
-
for (
|
|
1438
|
-
|
|
1420
|
+
for (const el of els) {
|
|
1421
|
+
el.classList.remove('off')
|
|
1439
1422
|
}
|
|
1440
1423
|
}
|
|
1441
1424
|
}
|
|
@@ -32,7 +32,7 @@ export const copyToClipboard = (textToCopy) => {
|
|
|
32
32
|
tooltip.open({ content: translate('✅ Copied!'), duration: 5000 })
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export const copiableInput = (parent, label, value) => {
|
|
35
|
+
export const copiableInput = (parent, label, value = '') => {
|
|
36
36
|
const [container, { input, button }] = Utils.loadTemplateWithRefs(`
|
|
37
37
|
<div class="copiable-input">
|
|
38
38
|
<label>${label}<input type="text" readOnly value="${value}" data-ref=input /></label>
|
|
@@ -83,11 +83,11 @@ export const hexToRGB = (hex) => {
|
|
|
83
83
|
.map((x) => Number.parseInt(x, 16))
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const
|
|
86
|
+
const CACHE_CONTRAST = {}
|
|
87
87
|
export const contrastedColor = (el, bgcolor) => {
|
|
88
88
|
// Return 0 for black and 1 for white
|
|
89
89
|
// bgcolor is a human color, it can be a any keyword (purple…)
|
|
90
|
-
if (typeof
|
|
90
|
+
if (typeof CACHE_CONTRAST[bgcolor] !== 'undefined') return CACHE_CONTRAST[bgcolor]
|
|
91
91
|
let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
|
|
92
92
|
rgb = RGBRegex.exec(rgb)
|
|
93
93
|
if (rgb && rgb.length === 4) {
|
|
@@ -104,6 +104,26 @@ export const contrastedColor = (el, bgcolor) => {
|
|
|
104
104
|
}
|
|
105
105
|
if (!rgb) return 1
|
|
106
106
|
const out = contrastWCAG21(rgb)
|
|
107
|
-
if (bgcolor)
|
|
107
|
+
if (bgcolor) CACHE_CONTRAST[bgcolor] = out
|
|
108
108
|
return out
|
|
109
109
|
}
|
|
110
|
+
|
|
111
|
+
export const createFieldset = (parent, title, options) => {
|
|
112
|
+
options = options || {}
|
|
113
|
+
const [details, { summary, fieldset }] = loadTemplateWithRefs(`
|
|
114
|
+
<details class="${options.className || ''}">
|
|
115
|
+
<summary data-ref="summary"><h4>${title}</h4></summary>
|
|
116
|
+
<fieldset data-ref="fieldset"></fieldset>
|
|
117
|
+
</details>
|
|
118
|
+
`)
|
|
119
|
+
details.open = options.on === true
|
|
120
|
+
parent.appendChild(details)
|
|
121
|
+
if (options.icon) {
|
|
122
|
+
const icon = loadTemplate(`<i class="icon icon-16 ${options.icon}"></i>`)
|
|
123
|
+
summary.insertBefore(icon, summary.firstChild)
|
|
124
|
+
}
|
|
125
|
+
return fieldset
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const loadTemplateWithRefs = Utils.loadTemplateWithRefs
|
|
129
|
+
export const loadTemplate = Utils.loadTemplate
|
|
@@ -6,27 +6,6 @@ import { Fields } from './form/fields.js'
|
|
|
6
6
|
|
|
7
7
|
const EMPTY_VALUE = translate('<empty value>')
|
|
8
8
|
|
|
9
|
-
const getParser = (type) => {
|
|
10
|
-
switch (type) {
|
|
11
|
-
case 'Number':
|
|
12
|
-
return Number.parseFloat
|
|
13
|
-
case 'Datetime':
|
|
14
|
-
return (v) => new Date(v)
|
|
15
|
-
case 'Date':
|
|
16
|
-
return Utils.parseNaiveDate
|
|
17
|
-
case 'Boolean':
|
|
18
|
-
return Boolean
|
|
19
|
-
case 'Enum':
|
|
20
|
-
return (v) => {
|
|
21
|
-
if (!v) return [EMPTY_VALUE]
|
|
22
|
-
return String(v || '')
|
|
23
|
-
.split(',')
|
|
24
|
-
.map((s) => s.trim())
|
|
25
|
-
}
|
|
26
|
-
default:
|
|
27
|
-
return (v) => String(v || '')
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
9
|
const Widgets = {}
|
|
31
10
|
|
|
32
11
|
class BaseWidget {
|
|
@@ -73,13 +52,13 @@ Widgets.MinMax = class extends BaseWidget {
|
|
|
73
52
|
return this.userData.min !== undefined || this.userData.max !== undefined
|
|
74
53
|
}
|
|
75
54
|
getFormField(field) {
|
|
76
|
-
if (field.
|
|
55
|
+
if (field.TYPE === 'Number') {
|
|
77
56
|
return 'FilterByNumber'
|
|
78
57
|
}
|
|
79
|
-
if (field.
|
|
58
|
+
if (field.TYPE === 'Date') {
|
|
80
59
|
return 'FilterByDate'
|
|
81
60
|
}
|
|
82
|
-
if (field.
|
|
61
|
+
if (field.TYPE === 'Datetime') {
|
|
83
62
|
return 'FilterByDateTime'
|
|
84
63
|
}
|
|
85
64
|
return super.getFormField(field)
|
|
@@ -179,23 +158,22 @@ export class Filters {
|
|
|
179
158
|
}
|
|
180
159
|
|
|
181
160
|
isActive() {
|
|
182
|
-
return this.available.values().some((obj) => obj.isActive())
|
|
161
|
+
return Array.from(this.available.values()).some((obj) => obj.isActive())
|
|
183
162
|
}
|
|
184
163
|
|
|
185
164
|
// Loop on the data to compute the list of choices, min
|
|
186
165
|
// and max values.
|
|
187
166
|
computeInitialData() {
|
|
188
167
|
const initialData = Object.fromEntries(
|
|
189
|
-
this.available.keys().map((name) => [name, {}])
|
|
168
|
+
Array.from(this.available.keys()).map((name) => [name, {}])
|
|
190
169
|
)
|
|
191
170
|
|
|
192
171
|
for (const [name, filter] of this.available.entries()) {
|
|
193
172
|
const field = this._parent.fields.get(name)
|
|
194
173
|
if (!field) continue
|
|
195
|
-
const parser = getParser(field.type)
|
|
196
174
|
this._parent.eachFeature((feature) => {
|
|
197
175
|
let value = feature.properties[name]
|
|
198
|
-
value =
|
|
176
|
+
value = field.parse(value)
|
|
199
177
|
filter.computeInitialData(initialData[name], value)
|
|
200
178
|
})
|
|
201
179
|
}
|
|
@@ -228,14 +206,8 @@ export class Filters {
|
|
|
228
206
|
}
|
|
229
207
|
|
|
230
208
|
load() {
|
|
231
|
-
let filters = this._parent.properties.filters
|
|
232
|
-
|
|
233
|
-
// filters to be an array
|
|
234
|
-
if (typeof filters === 'object' && !Array.isArray(filters) && filters !== null) {
|
|
235
|
-
filters = Object.entries(filters).map(([fieldKey, props]) => {
|
|
236
|
-
return { fieldKey, ...props }
|
|
237
|
-
})
|
|
238
|
-
}
|
|
209
|
+
let filters = this._parent.properties.filters
|
|
210
|
+
if (!Array.isArray(filters)) filters = []
|
|
239
211
|
for (const filter of filters) {
|
|
240
212
|
this._add({ ...filter })
|
|
241
213
|
}
|
|
@@ -306,7 +278,7 @@ export class Filters {
|
|
|
306
278
|
|
|
307
279
|
_listFilters(filters, container, title) {
|
|
308
280
|
const template = `
|
|
309
|
-
<details
|
|
281
|
+
<details>
|
|
310
282
|
<summary>${title}</summary>
|
|
311
283
|
<ul data-ref=ul></ul>
|
|
312
284
|
<div>
|
|
@@ -322,9 +294,8 @@ export class Filters {
|
|
|
322
294
|
`<li>${translate('Add a field prior to create a filter.')}</li>`
|
|
323
295
|
)
|
|
324
296
|
)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
body.open = false
|
|
297
|
+
} else if (!filters._parent.fields.isDefault()) {
|
|
298
|
+
body.open = true
|
|
328
299
|
}
|
|
329
300
|
filters.available.forEach((filter, fieldKey) => {
|
|
330
301
|
const [li, { edit, remove }] = Utils.loadTemplateWithRefs(
|
|
@@ -378,9 +349,9 @@ export class Filters {
|
|
|
378
349
|
createFilterForm(fieldKey) {
|
|
379
350
|
let widget = 'Checkbox'
|
|
380
351
|
const field = this._parent.fields.get(fieldKey)
|
|
381
|
-
if (['Number', 'Date', 'Datetime'].includes(field?.
|
|
352
|
+
if (['Number', 'Date', 'Datetime'].includes(field?.TYPE)) {
|
|
382
353
|
widget = 'MinMax'
|
|
383
|
-
} else if (field?.
|
|
354
|
+
} else if (field?.TYPE === 'Boolean') {
|
|
384
355
|
widget = 'Switch'
|
|
385
356
|
}
|
|
386
357
|
const properties = {
|
|
@@ -393,7 +364,9 @@ export class Filters {
|
|
|
393
364
|
? [fieldKey]
|
|
394
365
|
: [
|
|
395
366
|
'',
|
|
396
|
-
...this._parent.
|
|
367
|
+
...Array.from(this._parent.fields.keys()).filter(
|
|
368
|
+
(fieldKey) => !this.available.has(fieldKey)
|
|
369
|
+
),
|
|
397
370
|
]
|
|
398
371
|
const metadata = [
|
|
399
372
|
[
|
|
@@ -442,8 +415,9 @@ export class Filters {
|
|
|
442
415
|
`)
|
|
443
416
|
body.appendChild(form.build())
|
|
444
417
|
editField.addEventListener('click', () => {
|
|
445
|
-
this._umap.dialog.accept()
|
|
446
|
-
|
|
418
|
+
this._umap.dialog.accept().then(() => {
|
|
419
|
+
this._parent.fields.editField(fieldKey)
|
|
420
|
+
})
|
|
447
421
|
})
|
|
448
422
|
|
|
449
423
|
return this._umap.dialog.open({ template: container }).then(() => {
|
|
@@ -474,8 +448,7 @@ export class Filters {
|
|
|
474
448
|
// This field may only exist on another layer.
|
|
475
449
|
if (!field) continue
|
|
476
450
|
let value = feature.properties[fieldKey]
|
|
477
|
-
|
|
478
|
-
value = parser(value)
|
|
451
|
+
value = field.parse(value)
|
|
479
452
|
if (obj.match(value)) return true
|
|
480
453
|
}
|
|
481
454
|
return false
|
|
@@ -746,7 +746,7 @@ Fields.PropertyInput = class extends Fields.BlurInput {
|
|
|
746
746
|
super.build()
|
|
747
747
|
const autocomplete = new AutocompleteDatalist(this.input)
|
|
748
748
|
// Will be used on Umap and DataLayer
|
|
749
|
-
const properties = this.builder.obj.
|
|
749
|
+
const properties = Array.from(this.builder.obj.fields.keys())
|
|
750
750
|
autocomplete.suggestions = properties
|
|
751
751
|
}
|
|
752
752
|
}
|
|
@@ -1217,7 +1217,7 @@ Fields.ManageOwner = class extends Fields.Base {
|
|
|
1217
1217
|
super.build()
|
|
1218
1218
|
const options = {
|
|
1219
1219
|
className: 'edit-owner',
|
|
1220
|
-
on_select:
|
|
1220
|
+
on_select: (choice) => this.onSelect(choice),
|
|
1221
1221
|
placeholder: translate("Type new owner's username"),
|
|
1222
1222
|
}
|
|
1223
1223
|
this.autocomplete = new AjaxAutocomplete(this.container, options)
|
|
@@ -1248,8 +1248,8 @@ Fields.ManageEditors = class extends Fields.Base {
|
|
|
1248
1248
|
super.build()
|
|
1249
1249
|
const options = {
|
|
1250
1250
|
className: 'edit-editors',
|
|
1251
|
-
on_select:
|
|
1252
|
-
on_unselect:
|
|
1251
|
+
on_select: (choice) => this.onSelect(choice),
|
|
1252
|
+
on_unselect: (choice) => this.onUnselect(choice),
|
|
1253
1253
|
placeholder: translate("Type editor's username"),
|
|
1254
1254
|
}
|
|
1255
1255
|
this.autocomplete = new AjaxAutocompleteMultiple(this.container, options)
|
|
@@ -71,7 +71,15 @@ export class Formatter {
|
|
|
71
71
|
} catch (e) {
|
|
72
72
|
src = this.toDom(str)
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
const data = osmtogeojson(src, { flatProperties: true })
|
|
75
|
+
// FIXME: make a PR to osmtogeojson when it's more active
|
|
76
|
+
// cf https://github.com/umap-project/umap/issues/3072
|
|
77
|
+
for (const feature of data.features || []) {
|
|
78
|
+
const [osm_type, osm_id] = feature.properties.id.split('/')
|
|
79
|
+
feature.properties.osm_id = osm_id
|
|
80
|
+
feature.properties.osm_type = osm_type
|
|
81
|
+
}
|
|
82
|
+
return data
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
fromCSV(str, callback) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
1
|
import { translate } from './i18n.js'
|
|
3
2
|
import Dialog from './ui/dialog.js'
|
|
4
3
|
import * as Utils from './utils.js'
|
|
4
|
+
import * as DOMUtils from './domutils.js'
|
|
5
5
|
|
|
6
6
|
const SHORTCUTS = {
|
|
7
7
|
DRAW_MARKER: {
|
|
@@ -118,7 +118,7 @@ const ENTRIES = {
|
|
|
118
118
|
<dt>KML</dt>
|
|
119
119
|
<dd>${translate('Properties imported:')}name, description</dd>
|
|
120
120
|
<dt>CSV</dt>
|
|
121
|
-
<dd>${translate('Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the
|
|
121
|
+
<dd>${translate('Comma, tab or semi-colon separated values. SRS WGS84 is implied. Only Point geometries are imported. The import will look at the column headers for any mention of «lat» and «lon» at the beginning of the header, case insensitive. All other column are imported as properties.')}</dd>
|
|
122
122
|
<dt>uMap</dt>
|
|
123
123
|
<dd>${translate('Imports all umap data, including layers and settings.')}</dd>
|
|
124
124
|
</div>
|
|
@@ -209,15 +209,15 @@ export default class Help {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
show(entries) {
|
|
212
|
-
const container =
|
|
213
|
-
|
|
212
|
+
const container = DOMUtils.loadTemplate(`
|
|
213
|
+
<div>
|
|
214
|
+
<h3><i class="icon icon-16 icon-help"></i> ${translate('Help')}</h3>
|
|
215
|
+
</div>
|
|
216
|
+
`)
|
|
214
217
|
for (const name of entries) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
parent: container,
|
|
219
|
-
innerHTML: ENTRIES[name],
|
|
220
|
-
})
|
|
218
|
+
container.appendChild(
|
|
219
|
+
DOMUtils.loadTemplate(`<div class="umap-help-entry">${ENTRIES[name]}</div>`)
|
|
220
|
+
)
|
|
221
221
|
}
|
|
222
222
|
this.dialog.open({ template: container })
|
|
223
223
|
}
|
|
@@ -253,11 +253,10 @@ export default class Help {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
button(container, entries) {
|
|
256
|
-
const button =
|
|
257
|
-
|
|
258
|
-
container,
|
|
259
|
-
translate('Help')
|
|
256
|
+
const button = DOMUtils.loadTemplate(
|
|
257
|
+
`<button class="umap-help-button" type="button">${translate('Help')}</button>`
|
|
260
258
|
)
|
|
259
|
+
container.appendChild(button)
|
|
261
260
|
button.addEventListener('click', () => this.show(entries))
|
|
262
261
|
return button
|
|
263
262
|
}
|