umap-project 2.4.2__py3-none-any.whl → 2.5.1__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/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +100 -50
- umap/static/umap/base.css +4 -1
- umap/static/umap/css/contextmenu.css +11 -0
- umap/static/umap/css/dialog.css +24 -3
- umap/static/umap/css/panel.css +4 -2
- umap/static/umap/css/slideshow.css +69 -0
- umap/static/umap/css/tableeditor.css +69 -0
- umap/static/umap/css/tooltip.css +3 -3
- umap/static/umap/img/16-white.svg +4 -0
- umap/static/umap/img/source/16-white.svg +5 -1
- umap/static/umap/js/components/alerts/alert.css +10 -10
- umap/static/umap/js/modules/autocomplete.js +23 -1
- umap/static/umap/js/modules/browser.js +14 -8
- umap/static/umap/js/modules/facets.js +40 -10
- umap/static/umap/js/modules/formatter.js +153 -0
- umap/static/umap/js/modules/global.js +10 -1
- umap/static/umap/js/modules/help.js +25 -25
- umap/static/umap/js/modules/importer.js +4 -4
- umap/static/umap/js/modules/importers/communesfr.js +3 -1
- umap/static/umap/js/modules/importers/datasets.js +8 -6
- umap/static/umap/js/modules/importers/geodatamine.js +10 -10
- umap/static/umap/js/modules/importers/overpass.js +18 -14
- umap/static/umap/js/modules/rules.js +13 -1
- umap/static/umap/js/modules/schema.js +16 -12
- umap/static/umap/js/{umap.share.js → modules/share.js} +60 -99
- umap/static/umap/js/modules/slideshow.js +141 -0
- umap/static/umap/js/modules/tableeditor.js +329 -0
- umap/static/umap/js/modules/ui/base.js +93 -0
- umap/static/umap/js/modules/ui/contextmenu.js +50 -0
- umap/static/umap/js/modules/ui/dialog.js +169 -31
- umap/static/umap/js/modules/ui/panel.js +6 -4
- umap/static/umap/js/modules/ui/tooltip.js +5 -75
- umap/static/umap/js/modules/utils.js +20 -0
- umap/static/umap/js/umap.controls.js +1 -1
- umap/static/umap/js/umap.features.js +22 -14
- umap/static/umap/js/umap.forms.js +157 -154
- umap/static/umap/js/umap.js +48 -34
- umap/static/umap/js/umap.layer.js +232 -164
- umap/static/umap/js/umap.permissions.js +1 -1
- umap/static/umap/js/umap.popup.js +1 -1
- umap/static/umap/locale/am_ET.js +22 -5
- umap/static/umap/locale/am_ET.json +19 -5
- umap/static/umap/locale/ar.js +22 -5
- umap/static/umap/locale/ar.json +19 -5
- umap/static/umap/locale/ast.js +22 -5
- umap/static/umap/locale/ast.json +19 -5
- umap/static/umap/locale/bg.js +22 -5
- umap/static/umap/locale/bg.json +19 -5
- umap/static/umap/locale/br.js +22 -5
- umap/static/umap/locale/br.json +19 -5
- umap/static/umap/locale/ca.js +56 -39
- umap/static/umap/locale/ca.json +53 -39
- umap/static/umap/locale/cs_CZ.js +22 -5
- umap/static/umap/locale/cs_CZ.json +19 -5
- umap/static/umap/locale/da.js +22 -5
- umap/static/umap/locale/da.json +19 -5
- umap/static/umap/locale/de.js +22 -5
- umap/static/umap/locale/de.json +19 -5
- umap/static/umap/locale/el.js +27 -10
- umap/static/umap/locale/el.json +19 -5
- umap/static/umap/locale/en.js +22 -6
- umap/static/umap/locale/en.json +19 -5
- umap/static/umap/locale/en_US.json +19 -5
- umap/static/umap/locale/es.js +22 -6
- umap/static/umap/locale/es.json +19 -5
- umap/static/umap/locale/et.js +22 -5
- umap/static/umap/locale/et.json +19 -5
- umap/static/umap/locale/eu.js +167 -150
- umap/static/umap/locale/eu.json +167 -150
- umap/static/umap/locale/fa_IR.js +22 -5
- umap/static/umap/locale/fa_IR.json +19 -5
- umap/static/umap/locale/fi.js +22 -5
- umap/static/umap/locale/fi.json +19 -5
- umap/static/umap/locale/fr.js +22 -6
- umap/static/umap/locale/fr.json +19 -5
- umap/static/umap/locale/gl.js +22 -5
- umap/static/umap/locale/gl.json +19 -5
- umap/static/umap/locale/he.js +22 -5
- umap/static/umap/locale/he.json +19 -5
- umap/static/umap/locale/hr.js +22 -5
- umap/static/umap/locale/hr.json +19 -5
- umap/static/umap/locale/hu.js +89 -72
- umap/static/umap/locale/hu.json +89 -75
- umap/static/umap/locale/id.js +22 -5
- umap/static/umap/locale/id.json +19 -5
- umap/static/umap/locale/is.js +22 -5
- umap/static/umap/locale/is.json +19 -5
- umap/static/umap/locale/it.js +22 -5
- umap/static/umap/locale/it.json +19 -5
- umap/static/umap/locale/ja.js +22 -5
- umap/static/umap/locale/ja.json +19 -5
- umap/static/umap/locale/ko.js +22 -5
- umap/static/umap/locale/ko.json +19 -5
- umap/static/umap/locale/lt.js +22 -5
- umap/static/umap/locale/lt.json +19 -5
- umap/static/umap/locale/ms.js +22 -5
- umap/static/umap/locale/ms.json +19 -5
- umap/static/umap/locale/nl.js +22 -5
- umap/static/umap/locale/nl.json +19 -5
- umap/static/umap/locale/no.js +22 -5
- umap/static/umap/locale/no.json +19 -5
- umap/static/umap/locale/pl.js +22 -5
- umap/static/umap/locale/pl.json +19 -5
- umap/static/umap/locale/pl_PL.json +19 -5
- umap/static/umap/locale/pt.js +22 -6
- umap/static/umap/locale/pt.json +21 -7
- umap/static/umap/locale/pt_BR.js +22 -5
- umap/static/umap/locale/pt_BR.json +19 -5
- umap/static/umap/locale/pt_PT.js +22 -5
- umap/static/umap/locale/pt_PT.json +19 -5
- umap/static/umap/locale/ro.js +22 -5
- umap/static/umap/locale/ro.json +19 -5
- umap/static/umap/locale/ru.js +22 -5
- umap/static/umap/locale/ru.json +19 -5
- umap/static/umap/locale/sk_SK.js +22 -5
- umap/static/umap/locale/sk_SK.json +19 -5
- umap/static/umap/locale/sl.js +22 -5
- umap/static/umap/locale/sl.json +19 -5
- umap/static/umap/locale/sr.js +22 -5
- umap/static/umap/locale/sr.json +19 -5
- umap/static/umap/locale/sv.js +22 -5
- umap/static/umap/locale/sv.json +19 -5
- umap/static/umap/locale/th_TH.js +22 -5
- umap/static/umap/locale/th_TH.json +19 -5
- umap/static/umap/locale/tr.js +22 -5
- umap/static/umap/locale/tr.json +19 -5
- umap/static/umap/locale/uk_UA.js +22 -5
- umap/static/umap/locale/uk_UA.json +19 -5
- umap/static/umap/locale/vi.js +22 -5
- umap/static/umap/locale/vi.json +19 -5
- umap/static/umap/locale/vi_VN.json +19 -5
- umap/static/umap/locale/zh.js +22 -5
- umap/static/umap/locale/zh.json +19 -5
- umap/static/umap/locale/zh_CN.json +19 -5
- umap/static/umap/locale/zh_TW.Big5.json +19 -5
- umap/static/umap/locale/zh_TW.js +22 -5
- umap/static/umap/locale/zh_TW.json +19 -5
- umap/static/umap/map.css +2 -145
- umap/static/umap/vars.css +5 -0
- umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +410 -428
- umap/static/umap/vendors/geojson-to-gpx/index.js +155 -0
- umap/static/umap/vendors/osmtogeojson/osmtogeojson.js +1 -2
- umap/static/umap/vendors/togeojson/togeojson.es.js +1109 -0
- umap/static/umap/vendors/togeojson/{togeojson.umd.js.map → togeojson.es.mjs.map} +1 -1
- umap/static/umap/vendors/tokml/tokml.es.js +895 -0
- umap/static/umap/vendors/tokml/tokml.es.mjs.map +1 -0
- umap/storage.py +6 -2
- umap/templates/umap/components/alerts/alert.html +3 -3
- umap/templates/umap/css.html +3 -0
- umap/templates/umap/js.html +0 -6
- umap/tests/fixtures/categorized_highway.geojson +1 -0
- umap/tests/fixtures/test_import_osm_relation.json +130 -0
- umap/tests/integration/conftest.py +8 -1
- umap/tests/integration/test_browser.py +3 -2
- umap/tests/integration/test_categorized_layer.py +141 -0
- umap/tests/integration/test_conditional_rules.py +21 -0
- umap/tests/integration/test_datalayer.py +9 -4
- umap/tests/integration/test_edit_datalayer.py +1 -0
- umap/tests/integration/test_edit_polygon.py +1 -1
- umap/tests/integration/test_export_map.py +2 -3
- umap/tests/integration/test_import.py +22 -0
- umap/tests/integration/test_tableeditor.py +158 -4
- umap/tests/integration/test_websocket_sync.py +2 -2
- {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/METADATA +8 -8
- {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/RECORD +172 -162
- umap/static/umap/js/umap.slideshow.js +0 -163
- umap/static/umap/js/umap.tableeditor.js +0 -118
- umap/static/umap/vendors/togeojson/togeojson.umd.js +0 -2
- umap/static/umap/vendors/togpx/togpx.js +0 -547
- umap/static/umap/vendors/tokml/tokml.js +0 -343
- {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/WHEEL +0 -0
- {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/entry_points.txt +0 -0
- {umap_project-2.4.2.dist-info → umap_project-2.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[role="dialog"] {
|
|
1
|
+
.umap-alert[role="dialog"] {
|
|
2
2
|
box-sizing: border-box;
|
|
3
3
|
min-height: 46px;
|
|
4
4
|
line-height: 46px;
|
|
@@ -20,36 +20,36 @@
|
|
|
20
20
|
width: max-content;
|
|
21
21
|
z-index: var(--zindex-alert);
|
|
22
22
|
}
|
|
23
|
-
[role="dialog"] > div {
|
|
23
|
+
.umap-alert[role="dialog"] > div {
|
|
24
24
|
margin: 0 auto;
|
|
25
25
|
min-width: 60%;
|
|
26
26
|
background-size: 20px;
|
|
27
27
|
background-position: 0 15px;
|
|
28
28
|
padding-left: 28px;
|
|
29
29
|
}
|
|
30
|
-
[role="dialog"][data-level="info"] > div {
|
|
30
|
+
.umap-alert[role="dialog"][data-level="info"] > div {
|
|
31
31
|
background-image: url('../../../img/alert-icon-info.svg');
|
|
32
32
|
background-repeat: no-repeat;
|
|
33
33
|
}
|
|
34
|
-
[role="dialog"][data-level="success"] > div {
|
|
34
|
+
.umap-alert[role="dialog"][data-level="success"] > div {
|
|
35
35
|
background-image: url('../../../img/alert-icon-success.svg');
|
|
36
36
|
background-repeat: no-repeat;
|
|
37
37
|
}
|
|
38
|
-
[role="dialog"][data-level="error"] > div {
|
|
38
|
+
.umap-alert[role="dialog"][data-level="error"] > div {
|
|
39
39
|
background-image: url('../../../img/alert-icon-error.svg');
|
|
40
40
|
background-repeat: no-repeat;
|
|
41
41
|
}
|
|
42
|
-
[role="dialog"][data-level="error"] {
|
|
42
|
+
.umap-alert[role="dialog"][data-level="error"] {
|
|
43
43
|
background-color: var(--color-darkRed);
|
|
44
44
|
}
|
|
45
|
-
[role="dialog"] a {
|
|
45
|
+
.umap-alert[role="dialog"] a {
|
|
46
46
|
text-decoration: underline;
|
|
47
47
|
}
|
|
48
|
-
[role="dialog"] label {
|
|
48
|
+
.umap-alert[role="dialog"] label {
|
|
49
49
|
font-size: .8rem;
|
|
50
50
|
font-weight: normal;
|
|
51
51
|
}
|
|
52
|
-
[role="dialog"] a[target="_blank"] {
|
|
52
|
+
.umap-alert[role="dialog"] a[target="_blank"] {
|
|
53
53
|
background: url('../../../img/icon-external-link.svg') no-repeat right center;
|
|
54
54
|
padding-right: 14px;
|
|
55
55
|
background-size: 12px;
|
|
@@ -127,7 +127,7 @@ h3[role="alert"] + p {
|
|
|
127
127
|
#link-wrapper {
|
|
128
128
|
margin-bottom: 1rem;
|
|
129
129
|
}
|
|
130
|
-
[role="dialog"] #conflict-wrapper a[target="_blank"] {
|
|
130
|
+
.umap-alert[role="dialog"] #conflict-wrapper a[target="_blank"] {
|
|
131
131
|
background-position-y: 16px;
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
7
7
|
import { translate } from './i18n.js'
|
|
8
8
|
import { Request, ServerRequest } from './request.js'
|
|
9
|
+
import { escapeHTML, generateId } from './utils.js'
|
|
9
10
|
|
|
10
11
|
export class BaseAutocomplete {
|
|
11
12
|
constructor(el, options) {
|
|
@@ -16,6 +17,7 @@ export class BaseAutocomplete {
|
|
|
16
17
|
allowFree: true,
|
|
17
18
|
minChar: 2,
|
|
18
19
|
maxResults: 5,
|
|
20
|
+
throttling: 300,
|
|
19
21
|
}
|
|
20
22
|
this.cache = ''
|
|
21
23
|
this.results = []
|
|
@@ -45,6 +47,7 @@ export class BaseAutocomplete {
|
|
|
45
47
|
placeholder: this.options.placeholder,
|
|
46
48
|
autocomplete: 'off',
|
|
47
49
|
className: this.options.className,
|
|
50
|
+
name: this.options.name || 'autocomplete',
|
|
48
51
|
})
|
|
49
52
|
DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
|
|
50
53
|
DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
|
|
@@ -125,7 +128,10 @@ export class BaseAutocomplete {
|
|
|
125
128
|
'Control',
|
|
126
129
|
]
|
|
127
130
|
if (!special.includes(e.key)) {
|
|
128
|
-
this.
|
|
131
|
+
if (this._typing) window.clearTimeout(this._typing)
|
|
132
|
+
this._typing = window.setTimeout(() => {
|
|
133
|
+
this.search()
|
|
134
|
+
}, this.options.throttling)
|
|
129
135
|
}
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -345,3 +351,19 @@ export const MultipleMixin = (Base) =>
|
|
|
345
351
|
export class AjaxAutocompleteMultiple extends MultipleMixin(BaseServerAjax) {}
|
|
346
352
|
|
|
347
353
|
export class AjaxAutocomplete extends SingleMixin(BaseServerAjax) {}
|
|
354
|
+
|
|
355
|
+
export class AutocompleteDatalist {
|
|
356
|
+
constructor(input) {
|
|
357
|
+
this.input = input
|
|
358
|
+
this.datalist = document.createElement('datalist')
|
|
359
|
+
this.datalist.id = generateId()
|
|
360
|
+
this.input.setAttribute('list', this.datalist.id)
|
|
361
|
+
this.input.parentElement.appendChild(this.datalist)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
set suggestions(values) {
|
|
365
|
+
this.datalist.innerHTML = values
|
|
366
|
+
.map((value) => `<option>${escapeHTML(value)}</option>`)
|
|
367
|
+
.join('')
|
|
368
|
+
}
|
|
369
|
+
}
|
|
@@ -37,7 +37,7 @@ export default class Browser {
|
|
|
37
37
|
? U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
|
|
38
38
|
: null
|
|
39
39
|
title.textContent = feature.getDisplayName() || '—'
|
|
40
|
-
const bgcolor = feature.
|
|
40
|
+
const bgcolor = feature.getPreviewColor()
|
|
41
41
|
colorBox.style.backgroundColor = bgcolor
|
|
42
42
|
if (symbol && symbol !== U.SCHEMA.iconUrl.default) {
|
|
43
43
|
const icon = U.Icon.makeIconElement(symbol, colorBox)
|
|
@@ -107,6 +107,7 @@ export default class Browser {
|
|
|
107
107
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
|
108
108
|
datalayer.resetLayer(true)
|
|
109
109
|
this.updateDatalayer(datalayer)
|
|
110
|
+
if (this.map.fullPanel?.isOpen()) datalayer.tableEdit()
|
|
110
111
|
})
|
|
111
112
|
this.toggleBadge()
|
|
112
113
|
}
|
|
@@ -149,7 +150,7 @@ export default class Browser {
|
|
|
149
150
|
DomEvent.disableClickPropagation(container)
|
|
150
151
|
|
|
151
152
|
DomUtil.createTitle(container, translate('Data browser'), 'icon-layers')
|
|
152
|
-
|
|
153
|
+
this.formContainer = DomUtil.createFieldset(container, L._('Filters'), {
|
|
153
154
|
on: this.mode === 'filters',
|
|
154
155
|
className: 'filters',
|
|
155
156
|
icon: 'icon-filters',
|
|
@@ -169,7 +170,7 @@ export default class Browser {
|
|
|
169
170
|
callback: () => this.onFormChange(),
|
|
170
171
|
})
|
|
171
172
|
let filtersBuilder
|
|
172
|
-
formContainer.appendChild(builder.build())
|
|
173
|
+
this.formContainer.appendChild(builder.build())
|
|
173
174
|
DomEvent.on(builder.form, 'reset', () => {
|
|
174
175
|
window.setTimeout(builder.syncAll.bind(builder))
|
|
175
176
|
})
|
|
@@ -181,12 +182,11 @@ export default class Browser {
|
|
|
181
182
|
DomEvent.on(filtersBuilder.form, 'reset', () => {
|
|
182
183
|
window.setTimeout(filtersBuilder.syncAll.bind(filtersBuilder))
|
|
183
184
|
})
|
|
184
|
-
formContainer.appendChild(filtersBuilder.build())
|
|
185
|
+
this.formContainer.appendChild(filtersBuilder.build())
|
|
185
186
|
}
|
|
186
|
-
const reset = DomUtil.createButton('flat', formContainer, '', () =>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
})
|
|
187
|
+
const reset = DomUtil.createButton('flat', this.formContainer, '', () =>
|
|
188
|
+
this.resetFilters()
|
|
189
|
+
)
|
|
190
190
|
DomUtil.createIcon(reset, 'icon-restore')
|
|
191
191
|
DomUtil.element({
|
|
192
192
|
tagName: 'span',
|
|
@@ -202,6 +202,12 @@ export default class Browser {
|
|
|
202
202
|
this.update()
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
resetFilters() {
|
|
206
|
+
for (const form of this.formContainer?.querySelectorAll('form') || []) {
|
|
207
|
+
form.reset()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
205
211
|
static backButton(map) {
|
|
206
212
|
const button = DomUtil.createButtonIcon(
|
|
207
213
|
DomUtil.create('li', '', undefined),
|
|
@@ -12,8 +12,8 @@ export default class Facets {
|
|
|
12
12
|
const properties = {}
|
|
13
13
|
let selected
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const type = defined
|
|
15
|
+
for (const name of names) {
|
|
16
|
+
const type = defined.get(name).type
|
|
17
17
|
properties[name] = { type: type }
|
|
18
18
|
selected = this.selected[name] || {}
|
|
19
19
|
selected.type = type
|
|
@@ -22,13 +22,13 @@ export default class Facets {
|
|
|
22
22
|
selected.choices = selected.choices || []
|
|
23
23
|
}
|
|
24
24
|
this.selected[name] = selected
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
|
|
27
27
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
|
28
28
|
datalayer.eachFeature((feature) => {
|
|
29
|
-
|
|
29
|
+
for (const name of names) {
|
|
30
30
|
let value = feature.properties[name]
|
|
31
|
-
const type = defined
|
|
31
|
+
const type = defined.get(name).type
|
|
32
32
|
const parser = this.getParser(type)
|
|
33
33
|
value = parser(value)
|
|
34
34
|
switch (type) {
|
|
@@ -56,7 +56,7 @@ export default class Facets {
|
|
|
56
56
|
properties[name].choices.push(value)
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|
|
60
60
|
})
|
|
61
61
|
})
|
|
62
62
|
return properties
|
|
@@ -73,7 +73,7 @@ export default class Facets {
|
|
|
73
73
|
|
|
74
74
|
build() {
|
|
75
75
|
const defined = this.getDefined()
|
|
76
|
-
const names =
|
|
76
|
+
const names = [...defined.keys()]
|
|
77
77
|
const facetProperties = this.compute(names, defined)
|
|
78
78
|
|
|
79
79
|
const fields = names.map((name) => {
|
|
@@ -90,7 +90,7 @@ export default class Facets {
|
|
|
90
90
|
handler = 'FacetSearchDateTime'
|
|
91
91
|
break
|
|
92
92
|
}
|
|
93
|
-
const label = defined
|
|
93
|
+
const label = defined.get(name).label
|
|
94
94
|
return [
|
|
95
95
|
`selected.${name}`,
|
|
96
96
|
{
|
|
@@ -107,12 +107,14 @@ export default class Facets {
|
|
|
107
107
|
getDefined() {
|
|
108
108
|
const defaultType = 'checkbox'
|
|
109
109
|
const allowedTypes = [defaultType, 'radio', 'number', 'date', 'datetime']
|
|
110
|
+
const defined = new Map()
|
|
111
|
+
if (!this.map.options.facetKey) return defined
|
|
110
112
|
return (this.map.options.facetKey || '').split(',').reduce((acc, curr) => {
|
|
111
113
|
let [name, label, type] = curr.split('|')
|
|
112
114
|
type = allowedTypes.includes(type) ? type : defaultType
|
|
113
|
-
acc
|
|
115
|
+
acc.set(name, { label: label || name, type: type })
|
|
114
116
|
return acc
|
|
115
|
-
},
|
|
117
|
+
}, defined)
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
getParser(type) {
|
|
@@ -127,4 +129,32 @@ export default class Facets {
|
|
|
127
129
|
return (v) => String(v || '')
|
|
128
130
|
}
|
|
129
131
|
}
|
|
132
|
+
|
|
133
|
+
dumps(parsed) {
|
|
134
|
+
const dumped = []
|
|
135
|
+
for (const [property, { label, type }] of parsed) {
|
|
136
|
+
dumped.push([property, label, type].filter(Boolean).join('|'))
|
|
137
|
+
}
|
|
138
|
+
return dumped.join(',')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
has(property) {
|
|
142
|
+
return this.getDefined().has(property)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
add(property, label, type) {
|
|
146
|
+
const defined = this.getDefined()
|
|
147
|
+
if (!defined.has(property)) {
|
|
148
|
+
defined.set(property, { label, type })
|
|
149
|
+
this.map.options.facetKey = this.dumps(defined)
|
|
150
|
+
this.map.isDirty = true
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
remove(property) {
|
|
155
|
+
const defined = this.getDefined()
|
|
156
|
+
defined.delete(property)
|
|
157
|
+
this.map.options.facetKey = this.dumps(defined)
|
|
158
|
+
this.map.isDirty = true
|
|
159
|
+
}
|
|
130
160
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */
|
|
2
|
+
import { translate } from './i18n.js'
|
|
3
|
+
|
|
4
|
+
export const EXPORT_FORMATS = {
|
|
5
|
+
geojson: {
|
|
6
|
+
formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
|
|
7
|
+
ext: '.geojson',
|
|
8
|
+
filetype: 'application/json',
|
|
9
|
+
},
|
|
10
|
+
gpx: {
|
|
11
|
+
formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
|
|
12
|
+
ext: '.gpx',
|
|
13
|
+
filetype: 'application/gpx+xml',
|
|
14
|
+
},
|
|
15
|
+
kml: {
|
|
16
|
+
formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
|
|
17
|
+
ext: '.kml',
|
|
18
|
+
filetype: 'application/vnd.google-earth.kml+xml',
|
|
19
|
+
},
|
|
20
|
+
csv: {
|
|
21
|
+
formatter: async (map) => {
|
|
22
|
+
const table = []
|
|
23
|
+
map.eachFeature((feature) => {
|
|
24
|
+
const row = feature.toGeoJSON().properties
|
|
25
|
+
const center = feature.getCenter()
|
|
26
|
+
delete row._umap_options
|
|
27
|
+
row.Latitude = center.lat
|
|
28
|
+
row.Longitude = center.lng
|
|
29
|
+
table.push(row)
|
|
30
|
+
})
|
|
31
|
+
return csv2geojson.dsv.csvFormat(table)
|
|
32
|
+
},
|
|
33
|
+
ext: '.csv',
|
|
34
|
+
filetype: 'text/csv',
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class Formatter {
|
|
39
|
+
async fromGPX(str) {
|
|
40
|
+
const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
|
|
41
|
+
return togeojson.gpx(this.toDom(str))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async fromKML(str) {
|
|
45
|
+
const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
|
|
46
|
+
return togeojson.kml(this.toDom(str), {
|
|
47
|
+
skipNullGeometry: true,
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async fromGeoJSON(str) {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(str)
|
|
54
|
+
} catch (err) {
|
|
55
|
+
U.Alert.error(`Invalid JSON file: ${err}`)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async fromOSM(str) {
|
|
60
|
+
let src
|
|
61
|
+
try {
|
|
62
|
+
src = JSON.parse(str)
|
|
63
|
+
} catch (e) {
|
|
64
|
+
src = this.toDom(str)
|
|
65
|
+
}
|
|
66
|
+
return osmtogeojson(src, { flatProperties: true })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fromCSV(str, callback) {
|
|
70
|
+
csv2geojson.csv2geojson(
|
|
71
|
+
str,
|
|
72
|
+
{
|
|
73
|
+
delimiter: 'auto',
|
|
74
|
+
includeLatLon: false,
|
|
75
|
+
},
|
|
76
|
+
(err, result) => {
|
|
77
|
+
// csv2geojson fallback to null geometries when it cannot determine
|
|
78
|
+
// lat or lon columns. This is valid geojson, but unwanted from a user
|
|
79
|
+
// point of view.
|
|
80
|
+
if (result?.features.length) {
|
|
81
|
+
if (result.features[0].geometry === null) {
|
|
82
|
+
err = {
|
|
83
|
+
type: 'Error',
|
|
84
|
+
message: translate('Cannot determine latitude and longitude columns.'),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (err) {
|
|
89
|
+
let message
|
|
90
|
+
if (err.type === 'Error') {
|
|
91
|
+
message = err.message
|
|
92
|
+
} else {
|
|
93
|
+
message = translate('{count} errors during import: {message}', {
|
|
94
|
+
count: err.length,
|
|
95
|
+
message: err[0].message,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
U.Alert.error(message, 10000)
|
|
99
|
+
console.error(err)
|
|
100
|
+
}
|
|
101
|
+
if (result?.features.length) {
|
|
102
|
+
callback(result)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async fromGeoRSS(str) {
|
|
109
|
+
return GeoRSSToGeoJSON(this.toDom(c))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
toDom(x) {
|
|
113
|
+
const doc = new DOMParser().parseFromString(x, 'text/xml')
|
|
114
|
+
const errorNode = doc.querySelector('parsererror')
|
|
115
|
+
if (errorNode) {
|
|
116
|
+
U.Alert.error(translate('Cannot parse data'))
|
|
117
|
+
}
|
|
118
|
+
return doc
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async parse(str, format) {
|
|
122
|
+
switch (format) {
|
|
123
|
+
case 'csv':
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
return this.fromCSV(str, (data) => resolve(data))
|
|
126
|
+
})
|
|
127
|
+
case 'gpx':
|
|
128
|
+
return await this.fromGPX(str)
|
|
129
|
+
case 'kml':
|
|
130
|
+
return await this.fromKML(str)
|
|
131
|
+
case 'osm':
|
|
132
|
+
return await this.fromOSM(str)
|
|
133
|
+
case 'georss':
|
|
134
|
+
return await this.fromGeoRSS(str)
|
|
135
|
+
case 'geojson':
|
|
136
|
+
return await this.fromGeoJSON(str)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async toGPX(geojson) {
|
|
141
|
+
const togpx = await import('../../vendors/geojson-to-gpx/index.js')
|
|
142
|
+
for (const feature of geojson.features) {
|
|
143
|
+
feature.properties.desc = feature.properties.description
|
|
144
|
+
}
|
|
145
|
+
const gpx = togpx.default(geojson)
|
|
146
|
+
return new XMLSerializer().serializeToString(gpx)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async toKML(geojson) {
|
|
150
|
+
const tokml = await import('../../vendors/tokml/tokml.es.js')
|
|
151
|
+
return tokml.toKML(geojson)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -3,19 +3,23 @@ import {
|
|
|
3
3
|
uMapAlertConflict as AlertConflict,
|
|
4
4
|
uMapAlertCreation as AlertCreation,
|
|
5
5
|
} from '../components/alerts/alert.js'
|
|
6
|
-
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
|
6
|
+
import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js'
|
|
7
7
|
import Browser from './browser.js'
|
|
8
8
|
import Caption from './caption.js'
|
|
9
9
|
import Facets from './facets.js'
|
|
10
|
+
import { Formatter } from './formatter.js'
|
|
10
11
|
import Help from './help.js'
|
|
11
12
|
import Importer from './importer.js'
|
|
12
13
|
import Orderable from './orderable.js'
|
|
13
14
|
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
|
14
15
|
import Rules from './rules.js'
|
|
15
16
|
import { SCHEMA } from './schema.js'
|
|
17
|
+
import Share from './share.js'
|
|
18
|
+
import Slideshow from './slideshow.js'
|
|
16
19
|
import { SyncEngine } from './sync/engine.js'
|
|
17
20
|
import Dialog from './ui/dialog.js'
|
|
18
21
|
import { EditPanel, FullPanel, Panel } from './ui/panel.js'
|
|
22
|
+
import TableEditor from './tableeditor.js'
|
|
19
23
|
import Tooltip from './ui/tooltip.js'
|
|
20
24
|
import URLs from './urls.js'
|
|
21
25
|
import * as Utils from './utils.js'
|
|
@@ -30,11 +34,13 @@ window.U = {
|
|
|
30
34
|
AlertConflict,
|
|
31
35
|
AjaxAutocomplete,
|
|
32
36
|
AjaxAutocompleteMultiple,
|
|
37
|
+
AutocompleteDatalist,
|
|
33
38
|
Browser,
|
|
34
39
|
Caption,
|
|
35
40
|
Dialog,
|
|
36
41
|
EditPanel,
|
|
37
42
|
Facets,
|
|
43
|
+
Formatter,
|
|
38
44
|
FullPanel,
|
|
39
45
|
Help,
|
|
40
46
|
HTTPError,
|
|
@@ -47,7 +53,10 @@ window.U = {
|
|
|
47
53
|
Rules,
|
|
48
54
|
SCHEMA,
|
|
49
55
|
ServerRequest,
|
|
56
|
+
Share,
|
|
57
|
+
Slideshow,
|
|
50
58
|
SyncEngine,
|
|
59
|
+
TableEditor,
|
|
51
60
|
Tooltip,
|
|
52
61
|
URLs,
|
|
53
62
|
Utils,
|
|
@@ -165,6 +165,7 @@ const ENTRIES = {
|
|
|
165
165
|
export default class Help {
|
|
166
166
|
constructor(map) {
|
|
167
167
|
this.map = map
|
|
168
|
+
this.dialog = new U.Dialog()
|
|
168
169
|
this.isMacOS = /mac/i.test(
|
|
169
170
|
// eslint-disable-next-line compat/compat -- Fallback available.
|
|
170
171
|
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
|
@@ -190,42 +191,43 @@ export default class Help {
|
|
|
190
191
|
show(entries) {
|
|
191
192
|
const container = DomUtil.add('div')
|
|
192
193
|
DomUtil.createTitle(container, translate('Help'))
|
|
193
|
-
|
|
194
|
-
if (entries === 'edit') {
|
|
194
|
+
for (const name of entries) {
|
|
195
195
|
DomUtil.element({
|
|
196
196
|
tagName: 'div',
|
|
197
197
|
className: 'umap-help-entry',
|
|
198
198
|
parent: container,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
for (const name of entries) {
|
|
202
|
-
DomUtil.element({
|
|
203
|
-
tagName: 'div',
|
|
204
|
-
className: 'umap-help-entry',
|
|
205
|
-
parent: container,
|
|
206
|
-
innerHTML: ENTRIES[name],
|
|
207
|
-
})
|
|
208
|
-
}
|
|
199
|
+
innerHTML: ENTRIES[name],
|
|
200
|
+
})
|
|
209
201
|
}
|
|
202
|
+
this.dialog.open({ template: container, className: 'dark', cancel: false, accept: false })
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Special dynamic case. Do we still think this dialog is useful?
|
|
206
|
+
showGetStarted() {
|
|
207
|
+
const container = DomUtil.add('div')
|
|
208
|
+
DomUtil.createTitle(container, translate('Where do we go from here?'))
|
|
209
|
+
DomUtil.element({
|
|
210
|
+
tagName: 'div',
|
|
211
|
+
className: 'umap-help-entry',
|
|
212
|
+
parent: container,
|
|
213
|
+
}).appendChild(this._buildEditEntry())
|
|
210
214
|
this.map.dialog.open({ content: container, className: 'dark' })
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
button(container, entries
|
|
217
|
+
button(container, entries) {
|
|
214
218
|
const button = DomUtil.createButton(
|
|
215
|
-
|
|
219
|
+
'umap-help-button',
|
|
216
220
|
container,
|
|
217
221
|
translate('Help')
|
|
218
222
|
)
|
|
219
|
-
|
|
220
|
-
DomEvent.on(button, 'click', DomEvent.stop).on(button, 'click', () =>
|
|
221
|
-
this.show(entries)
|
|
222
|
-
)
|
|
223
|
+
button.addEventListener('click', () => this.show(entries))
|
|
223
224
|
return button
|
|
224
225
|
}
|
|
225
226
|
|
|
226
|
-
|
|
227
|
-
const button =
|
|
227
|
+
getStartedLink(container) {
|
|
228
|
+
const button = DomUtil.createButton('umap-help-link', container, translate('Help'))
|
|
228
229
|
button.textContent = translate('Help')
|
|
230
|
+
button.addEventListener('click', () => this.showGetStarted())
|
|
229
231
|
return button
|
|
230
232
|
}
|
|
231
233
|
|
|
@@ -237,16 +239,14 @@ export default class Help {
|
|
|
237
239
|
|
|
238
240
|
_buildEditEntry() {
|
|
239
241
|
const container = DomUtil.create('div', '')
|
|
240
|
-
const title = DomUtil.create('h4', '', container)
|
|
241
242
|
const actionsContainer = DomUtil.create('ul', 'umap-edit-actions', container)
|
|
242
243
|
const addAction = (action) => {
|
|
243
244
|
const actionContainer = DomUtil.add('li', '', actionsContainer)
|
|
244
|
-
DomUtil.add('i', action.options.className, actionContainer)
|
|
245
|
-
|
|
245
|
+
DomUtil.add('i', action.options.className, actionContainer)
|
|
246
|
+
DomUtil.add('span', '', actionContainer, action.options.tooltip)
|
|
246
247
|
DomEvent.on(actionContainer, 'click', action.addHooks, action)
|
|
247
|
-
DomEvent.on(actionContainer, 'click', this.
|
|
248
|
+
DomEvent.on(actionContainer, 'click', this.dialog.close, this.dialog)
|
|
248
249
|
}
|
|
249
|
-
title.textContent = translate('Where do we go from here?')
|
|
250
250
|
for (const id in this.map.helpMenuActions) {
|
|
251
251
|
addAction(this.map.helpMenuActions[id])
|
|
252
252
|
}
|
|
@@ -53,7 +53,7 @@ export default class Importer {
|
|
|
53
53
|
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
|
54
54
|
this.IMPORTERS = []
|
|
55
55
|
this.loadImporters()
|
|
56
|
-
this.dialog = new Dialog(
|
|
56
|
+
this.dialog = new Dialog()
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
loadImporters() {
|
|
@@ -114,7 +114,7 @@ export default class Importer {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
get action() {
|
|
117
|
-
return this.qs('[name=action]:checked')
|
|
117
|
+
return this.qs('[name=action]:checked')?.value
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
get layerId() {
|
|
@@ -234,7 +234,7 @@ export default class Importer {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
submit() {
|
|
237
|
-
let hasErrors
|
|
237
|
+
let hasErrors
|
|
238
238
|
if (this.format === 'umap') {
|
|
239
239
|
hasErrors = !this.full()
|
|
240
240
|
} else if (!this.url) {
|
|
@@ -242,7 +242,7 @@ export default class Importer {
|
|
|
242
242
|
} else if (this.action) {
|
|
243
243
|
hasErrors = !this[this.action]()
|
|
244
244
|
}
|
|
245
|
-
if (
|
|
245
|
+
if (hasErrors === false) {
|
|
246
246
|
Alert.info(translate('Data successfully imported!'))
|
|
247
247
|
}
|
|
248
248
|
}
|
|
@@ -37,8 +37,10 @@ export class Importer {
|
|
|
37
37
|
this.autocomplete = new Autocomplete(container, options)
|
|
38
38
|
|
|
39
39
|
importer.dialog.open({
|
|
40
|
-
|
|
40
|
+
template: container,
|
|
41
41
|
className: `${this.id} importer dark`,
|
|
42
|
+
cancel: false,
|
|
43
|
+
accept: false,
|
|
42
44
|
})
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -30,13 +30,15 @@ export class Importer {
|
|
|
30
30
|
importer.format = select.options[select.selectedIndex].dataset.format
|
|
31
31
|
importer.layerName = select.options[select.selectedIndex].textContent
|
|
32
32
|
}
|
|
33
|
-
importer.dialog.close()
|
|
34
33
|
}
|
|
35
|
-
L.DomUtil.createButton('', container, translate('Choose this dataset'), confirm)
|
|
36
34
|
|
|
37
|
-
importer.dialog
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
importer.dialog
|
|
36
|
+
.open({
|
|
37
|
+
template: container,
|
|
38
|
+
className: `${this.id} importer dark`,
|
|
39
|
+
accept: translate('Choose this dataset'),
|
|
40
|
+
cancel: false,
|
|
41
|
+
})
|
|
42
|
+
.then(confirm)
|
|
41
43
|
}
|
|
42
44
|
}
|