umap-project 3.3.5__py3-none-any.whl → 3.4.0b0__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/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +43 -33
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +35 -29
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +35 -29
- umap/locale/en/LC_MESSAGES/django.po +34 -28
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +43 -33
- umap/locale/et/LC_MESSAGES/django.mo +0 -0
- umap/locale/et/LC_MESSAGES/django.po +58 -54
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +43 -33
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +36 -30
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +43 -33
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +35 -29
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +43 -33
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +43 -33
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +35 -29
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +43 -33
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +43 -33
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
- umap/management/commands/switch_user.py +2 -2
- umap/static/umap/base.css +89 -32
- umap/static/umap/content.css +129 -33
- umap/static/umap/css/bar.css +82 -20
- umap/static/umap/css/browser.css +163 -0
- umap/static/umap/css/contextmenu.css +15 -0
- umap/static/umap/css/dialog.css +36 -16
- umap/static/umap/css/form.css +122 -32
- umap/static/umap/css/icon.css +46 -3
- umap/static/umap/css/panel.css +7 -3
- umap/static/umap/css/popup.css +34 -8
- umap/static/umap/css/tooltip.css +8 -4
- umap/static/umap/img/16-white.svg +26 -8
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +36 -18
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/js/components/alerts/alert.css +69 -31
- umap/static/umap/js/components/alerts/alert.js +20 -2
- umap/static/umap/js/modules/browser.js +64 -56
- umap/static/umap/js/modules/caption.js +10 -7
- umap/static/umap/js/modules/data/features.js +82 -59
- umap/static/umap/js/modules/data/layer.js +75 -166
- umap/static/umap/js/modules/domutils.js +109 -0
- umap/static/umap/js/modules/filters.js +807 -0
- umap/static/umap/js/modules/form/builder.js +8 -5
- umap/static/umap/js/modules/form/fields.js +110 -220
- umap/static/umap/js/modules/formatter.js +24 -1
- umap/static/umap/js/modules/help.js +3 -2
- umap/static/umap/js/modules/importers/opendata.js +5 -0
- umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
- umap/static/umap/js/modules/managers.js +265 -0
- umap/static/umap/js/modules/permissions.js +35 -31
- umap/static/umap/js/modules/rendering/controls.js +7 -7
- umap/static/umap/js/modules/rendering/icon.js +3 -8
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +17 -10
- umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
- umap/static/umap/js/modules/rendering/template.js +44 -8
- umap/static/umap/js/modules/rendering/ui.js +29 -23
- umap/static/umap/js/modules/rules.js +4 -3
- umap/static/umap/js/modules/schema.js +3 -6
- umap/static/umap/js/modules/share.js +4 -3
- umap/static/umap/js/modules/tableeditor.js +50 -38
- umap/static/umap/js/modules/templates.js +2 -3
- umap/static/umap/js/modules/ui/bar.js +42 -18
- umap/static/umap/js/modules/ui/dialog.js +33 -31
- umap/static/umap/js/modules/ui/panel.js +21 -7
- umap/static/umap/js/modules/ui/tooltip.js +6 -5
- umap/static/umap/js/modules/umap.js +155 -46
- umap/static/umap/js/modules/utils.js +23 -1
- umap/static/umap/js/umap.core.js +1 -110
- umap/static/umap/locale/am_ET.js +40 -14
- umap/static/umap/locale/am_ET.json +40 -14
- umap/static/umap/locale/ar.js +40 -14
- umap/static/umap/locale/ar.json +40 -14
- umap/static/umap/locale/ast.js +40 -14
- umap/static/umap/locale/ast.json +40 -14
- umap/static/umap/locale/bg.js +40 -14
- umap/static/umap/locale/bg.json +40 -14
- umap/static/umap/locale/br.js +47 -21
- umap/static/umap/locale/br.json +47 -21
- umap/static/umap/locale/ca.js +40 -14
- umap/static/umap/locale/ca.json +40 -14
- umap/static/umap/locale/cs_CZ.js +40 -14
- umap/static/umap/locale/cs_CZ.json +40 -14
- umap/static/umap/locale/da.js +40 -14
- umap/static/umap/locale/da.json +40 -14
- umap/static/umap/locale/de.js +39 -13
- umap/static/umap/locale/de.json +39 -13
- umap/static/umap/locale/el.js +40 -14
- umap/static/umap/locale/el.json +40 -14
- umap/static/umap/locale/en.js +39 -13
- umap/static/umap/locale/en.json +39 -13
- umap/static/umap/locale/en_US.json +40 -14
- umap/static/umap/locale/es.js +40 -14
- umap/static/umap/locale/es.json +40 -14
- umap/static/umap/locale/et.js +79 -53
- umap/static/umap/locale/et.json +79 -53
- umap/static/umap/locale/eu.js +72 -46
- umap/static/umap/locale/eu.json +72 -46
- umap/static/umap/locale/fa_IR.js +40 -14
- umap/static/umap/locale/fa_IR.json +40 -14
- umap/static/umap/locale/fi.js +40 -14
- umap/static/umap/locale/fi.json +40 -14
- umap/static/umap/locale/fr.js +39 -13
- umap/static/umap/locale/fr.json +39 -13
- umap/static/umap/locale/gl.js +40 -14
- umap/static/umap/locale/gl.json +40 -14
- umap/static/umap/locale/he.js +40 -14
- umap/static/umap/locale/he.json +40 -14
- umap/static/umap/locale/hr.js +40 -14
- umap/static/umap/locale/hr.json +40 -14
- umap/static/umap/locale/hu.js +40 -14
- umap/static/umap/locale/hu.json +40 -14
- umap/static/umap/locale/id.js +40 -14
- umap/static/umap/locale/id.json +40 -14
- umap/static/umap/locale/is.js +40 -14
- umap/static/umap/locale/is.json +40 -14
- umap/static/umap/locale/it.js +40 -14
- umap/static/umap/locale/it.json +40 -14
- umap/static/umap/locale/ja.js +40 -14
- umap/static/umap/locale/ja.json +40 -14
- umap/static/umap/locale/ko.js +40 -14
- umap/static/umap/locale/ko.json +40 -14
- umap/static/umap/locale/lt.js +40 -14
- umap/static/umap/locale/lt.json +40 -14
- umap/static/umap/locale/ms.js +40 -14
- umap/static/umap/locale/ms.json +40 -14
- umap/static/umap/locale/nl.js +40 -14
- umap/static/umap/locale/nl.json +40 -14
- umap/static/umap/locale/no.js +40 -14
- umap/static/umap/locale/no.json +40 -14
- umap/static/umap/locale/pl.js +40 -14
- umap/static/umap/locale/pl.json +40 -14
- umap/static/umap/locale/pl_PL.json +40 -14
- umap/static/umap/locale/pt.js +40 -14
- umap/static/umap/locale/pt.json +40 -14
- umap/static/umap/locale/pt_BR.js +40 -14
- umap/static/umap/locale/pt_BR.json +40 -14
- umap/static/umap/locale/pt_PT.js +40 -14
- umap/static/umap/locale/pt_PT.json +40 -14
- umap/static/umap/locale/ro.js +40 -14
- umap/static/umap/locale/ro.json +40 -14
- umap/static/umap/locale/ru.js +40 -14
- umap/static/umap/locale/ru.json +40 -14
- umap/static/umap/locale/sk_SK.js +40 -14
- umap/static/umap/locale/sk_SK.json +40 -14
- umap/static/umap/locale/sl.js +40 -14
- umap/static/umap/locale/sl.json +40 -14
- umap/static/umap/locale/sr.js +40 -14
- umap/static/umap/locale/sr.json +40 -14
- umap/static/umap/locale/sv.js +40 -14
- umap/static/umap/locale/sv.json +40 -14
- umap/static/umap/locale/th_TH.js +40 -14
- umap/static/umap/locale/th_TH.json +40 -14
- umap/static/umap/locale/tr.js +40 -14
- umap/static/umap/locale/tr.json +40 -14
- umap/static/umap/locale/uk_UA.js +40 -14
- umap/static/umap/locale/uk_UA.json +40 -14
- umap/static/umap/locale/vi.js +40 -14
- umap/static/umap/locale/vi.json +40 -14
- umap/static/umap/locale/vi_VN.json +40 -14
- umap/static/umap/locale/zh.js +40 -14
- umap/static/umap/locale/zh.json +40 -14
- umap/static/umap/locale/zh_CN.json +40 -14
- umap/static/umap/locale/zh_TW.Big5.json +40 -14
- umap/static/umap/locale/zh_TW.js +40 -14
- umap/static/umap/locale/zh_TW.json +40 -14
- umap/static/umap/map.css +60 -223
- umap/static/umap/unittests/utils.js +18 -0
- umap/static/umap/vars.css +23 -5
- umap/templates/umap/components/alerts/alert.html +32 -29
- umap/templates/umap/css.html +2 -1
- umap/templates/umap/login_popup_end.html +18 -9
- umap/templates/umap/user_map_table.html +7 -2
- umap/tests/integration/conftest.py +2 -6
- umap/tests/integration/test_anonymous_owned_map.py +89 -36
- umap/tests/integration/test_basics.py +25 -1
- umap/tests/integration/test_browser.py +37 -0
- umap/tests/integration/test_datalayer.py +9 -16
- umap/tests/integration/test_draw_polygon.py +2 -0
- umap/tests/integration/test_edit_marker.py +1 -1
- umap/tests/integration/test_export_map.py +19 -0
- umap/tests/integration/test_fields.py +522 -0
- umap/tests/integration/test_filters.py +617 -0
- umap/tests/integration/test_import.py +15 -42
- umap/tests/integration/test_remote_data.py +60 -4
- umap/tests/integration/test_share.py +4 -4
- umap/tests/integration/test_tableeditor.py +31 -7
- umap/tests/integration/test_websocket_sync.py +3 -1
- umap/tests/test_dashboard.py +10 -0
- umap/urls.py +1 -0
- umap/views.py +5 -0
- {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
- {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +216 -213
- umap/static/umap/js/modules/facets.js +0 -164
- umap/tests/integration/test_facets_browser.py +0 -279
- {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/WHEEL +0 -0
- {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
uMapAlert as Alert,
|
|
10
10
|
uMapAlertConflict as AlertConflict,
|
|
11
11
|
} from '../../components/alerts/alert.js'
|
|
12
|
-
import { MutatingForm } from '../form/builder.js'
|
|
12
|
+
import { MutatingForm, Form } from '../form/builder.js'
|
|
13
13
|
import { translate } from '../i18n.js'
|
|
14
14
|
import { DataLayerPermissions } from '../permissions.js'
|
|
15
15
|
import { Default as DefaultLayer } from '../rendering/layers/base.js'
|
|
@@ -21,8 +21,8 @@ import TableEditor from '../tableeditor.js'
|
|
|
21
21
|
import * as Utils from '../utils.js'
|
|
22
22
|
import { LineString, Point, Polygon } from './features.js'
|
|
23
23
|
import Rules from '../rules.js'
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
24
|
+
import { FeatureManager, FieldManager } from '../managers.js'
|
|
25
|
+
import { Filters } from '../filters.js'
|
|
26
26
|
|
|
27
27
|
export const LAYER_TYPES = [
|
|
28
28
|
DefaultLayer,
|
|
@@ -89,12 +89,14 @@ export class DataLayer {
|
|
|
89
89
|
if (!this.createdOnServer) {
|
|
90
90
|
if (this.showAtLoad()) this.show()
|
|
91
91
|
}
|
|
92
|
-
if (!this._needsFetch && !this._umap.fields.
|
|
92
|
+
if (!this._needsFetch && !this._umap.fields.size) {
|
|
93
93
|
this.properties.fields = [
|
|
94
94
|
{ key: U.DEFAULT_LABEL_KEY, type: 'String' },
|
|
95
95
|
{ key: 'description', type: 'Text' },
|
|
96
96
|
]
|
|
97
97
|
}
|
|
98
|
+
this.fields = new FieldManager(this, this._umap.dialog)
|
|
99
|
+
this.filters = new Filters(this, this._umap)
|
|
98
100
|
|
|
99
101
|
// Only layers that are displayed on load must be hidden/shown
|
|
100
102
|
// Automatically, others will be shown manually, and thus will
|
|
@@ -129,7 +131,7 @@ export class DataLayer {
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
get cssId() {
|
|
132
|
-
return `datalayer-${
|
|
134
|
+
return `datalayer-${this.id}`
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
get rank() {
|
|
@@ -146,17 +148,10 @@ export class DataLayer {
|
|
|
146
148
|
this.properties.rank = value
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
get fields() {
|
|
150
|
-
if (!this.properties.fields) this.properties.fields = []
|
|
151
|
-
return this.properties.fields
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
set fields(fields) {
|
|
155
|
-
this.properties.fields = fields
|
|
156
|
-
}
|
|
157
|
-
|
|
158
151
|
get fieldKeys() {
|
|
159
|
-
|
|
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())
|
|
160
155
|
}
|
|
161
156
|
|
|
162
157
|
get sortKey() {
|
|
@@ -174,6 +169,18 @@ export class DataLayer {
|
|
|
174
169
|
// Propagate will remove the fields it has already
|
|
175
170
|
// processed
|
|
176
171
|
fields = this.propagate(fields)
|
|
172
|
+
if (fields.includes('properties.fields')) {
|
|
173
|
+
this.fields?.pull()
|
|
174
|
+
if (this._umap.browser.isOpen()) {
|
|
175
|
+
this._umap.browser.buildFilters()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (fields.includes('properties.filters')) {
|
|
179
|
+
this.filters.load()
|
|
180
|
+
if (this._umap.browser.isOpen()) {
|
|
181
|
+
this._umap.browser.buildFilters()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
177
184
|
|
|
178
185
|
const impacts = Utils.getImpactsFromSchema(fields)
|
|
179
186
|
|
|
@@ -225,17 +232,27 @@ export class DataLayer {
|
|
|
225
232
|
}
|
|
226
233
|
|
|
227
234
|
showAtLoad() {
|
|
228
|
-
return this.autoLoaded
|
|
235
|
+
return this.autoLoaded && this.showAtZoom()
|
|
229
236
|
}
|
|
230
237
|
|
|
231
|
-
autoLoaded() {
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
get autoLoaded() {
|
|
239
|
+
if (this._autoLoaded === undefined) {
|
|
240
|
+
if (this._umap.datalayersFromQueryString) {
|
|
241
|
+
const datalayerIds = this._umap.datalayersFromQueryString
|
|
242
|
+
this._autoLoaded = datalayerIds.includes(this.id.toString())
|
|
243
|
+
if (this.properties.old_id) {
|
|
244
|
+
this._autoLoaded =
|
|
245
|
+
this._autoLoaded || datalayerIds.includes(this.properties.old_id.toString())
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
this._autoLoaded = this.properties.displayOnLoad
|
|
249
|
+
}
|
|
237
250
|
}
|
|
238
|
-
return
|
|
251
|
+
return this._autoLoaded
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
set autoLoaded(value) {
|
|
255
|
+
this._autoLoaded = value
|
|
239
256
|
}
|
|
240
257
|
|
|
241
258
|
insertBefore(other) {
|
|
@@ -438,7 +455,8 @@ export class DataLayer {
|
|
|
438
455
|
this.inferFields(feature)
|
|
439
456
|
try {
|
|
440
457
|
this.showFeature(feature)
|
|
441
|
-
} catch {
|
|
458
|
+
} catch (error) {
|
|
459
|
+
console.error(error)
|
|
442
460
|
if (this._umap.editEnabled) {
|
|
443
461
|
Alert.error(translate('Skipping invalid geometry'))
|
|
444
462
|
}
|
|
@@ -472,88 +490,43 @@ export class DataLayer {
|
|
|
472
490
|
}
|
|
473
491
|
|
|
474
492
|
inferFields(feature) {
|
|
475
|
-
if (!this.properties.fields) this.properties.fields = []
|
|
476
|
-
const keys = this.fieldKeys
|
|
477
493
|
for (const key in feature.properties) {
|
|
478
494
|
if (typeof feature.properties[key] !== 'object') {
|
|
479
495
|
if (key.indexOf('_') === 0) continue
|
|
480
|
-
if (
|
|
481
|
-
this.
|
|
496
|
+
if (this.fields.has(key)) continue
|
|
497
|
+
if (this._umap.fields.has(key)) continue
|
|
498
|
+
let type = 'String'
|
|
499
|
+
if (key === 'description') type = 'Text'
|
|
500
|
+
this.fields.add({ key, type })
|
|
482
501
|
}
|
|
483
502
|
}
|
|
503
|
+
this.fields.push()
|
|
484
504
|
}
|
|
485
505
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
.confirm(
|
|
489
|
-
translate('Are you sure you want to delete this field on all the features?')
|
|
490
|
-
)
|
|
491
|
-
.then(() => {
|
|
492
|
-
this.deleteProperty(property)
|
|
493
|
-
})
|
|
506
|
+
renameField(oldName, newName) {
|
|
507
|
+
this.renameFeaturesField(oldName, newName)
|
|
494
508
|
}
|
|
495
509
|
|
|
496
|
-
|
|
497
|
-
return this._umap.dialog
|
|
498
|
-
.prompt(translate('Please enter the new name of this field'))
|
|
499
|
-
.then(({ prompt }) => {
|
|
500
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
501
|
-
this.renameProperty(property, prompt)
|
|
502
|
-
})
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
renameProperty(oldName, newName) {
|
|
506
|
-
this.sync.startBatch()
|
|
507
|
-
const oldFields = Utils.CopyJSON(this.fields)
|
|
508
|
-
for (const field of this.fields) {
|
|
509
|
-
if (field.key === oldName) {
|
|
510
|
-
field.key = newName
|
|
511
|
-
break
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
this.sync.update('properties.fields', this.fields, oldFields)
|
|
510
|
+
renameFeaturesField(oldName, newName) {
|
|
515
511
|
this.features.forEach((feature) => {
|
|
516
|
-
feature.
|
|
512
|
+
feature.renameField(oldName, newName)
|
|
517
513
|
})
|
|
518
|
-
this.sync.commitBatch()
|
|
519
514
|
}
|
|
520
515
|
|
|
521
|
-
|
|
522
|
-
this.
|
|
523
|
-
const oldFields = Utils.CopyJSON(this.fields)
|
|
524
|
-
this.fields = this.fields.filter((field) => field.key !== property)
|
|
525
|
-
this.sync.update('properties.fields', this.fields, oldFields)
|
|
526
|
-
this.features.forEach((feature) => {
|
|
527
|
-
feature.deleteProperty(property)
|
|
528
|
-
})
|
|
529
|
-
this.sync.commitBatch()
|
|
516
|
+
deleteField(name) {
|
|
517
|
+
this.deleteFeaturesField(name)
|
|
530
518
|
}
|
|
531
519
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
})
|
|
537
|
-
this._umap.dialog
|
|
538
|
-
.prompt(translate('Please enter the name of the property'))
|
|
539
|
-
.then(({ prompt }) => {
|
|
540
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
541
|
-
this.properties.fields.push({ key: prompt, type: 'String' })
|
|
542
|
-
resolve()
|
|
520
|
+
deleteFeaturesField(name) {
|
|
521
|
+
if (!this._umap.fields.has(name) && !this.fields.has(name)) {
|
|
522
|
+
this.features.forEach((feature) => {
|
|
523
|
+
feature.deleteField(name)
|
|
543
524
|
})
|
|
544
|
-
|
|
525
|
+
}
|
|
545
526
|
}
|
|
546
527
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
Alert.error(translate('Name “{name}” should not contain a dot.', { name }))
|
|
550
|
-
return false
|
|
551
|
-
}
|
|
552
|
-
if (this.fieldKeys.includes(name)) {
|
|
553
|
-
Alert.error(translate('This name already exists: “{name}”', { name }))
|
|
554
|
-
return false
|
|
555
|
-
}
|
|
556
|
-
return true
|
|
528
|
+
eachFeature(callback) {
|
|
529
|
+
this.features.forEach((feature) => callback(feature))
|
|
557
530
|
}
|
|
558
531
|
|
|
559
532
|
sortedValues(property) {
|
|
@@ -824,7 +797,7 @@ export class DataLayer {
|
|
|
824
797
|
const builder = new MutatingForm(this, layerFields)
|
|
825
798
|
const template = `
|
|
826
799
|
<details id="layer-properties">
|
|
827
|
-
<summary>${this.layer.getName()}: ${translate('settings')}</summary>
|
|
800
|
+
<summary><h4>${this.layer.getName()}: ${translate('settings')}</h4></summary>
|
|
828
801
|
<fieldset data-ref=fieldset></fieldset>
|
|
829
802
|
</details>
|
|
830
803
|
`
|
|
@@ -918,64 +891,6 @@ export class DataLayer {
|
|
|
918
891
|
fieldset.appendChild(builder.build())
|
|
919
892
|
}
|
|
920
893
|
|
|
921
|
-
_editFields(container) {
|
|
922
|
-
const template = `
|
|
923
|
-
<details id="fields">
|
|
924
|
-
<summary>${translate('Manage Fields')}</summary>
|
|
925
|
-
<fieldset>
|
|
926
|
-
<ul data-ref=ul></ul>
|
|
927
|
-
<button type="button" data-ref=add><i class="icon icon-16 icon-add"></i>${translate('Add a new field')}</button>
|
|
928
|
-
</fieldset>
|
|
929
|
-
</details>
|
|
930
|
-
`
|
|
931
|
-
const [fieldset, { ul, add }] = Utils.loadTemplateWithRefs(template)
|
|
932
|
-
add.addEventListener('click', () => {
|
|
933
|
-
this.addProperty().then(() => {
|
|
934
|
-
this.edit().then((panel) => {
|
|
935
|
-
panel.scrollTo('details#fields')
|
|
936
|
-
})
|
|
937
|
-
})
|
|
938
|
-
})
|
|
939
|
-
container.appendChild(fieldset)
|
|
940
|
-
for (const field of this.fields) {
|
|
941
|
-
const [row, { rename, del }] = Utils.loadTemplateWithRefs(
|
|
942
|
-
`<li class="orderable" data-key="${field.key}">
|
|
943
|
-
<button class="icon icon-16 icon-edit" title="${translate('Rename this field')}" data-ref=rename></button>
|
|
944
|
-
<button class="icon icon-16 icon-delete" title="${translate('Delete this field')}" data-ref=del></button>
|
|
945
|
-
<i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
|
|
946
|
-
${field.key}
|
|
947
|
-
</li>`
|
|
948
|
-
)
|
|
949
|
-
ul.appendChild(row)
|
|
950
|
-
rename.addEventListener('click', () => {
|
|
951
|
-
this.askForRenameProperty(field.key).then(() => {
|
|
952
|
-
this.edit().then((panel) => {
|
|
953
|
-
panel.scrollTo('details#fields')
|
|
954
|
-
})
|
|
955
|
-
})
|
|
956
|
-
})
|
|
957
|
-
del.addEventListener('click', () => {
|
|
958
|
-
this.confirmDeleteProperty(field.key).then(() => {
|
|
959
|
-
this.edit().then((panel) => {
|
|
960
|
-
panel.scrollTo('details#fields')
|
|
961
|
-
})
|
|
962
|
-
})
|
|
963
|
-
})
|
|
964
|
-
}
|
|
965
|
-
const onReorder = (src, dst, initialIndex, finalIndex) => {
|
|
966
|
-
const orderedKeys = Array.from(ul.querySelectorAll('li')).map(
|
|
967
|
-
(el) => el.dataset.key
|
|
968
|
-
)
|
|
969
|
-
const oldFields = Utils.CopyJSON(this.properties.fields)
|
|
970
|
-
this.properties.fields.sort(
|
|
971
|
-
(fieldA, fieldB) =>
|
|
972
|
-
orderedKeys.indexOf(fieldA.key) > orderedKeys.indexOf(fieldB.key)
|
|
973
|
-
)
|
|
974
|
-
this.sync.update('properties.fields', this.properties.fields, oldFields)
|
|
975
|
-
}
|
|
976
|
-
const orderable = new Orderable(ul, onReorder)
|
|
977
|
-
}
|
|
978
|
-
|
|
979
894
|
_editRemoteDataProperties(container) {
|
|
980
895
|
// XXX I'm not sure **why** this is needed (as it's set during `this.initialize`)
|
|
981
896
|
// but apparently it's needed.
|
|
@@ -1086,9 +1001,7 @@ export class DataLayer {
|
|
|
1086
1001
|
this._editInteractionProperties(container)
|
|
1087
1002
|
this._editTextPathProperties(container)
|
|
1088
1003
|
this._editRemoteDataProperties(container)
|
|
1089
|
-
|
|
1090
|
-
this._editFields(container)
|
|
1091
|
-
}
|
|
1004
|
+
this.fields.edit(container)
|
|
1092
1005
|
this.rules.edit(container)
|
|
1093
1006
|
|
|
1094
1007
|
if (this._umap.properties.urls.datalayer_versions) {
|
|
@@ -1213,7 +1126,7 @@ export class DataLayer {
|
|
|
1213
1126
|
// From now on, do not try to how/hide
|
|
1214
1127
|
// automatically this layer, as user
|
|
1215
1128
|
// has taken control on this.
|
|
1216
|
-
this.
|
|
1129
|
+
this.autoLoaded = false
|
|
1217
1130
|
let display = force
|
|
1218
1131
|
if (force === undefined) {
|
|
1219
1132
|
if (!this.isVisible()) display = true
|
|
@@ -1284,7 +1197,8 @@ export class DataLayer {
|
|
|
1284
1197
|
}
|
|
1285
1198
|
|
|
1286
1199
|
umapGeoJSON() {
|
|
1287
|
-
const
|
|
1200
|
+
const features = this.isRemoteLayer() ? [] : this.features.all()
|
|
1201
|
+
const geojson = this._umap.formatter.toFeatureCollection(features)
|
|
1288
1202
|
geojson._umap_options = this.properties
|
|
1289
1203
|
return geojson
|
|
1290
1204
|
}
|
|
@@ -1321,12 +1235,12 @@ export class DataLayer {
|
|
|
1321
1235
|
async save() {
|
|
1322
1236
|
if (this.isDeleted) return await this.saveDelete()
|
|
1323
1237
|
if (!this.isRemoteLayer() && !this.isLoaded()) return
|
|
1324
|
-
const geojson = this.umapGeoJSON()
|
|
1325
1238
|
const formData = new FormData()
|
|
1326
1239
|
formData.append('name', this.properties.name)
|
|
1327
1240
|
formData.append('display_on_load', !!this.properties.displayOnLoad)
|
|
1328
1241
|
formData.append('rank', this.rank)
|
|
1329
1242
|
formData.append('settings', this.prepareProperties())
|
|
1243
|
+
const geojson = this.umapGeoJSON()
|
|
1330
1244
|
// Filename support is shaky, don't do it for now.
|
|
1331
1245
|
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
|
1332
1246
|
formData.append('geojson', blob)
|
|
@@ -1428,13 +1342,12 @@ export class DataLayer {
|
|
|
1428
1342
|
)) {
|
|
1429
1343
|
container.innerHTML = ''
|
|
1430
1344
|
if (this.layer.renderLegend) return this.layer.renderLegend(container)
|
|
1431
|
-
const keys = new Set(this.fieldKeys)
|
|
1432
1345
|
const rules = new Map()
|
|
1433
1346
|
for (const rule of this.rules) {
|
|
1434
1347
|
rules.set(rule.condition, rule)
|
|
1435
1348
|
}
|
|
1436
1349
|
for (const rule of this._umap.rules) {
|
|
1437
|
-
if (!rules.has(rule.condition) &&
|
|
1350
|
+
if (!rules.has(rule.condition) && this.fields.has(rule.key)) {
|
|
1438
1351
|
rules.set(rule.condition, rule)
|
|
1439
1352
|
}
|
|
1440
1353
|
}
|
|
@@ -1458,6 +1371,11 @@ export class DataLayer {
|
|
|
1458
1371
|
'icon-eye',
|
|
1459
1372
|
translate('Show/hide layer')
|
|
1460
1373
|
)
|
|
1374
|
+
const table = DomUtil.createButtonIcon(
|
|
1375
|
+
container,
|
|
1376
|
+
'icon-table show-on-edit',
|
|
1377
|
+
translate('Edit properties in a table')
|
|
1378
|
+
)
|
|
1461
1379
|
const zoomTo = DomUtil.createButtonIcon(
|
|
1462
1380
|
container,
|
|
1463
1381
|
'icon-zoom',
|
|
@@ -1468,11 +1386,6 @@ export class DataLayer {
|
|
|
1468
1386
|
'icon-edit show-on-edit',
|
|
1469
1387
|
translate('Edit')
|
|
1470
1388
|
)
|
|
1471
|
-
const table = DomUtil.createButtonIcon(
|
|
1472
|
-
container,
|
|
1473
|
-
'icon-table show-on-edit',
|
|
1474
|
-
translate('Edit properties in a table')
|
|
1475
|
-
)
|
|
1476
1389
|
const remove = DomUtil.createButtonIcon(
|
|
1477
1390
|
container,
|
|
1478
1391
|
'icon-delete show-on-edit',
|
|
@@ -1490,16 +1403,12 @@ export class DataLayer {
|
|
|
1490
1403
|
}
|
|
1491
1404
|
DomEvent.on(toggle, 'click', () => this.toggle())
|
|
1492
1405
|
DomEvent.on(zoomTo, 'click', this.zoomTo, this)
|
|
1493
|
-
container.classList.add(this.
|
|
1406
|
+
container.classList.add(this.cssId)
|
|
1494
1407
|
container.classList.toggle('off', !this.isVisible())
|
|
1495
1408
|
}
|
|
1496
1409
|
|
|
1497
1410
|
getHidableElements() {
|
|
1498
|
-
return document.querySelectorAll(`.${this.
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
getHidableClass() {
|
|
1502
|
-
return `show_with_datalayer_${stamp(this)}`
|
|
1411
|
+
return document.querySelectorAll(`.${this.cssId}`)
|
|
1503
1412
|
}
|
|
1504
1413
|
|
|
1505
1414
|
propagateDelete() {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Utils that needs the DOM
|
|
2
|
+
import * as Utils from './utils.js'
|
|
3
|
+
import { translate } from './i18n.js'
|
|
4
|
+
import Tooltip from './ui/tooltip.js'
|
|
5
|
+
|
|
6
|
+
export const copyToClipboard = (textToCopy) => {
|
|
7
|
+
const tooltip = new Tooltip()
|
|
8
|
+
// https://stackoverflow.com/a/65996386
|
|
9
|
+
// Navigator clipboard api needs a secure context (https)
|
|
10
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
11
|
+
navigator.clipboard.writeText(textToCopy)
|
|
12
|
+
} else {
|
|
13
|
+
// Use the 'out of viewport hidden text area' trick
|
|
14
|
+
const textArea = document.createElement('textarea')
|
|
15
|
+
textArea.value = textToCopy
|
|
16
|
+
|
|
17
|
+
// Move textarea out of the viewport so it's not visible
|
|
18
|
+
textArea.style.position = 'absolute'
|
|
19
|
+
textArea.style.left = '-999999px'
|
|
20
|
+
|
|
21
|
+
document.body.prepend(textArea)
|
|
22
|
+
textArea.select()
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
document.execCommand('copy')
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(error)
|
|
28
|
+
} finally {
|
|
29
|
+
textArea.remove()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
tooltip.open({ content: translate('✅ Copied!'), duration: 5000 })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const copiableInput = (parent, label, value) => {
|
|
36
|
+
const [container, { input, button }] = Utils.loadTemplateWithRefs(`
|
|
37
|
+
<div class="copiable-input">
|
|
38
|
+
<label>${label}<input type="text" readOnly value="${value}" data-ref=input /></label>
|
|
39
|
+
<button type="button" class="icon icon-24 icon-copy" title="${translate('copy')}" data-ref=button></button>
|
|
40
|
+
</div>
|
|
41
|
+
`)
|
|
42
|
+
button.addEventListener('click', () => copyToClipboard(input.value))
|
|
43
|
+
parent.appendChild(container)
|
|
44
|
+
return input
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// From https://gist.github.com/Accudio/b9cb16e0e3df858cef0d31e38f1fe46f
|
|
48
|
+
// convert colour in range 0-255 to the modifier used within luminance calculation
|
|
49
|
+
const colourMod = (colour) => {
|
|
50
|
+
const sRGB = colour / 255
|
|
51
|
+
let mod = ((sRGB + 0.055) / 1.055) ** 2.4
|
|
52
|
+
if (sRGB < 0.03928) mod = sRGB / 12.92
|
|
53
|
+
return mod
|
|
54
|
+
}
|
|
55
|
+
const RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
|
|
56
|
+
|
|
57
|
+
export const textColorFromBackgroundColor = (el, bgcolor) => {
|
|
58
|
+
return contrastedColor(el, bgcolor) ? '#ffffff' : '#000000'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const contrastWCAG21 = (rgb) => {
|
|
62
|
+
const [r, g, b] = rgb
|
|
63
|
+
// luminance of inputted colour
|
|
64
|
+
const lum = 0.2126 * colourMod(r) + 0.7152 * colourMod(g) + 0.0722 * colourMod(b)
|
|
65
|
+
// white has a luminance of 1
|
|
66
|
+
const whiteLum = 1
|
|
67
|
+
const contrast = (whiteLum + 0.05) / (lum + 0.05)
|
|
68
|
+
return contrast > 3 ? 1 : 0
|
|
69
|
+
}
|
|
70
|
+
const colorNameToHex = (str) => {
|
|
71
|
+
const ctx = document.createElement('canvas').getContext('2d')
|
|
72
|
+
ctx.fillStyle = str
|
|
73
|
+
return ctx.fillStyle
|
|
74
|
+
}
|
|
75
|
+
export const hexToRGB = (hex) => {
|
|
76
|
+
return hex
|
|
77
|
+
.replace(
|
|
78
|
+
/^#?([a-f\d])([a-f\d])([a-f\d])$/i,
|
|
79
|
+
(m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`
|
|
80
|
+
)
|
|
81
|
+
.substring(1)
|
|
82
|
+
.match(/.{2}/g)
|
|
83
|
+
.map((x) => Number.parseInt(x, 16))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const CACHE_CONSTRAST = {}
|
|
87
|
+
export const contrastedColor = (el, bgcolor) => {
|
|
88
|
+
// Return 0 for black and 1 for white
|
|
89
|
+
// bgcolor is a human color, it can be a any keyword (purple…)
|
|
90
|
+
if (typeof CACHE_CONSTRAST[bgcolor] !== 'undefined') return CACHE_CONSTRAST[bgcolor]
|
|
91
|
+
let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
|
|
92
|
+
rgb = RGBRegex.exec(rgb)
|
|
93
|
+
if (rgb && rgb.length === 4) {
|
|
94
|
+
rgb = [
|
|
95
|
+
Number.parseInt(rgb[1], 10),
|
|
96
|
+
Number.parseInt(rgb[2], 10),
|
|
97
|
+
Number.parseInt(rgb[3], 10),
|
|
98
|
+
]
|
|
99
|
+
} else {
|
|
100
|
+
// The element may not yet be added to the DOM, so let's try
|
|
101
|
+
// another way
|
|
102
|
+
const hex = colorNameToHex(bgcolor)
|
|
103
|
+
rgb = hexToRGB(hex)
|
|
104
|
+
}
|
|
105
|
+
if (!rgb) return 1
|
|
106
|
+
const out = contrastWCAG21(rgb)
|
|
107
|
+
if (bgcolor) CACHE_CONSTRAST[bgcolor] = out
|
|
108
|
+
return out
|
|
109
|
+
}
|