umap-project 3.3.6__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 +63 -55
- 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 +56 -157
- 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 -1
- 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/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 +148 -51
- 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 +39 -13
- umap/static/umap/locale/zh_TW.json +39 -13
- 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_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.6.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
- {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +214 -211
- 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.0b0.dist-info}/WHEEL +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import * as Utils from './utils.js'
|
|
2
|
+
import { translate } from './i18n.js'
|
|
3
|
+
import Orderable from './orderable.js'
|
|
4
|
+
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
5
|
+
import { Form } from './form/builder.js'
|
|
2
6
|
|
|
3
7
|
export class DataLayerManager extends Object {
|
|
4
8
|
add(datalayer) {
|
|
@@ -59,7 +63,7 @@ export class FeatureManager extends Map {
|
|
|
59
63
|
if (this.has(feature.id)) {
|
|
60
64
|
console.error('Duplicate id', feature, this.get(feature.id))
|
|
61
65
|
feature.id = Utils.generateId()
|
|
62
|
-
feature.datalayer.
|
|
66
|
+
feature.datalayer._migrated = true
|
|
63
67
|
}
|
|
64
68
|
this.set(feature.id, feature)
|
|
65
69
|
}
|
|
@@ -112,3 +116,263 @@ export class FeatureManager extends Map {
|
|
|
112
116
|
return this.all()[index - 1]
|
|
113
117
|
}
|
|
114
118
|
}
|
|
119
|
+
|
|
120
|
+
export class FieldManager extends Map {
|
|
121
|
+
constructor(parent, dialog) {
|
|
122
|
+
super()
|
|
123
|
+
this.parent = parent
|
|
124
|
+
this.dialog = dialog
|
|
125
|
+
this.parent.properties.fields ??= []
|
|
126
|
+
this.pull()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pull() {
|
|
130
|
+
this.clear()
|
|
131
|
+
for (const field of this.parent.properties.fields) {
|
|
132
|
+
this.add(field)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
push() {
|
|
137
|
+
this.parent.properties.fields = this.all().map((field) => {
|
|
138
|
+
// We don't want to keep the reference, otherwise editing
|
|
139
|
+
// it will also change the old value
|
|
140
|
+
return { ...field }
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async commit() {
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
const oldFields = Utils.CopyJSON(this.parent.properties.fields)
|
|
147
|
+
resolve()
|
|
148
|
+
this.push()
|
|
149
|
+
this.parent.sync.update(
|
|
150
|
+
'properties.fields',
|
|
151
|
+
this.parent.properties.fields,
|
|
152
|
+
oldFields
|
|
153
|
+
)
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
add(field) {
|
|
158
|
+
if (!field?.key) {
|
|
159
|
+
console.error('Invalid field', field)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
field.type ??= 'String'
|
|
163
|
+
// Copy object, so not to affect original
|
|
164
|
+
// when edited.
|
|
165
|
+
this.set(field.key, { ...field })
|
|
166
|
+
this.push()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
delete(key) {
|
|
170
|
+
super.delete(key)
|
|
171
|
+
this.push()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
all() {
|
|
175
|
+
return Array.from(this.values())
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
edit(container) {
|
|
179
|
+
const [root, { ul, add, manageFilters }] = Utils.loadTemplateWithRefs(`
|
|
180
|
+
<details id="fields-management">
|
|
181
|
+
<summary><h4>${translate('Manage Fields')}</h4></summary>
|
|
182
|
+
<fieldset>
|
|
183
|
+
<ul data-ref=ul></ul>
|
|
184
|
+
<div class="button-bar half">
|
|
185
|
+
<button type="button" data-ref=add>${translate('Add a new field')}</button>
|
|
186
|
+
<button type="button" data-ref="manageFilters">${translate('Manage filters')}</button>
|
|
187
|
+
</div>
|
|
188
|
+
</fieldset>
|
|
189
|
+
</details>
|
|
190
|
+
`)
|
|
191
|
+
container.appendChild(root)
|
|
192
|
+
add.hidden = this.parent.isRemoteLayer?.()
|
|
193
|
+
add.addEventListener('click', () => {
|
|
194
|
+
this.editField().then(() => {
|
|
195
|
+
this.parent.edit().then((panel) => {
|
|
196
|
+
panel.scrollTo('details#fields-management')
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
manageFilters.addEventListener('click', () => this.parent.filters.edit())
|
|
201
|
+
for (const field of this.all()) {
|
|
202
|
+
const [row, { edit, del, addFilter, editFilter }] = Utils.loadTemplateWithRefs(
|
|
203
|
+
`<li class="orderable with-toolbox" data-key="${field.key}">
|
|
204
|
+
<span>
|
|
205
|
+
<i class="icon icon-16 icon-field-${field.type}" title="${field.type}"></i>
|
|
206
|
+
${field.key}
|
|
207
|
+
</span>
|
|
208
|
+
<span>
|
|
209
|
+
<button class="icon icon-16 icon-edit" title="${translate('Edit this field')}" data-ref=edit></button>
|
|
210
|
+
<button class="icon icon-16 icon-filters" title="${translate('Edit filter')}" data-ref=editFilter></button>
|
|
211
|
+
<button class="icon icon-16 icon-filters-empty" title="${translate('Add a filter for this field')}" data-ref=addFilter></button>
|
|
212
|
+
<button class="icon icon-16 icon-delete" title="${translate('Delete this field')}" data-ref=del></button>
|
|
213
|
+
<i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
|
|
214
|
+
</span>
|
|
215
|
+
</li>`
|
|
216
|
+
)
|
|
217
|
+
editFilter.hidden = !this.parent.filters.has(field.key)
|
|
218
|
+
addFilter.hidden = this.parent.filters.has(field.key)
|
|
219
|
+
del.hidden = this.parent.isRemoteLayer?.()
|
|
220
|
+
editFilter.addEventListener('click', () =>
|
|
221
|
+
this.parent.filters.createFilterForm(field.key)
|
|
222
|
+
)
|
|
223
|
+
addFilter.addEventListener('click', () =>
|
|
224
|
+
this.parent.filters.createFilterForm(field.key)
|
|
225
|
+
)
|
|
226
|
+
ul.appendChild(row)
|
|
227
|
+
edit.addEventListener('click', () => {
|
|
228
|
+
this.editField(field.key).then(() => {
|
|
229
|
+
this.parent.edit().then((panel) => {
|
|
230
|
+
panel.scrollTo('details#fields-management')
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
del.addEventListener('click', () => {
|
|
235
|
+
this.confirmDelete(field.key).then(() => {
|
|
236
|
+
this.parent.edit().then((panel) => {
|
|
237
|
+
panel.scrollTo('details#fields-management')
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
const onReorder = (src, dst, initialIndex, finalIndex) => {
|
|
243
|
+
const orderedKeys = Array.from(ul.querySelectorAll('li')).map(
|
|
244
|
+
(el) => el.dataset.key
|
|
245
|
+
)
|
|
246
|
+
const oldFields = Utils.CopyJSON(this.parent.properties.fields)
|
|
247
|
+
const copy = Object.fromEntries(this)
|
|
248
|
+
this.clear()
|
|
249
|
+
for (const key of orderedKeys) {
|
|
250
|
+
this.add(copy[key])
|
|
251
|
+
}
|
|
252
|
+
this.parent.sync.update(
|
|
253
|
+
'properties.fields',
|
|
254
|
+
this.parent.properties.fields,
|
|
255
|
+
oldFields
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
const orderable = new Orderable(ul, onReorder)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async editField(name) {
|
|
262
|
+
if (!name && this.parent.isRemoteLayer?.()) return
|
|
263
|
+
const FIELD_TYPES = [
|
|
264
|
+
'String',
|
|
265
|
+
'Text',
|
|
266
|
+
'Number',
|
|
267
|
+
'Date',
|
|
268
|
+
'Datetime',
|
|
269
|
+
'Enum',
|
|
270
|
+
'Boolean',
|
|
271
|
+
]
|
|
272
|
+
const field = this.get(name) || {}
|
|
273
|
+
const metadatas = [
|
|
274
|
+
[
|
|
275
|
+
'key',
|
|
276
|
+
{
|
|
277
|
+
handler: 'BlurInput',
|
|
278
|
+
label: translate('Field Name'),
|
|
279
|
+
disabled: this.parent.isRemoteLayer?.(),
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
'type',
|
|
284
|
+
{
|
|
285
|
+
handler: 'Select',
|
|
286
|
+
selectOptions: FIELD_TYPES,
|
|
287
|
+
label: translate('Field Type'),
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
]
|
|
291
|
+
const form = new Form(field, metadatas)
|
|
292
|
+
|
|
293
|
+
const [container, { body, addFilter }] = Utils.loadTemplateWithRefs(`
|
|
294
|
+
<div>
|
|
295
|
+
<h3>${translate('Manage field')}</h3>
|
|
296
|
+
<div data-ref=body></div>
|
|
297
|
+
<button type="button" data-ref=addFilter hidden><i class="icon icon-16 icon-filters"></i>${translate('Add filter for this field')}</button>
|
|
298
|
+
</div>
|
|
299
|
+
`)
|
|
300
|
+
body.appendChild(form.build())
|
|
301
|
+
if (this.parent.filters) {
|
|
302
|
+
addFilter.addEventListener('click', () => {
|
|
303
|
+
this.dialog.accept()
|
|
304
|
+
this.parent.filters.createFilterForm(field.key)
|
|
305
|
+
})
|
|
306
|
+
addFilter.hidden = false
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return this.dialog.open({ template: container }).then(() => {
|
|
310
|
+
if (!this.validateName(field.key, field.key !== name)) {
|
|
311
|
+
this.pull()
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
this.parent.sync.startBatch()
|
|
315
|
+
const oldFields = Utils.CopyJSON(this.parent.properties.fields)
|
|
316
|
+
if (!name) {
|
|
317
|
+
this.add(field)
|
|
318
|
+
} else if (name !== field.key) {
|
|
319
|
+
this.clear()
|
|
320
|
+
// Keep order on rename
|
|
321
|
+
for (const old of oldFields) {
|
|
322
|
+
if (old.key === name) {
|
|
323
|
+
this.add(field)
|
|
324
|
+
} else {
|
|
325
|
+
this.add(old)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
this.parent.renameField(name, field.key)
|
|
329
|
+
} else {
|
|
330
|
+
this.push()
|
|
331
|
+
}
|
|
332
|
+
this.parent.sync.update(
|
|
333
|
+
'properties.fields',
|
|
334
|
+
this.parent.properties.fields,
|
|
335
|
+
oldFields
|
|
336
|
+
)
|
|
337
|
+
this.parent.sync.commitBatch()
|
|
338
|
+
this.parent.render(['properties.fields'])
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
validateName(name, isNew = false) {
|
|
343
|
+
if (!name) {
|
|
344
|
+
Alert.error(translate('Name cannot be empty.'))
|
|
345
|
+
return false
|
|
346
|
+
}
|
|
347
|
+
if (name.includes('.')) {
|
|
348
|
+
Alert.error(translate('Name “{name}” should not contain a dot.', { name }))
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
351
|
+
if (isNew && this.has(name)) {
|
|
352
|
+
Alert.error(translate('This name already exists: “{name}”', { name }))
|
|
353
|
+
return false
|
|
354
|
+
}
|
|
355
|
+
return true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async confirmDelete(name) {
|
|
359
|
+
return this.dialog
|
|
360
|
+
.confirm(translate('Are you sure you want to delete this field on all the data?'))
|
|
361
|
+
.then(() => {
|
|
362
|
+
this.parent.sync.startBatch()
|
|
363
|
+
const oldFields = Utils.CopyJSON(this.parent.properties.fields)
|
|
364
|
+
this.delete(name)
|
|
365
|
+
this.push()
|
|
366
|
+
if (this.parent.filters.has(name)) {
|
|
367
|
+
this.parent.filters.remove(name)
|
|
368
|
+
}
|
|
369
|
+
this.parent.deleteField(name)
|
|
370
|
+
this.parent.sync.update(
|
|
371
|
+
'properties.fields',
|
|
372
|
+
this.parent.properties.fields,
|
|
373
|
+
oldFields
|
|
374
|
+
)
|
|
375
|
+
this.parent.sync.commitBatch()
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -3,6 +3,7 @@ import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
|
3
3
|
import { MutatingForm } from './form/builder.js'
|
|
4
4
|
import { translate } from './i18n.js'
|
|
5
5
|
import * as Utils from './utils.js'
|
|
6
|
+
import * as DOMUtils from './domutils.js'
|
|
6
7
|
|
|
7
8
|
// Dedicated object so we can deal with a separate dirty status, and thus
|
|
8
9
|
// call the endpoint only when needed, saving one call at each save.
|
|
@@ -42,12 +43,42 @@ export class MapPermissions {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
isAnonymousMap() {
|
|
45
|
-
return !this.
|
|
46
|
+
return !this.properties.owner
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isDraft() {
|
|
50
|
+
return this.properties.share_status === 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
userIsAuth() {
|
|
54
|
+
return Boolean(this._umap.properties.user?.id)
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
_editAnonymous(container) {
|
|
49
|
-
const fields = []
|
|
50
58
|
if (this.isOwner()) {
|
|
59
|
+
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
|
60
|
+
// Note: real check is made on the back office anyway.
|
|
61
|
+
const template = `
|
|
62
|
+
<div class="anonymous soft-round aplat">
|
|
63
|
+
<h4><i class="icon icon-16 icon-anonymous"></i> ${translate('Anonymous map')}</h4>
|
|
64
|
+
<div data-ref="copiableInput"></div>
|
|
65
|
+
<p data-ref="p" hidden><button type="button" data-ref="button">${translate('Attach the map to my account')}</button></p>
|
|
66
|
+
</div>
|
|
67
|
+
`
|
|
68
|
+
const [root, { button, copiableInput, p }] = Utils.loadTemplateWithRefs(template)
|
|
69
|
+
container.appendChild(root)
|
|
70
|
+
if (this.properties.anonymous_edit_url) {
|
|
71
|
+
DOMUtils.copiableInput(
|
|
72
|
+
copiableInput,
|
|
73
|
+
translate('Secret edit link:'),
|
|
74
|
+
this.properties.anonymous_edit_url
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
if (this.userIsAuth()) {
|
|
78
|
+
button.addEventListener('click', () => this.attach())
|
|
79
|
+
p.hidden = false
|
|
80
|
+
}
|
|
81
|
+
const fields = []
|
|
51
82
|
fields.push([
|
|
52
83
|
'properties.edit_status',
|
|
53
84
|
{
|
|
@@ -67,31 +98,6 @@ export class MapPermissions {
|
|
|
67
98
|
const builder = new MutatingForm(this, fields)
|
|
68
99
|
const form = builder.build()
|
|
69
100
|
container.appendChild(form)
|
|
70
|
-
|
|
71
|
-
if (this.properties.anonymous_edit_url) {
|
|
72
|
-
DomUtil.createCopiableInput(
|
|
73
|
-
container,
|
|
74
|
-
translate('Secret edit link:'),
|
|
75
|
-
this.properties.anonymous_edit_url
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (this._umap.properties.user?.id) {
|
|
80
|
-
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
|
81
|
-
// Note: real check is made on the back office anyway.
|
|
82
|
-
const advancedActions = DomUtil.createFieldset(
|
|
83
|
-
container,
|
|
84
|
-
translate('Advanced actions')
|
|
85
|
-
)
|
|
86
|
-
const advancedButtons = DomUtil.create('div', 'button-bar', advancedActions)
|
|
87
|
-
DomUtil.createButton(
|
|
88
|
-
'button',
|
|
89
|
-
advancedButtons,
|
|
90
|
-
translate('Attach the map to my account'),
|
|
91
|
-
this.attach,
|
|
92
|
-
this
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -187,6 +193,8 @@ export class MapPermissions {
|
|
|
187
193
|
const [data, response, error] = await this._umap.server.post(this.getAttachUrl())
|
|
188
194
|
if (!error) {
|
|
189
195
|
this.properties.owner = this._umap.properties.user
|
|
196
|
+
this._umap.properties.user.is_owner = true
|
|
197
|
+
this.render()
|
|
190
198
|
Alert.success(translate('Map has been attached to your account'))
|
|
191
199
|
this._umap.editPanel.close()
|
|
192
200
|
}
|
|
@@ -245,10 +253,6 @@ export class MapPermissions {
|
|
|
245
253
|
]
|
|
246
254
|
}
|
|
247
255
|
}
|
|
248
|
-
|
|
249
|
-
isDraft() {
|
|
250
|
-
return this.properties.share_status === 0
|
|
251
|
-
}
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
export class DataLayerPermissions {
|
|
@@ -177,7 +177,7 @@ export const CaptionControl = BaseButton.extend({
|
|
|
177
177
|
position: 'topleft',
|
|
178
178
|
className: 'umap-control-caption',
|
|
179
179
|
title: translate('About'),
|
|
180
|
-
icon: 'icon-
|
|
180
|
+
icon: 'icon-info',
|
|
181
181
|
},
|
|
182
182
|
|
|
183
183
|
onClick: function () {
|
|
@@ -242,13 +242,13 @@ export const SearchControl = BaseButton.extend({
|
|
|
242
242
|
const [container, { input, resultsContainer }] =
|
|
243
243
|
Utils.loadTemplateWithRefs(template)
|
|
244
244
|
const id = Math.random()
|
|
245
|
+
this.search = new U.Search(
|
|
246
|
+
this._umap._leafletMap,
|
|
247
|
+
input,
|
|
248
|
+
this.layer,
|
|
249
|
+
this.photonOptions
|
|
250
|
+
)
|
|
245
251
|
this._umap.panel.open({ content: container }).then(() => {
|
|
246
|
-
this.search = new U.Search(
|
|
247
|
-
this._umap._leafletMap,
|
|
248
|
-
input,
|
|
249
|
-
this.layer,
|
|
250
|
-
this.photonOptions
|
|
251
|
-
)
|
|
252
252
|
this.search.on('ajax:send', () => {
|
|
253
253
|
this._umap.fire('dataloading', { id: id })
|
|
254
254
|
})
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DivIcon,
|
|
3
|
-
DomEvent,
|
|
4
|
-
DomUtil,
|
|
5
|
-
Icon,
|
|
6
|
-
Point,
|
|
7
|
-
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
1
|
+
import { DivIcon, Icon, Point } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
8
2
|
import { SCHEMA } from '../schema.js'
|
|
9
3
|
import * as Utils from '../utils.js'
|
|
4
|
+
import * as DOMUtils from '../domutils.js'
|
|
10
5
|
|
|
11
6
|
export function getClass(name) {
|
|
12
7
|
switch (name) {
|
|
@@ -299,7 +294,7 @@ export function setContrast(icon, parent, src, bgcolor) {
|
|
|
299
294
|
*/
|
|
300
295
|
if (!icon) return
|
|
301
296
|
|
|
302
|
-
if (
|
|
297
|
+
if (DOMUtils.contrastedColor(parent, bgcolor)) {
|
|
303
298
|
// Decide whether to switch svg to white or not, but do it
|
|
304
299
|
// only for internal SVG, as invert could do weird things
|
|
305
300
|
if (src.endsWith('.svg') && src !== SCHEMA.iconUrl.default) {
|
|
@@ -2,6 +2,7 @@ import colorbrewer from '../../../../vendors/colorbrewer/colorbrewer.js'
|
|
|
2
2
|
import { DomUtil, FeatureGroup } from '../../../../vendors/leaflet/leaflet-src.esm.js'
|
|
3
3
|
import { translate } from '../../i18n.js'
|
|
4
4
|
import * as Utils from '../../utils.js'
|
|
5
|
+
import * as DOMUtils from '../../domutils.js'
|
|
5
6
|
import { CircleMarker } from '../ui.js'
|
|
6
7
|
import { LayerMixin } from './base.js'
|
|
7
8
|
|
|
@@ -76,15 +77,21 @@ const ClassifiedMixin = {
|
|
|
76
77
|
|
|
77
78
|
renderLegend: function (container) {
|
|
78
79
|
if (!this.datalayer.isLoaded()) return
|
|
79
|
-
const
|
|
80
|
+
const ul = Utils.loadTemplate('<ul></ul>')
|
|
80
81
|
const items = this.getLegendItems()
|
|
81
82
|
for (const [color, label] of items) {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
83
|
+
const rgbColor = DOMUtils.hexToRGB(color)
|
|
84
|
+
const opacity = this.datalayer.getOption('fillOpacity')
|
|
85
|
+
const bgColor = `rgba(${rgbColor.join(',')}, ${opacity})`
|
|
86
|
+
const li = Utils.loadTemplate(`
|
|
87
|
+
<li>
|
|
88
|
+
<span class="datalayer-color" style="background-color: ${bgColor};"></span>
|
|
89
|
+
<span>${label}</span>
|
|
90
|
+
</li>
|
|
91
|
+
`)
|
|
92
|
+
ul.appendChild(li)
|
|
87
93
|
}
|
|
94
|
+
container.appendChild(ul)
|
|
88
95
|
},
|
|
89
96
|
|
|
90
97
|
getColorSchemes: function (classes) {
|
|
@@ -198,7 +205,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
198
205
|
'properties.choropleth.property',
|
|
199
206
|
{
|
|
200
207
|
handler: 'Select',
|
|
201
|
-
selectOptions: this.datalayer.
|
|
208
|
+
selectOptions: this.datalayer.fields.keys(),
|
|
202
209
|
label: translate('Choropleth property value'),
|
|
203
210
|
},
|
|
204
211
|
],
|
|
@@ -307,7 +314,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
307
314
|
'properties.circles.property',
|
|
308
315
|
{
|
|
309
316
|
handler: 'Select',
|
|
310
|
-
selectOptions: this.datalayer.
|
|
317
|
+
selectOptions: this.datalayer.fields.keys(),
|
|
311
318
|
label: translate('Property name to compute circles'),
|
|
312
319
|
},
|
|
313
320
|
],
|
|
@@ -384,7 +391,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
384
391
|
|
|
385
392
|
_getValue: function (feature) {
|
|
386
393
|
const key =
|
|
387
|
-
this.datalayer.properties.categorized.property || this.datalayer.
|
|
394
|
+
this.datalayer.properties.categorized.property || this.datalayer.fields.keys()[0]
|
|
388
395
|
return feature.properties[key]
|
|
389
396
|
},
|
|
390
397
|
|
|
@@ -437,7 +444,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
437
444
|
'properties.categorized.property',
|
|
438
445
|
{
|
|
439
446
|
handler: 'Select',
|
|
440
|
-
selectOptions: this.datalayer.
|
|
447
|
+
selectOptions: this.datalayer.fields.keys(),
|
|
441
448
|
label: translate('Category property'),
|
|
442
449
|
},
|
|
443
450
|
],
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
Marker,
|
|
6
6
|
Rectangle,
|
|
7
7
|
Polyline,
|
|
8
|
-
DomUtil,
|
|
9
8
|
latLngBounds,
|
|
10
9
|
} from '../../../../vendors/leaflet/leaflet-src.esm.js'
|
|
11
10
|
import { translate } from '../../i18n.js'
|
|
12
11
|
import * as Utils from '../../utils.js'
|
|
12
|
+
import * as DOMUtils from '../../domutils.js'
|
|
13
13
|
import { Cluster as ClusterIcon } from '../icon.js'
|
|
14
14
|
import { LayerMixin } from './base.js'
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ const MarkerCluster = Marker.extend({
|
|
|
21
21
|
const bgColor = this.options.icon.options.color
|
|
22
22
|
const textColor = this.options.icon.options.textColor
|
|
23
23
|
counter.style.color =
|
|
24
|
-
textColor ||
|
|
24
|
+
textColor || DOMUtils.textColorFromBackgroundColor(counter, bgColor)
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
computeCoverage() {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { getLocale, translate } from '../i18n.js'
|
|
7
7
|
import { Request } from '../request.js'
|
|
8
8
|
import * as Utils from '../utils.js'
|
|
9
|
+
import * as DOMUtils from '../domutils.js'
|
|
9
10
|
import * as Icon from './icon.js'
|
|
10
11
|
|
|
11
12
|
export default async function loadTemplate(name, feature, container) {
|
|
@@ -171,7 +172,7 @@ class OSM extends PopupTemplate {
|
|
|
171
172
|
const icon = Icon.makeElement(iconUrl, title)
|
|
172
173
|
DomUtil.addClass(icon, 'icon')
|
|
173
174
|
Icon.setContrast(icon, title, iconUrl, color)
|
|
174
|
-
if (
|
|
175
|
+
if (DOMUtils.contrastedColor(title, color)) title.style.color = 'white'
|
|
175
176
|
DomUtil.add('span', '', title, this.getName(feature))
|
|
176
177
|
return title
|
|
177
178
|
}
|
|
@@ -180,7 +181,7 @@ class OSM extends PopupTemplate {
|
|
|
180
181
|
const props = feature.properties
|
|
181
182
|
const locale = getLocale()
|
|
182
183
|
if (locale && props[`name:${locale}`]) return props[`name:${locale}`]
|
|
183
|
-
return props.name
|
|
184
|
+
return props.name || feature.getDisplayName()
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
async renderBody(feature) {
|
|
@@ -230,11 +231,31 @@ class OSM extends PopupTemplate {
|
|
|
230
231
|
)
|
|
231
232
|
)
|
|
232
233
|
}
|
|
234
|
+
if (props.image) {
|
|
235
|
+
body.appendChild(
|
|
236
|
+
Utils.loadTemplate(`<div><img src="${props.image}" alt="" /></div>`)
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
if (props.mapillary) {
|
|
240
|
+
body.appendChild(
|
|
241
|
+
Utils.loadTemplate(
|
|
242
|
+
`<div><a href="https://www.mapillary.com/app/?focus=photo&pKey=${props.mapillary}" target="_blank">${translate('Mapillary')}<i class="icon icon-16 icon-external-link"></i></a></div>`
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
}
|
|
233
246
|
const wikipedia = props[`wikipedia:${locale}`] || props.wikipedia
|
|
234
247
|
if (wikipedia) {
|
|
235
248
|
body.appendChild(
|
|
236
249
|
Utils.loadTemplate(
|
|
237
|
-
`<div
|
|
250
|
+
`<div><a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">${translate('Wikipedia')}<i class="icon icon-16 icon-external-link"></i></a></div>`
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
const wikidata = props[`wikidata:${locale}`] || props.wikidata
|
|
255
|
+
if (wikidata) {
|
|
256
|
+
body.appendChild(
|
|
257
|
+
Utils.loadTemplate(
|
|
258
|
+
`<div><a href="https://www.wikidata.org/wiki/${wikidata}" target="_blank">${translate('Wikidata')}<i class="icon icon-16 icon-external-link"></i></a></div>`
|
|
238
259
|
)
|
|
239
260
|
)
|
|
240
261
|
}
|
|
@@ -242,7 +263,7 @@ class OSM extends PopupTemplate {
|
|
|
242
263
|
if (id) {
|
|
243
264
|
body.appendChild(
|
|
244
265
|
Utils.loadTemplate(
|
|
245
|
-
`<div class="osm-link"><a href="https://www.openstreetmap.org/${id}">${translate('See on OpenStreetMap')}
|
|
266
|
+
`<div class="osm-link"><a href="https://www.openstreetmap.org/${id}">${translate('See on OpenStreetMap')}<i class="icon icon-16 icon-external-link"></i></a></div>`
|
|
246
267
|
)
|
|
247
268
|
)
|
|
248
269
|
}
|
|
@@ -257,7 +278,7 @@ class Wikipedia extends PopupTemplate {
|
|
|
257
278
|
if (wikipedia && _WIKIPEDIA_CACHE[wikipedia]) return _WIKIPEDIA_CACHE[wikipedia]
|
|
258
279
|
// Wikipedia value should be in form of "{locale}:{title}", according to https://wiki.openstreetmap.org/wiki/Key:wikipedia
|
|
259
280
|
const [locale, page] = wikipedia.split(':')
|
|
260
|
-
const url = `https://${locale}.wikipedia.org/w/api.php?action=query&format=json&origin=*&pithumbsize=500&prop=extracts|pageimages&titles=${page}`
|
|
281
|
+
const url = `https://${locale}.wikipedia.org/w/api.php?action=query&format=json&origin=*&pithumbsize=500&exintro=1&prop=extracts|pageimages&titles=${page}`
|
|
261
282
|
const request = new Request()
|
|
262
283
|
const response = await request.get(url)
|
|
263
284
|
if (response?.ok) {
|
|
@@ -277,9 +298,21 @@ class Wikipedia extends PopupTemplate {
|
|
|
277
298
|
const title = page.title || feature.getDisplayName()
|
|
278
299
|
const extract = page.extract || ''
|
|
279
300
|
const thumbnail = page.thumbnail?.source
|
|
280
|
-
const [content, { image }] = Utils.loadTemplateWithRefs(
|
|
281
|
-
|
|
282
|
-
|
|
301
|
+
const [content, { image }] = Utils.loadTemplateWithRefs(`
|
|
302
|
+
<div>
|
|
303
|
+
<h3>${Utils.escapeHTML(title)}</h3>
|
|
304
|
+
<img data-ref="image" hidden src="" />
|
|
305
|
+
<p>
|
|
306
|
+
${Utils.escapeHTML(extract)}
|
|
307
|
+
</p>
|
|
308
|
+
<p>
|
|
309
|
+
© ${translate('Wikipedia contributors')} •
|
|
310
|
+
<a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">
|
|
311
|
+
${translate('See on Wikipedia')}
|
|
312
|
+
<i class="icon icon-16 icon-external-link"></i>
|
|
313
|
+
</a>
|
|
314
|
+
</p>
|
|
315
|
+
</div>`)
|
|
283
316
|
if (thumbnail) {
|
|
284
317
|
image.src = thumbnail
|
|
285
318
|
image.hidden = false
|
|
@@ -349,6 +382,9 @@ class Route extends TitleMixin(PopupTemplate) {
|
|
|
349
382
|
color: 'orange',
|
|
350
383
|
}).addTo(map)
|
|
351
384
|
})
|
|
385
|
+
if (feature.properties.description) {
|
|
386
|
+
root.appendChild(Utils.loadTemplate(`<p>${feature.properties.description}</p>`))
|
|
387
|
+
}
|
|
352
388
|
return root
|
|
353
389
|
}
|
|
354
390
|
}
|