umap-project 3.3.6__py3-none-any.whl → 3.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- umap/__init__.py +1 -1
- umap/context_processors.py +4 -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 +47 -41
- 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 +114 -103
- 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/migrations/0018_datalayer_uuid.py +1 -1
- umap/models.py +7 -3
- umap/settings/local.py.sample +1 -1
- 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 +123 -33
- 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/components/base.js +1 -1
- umap/static/umap/js/modules/browser.js +69 -61
- umap/static/umap/js/modules/caption.js +10 -7
- umap/static/umap/js/modules/data/features.js +85 -60
- umap/static/umap/js/modules/data/fields.js +446 -0
- umap/static/umap/js/modules/data/layer.js +78 -184
- umap/static/umap/js/modules/domutils.js +109 -0
- umap/static/umap/js/modules/filters.js +780 -0
- umap/static/umap/js/modules/form/builder.js +8 -5
- umap/static/umap/js/modules/form/fields.js +111 -221
- umap/static/umap/js/modules/formatter.js +24 -1
- umap/static/umap/js/modules/help.js +4 -3
- umap/static/umap/js/modules/i18n.js +1 -1
- umap/static/umap/js/modules/importer.js +1 -1
- umap/static/umap/js/modules/importers/opendata.js +15 -0
- umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
- umap/static/umap/js/modules/managers.js +2 -2
- umap/static/umap/js/modules/permissions.js +39 -31
- umap/static/umap/js/modules/rendering/controls.js +11 -9
- 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 +18 -11
- umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
- umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
- umap/static/umap/js/modules/rendering/template.js +50 -23
- umap/static/umap/js/modules/rendering/ui.js +29 -23
- umap/static/umap/js/modules/rules.js +38 -44
- umap/static/umap/js/modules/schema.js +3 -6
- umap/static/umap/js/modules/share.js +5 -4
- 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 +55 -23
- umap/static/umap/js/modules/ui/dialog.js +38 -27
- umap/static/umap/js/modules/ui/panel.js +23 -8
- umap/static/umap/js/modules/ui/tooltip.js +6 -5
- umap/static/umap/js/modules/umap.js +151 -56
- umap/static/umap/js/modules/utils.js +24 -2
- umap/static/umap/js/umap.core.js +1 -110
- umap/static/umap/locale/am_ET.js +52 -17
- umap/static/umap/locale/am_ET.json +52 -17
- umap/static/umap/locale/ar.js +52 -17
- umap/static/umap/locale/ar.json +52 -17
- umap/static/umap/locale/ast.js +52 -17
- umap/static/umap/locale/ast.json +52 -17
- umap/static/umap/locale/bg.js +52 -17
- umap/static/umap/locale/bg.json +52 -17
- umap/static/umap/locale/br.js +48 -22
- umap/static/umap/locale/br.json +48 -22
- umap/static/umap/locale/ca.js +52 -17
- umap/static/umap/locale/ca.json +52 -17
- umap/static/umap/locale/cs_CZ.js +52 -17
- umap/static/umap/locale/cs_CZ.json +52 -17
- umap/static/umap/locale/da.js +54 -17
- umap/static/umap/locale/da.json +54 -17
- umap/static/umap/locale/de.js +51 -16
- umap/static/umap/locale/de.json +51 -16
- umap/static/umap/locale/el.js +52 -17
- umap/static/umap/locale/el.json +52 -17
- umap/static/umap/locale/en.js +53 -16
- umap/static/umap/locale/en.json +53 -16
- umap/static/umap/locale/en_US.json +52 -17
- umap/static/umap/locale/es.js +54 -17
- umap/static/umap/locale/es.json +54 -17
- umap/static/umap/locale/et.js +91 -56
- umap/static/umap/locale/et.json +91 -56
- umap/static/umap/locale/eu.js +84 -49
- umap/static/umap/locale/eu.json +84 -49
- umap/static/umap/locale/fa_IR.js +52 -17
- umap/static/umap/locale/fa_IR.json +52 -17
- umap/static/umap/locale/fi.js +52 -17
- umap/static/umap/locale/fi.json +52 -17
- umap/static/umap/locale/fr.js +53 -16
- umap/static/umap/locale/fr.json +53 -16
- umap/static/umap/locale/gl.js +52 -17
- umap/static/umap/locale/gl.json +52 -17
- umap/static/umap/locale/he.js +52 -17
- umap/static/umap/locale/he.json +52 -17
- umap/static/umap/locale/hr.js +52 -17
- umap/static/umap/locale/hr.json +52 -17
- umap/static/umap/locale/hu.js +59 -24
- umap/static/umap/locale/hu.json +59 -24
- umap/static/umap/locale/id.js +52 -17
- umap/static/umap/locale/id.json +52 -17
- umap/static/umap/locale/is.js +52 -17
- umap/static/umap/locale/is.json +52 -17
- umap/static/umap/locale/it.js +52 -17
- umap/static/umap/locale/it.json +52 -17
- umap/static/umap/locale/ja.js +52 -17
- umap/static/umap/locale/ja.json +52 -17
- umap/static/umap/locale/ko.js +52 -17
- umap/static/umap/locale/ko.json +52 -17
- umap/static/umap/locale/lt.js +52 -17
- umap/static/umap/locale/lt.json +52 -17
- umap/static/umap/locale/ms.js +52 -17
- umap/static/umap/locale/ms.json +52 -17
- umap/static/umap/locale/nl.js +52 -17
- umap/static/umap/locale/nl.json +52 -17
- umap/static/umap/locale/no.js +52 -17
- umap/static/umap/locale/no.json +52 -17
- umap/static/umap/locale/pl.js +53 -17
- umap/static/umap/locale/pl.json +53 -17
- umap/static/umap/locale/pl_PL.json +52 -17
- umap/static/umap/locale/pt.js +52 -17
- umap/static/umap/locale/pt.json +52 -17
- umap/static/umap/locale/pt_BR.js +52 -17
- umap/static/umap/locale/pt_BR.json +52 -17
- umap/static/umap/locale/pt_PT.js +52 -17
- umap/static/umap/locale/pt_PT.json +52 -17
- umap/static/umap/locale/ro.js +52 -17
- umap/static/umap/locale/ro.json +52 -17
- umap/static/umap/locale/ru.js +52 -17
- umap/static/umap/locale/ru.json +52 -17
- umap/static/umap/locale/si.js +1 -1
- umap/static/umap/locale/si.json +1 -1
- umap/static/umap/locale/sk_SK.js +52 -17
- umap/static/umap/locale/sk_SK.json +52 -17
- umap/static/umap/locale/sl.js +52 -17
- umap/static/umap/locale/sl.json +52 -17
- umap/static/umap/locale/sr.js +52 -17
- umap/static/umap/locale/sr.json +52 -17
- umap/static/umap/locale/sv.js +52 -17
- umap/static/umap/locale/sv.json +52 -17
- umap/static/umap/locale/th_TH.js +52 -17
- umap/static/umap/locale/th_TH.json +52 -17
- umap/static/umap/locale/tr.js +52 -17
- umap/static/umap/locale/tr.json +52 -17
- umap/static/umap/locale/uk_UA.js +52 -17
- umap/static/umap/locale/uk_UA.json +52 -17
- umap/static/umap/locale/vi.js +52 -17
- umap/static/umap/locale/vi.json +52 -17
- umap/static/umap/locale/vi_VN.json +52 -17
- umap/static/umap/locale/zh.js +52 -17
- umap/static/umap/locale/zh.json +52 -17
- umap/static/umap/locale/zh_CN.json +52 -17
- umap/static/umap/locale/zh_TW.Big5.json +52 -17
- umap/static/umap/locale/zh_TW.js +52 -16
- umap/static/umap/locale/zh_TW.json +52 -16
- umap/static/umap/map.css +63 -226
- 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 +10 -6
- umap/tests/integration/test_anonymous_owned_map.py +90 -37
- umap/tests/integration/test_basics.py +25 -1
- umap/tests/integration/test_browser.py +37 -0
- umap/tests/integration/test_conditional_rules.py +107 -52
- umap/tests/integration/test_draw_polygon.py +6 -0
- umap/tests/integration/test_draw_polyline.py +11 -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 +541 -0
- umap/tests/integration/test_filters.py +616 -0
- umap/tests/integration/test_iframe.py +1 -1
- umap/tests/integration/test_import.py +38 -42
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_picto.py +1 -1
- umap/tests/integration/test_popup.py +31 -0
- umap/tests/integration/test_remote_data.py +60 -4
- umap/tests/integration/test_save.py +1 -1
- umap/tests/integration/test_share.py +4 -4
- umap/tests/integration/test_tableeditor.py +31 -7
- umap/tests/integration/test_websocket_sync.py +71 -20
- umap/tests/test_dashboard.py +11 -1
- umap/tests/test_statics.py +2 -2
- umap/tests/test_utils.py +19 -2
- umap/tests/test_views.py +1 -1
- umap/urls.py +1 -0
- umap/utils.py +8 -1
- umap/views.py +5 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
- umap/static/umap/js/modules/facets.js +0 -164
- umap/tests/integration/test_facets_browser.py +0 -279
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.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,9 @@ 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 Orderable from '../orderable.js'
|
|
25
24
|
import { FeatureManager } from '../managers.js'
|
|
25
|
+
import { Filters } from '../filters.js'
|
|
26
|
+
import { Fields, getDefaultFields } from './fields.js'
|
|
26
27
|
|
|
27
28
|
export const LAYER_TYPES = [
|
|
28
29
|
DefaultLayer,
|
|
@@ -83,18 +84,18 @@ export class DataLayer {
|
|
|
83
84
|
}
|
|
84
85
|
this.connectToMap()
|
|
85
86
|
this.permissions = new DataLayerPermissions(this._umap, this)
|
|
86
|
-
this.rules = new Rules(umap, this)
|
|
87
87
|
|
|
88
88
|
this._needsFetch = this.createdOnServer || this.isRemoteLayer()
|
|
89
|
+
if (!this._needsFetch && !this._umap.fields.size) {
|
|
90
|
+
this.properties.fields = getDefaultFields()
|
|
91
|
+
}
|
|
92
|
+
this.fields = new Fields(this, this._umap.dialog)
|
|
93
|
+
this.filters = new Filters(this, this._umap)
|
|
94
|
+
this.rules = new Rules(umap, this)
|
|
95
|
+
|
|
89
96
|
if (!this.createdOnServer) {
|
|
90
97
|
if (this.showAtLoad()) this.show()
|
|
91
98
|
}
|
|
92
|
-
if (!this._needsFetch && !this._umap.fields.length) {
|
|
93
|
-
this.properties.fields = [
|
|
94
|
-
{ key: U.DEFAULT_LABEL_KEY, type: 'String' },
|
|
95
|
-
{ key: 'description', type: 'Text' },
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
99
|
|
|
99
100
|
// Only layers that are displayed on load must be hidden/shown
|
|
100
101
|
// Automatically, others will be shown manually, and thus will
|
|
@@ -129,7 +130,7 @@ export class DataLayer {
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
get cssId() {
|
|
132
|
-
return `datalayer-${
|
|
133
|
+
return `datalayer-${this.id}`
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
get rank() {
|
|
@@ -146,19 +147,6 @@ export class DataLayer {
|
|
|
146
147
|
this.properties.rank = value
|
|
147
148
|
}
|
|
148
149
|
|
|
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
|
-
get fieldKeys() {
|
|
159
|
-
return this.fields.map((field) => field.key)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
150
|
get sortKey() {
|
|
163
151
|
return this.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
|
|
164
152
|
}
|
|
@@ -174,6 +162,18 @@ export class DataLayer {
|
|
|
174
162
|
// Propagate will remove the fields it has already
|
|
175
163
|
// processed
|
|
176
164
|
fields = this.propagate(fields)
|
|
165
|
+
if (fields.includes('properties.fields')) {
|
|
166
|
+
this.fields?.pull()
|
|
167
|
+
if (this._umap.browser.isOpen()) {
|
|
168
|
+
this._umap.browser.buildFilters()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (fields.includes('properties.filters')) {
|
|
172
|
+
this.filters.load()
|
|
173
|
+
if (this._umap.browser.isOpen()) {
|
|
174
|
+
this._umap.browser.buildFilters()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
177
|
|
|
178
178
|
const impacts = Utils.getImpactsFromSchema(fields)
|
|
179
179
|
|
|
@@ -202,7 +202,7 @@ export class DataLayer {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// This method does a targeted update of the UI,
|
|
205
|
-
// it
|
|
205
|
+
// it would be merged with `render`` method and the
|
|
206
206
|
// SCHEMA at some point
|
|
207
207
|
propagate(fields = []) {
|
|
208
208
|
const impacts = {
|
|
@@ -225,27 +225,28 @@ export class DataLayer {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
showAtLoad() {
|
|
228
|
-
return this.
|
|
228
|
+
return this.autoVisibility && this.showAtZoom()
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
get
|
|
232
|
-
if (this.
|
|
231
|
+
get autoVisibility() {
|
|
232
|
+
if (this._autoVisibility === undefined) {
|
|
233
233
|
if (this._umap.datalayersFromQueryString) {
|
|
234
234
|
const datalayerIds = this._umap.datalayersFromQueryString
|
|
235
|
-
this.
|
|
235
|
+
this._autoVisibility = datalayerIds.includes(this.id.toString())
|
|
236
236
|
if (this.properties.old_id) {
|
|
237
|
-
this.
|
|
238
|
-
this.
|
|
237
|
+
this._autoVisibility =
|
|
238
|
+
this._autoVisibility ||
|
|
239
|
+
datalayerIds.includes(this.properties.old_id.toString())
|
|
239
240
|
}
|
|
240
241
|
} else {
|
|
241
|
-
this.
|
|
242
|
+
this._autoVisibility = this.properties.displayOnLoad
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
|
-
return this.
|
|
245
|
+
return this._autoVisibility
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
set
|
|
248
|
-
this.
|
|
248
|
+
set autoVisibility(value) {
|
|
249
|
+
this._autoVisibility = value
|
|
249
250
|
}
|
|
250
251
|
|
|
251
252
|
insertBefore(other) {
|
|
@@ -448,7 +449,8 @@ export class DataLayer {
|
|
|
448
449
|
this.inferFields(feature)
|
|
449
450
|
try {
|
|
450
451
|
this.showFeature(feature)
|
|
451
|
-
} catch {
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error(error)
|
|
452
454
|
if (this._umap.editEnabled) {
|
|
453
455
|
Alert.error(translate('Skipping invalid geometry'))
|
|
454
456
|
}
|
|
@@ -482,96 +484,49 @@ export class DataLayer {
|
|
|
482
484
|
}
|
|
483
485
|
|
|
484
486
|
inferFields(feature) {
|
|
485
|
-
if (!this.properties.fields) this.properties.fields = []
|
|
486
|
-
const keys = this.fieldKeys
|
|
487
487
|
for (const key in feature.properties) {
|
|
488
|
-
if (typeof feature.properties[key] !== 'object') {
|
|
488
|
+
if (key && typeof feature.properties[key] !== 'object') {
|
|
489
489
|
if (key.indexOf('_') === 0) continue
|
|
490
|
-
if (
|
|
491
|
-
this.
|
|
490
|
+
if (this.fields.has(key)) continue
|
|
491
|
+
if (this._umap.fields.has(key)) continue
|
|
492
|
+
let type = 'String'
|
|
493
|
+
if (key === 'description') type = 'Text'
|
|
494
|
+
this.fields.add({ key, type })
|
|
492
495
|
}
|
|
493
496
|
}
|
|
497
|
+
this.fields.push()
|
|
494
498
|
}
|
|
495
499
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
.confirm(
|
|
499
|
-
translate('Are you sure you want to delete this field on all the features?')
|
|
500
|
-
)
|
|
501
|
-
.then(() => {
|
|
502
|
-
this.deleteProperty(property)
|
|
503
|
-
})
|
|
500
|
+
renameField(oldName, newName) {
|
|
501
|
+
this.renameFeaturesField(oldName, newName)
|
|
504
502
|
}
|
|
505
503
|
|
|
506
|
-
|
|
507
|
-
return this._umap.dialog
|
|
508
|
-
.prompt(translate('Please enter the new name of this field'))
|
|
509
|
-
.then(({ prompt }) => {
|
|
510
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
511
|
-
this.renameProperty(property, prompt)
|
|
512
|
-
})
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
renameProperty(oldName, newName) {
|
|
516
|
-
this.sync.startBatch()
|
|
517
|
-
const oldFields = Utils.CopyJSON(this.fields)
|
|
518
|
-
for (const field of this.fields) {
|
|
519
|
-
if (field.key === oldName) {
|
|
520
|
-
field.key = newName
|
|
521
|
-
break
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
this.sync.update('properties.fields', this.fields, oldFields)
|
|
504
|
+
renameFeaturesField(oldName, newName) {
|
|
525
505
|
this.features.forEach((feature) => {
|
|
526
|
-
feature.
|
|
506
|
+
feature.renameField(oldName, newName)
|
|
527
507
|
})
|
|
528
|
-
this.sync.commitBatch()
|
|
529
508
|
}
|
|
530
509
|
|
|
531
|
-
|
|
532
|
-
this.
|
|
533
|
-
const oldFields = Utils.CopyJSON(this.fields)
|
|
534
|
-
this.fields = this.fields.filter((field) => field.key !== property)
|
|
535
|
-
this.sync.update('properties.fields', this.fields, oldFields)
|
|
536
|
-
this.features.forEach((feature) => {
|
|
537
|
-
feature.deleteProperty(property)
|
|
538
|
-
})
|
|
539
|
-
this.sync.commitBatch()
|
|
510
|
+
deleteField(name) {
|
|
511
|
+
this.deleteFeaturesField(name)
|
|
540
512
|
}
|
|
541
513
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
})
|
|
547
|
-
this._umap.dialog
|
|
548
|
-
.prompt(translate('Please enter the name of the property'))
|
|
549
|
-
.then(({ prompt }) => {
|
|
550
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
551
|
-
this.properties.fields.push({ key: prompt, type: 'String' })
|
|
552
|
-
resolve()
|
|
514
|
+
deleteFeaturesField(name) {
|
|
515
|
+
if (!this._umap.fields.has(name) && !this.fields.has(name)) {
|
|
516
|
+
this.features.forEach((feature) => {
|
|
517
|
+
feature.deleteField(name)
|
|
553
518
|
})
|
|
554
|
-
|
|
519
|
+
}
|
|
555
520
|
}
|
|
556
521
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
Alert.error(translate('Name “{name}” should not contain a dot.', { name }))
|
|
560
|
-
return false
|
|
561
|
-
}
|
|
562
|
-
if (this.fieldKeys.includes(name)) {
|
|
563
|
-
Alert.error(translate('This name already exists: “{name}”', { name }))
|
|
564
|
-
return false
|
|
565
|
-
}
|
|
566
|
-
return true
|
|
522
|
+
eachFeature(callback) {
|
|
523
|
+
this.features.forEach((feature) => callback(feature))
|
|
567
524
|
}
|
|
568
525
|
|
|
569
|
-
sortedValues(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
574
|
-
.sort(Utils.naturalSort)
|
|
526
|
+
sortedValues(key) {
|
|
527
|
+
const field = this.fields.get(key) || this._umap.fields.get(key)
|
|
528
|
+
if (!field) return []
|
|
529
|
+
return field.values(this.features.all()).sort(Utils.naturalSort)
|
|
575
530
|
}
|
|
576
531
|
|
|
577
532
|
addData(geojson, sync) {
|
|
@@ -834,7 +789,7 @@ export class DataLayer {
|
|
|
834
789
|
const builder = new MutatingForm(this, layerFields)
|
|
835
790
|
const template = `
|
|
836
791
|
<details id="layer-properties">
|
|
837
|
-
<summary>${this.layer.getName()}: ${translate('settings')}</summary>
|
|
792
|
+
<summary><h4>${this.layer.getName()}: ${translate('settings')}</h4></summary>
|
|
838
793
|
<fieldset data-ref=fieldset></fieldset>
|
|
839
794
|
</details>
|
|
840
795
|
`
|
|
@@ -928,64 +883,6 @@ export class DataLayer {
|
|
|
928
883
|
fieldset.appendChild(builder.build())
|
|
929
884
|
}
|
|
930
885
|
|
|
931
|
-
_editFields(container) {
|
|
932
|
-
const template = `
|
|
933
|
-
<details id="fields">
|
|
934
|
-
<summary>${translate('Manage Fields')}</summary>
|
|
935
|
-
<fieldset>
|
|
936
|
-
<ul data-ref=ul></ul>
|
|
937
|
-
<button type="button" data-ref=add><i class="icon icon-16 icon-add"></i>${translate('Add a new field')}</button>
|
|
938
|
-
</fieldset>
|
|
939
|
-
</details>
|
|
940
|
-
`
|
|
941
|
-
const [fieldset, { ul, add }] = Utils.loadTemplateWithRefs(template)
|
|
942
|
-
add.addEventListener('click', () => {
|
|
943
|
-
this.addProperty().then(() => {
|
|
944
|
-
this.edit().then((panel) => {
|
|
945
|
-
panel.scrollTo('details#fields')
|
|
946
|
-
})
|
|
947
|
-
})
|
|
948
|
-
})
|
|
949
|
-
container.appendChild(fieldset)
|
|
950
|
-
for (const field of this.fields) {
|
|
951
|
-
const [row, { rename, del }] = Utils.loadTemplateWithRefs(
|
|
952
|
-
`<li class="orderable" data-key="${field.key}">
|
|
953
|
-
<button class="icon icon-16 icon-edit" title="${translate('Rename this field')}" data-ref=rename></button>
|
|
954
|
-
<button class="icon icon-16 icon-delete" title="${translate('Delete this field')}" data-ref=del></button>
|
|
955
|
-
<i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
|
|
956
|
-
${field.key}
|
|
957
|
-
</li>`
|
|
958
|
-
)
|
|
959
|
-
ul.appendChild(row)
|
|
960
|
-
rename.addEventListener('click', () => {
|
|
961
|
-
this.askForRenameProperty(field.key).then(() => {
|
|
962
|
-
this.edit().then((panel) => {
|
|
963
|
-
panel.scrollTo('details#fields')
|
|
964
|
-
})
|
|
965
|
-
})
|
|
966
|
-
})
|
|
967
|
-
del.addEventListener('click', () => {
|
|
968
|
-
this.confirmDeleteProperty(field.key).then(() => {
|
|
969
|
-
this.edit().then((panel) => {
|
|
970
|
-
panel.scrollTo('details#fields')
|
|
971
|
-
})
|
|
972
|
-
})
|
|
973
|
-
})
|
|
974
|
-
}
|
|
975
|
-
const onReorder = (src, dst, initialIndex, finalIndex) => {
|
|
976
|
-
const orderedKeys = Array.from(ul.querySelectorAll('li')).map(
|
|
977
|
-
(el) => el.dataset.key
|
|
978
|
-
)
|
|
979
|
-
const oldFields = Utils.CopyJSON(this.properties.fields)
|
|
980
|
-
this.properties.fields.sort(
|
|
981
|
-
(fieldA, fieldB) =>
|
|
982
|
-
orderedKeys.indexOf(fieldA.key) > orderedKeys.indexOf(fieldB.key)
|
|
983
|
-
)
|
|
984
|
-
this.sync.update('properties.fields', this.properties.fields, oldFields)
|
|
985
|
-
}
|
|
986
|
-
const orderable = new Orderable(ul, onReorder)
|
|
987
|
-
}
|
|
988
|
-
|
|
989
886
|
_editRemoteDataProperties(container) {
|
|
990
887
|
// XXX I'm not sure **why** this is needed (as it's set during `this.initialize`)
|
|
991
888
|
// but apparently it's needed.
|
|
@@ -1096,9 +993,7 @@ export class DataLayer {
|
|
|
1096
993
|
this._editInteractionProperties(container)
|
|
1097
994
|
this._editTextPathProperties(container)
|
|
1098
995
|
this._editRemoteDataProperties(container)
|
|
1099
|
-
|
|
1100
|
-
this._editFields(container)
|
|
1101
|
-
}
|
|
996
|
+
this.fields.edit(container)
|
|
1102
997
|
this.rules.edit(container)
|
|
1103
998
|
|
|
1104
999
|
if (this._umap.properties.urls.datalayer_versions) {
|
|
@@ -1223,7 +1118,7 @@ export class DataLayer {
|
|
|
1223
1118
|
// From now on, do not try to how/hide
|
|
1224
1119
|
// automatically this layer, as user
|
|
1225
1120
|
// has taken control on this.
|
|
1226
|
-
this.
|
|
1121
|
+
this.autoVisibility = false
|
|
1227
1122
|
let display = force
|
|
1228
1123
|
if (force === undefined) {
|
|
1229
1124
|
if (!this.isVisible()) display = true
|
|
@@ -1294,7 +1189,8 @@ export class DataLayer {
|
|
|
1294
1189
|
}
|
|
1295
1190
|
|
|
1296
1191
|
umapGeoJSON() {
|
|
1297
|
-
const
|
|
1192
|
+
const features = this.isRemoteLayer() ? [] : this.features.all()
|
|
1193
|
+
const geojson = this._umap.formatter.toFeatureCollection(features)
|
|
1298
1194
|
geojson._umap_options = this.properties
|
|
1299
1195
|
return geojson
|
|
1300
1196
|
}
|
|
@@ -1331,12 +1227,12 @@ export class DataLayer {
|
|
|
1331
1227
|
async save() {
|
|
1332
1228
|
if (this.isDeleted) return await this.saveDelete()
|
|
1333
1229
|
if (!this.isRemoteLayer() && !this.isLoaded()) return
|
|
1334
|
-
const geojson = this.umapGeoJSON()
|
|
1335
1230
|
const formData = new FormData()
|
|
1336
1231
|
formData.append('name', this.properties.name)
|
|
1337
1232
|
formData.append('display_on_load', !!this.properties.displayOnLoad)
|
|
1338
1233
|
formData.append('rank', this.rank)
|
|
1339
1234
|
formData.append('settings', this.prepareProperties())
|
|
1235
|
+
const geojson = this.umapGeoJSON()
|
|
1340
1236
|
// Filename support is shaky, don't do it for now.
|
|
1341
1237
|
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
|
1342
1238
|
formData.append('geojson', blob)
|
|
@@ -1438,13 +1334,15 @@ export class DataLayer {
|
|
|
1438
1334
|
)) {
|
|
1439
1335
|
container.innerHTML = ''
|
|
1440
1336
|
if (this.layer.renderLegend) return this.layer.renderLegend(container)
|
|
1441
|
-
const keys = new Set(this.fieldKeys)
|
|
1442
1337
|
const rules = new Map()
|
|
1443
1338
|
for (const rule of this.rules) {
|
|
1444
1339
|
rules.set(rule.condition, rule)
|
|
1445
1340
|
}
|
|
1446
1341
|
for (const rule of this._umap.rules) {
|
|
1447
|
-
if (
|
|
1342
|
+
if (
|
|
1343
|
+
!rules.has(rule.condition) &&
|
|
1344
|
+
(this.fields.has(rule.field.key) || this._umap.fields.has(rule.field.key))
|
|
1345
|
+
) {
|
|
1448
1346
|
rules.set(rule.condition, rule)
|
|
1449
1347
|
}
|
|
1450
1348
|
}
|
|
@@ -1468,6 +1366,11 @@ export class DataLayer {
|
|
|
1468
1366
|
'icon-eye',
|
|
1469
1367
|
translate('Show/hide layer')
|
|
1470
1368
|
)
|
|
1369
|
+
const table = DomUtil.createButtonIcon(
|
|
1370
|
+
container,
|
|
1371
|
+
'icon-table show-on-edit',
|
|
1372
|
+
translate('Edit properties in a table')
|
|
1373
|
+
)
|
|
1471
1374
|
const zoomTo = DomUtil.createButtonIcon(
|
|
1472
1375
|
container,
|
|
1473
1376
|
'icon-zoom',
|
|
@@ -1478,11 +1381,6 @@ export class DataLayer {
|
|
|
1478
1381
|
'icon-edit show-on-edit',
|
|
1479
1382
|
translate('Edit')
|
|
1480
1383
|
)
|
|
1481
|
-
const table = DomUtil.createButtonIcon(
|
|
1482
|
-
container,
|
|
1483
|
-
'icon-table show-on-edit',
|
|
1484
|
-
translate('Edit properties in a table')
|
|
1485
|
-
)
|
|
1486
1384
|
const remove = DomUtil.createButtonIcon(
|
|
1487
1385
|
container,
|
|
1488
1386
|
'icon-delete show-on-edit',
|
|
@@ -1500,16 +1398,12 @@ export class DataLayer {
|
|
|
1500
1398
|
}
|
|
1501
1399
|
DomEvent.on(toggle, 'click', () => this.toggle())
|
|
1502
1400
|
DomEvent.on(zoomTo, 'click', this.zoomTo, this)
|
|
1503
|
-
container.classList.add(this.
|
|
1401
|
+
container.classList.add(this.cssId)
|
|
1504
1402
|
container.classList.toggle('off', !this.isVisible())
|
|
1505
1403
|
}
|
|
1506
1404
|
|
|
1507
1405
|
getHidableElements() {
|
|
1508
|
-
return document.querySelectorAll(`.${this.
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
getHidableClass() {
|
|
1512
|
-
return `show_with_datalayer_${stamp(this)}`
|
|
1406
|
+
return document.querySelectorAll(`.${this.cssId}`)
|
|
1513
1407
|
}
|
|
1514
1408
|
|
|
1515
1409
|
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_CONTRAST = {}
|
|
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_CONTRAST[bgcolor] !== 'undefined') return CACHE_CONTRAST[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_CONTRAST[bgcolor] = out
|
|
108
|
+
return out
|
|
109
|
+
}
|