umap-project 2.3.1__py3-none-any.whl → 2.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.
- umap/.DS_Store +0 -0
- umap/__init__.py +1 -1
- umap/locale/en/LC_MESSAGES/django.po +81 -31
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +109 -59
- umap/management/commands/run_websocket_server.py +23 -0
- umap/models.py +6 -1
- umap/settings/base.py +11 -3
- umap/static/.DS_Store +0 -0
- umap/static/umap/.DS_Store +0 -0
- umap/static/umap/base.css +53 -162
- umap/static/umap/content.css +3 -2
- umap/static/umap/css/dialog.css +18 -0
- umap/static/umap/css/icon.css +8 -0
- umap/static/umap/css/importers.css +44 -0
- umap/static/umap/css/panel.css +19 -57
- umap/static/umap/css/tooltip.css +59 -0
- umap/static/umap/css/window.css +35 -0
- umap/static/umap/favicons/.DS_Store +0 -0
- umap/static/umap/fonts/.DS_Store +0 -0
- umap/static/umap/img/.DS_Store +0 -0
- umap/static/umap/img/alert-icon-error.svg +8 -0
- umap/static/umap/img/alert-icon-info.svg +4 -0
- umap/static/umap/img/alert-icon-success.svg +3 -0
- umap/static/umap/img/icon-external-link.svg +3 -0
- umap/static/umap/img/importers/communesfr.svg +5 -0
- umap/static/umap/img/importers/datasets.svg +13 -0
- umap/static/umap/img/importers/geodatamine.svg +10 -0
- umap/static/umap/img/importers/overpass.svg +7 -0
- umap/static/umap/img/importers/random.svg +18 -0
- umap/static/umap/img/importers/random1.svg +4 -0
- umap/static/umap/img/importers/random2.svg +4 -0
- umap/static/umap/img/source/.DS_Store +0 -0
- umap/static/umap/js/components/alerts/alert.css +160 -0
- umap/static/umap/js/components/alerts/alert.js +169 -0
- umap/static/umap/js/components/base.js +54 -0
- umap/static/umap/js/modules/autocomplete.js +347 -0
- umap/static/umap/js/modules/browser.js +1 -1
- umap/static/umap/js/modules/caption.js +4 -3
- umap/static/umap/js/modules/global.js +36 -12
- umap/static/umap/js/modules/help.js +255 -0
- umap/static/umap/js/modules/importer.js +280 -0
- umap/static/umap/js/modules/importers/communesfr.js +44 -0
- umap/static/umap/js/modules/importers/datasets.js +41 -0
- umap/static/umap/js/modules/importers/geodatamine.js +95 -0
- umap/static/umap/js/modules/importers/overpass.js +84 -0
- umap/static/umap/js/modules/request.js +12 -14
- umap/static/umap/js/modules/rules.js +241 -0
- umap/static/umap/js/modules/schema.js +63 -14
- umap/static/umap/js/modules/sync/engine.js +93 -0
- umap/static/umap/js/modules/sync/updaters.js +109 -0
- umap/static/umap/js/modules/sync/websocket.js +25 -0
- umap/static/umap/js/modules/ui/dialog.js +52 -0
- umap/static/umap/js/modules/{panel.js → ui/panel.js} +25 -14
- umap/static/umap/js/modules/ui/tooltip.js +116 -0
- umap/static/umap/js/modules/utils.js +25 -18
- umap/static/umap/js/umap.controls.js +13 -14
- umap/static/umap/js/umap.core.js +1 -324
- umap/static/umap/js/umap.features.js +67 -27
- umap/static/umap/js/umap.forms.js +9 -13
- umap/static/umap/js/umap.js +220 -180
- umap/static/umap/js/umap.layer.js +142 -74
- umap/static/umap/js/umap.permissions.js +5 -9
- umap/static/umap/js/umap.tableeditor.js +8 -8
- umap/static/umap/locale/am_ET.js +51 -16
- umap/static/umap/locale/am_ET.json +51 -16
- umap/static/umap/locale/ar.js +51 -16
- umap/static/umap/locale/ar.json +51 -16
- umap/static/umap/locale/ast.js +51 -16
- umap/static/umap/locale/ast.json +51 -16
- umap/static/umap/locale/bg.js +51 -16
- umap/static/umap/locale/bg.json +51 -16
- umap/static/umap/locale/br.js +55 -20
- umap/static/umap/locale/br.json +55 -20
- umap/static/umap/locale/ca.js +51 -16
- umap/static/umap/locale/ca.json +51 -16
- umap/static/umap/locale/cs_CZ.js +93 -58
- umap/static/umap/locale/cs_CZ.json +93 -58
- umap/static/umap/locale/da.js +51 -16
- umap/static/umap/locale/da.json +51 -16
- umap/static/umap/locale/de.js +56 -21
- umap/static/umap/locale/de.json +56 -21
- umap/static/umap/locale/el.js +51 -16
- umap/static/umap/locale/el.json +51 -16
- umap/static/umap/locale/en.js +51 -16
- umap/static/umap/locale/en.json +51 -16
- umap/static/umap/locale/en_US.json +51 -16
- umap/static/umap/locale/es.js +51 -16
- umap/static/umap/locale/es.json +51 -16
- umap/static/umap/locale/et.js +51 -16
- umap/static/umap/locale/et.json +51 -16
- umap/static/umap/locale/eu.js +51 -16
- umap/static/umap/locale/eu.json +51 -16
- umap/static/umap/locale/fa_IR.js +51 -16
- umap/static/umap/locale/fa_IR.json +51 -16
- umap/static/umap/locale/fi.js +51 -16
- umap/static/umap/locale/fi.json +51 -16
- umap/static/umap/locale/fr.js +52 -17
- umap/static/umap/locale/fr.json +52 -17
- umap/static/umap/locale/gl.js +51 -16
- umap/static/umap/locale/gl.json +51 -16
- umap/static/umap/locale/he.js +51 -16
- umap/static/umap/locale/he.json +51 -16
- umap/static/umap/locale/hr.js +51 -16
- umap/static/umap/locale/hr.json +51 -16
- umap/static/umap/locale/hu.js +51 -16
- umap/static/umap/locale/hu.json +51 -16
- umap/static/umap/locale/id.js +51 -16
- umap/static/umap/locale/id.json +51 -16
- umap/static/umap/locale/is.js +51 -16
- umap/static/umap/locale/is.json +51 -16
- umap/static/umap/locale/it.js +51 -16
- umap/static/umap/locale/it.json +51 -16
- umap/static/umap/locale/ja.js +51 -16
- umap/static/umap/locale/ja.json +51 -16
- umap/static/umap/locale/ko.js +51 -16
- umap/static/umap/locale/ko.json +51 -16
- umap/static/umap/locale/lt.js +51 -16
- umap/static/umap/locale/lt.json +51 -16
- umap/static/umap/locale/ms.js +51 -16
- umap/static/umap/locale/ms.json +51 -16
- umap/static/umap/locale/nl.js +51 -16
- umap/static/umap/locale/nl.json +51 -16
- umap/static/umap/locale/no.js +51 -16
- umap/static/umap/locale/no.json +51 -16
- umap/static/umap/locale/pl.js +93 -58
- umap/static/umap/locale/pl.json +93 -58
- umap/static/umap/locale/pl_PL.json +51 -16
- umap/static/umap/locale/pt.js +215 -180
- umap/static/umap/locale/pt.json +215 -180
- umap/static/umap/locale/pt_BR.js +51 -16
- umap/static/umap/locale/pt_BR.json +51 -16
- umap/static/umap/locale/pt_PT.js +51 -16
- umap/static/umap/locale/pt_PT.json +51 -16
- umap/static/umap/locale/ro.js +51 -16
- umap/static/umap/locale/ro.json +51 -16
- umap/static/umap/locale/ru.js +51 -16
- umap/static/umap/locale/ru.json +51 -16
- umap/static/umap/locale/si.js +51 -16
- umap/static/umap/locale/si.json +51 -16
- umap/static/umap/locale/sk_SK.js +51 -16
- umap/static/umap/locale/sk_SK.json +51 -16
- umap/static/umap/locale/sl.js +51 -16
- umap/static/umap/locale/sl.json +51 -16
- umap/static/umap/locale/sr.js +51 -16
- umap/static/umap/locale/sr.json +51 -16
- umap/static/umap/locale/sv.js +51 -16
- umap/static/umap/locale/sv.json +51 -16
- umap/static/umap/locale/th_TH.js +51 -16
- umap/static/umap/locale/th_TH.json +51 -16
- umap/static/umap/locale/tr.js +51 -16
- umap/static/umap/locale/tr.json +51 -16
- umap/static/umap/locale/uk_UA.js +51 -16
- umap/static/umap/locale/uk_UA.json +51 -16
- umap/static/umap/locale/vi.js +51 -16
- umap/static/umap/locale/vi.json +51 -16
- umap/static/umap/locale/vi_VN.json +51 -16
- umap/static/umap/locale/zh.js +51 -16
- umap/static/umap/locale/zh.json +51 -16
- umap/static/umap/locale/zh_CN.json +51 -16
- umap/static/umap/locale/zh_TW.Big5.json +51 -16
- umap/static/umap/locale/zh_TW.js +51 -16
- umap/static/umap/locale/zh_TW.json +51 -16
- umap/static/umap/map.css +27 -41
- umap/static/umap/unittests/sync.js +105 -0
- umap/static/umap/unittests/utils.js +76 -34
- umap/static/umap/vars.css +18 -1
- umap/static/umap/vendors/dompurify/purify.es.js +5 -59
- umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
- umap/templates/umap/components/alerts/alert.html +89 -0
- umap/templates/umap/content.html +4 -3
- umap/templates/umap/css.html +4 -0
- umap/templates/umap/home.html +3 -0
- umap/templates/umap/js.html +0 -3
- umap/templates/umap/map_init.html +2 -8
- umap/templates/umap/messages.html +9 -11
- umap/templates/umap/search.html +3 -0
- umap/tests/.DS_Store +0 -0
- umap/tests/base.py +2 -0
- umap/tests/integration/.DS_Store +0 -0
- umap/tests/integration/conftest.py +30 -0
- umap/tests/integration/test_anonymous_owned_map.py +8 -13
- umap/tests/integration/test_browser.py +1 -1
- umap/tests/integration/test_conditional_rules.py +201 -0
- umap/tests/integration/test_dashboard.py +1 -1
- umap/tests/integration/test_datalayer.py +2 -3
- umap/tests/integration/test_edit_datalayer.py +4 -4
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_facets_browser.py +3 -3
- umap/tests/integration/test_import.py +138 -49
- umap/tests/integration/test_map.py +2 -2
- umap/tests/integration/{test_collaborative_editing.py → test_optimistic_merge.py} +7 -7
- umap/tests/integration/test_owned_map.py +1 -1
- umap/tests/integration/test_picto.py +2 -2
- umap/tests/integration/test_statics.py +1 -1
- umap/tests/integration/test_websocket_sync.py +283 -0
- umap/tests/settings.py +5 -0
- umap/tests/test_datalayer_views.py +0 -1
- umap/tests/test_views.py +53 -0
- umap/urls.py +5 -0
- umap/views.py +40 -11
- umap/websocket_server.py +92 -0
- {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/METADATA +11 -9
- {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/RECORD +207 -164
- {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/WHEEL +1 -1
- umap/static/umap/js/umap.autocomplete.js +0 -341
- umap/static/umap/js/umap.importer.js +0 -187
- umap/static/umap/js/umap.ui.js +0 -190
- {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.3.1.dist-info → umap_project-2.4.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { translate } from './i18n.js'
|
|
3
|
+
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
4
|
+
import Dialog from './ui/dialog.js'
|
|
5
|
+
import { SCHEMA } from './schema.js'
|
|
6
|
+
import * as Utils from './utils.js'
|
|
7
|
+
|
|
8
|
+
const TEMPLATE = `
|
|
9
|
+
<h3><i class="icon icon-16 icon-upload"></i><span>${translate('Import data')}</span></h3>
|
|
10
|
+
<fieldset class="formbox">
|
|
11
|
+
<legend class="counter">${translate('Choose data')}</legend>
|
|
12
|
+
<input type="file" multiple autofocus onchange />
|
|
13
|
+
<input type="url" placeholder="${translate('Provide an URL here')}" onchange />
|
|
14
|
+
<textarea onchange placeholder="${translate('Paste your data here')}"></textarea>
|
|
15
|
+
<div class="importers">
|
|
16
|
+
<h4>${translate('Import helpers:')}</h4>
|
|
17
|
+
<ul class="grid-container">
|
|
18
|
+
</ul>
|
|
19
|
+
</div>
|
|
20
|
+
</fieldset>
|
|
21
|
+
<fieldset class="formbox">
|
|
22
|
+
<legend class="counter" data-help="importFormats">${translate('Choose the format')}</legend>
|
|
23
|
+
<select name="format" onchange></select>
|
|
24
|
+
</fieldset>
|
|
25
|
+
<fieldset id="destination" class="formbox">
|
|
26
|
+
<legend class="counter">${translate('Choose the layer')}</legend>
|
|
27
|
+
<select name="layer-id" onchange></select>
|
|
28
|
+
<label id="clear">
|
|
29
|
+
<input type="checkbox" name="clear" />
|
|
30
|
+
${translate('Replace layer content')}
|
|
31
|
+
</label>
|
|
32
|
+
<input type="text" name="layer-name" placeholder="${translate('Layer name')}" />
|
|
33
|
+
</fieldset>
|
|
34
|
+
<fieldset id="import-mode" class="formbox">
|
|
35
|
+
<legend class="counter" data-help="importMode">${translate('Choose import mode')}</legend>
|
|
36
|
+
<label>
|
|
37
|
+
<input type="radio" name="action" value="copy" />
|
|
38
|
+
${translate('Copy into the layer')}
|
|
39
|
+
</label>
|
|
40
|
+
<label>
|
|
41
|
+
<input type="radio" name="action" value="link" />
|
|
42
|
+
${translate('Link to the layer as remote data')}
|
|
43
|
+
</label>
|
|
44
|
+
</fieldset>
|
|
45
|
+
<input type="button" class="button" name="submit" value="${translate('Import data')}" />
|
|
46
|
+
`
|
|
47
|
+
|
|
48
|
+
export default class Importer {
|
|
49
|
+
constructor(map) {
|
|
50
|
+
this.map = map
|
|
51
|
+
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
|
52
|
+
this.IMPORTERS = []
|
|
53
|
+
this.loadImporters()
|
|
54
|
+
this.dialog = new Dialog(this.map._controlContainer)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
loadImporters() {
|
|
58
|
+
for (const key of Object.keys(this.map.options.importers || {})) {
|
|
59
|
+
import(`./importers/${key}.js`).then((mod) => {
|
|
60
|
+
this.IMPORTERS.push(new mod.Importer(this.map, this.map.options.importers[key]))
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
qs(query) {
|
|
66
|
+
return this.container.querySelector(query)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get url() {
|
|
70
|
+
return this.qs('[type=url]').value
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
set url(value) {
|
|
74
|
+
this.qs('[type=url]').value = value
|
|
75
|
+
this.onChange()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get format() {
|
|
79
|
+
return this.qs('[name=format]').value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
set format(value) {
|
|
83
|
+
this.qs('[name=format]').value = value
|
|
84
|
+
this.onChange()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get files() {
|
|
88
|
+
return this.qs('[type=file]').files
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get raw() {
|
|
92
|
+
return this.qs('textarea').value
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get clear() {
|
|
96
|
+
return Boolean(this.qs('[name=clear]').checked)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get action() {
|
|
100
|
+
return this.qs('[name=action]:checked').value
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get layerId() {
|
|
104
|
+
return this.qs('[name=layer-id]').value
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set layerId(value) {
|
|
108
|
+
this.qs('[name=layer-id]').value = value
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get layerName() {
|
|
112
|
+
return this.qs('[name=layer-name]').value
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
set layerName(name) {
|
|
116
|
+
this.qs('[name=layer-name]').value = name
|
|
117
|
+
this.onChange()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get layer() {
|
|
121
|
+
return (
|
|
122
|
+
this.map.datalayers[this.layerId] ||
|
|
123
|
+
this.map.createDataLayer({ name: this.layerName })
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
build() {
|
|
128
|
+
this.container = DomUtil.create('div', 'umap-upload')
|
|
129
|
+
this.container.innerHTML = TEMPLATE
|
|
130
|
+
if (this.IMPORTERS.length) {
|
|
131
|
+
const parent = this.container.querySelector('.importers ul')
|
|
132
|
+
for (const plugin of this.IMPORTERS.sort((a, b) => (a.id > b.id ? 1 : -1))) {
|
|
133
|
+
L.DomUtil.createButton(
|
|
134
|
+
plugin.id,
|
|
135
|
+
DomUtil.element({tagName: 'li', parent}),
|
|
136
|
+
plugin.name,
|
|
137
|
+
() => plugin.open(this)
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
this.qs('.importers').toggleAttribute('hidden', false)
|
|
141
|
+
}
|
|
142
|
+
for (const type of this.TYPES) {
|
|
143
|
+
DomUtil.element({
|
|
144
|
+
tagName: 'option',
|
|
145
|
+
parent: this.qs('[name=format]'),
|
|
146
|
+
value: type,
|
|
147
|
+
textContent: type,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
this.map.help.parse(this.container)
|
|
151
|
+
DomEvent.on(this.qs('[name=submit]'), 'click', this.submit, this)
|
|
152
|
+
DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this)
|
|
153
|
+
for (const element of this.container.querySelectorAll('[onchange]')) {
|
|
154
|
+
DomEvent.on(element, 'change', this.onChange, this)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onChange() {
|
|
159
|
+
this.qs('#destination').toggleAttribute('hidden', this.format === 'umap')
|
|
160
|
+
this.qs('#import-mode').toggleAttribute(
|
|
161
|
+
'hidden',
|
|
162
|
+
this.format === 'umap' || !this.url
|
|
163
|
+
)
|
|
164
|
+
this.qs('[name=layer-name]').toggleAttribute('hidden', Boolean(this.layerId))
|
|
165
|
+
this.qs('#clear').toggleAttribute('hidden', !Boolean(this.layerId))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
onFileChange(e) {
|
|
169
|
+
let type = '',
|
|
170
|
+
newType
|
|
171
|
+
for (const file of e.target.files) {
|
|
172
|
+
newType = U.Utils.detectFileType(file)
|
|
173
|
+
if (!type && newType) type = newType
|
|
174
|
+
if (type && newType !== type) {
|
|
175
|
+
type = ''
|
|
176
|
+
break
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
this.format = type
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
onLoad() {
|
|
183
|
+
this.qs('[type=file]').value = null
|
|
184
|
+
this.url = null
|
|
185
|
+
this.format = undefined
|
|
186
|
+
this.layerName = null
|
|
187
|
+
const layerSelect = this.qs('[name="layer-id"]')
|
|
188
|
+
layerSelect.innerHTML = ''
|
|
189
|
+
this.map.eachDataLayerReverse((datalayer) => {
|
|
190
|
+
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
|
191
|
+
DomUtil.element({
|
|
192
|
+
tagName: 'option',
|
|
193
|
+
parent: layerSelect,
|
|
194
|
+
textContent: datalayer.options.name,
|
|
195
|
+
value: L.stamp(datalayer),
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
DomUtil.element({
|
|
200
|
+
tagName: 'option',
|
|
201
|
+
value: '',
|
|
202
|
+
textContent: translate('Import in a new layer'),
|
|
203
|
+
parent: layerSelect,
|
|
204
|
+
selected: true,
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
open() {
|
|
209
|
+
if (!this.container) this.build()
|
|
210
|
+
const onLoad = this.map.editPanel.open({ content: this.container })
|
|
211
|
+
onLoad.then(() => this.onLoad())
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
openFiles() {
|
|
215
|
+
this.open()
|
|
216
|
+
this.fileInput.showPicker()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
submit() {
|
|
220
|
+
if (this.format === 'umap') this.full()
|
|
221
|
+
else if (!this.url) this.copy()
|
|
222
|
+
else if (this.action) this[this.action]()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
full() {
|
|
226
|
+
this.map.once('postsync', this.map._setDefaultCenter)
|
|
227
|
+
try {
|
|
228
|
+
if (this.files.length) {
|
|
229
|
+
for (const file of this.files) {
|
|
230
|
+
this.map.processFileToImport(file, null, 'umap')
|
|
231
|
+
}
|
|
232
|
+
} else if (this.raw) {
|
|
233
|
+
this.map.importRaw(this.raw)
|
|
234
|
+
} else if (this.url) {
|
|
235
|
+
this.map.importFromUrl(this.url, this.format)
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
Alert.error(translate('Invalid umap data'))
|
|
239
|
+
console.error(e)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
link() {
|
|
244
|
+
if (!this.url) return
|
|
245
|
+
if (!this.format) {
|
|
246
|
+
Alert.error(translate('Please choose a format'))
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
let layer = this.layer
|
|
250
|
+
layer.options.remoteData = {
|
|
251
|
+
url: this.url,
|
|
252
|
+
format: this.format,
|
|
253
|
+
}
|
|
254
|
+
if (this.map.options.urls.ajax_proxy) {
|
|
255
|
+
layer.options.remoteData.proxy = true
|
|
256
|
+
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
|
257
|
+
}
|
|
258
|
+
layer.fetchRemoteData(true)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
copy() {
|
|
262
|
+
// Format may be guessed from file later.
|
|
263
|
+
// Usefull in case of multiple files with different formats.
|
|
264
|
+
if (!this.format && !this.files.length) {
|
|
265
|
+
Alert.error(translate('Please choose a format'))
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
let layer = this.layer
|
|
269
|
+
if (this.clear) layer.empty()
|
|
270
|
+
if (this.files.length) {
|
|
271
|
+
for (const file of this.files) {
|
|
272
|
+
this.map.processFileToImport(file, layer, this.format)
|
|
273
|
+
}
|
|
274
|
+
} else if (this.raw) {
|
|
275
|
+
layer.importRaw(this.raw, this.format)
|
|
276
|
+
} else if (this.url) {
|
|
277
|
+
layer.importFromUrl(this.url, this.format)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { BaseAjax, SingleMixin } from '../autocomplete.js'
|
|
3
|
+
|
|
4
|
+
class Autocomplete extends SingleMixin(BaseAjax) {
|
|
5
|
+
createResult(item) {
|
|
6
|
+
return super.createResult({
|
|
7
|
+
value: item.code,
|
|
8
|
+
label: `${item.nom} (${item.code})`,
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Importer {
|
|
14
|
+
constructor(map, options) {
|
|
15
|
+
this.name = options.name || 'Communes'
|
|
16
|
+
this.id = 'communesfr'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async open(importer) {
|
|
20
|
+
const container = DomUtil.create('div')
|
|
21
|
+
DomUtil.createTitle(container, this.name)
|
|
22
|
+
DomUtil.element({
|
|
23
|
+
tagName: 'p',
|
|
24
|
+
parent: container,
|
|
25
|
+
textContent: "Importer les contours d'une commune française.",
|
|
26
|
+
})
|
|
27
|
+
const options = {
|
|
28
|
+
placeholder: 'Commune…',
|
|
29
|
+
url: 'https://geo.api.gouv.fr/communes?nom={q}&limit=5',
|
|
30
|
+
on_select: (choice) => {
|
|
31
|
+
importer.url = `https://geo.api.gouv.fr/communes?code=${choice.item.value}&format=geojson&geometry=contour`
|
|
32
|
+
importer.format = 'geojson'
|
|
33
|
+
importer.layerName = choice.item.label
|
|
34
|
+
importer.dialog.close()
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
this.autocomplete = new Autocomplete(container, options)
|
|
38
|
+
|
|
39
|
+
importer.dialog.open({
|
|
40
|
+
content: container,
|
|
41
|
+
className: `${this.id} importer dark`,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { translate } from '../i18n.js'
|
|
3
|
+
|
|
4
|
+
export class Importer {
|
|
5
|
+
constructor(map, options) {
|
|
6
|
+
this.name = options.name || 'Datasets'
|
|
7
|
+
this.choices = options?.choices
|
|
8
|
+
this.id = 'datasets'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async open(importer) {
|
|
12
|
+
const container = DomUtil.create('div', 'formbox')
|
|
13
|
+
DomUtil.element({ tagName: 'h3', textContent: this.name, parent: container })
|
|
14
|
+
const select = DomUtil.create('select', '', container)
|
|
15
|
+
const noPreset = DomUtil.element({
|
|
16
|
+
tagName: 'option',
|
|
17
|
+
parent: select,
|
|
18
|
+
value: '',
|
|
19
|
+
textContent: translate('Choose a dataset'),
|
|
20
|
+
})
|
|
21
|
+
for (const dataset of this.choices) {
|
|
22
|
+
const option = DomUtil.create('option', '', select)
|
|
23
|
+
option.value = dataset.url
|
|
24
|
+
option.textContent = dataset.label
|
|
25
|
+
option.dataset.format = dataset.format || 'geojson'
|
|
26
|
+
}
|
|
27
|
+
const confirm = () => {
|
|
28
|
+
if (select.value) {
|
|
29
|
+
importer.url = select.value
|
|
30
|
+
importer.format = select.options[select.selectedIndex].dataset.format
|
|
31
|
+
}
|
|
32
|
+
importer.dialog.close()
|
|
33
|
+
}
|
|
34
|
+
L.DomUtil.createButton('', container, translate('Choose this dataset'), confirm)
|
|
35
|
+
|
|
36
|
+
importer.dialog.open({
|
|
37
|
+
content: container,
|
|
38
|
+
className: `${this.id} importer dark`,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { BaseAjax, SingleMixin } from '../autocomplete.js'
|
|
3
|
+
import { translate } from '../i18n.js'
|
|
4
|
+
import * as Utils from '../utils.js'
|
|
5
|
+
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
6
|
+
|
|
7
|
+
const BOUNDARY_TYPES = {
|
|
8
|
+
admin_6: 'département',
|
|
9
|
+
admin_7: 'pays (loi Voynet)',
|
|
10
|
+
admin_8: 'commune',
|
|
11
|
+
admin_9: 'quartier, hameau, arrondissement',
|
|
12
|
+
political: 'canton',
|
|
13
|
+
local_authority: 'EPCI',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TEMPLATE = `
|
|
17
|
+
<h3>GeoDataMine</h3>
|
|
18
|
+
<p>${translate('GeoDataMine: thematic data from OpenStreetMap')}.</p>
|
|
19
|
+
<select name="theme">
|
|
20
|
+
<option value="">${translate('Choose a theme')}</option>
|
|
21
|
+
</select>
|
|
22
|
+
<label>
|
|
23
|
+
<input type="checkbox" name="aspoint" />
|
|
24
|
+
${translate('Symplify all geometries to points')}
|
|
25
|
+
</label>
|
|
26
|
+
<label id="boundary">
|
|
27
|
+
</label>
|
|
28
|
+
<button class="button">${translate('Choose this data')}</button>
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
class Autocomplete extends SingleMixin(BaseAjax) {
|
|
32
|
+
createResult(item) {
|
|
33
|
+
return super.createResult({
|
|
34
|
+
value: item.id,
|
|
35
|
+
label: `${item.name} (${BOUNDARY_TYPES[item.type]} — ${item.ref})`,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class Importer {
|
|
41
|
+
constructor(map, options = {}) {
|
|
42
|
+
this.map = map
|
|
43
|
+
this.name = options.name || 'GeoDataMine'
|
|
44
|
+
this.baseUrl = options?.url || 'https://geodatamine.fr'
|
|
45
|
+
this.id = 'geodatamine'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async open(importer) {
|
|
49
|
+
let boundary = null
|
|
50
|
+
let boundaryName = null
|
|
51
|
+
const container = DomUtil.create('div')
|
|
52
|
+
container.innerHTML = TEMPLATE
|
|
53
|
+
const response = await importer.map.request.get(`${this.baseUrl}/themes`)
|
|
54
|
+
const select = container.querySelector('select')
|
|
55
|
+
if (response && response.ok) {
|
|
56
|
+
const { themes } = await response.json()
|
|
57
|
+
themes.sort((a, b) => Utils.naturalSort(a['name:fr'], b ['name:fr']))
|
|
58
|
+
for (const theme of themes) {
|
|
59
|
+
DomUtil.element({
|
|
60
|
+
tagName: 'option',
|
|
61
|
+
value: theme.id,
|
|
62
|
+
textContent: theme['name:fr'],
|
|
63
|
+
parent: select,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
console.error(response)
|
|
68
|
+
}
|
|
69
|
+
const asPoint = container.querySelector('[name=aspoint]')
|
|
70
|
+
this.autocomplete = new Autocomplete(container.querySelector('#boundary'), {
|
|
71
|
+
placeholder: translate('Search admin boundary'),
|
|
72
|
+
url: `${this.baseUrl}/boundaries/search?text={q}`,
|
|
73
|
+
on_select: (choice) => {
|
|
74
|
+
boundary = choice.item.value
|
|
75
|
+
boundaryName = choice.item.label
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
const confirm = () => {
|
|
79
|
+
if (!boundary || !select.value) {
|
|
80
|
+
Alert.error(translate('Please choose a theme and a boundary first.'))
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
importer.url = `${this.baseUrl}/data/${select.value}/${boundary}?format=geojson&aspoint=${asPoint.checked}`
|
|
84
|
+
importer.format = 'geojson'
|
|
85
|
+
importer.layerName = `${boundaryName} — ${select.options[select.selectedIndex].textContent}`
|
|
86
|
+
importer.dialog.close()
|
|
87
|
+
}
|
|
88
|
+
DomEvent.on(container.querySelector('button'), 'click', confirm)
|
|
89
|
+
|
|
90
|
+
importer.dialog.open({
|
|
91
|
+
content: container,
|
|
92
|
+
className: `${this.id} importer dark`,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { BaseAjax, SingleMixin } from '../autocomplete.js'
|
|
3
|
+
import { translate } from '../i18n.js'
|
|
4
|
+
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
5
|
+
|
|
6
|
+
const TEMPLATE = `
|
|
7
|
+
<h3>Overpass</h3>
|
|
8
|
+
<label>
|
|
9
|
+
<span data-help="overpassImporter">${translate('Expression')}</span>
|
|
10
|
+
<input type="text" placeholder="amenity=drinking_water" name="tags" />
|
|
11
|
+
</label>
|
|
12
|
+
<label>
|
|
13
|
+
${translate('Geometry mode')}
|
|
14
|
+
<select name="out-mode">
|
|
15
|
+
<option value="geom" selected>${translate('Default')}</option>
|
|
16
|
+
<option value="center">${translate('Only geometry centers')}</option>
|
|
17
|
+
</select>
|
|
18
|
+
</label>
|
|
19
|
+
<label id="area"><span>${translate('Search area')}</span></label>
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
class Autocomplete extends SingleMixin(BaseAjax) {
|
|
23
|
+
handleResults(data) {
|
|
24
|
+
return super.handleResults(data.features)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
createResult(item) {
|
|
28
|
+
return super.createResult({
|
|
29
|
+
// Overpass convention to get their id from an osm one.
|
|
30
|
+
value: item.properties.osm_id + 3600000000,
|
|
31
|
+
label: `${item.properties.name}`,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class Importer {
|
|
37
|
+
constructor(map, options) {
|
|
38
|
+
this.map = map
|
|
39
|
+
this.name = options.name || 'Overpass'
|
|
40
|
+
this.baseUrl = options?.url || 'https://overpass-api.de/api/interpreter'
|
|
41
|
+
this.id = 'overpass'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async open(importer) {
|
|
45
|
+
let boundary = null
|
|
46
|
+
let boundaryName = null
|
|
47
|
+
const container = DomUtil.create('div')
|
|
48
|
+
container.innerHTML = TEMPLATE
|
|
49
|
+
this.autocomplete = new Autocomplete(container.querySelector('#area'), {
|
|
50
|
+
url: 'https://photon.komoot.io/api?q={q}&osm_tag=place',
|
|
51
|
+
placeholder: translate(
|
|
52
|
+
'Type area name, or let empty to load data in current map view'
|
|
53
|
+
),
|
|
54
|
+
on_select: (choice) => {
|
|
55
|
+
boundary = choice.item.value
|
|
56
|
+
boundaryName = choice.item.label
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
this.map.help.parse(container)
|
|
60
|
+
|
|
61
|
+
const confirm = () => {
|
|
62
|
+
let tags = container.querySelector('[name=tags]').value
|
|
63
|
+
if (!tags) {
|
|
64
|
+
Alert.error(translate('Please define an expression for the query first'))
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
const outMode = container.querySelector('[name=out-mode]').value
|
|
68
|
+
if (!tags.startsWith('[')) tags = `[${tags}]`
|
|
69
|
+
let area = '{south},{west},{north},{east}'
|
|
70
|
+
if (boundary) area = `area:${boundary}`
|
|
71
|
+
let query = `[out:json];nwr${tags}(${area});out ${outMode};`
|
|
72
|
+
importer.url = `${this.baseUrl}?data=${query}`
|
|
73
|
+
if (boundary) importer.layerName = boundaryName
|
|
74
|
+
importer.format = 'osm'
|
|
75
|
+
importer.dialog.close()
|
|
76
|
+
}
|
|
77
|
+
L.DomUtil.createButton('', container, translate('Choose this data'), confirm)
|
|
78
|
+
|
|
79
|
+
importer.dialog.open({
|
|
80
|
+
content: container,
|
|
81
|
+
className: `${this.id} importer dark`,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
import { translate } from './i18n.js'
|
|
2
|
+
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
3
3
|
|
|
4
4
|
export class RequestError extends Error {}
|
|
5
5
|
|
|
@@ -47,14 +47,13 @@ class BaseRequest {
|
|
|
47
47
|
// In case of error, an alert is sent, but non 20X status are not handled
|
|
48
48
|
// The consumer must check the response status by hand
|
|
49
49
|
export class Request extends BaseRequest {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.ui = ui
|
|
50
|
+
fire(name, params) {
|
|
51
|
+
document.body.dispatchEvent(new CustomEvent(name, params))
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
async _fetch(method, uri, headers, data) {
|
|
56
55
|
const id = Math.random()
|
|
57
|
-
this.
|
|
56
|
+
this.fire('dataloading', { id: id })
|
|
58
57
|
try {
|
|
59
58
|
const response = await BaseRequest.prototype._fetch.call(
|
|
60
59
|
this,
|
|
@@ -68,7 +67,7 @@ export class Request extends BaseRequest {
|
|
|
68
67
|
if (error instanceof NOKError) return this._onNOK(error)
|
|
69
68
|
return this._onError(error)
|
|
70
69
|
} finally {
|
|
71
|
-
this.
|
|
70
|
+
this.fire('dataload', { id: id })
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -81,7 +80,7 @@ export class Request extends BaseRequest {
|
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
_onError(error) {
|
|
84
|
-
|
|
83
|
+
Alert.error(translate('Problem in the response'))
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
_onNOK(error) {
|
|
@@ -127,9 +126,9 @@ export class ServerRequest extends Request {
|
|
|
127
126
|
try {
|
|
128
127
|
const data = await response.json()
|
|
129
128
|
if (data.info) {
|
|
130
|
-
|
|
129
|
+
Alert.info(data.info)
|
|
131
130
|
} else if (data.error) {
|
|
132
|
-
|
|
131
|
+
Alert.error(data.error)
|
|
133
132
|
return this._onError(new Error(data.error))
|
|
134
133
|
}
|
|
135
134
|
return [data, response, null]
|
|
@@ -144,10 +143,9 @@ export class ServerRequest extends Request {
|
|
|
144
143
|
|
|
145
144
|
_onNOK(error) {
|
|
146
145
|
if (error.status === 403) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
})
|
|
146
|
+
Alert.error(error.message || translate('Action not allowed :('))
|
|
147
|
+
} else {
|
|
148
|
+
super._onError(error)
|
|
151
149
|
}
|
|
152
150
|
return [{}, error.response, error]
|
|
153
151
|
}
|