umap-project 3.2.0__py3-none-any.whl → 3.3.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.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +42 -38
- umap/locale/en/LC_MESSAGES/django.mo +0 -0
- umap/locale/en/LC_MESSAGES/django.po +15 -15
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +39 -35
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +31 -27
- umap/settings/base.py +2 -0
- umap/static/umap/css/contextmenu.css +58 -2
- umap/static/umap/css/form.css +175 -45
- umap/static/umap/css/icon.css +20 -0
- umap/static/umap/img/16-white.svg +21 -40
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24-white.svg +9 -9
- umap/static/umap/img/24.svg +23 -10
- umap/static/umap/img/source/16-white.svg +23 -41
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24-white.svg +11 -11
- umap/static/umap/img/source/24.svg +25 -12
- umap/static/umap/js/modules/caption.js +8 -0
- umap/static/umap/js/modules/data/features.js +318 -174
- umap/static/umap/js/modules/data/layer.js +27 -20
- umap/static/umap/js/modules/form/builder.js +11 -7
- umap/static/umap/js/modules/form/fields.js +10 -7
- umap/static/umap/js/modules/formatter.js +42 -20
- umap/static/umap/js/modules/importer.js +6 -1
- umap/static/umap/js/modules/importers/opendata.js +125 -37
- umap/static/umap/js/modules/importers/openrouteservice.js +140 -0
- umap/static/umap/js/modules/managers.js +12 -4
- umap/static/umap/js/modules/printer.js +107 -0
- umap/static/umap/js/modules/rendering/controls.js +78 -2
- umap/static/umap/js/modules/rendering/icon.js +113 -82
- umap/static/umap/js/modules/rendering/layers/cluster.js +220 -64
- umap/static/umap/js/modules/rendering/map.js +5 -1
- umap/static/umap/js/modules/rendering/template.js +71 -1
- umap/static/umap/js/modules/rendering/ui.js +101 -34
- umap/static/umap/js/modules/schema.js +24 -0
- umap/static/umap/js/modules/share.js +19 -12
- umap/static/umap/js/modules/ui/bar.js +6 -1
- umap/static/umap/js/modules/ui/base.js +24 -9
- umap/static/umap/js/modules/ui/contextmenu.js +17 -7
- umap/static/umap/js/modules/ui/dialog.js +7 -4
- umap/static/umap/js/modules/umap.js +68 -62
- umap/static/umap/js/umap.controls.js +22 -57
- umap/static/umap/locale/am_ET.js +39 -4
- umap/static/umap/locale/am_ET.json +39 -4
- umap/static/umap/locale/ar.js +39 -4
- umap/static/umap/locale/ar.json +39 -4
- umap/static/umap/locale/ast.js +39 -4
- umap/static/umap/locale/ast.json +39 -4
- umap/static/umap/locale/bg.js +39 -4
- umap/static/umap/locale/bg.json +39 -4
- umap/static/umap/locale/br.js +39 -4
- umap/static/umap/locale/br.json +39 -4
- umap/static/umap/locale/ca.js +39 -4
- umap/static/umap/locale/ca.json +39 -4
- umap/static/umap/locale/cs_CZ.js +39 -4
- umap/static/umap/locale/cs_CZ.json +39 -4
- umap/static/umap/locale/da.js +47 -12
- umap/static/umap/locale/da.json +47 -12
- umap/static/umap/locale/de.js +39 -4
- umap/static/umap/locale/de.json +39 -4
- umap/static/umap/locale/el.js +81 -46
- umap/static/umap/locale/el.json +81 -46
- umap/static/umap/locale/en.js +38 -4
- umap/static/umap/locale/en.json +38 -4
- umap/static/umap/locale/en_US.json +39 -4
- umap/static/umap/locale/es.js +47 -12
- umap/static/umap/locale/es.json +47 -12
- umap/static/umap/locale/et.js +39 -4
- umap/static/umap/locale/et.json +39 -4
- umap/static/umap/locale/eu.js +80 -45
- umap/static/umap/locale/eu.json +80 -45
- umap/static/umap/locale/fa_IR.js +39 -4
- umap/static/umap/locale/fa_IR.json +39 -4
- umap/static/umap/locale/fi.js +39 -4
- umap/static/umap/locale/fi.json +39 -4
- umap/static/umap/locale/fr.js +39 -4
- umap/static/umap/locale/fr.json +39 -4
- umap/static/umap/locale/gl.js +39 -4
- umap/static/umap/locale/gl.json +39 -4
- umap/static/umap/locale/he.js +39 -4
- umap/static/umap/locale/he.json +39 -4
- umap/static/umap/locale/hr.js +39 -4
- umap/static/umap/locale/hr.json +39 -4
- umap/static/umap/locale/hu.js +55 -20
- umap/static/umap/locale/hu.json +55 -20
- umap/static/umap/locale/id.js +39 -4
- umap/static/umap/locale/id.json +39 -4
- umap/static/umap/locale/is.js +39 -4
- umap/static/umap/locale/is.json +39 -4
- umap/static/umap/locale/it.js +39 -4
- umap/static/umap/locale/it.json +39 -4
- umap/static/umap/locale/ja.js +39 -4
- umap/static/umap/locale/ja.json +39 -4
- umap/static/umap/locale/ko.js +39 -4
- umap/static/umap/locale/ko.json +39 -4
- umap/static/umap/locale/lt.js +39 -4
- umap/static/umap/locale/lt.json +39 -4
- umap/static/umap/locale/ms.js +52 -17
- umap/static/umap/locale/ms.json +52 -17
- umap/static/umap/locale/nl.js +58 -23
- umap/static/umap/locale/nl.json +58 -23
- umap/static/umap/locale/no.js +39 -4
- umap/static/umap/locale/no.json +39 -4
- umap/static/umap/locale/pl.js +39 -4
- umap/static/umap/locale/pl.json +39 -4
- umap/static/umap/locale/pl_PL.json +39 -4
- umap/static/umap/locale/pt.js +39 -4
- umap/static/umap/locale/pt.json +39 -4
- umap/static/umap/locale/pt_BR.js +39 -4
- umap/static/umap/locale/pt_BR.json +39 -4
- umap/static/umap/locale/pt_PT.js +39 -4
- umap/static/umap/locale/pt_PT.json +39 -4
- umap/static/umap/locale/ro.js +39 -4
- umap/static/umap/locale/ro.json +39 -4
- umap/static/umap/locale/ru.js +39 -4
- umap/static/umap/locale/ru.json +39 -4
- umap/static/umap/locale/sk_SK.js +39 -4
- umap/static/umap/locale/sk_SK.json +39 -4
- umap/static/umap/locale/sl.js +39 -4
- umap/static/umap/locale/sl.json +39 -4
- umap/static/umap/locale/sr.js +39 -4
- umap/static/umap/locale/sr.json +39 -4
- umap/static/umap/locale/sv.js +39 -4
- umap/static/umap/locale/sv.json +39 -4
- umap/static/umap/locale/th_TH.js +39 -4
- umap/static/umap/locale/th_TH.json +39 -4
- umap/static/umap/locale/tr.js +39 -4
- umap/static/umap/locale/tr.json +39 -4
- umap/static/umap/locale/uk_UA.js +39 -4
- umap/static/umap/locale/uk_UA.json +39 -4
- umap/static/umap/locale/vi.js +39 -4
- umap/static/umap/locale/vi.json +39 -4
- umap/static/umap/locale/vi_VN.json +39 -4
- umap/static/umap/locale/zh.js +39 -4
- umap/static/umap/locale/zh.json +39 -4
- umap/static/umap/locale/zh_CN.json +39 -4
- umap/static/umap/locale/zh_TW.Big5.json +39 -4
- umap/static/umap/locale/zh_TW.js +98 -63
- umap/static/umap/locale/zh_TW.json +98 -63
- umap/static/umap/map.css +90 -41
- umap/static/umap/vars.css +1 -0
- umap/static/umap/vendors/editable/Leaflet.Editable.js +3 -1
- umap/static/umap/vendors/openrouteservice/ors-js-client.js +521 -0
- umap/static/umap/vendors/openrouteservice/ors-js-client.js.map +1 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.js +63 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.svg +8 -0
- umap/static/umap/vendors/snapdom/snapdom.min.mjs +3 -0
- umap/storage/staticfiles.py +12 -0
- umap/templates/umap/css.html +0 -4
- umap/templates/umap/js.html +1 -3
- umap/tests/integration/test_basics.py +2 -0
- umap/tests/integration/test_conditional_rules.py +17 -17
- umap/tests/integration/test_datalayer.py +1 -1
- umap/tests/integration/test_draw_polygon.py +3 -5
- umap/tests/integration/test_draw_polyline.py +4 -6
- umap/tests/integration/test_draw_route.py +178 -0
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_edit_marker.py +7 -7
- umap/tests/integration/test_edit_polygon.py +2 -2
- umap/tests/integration/test_export_map.py +74 -10
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_share.py +1 -1
- umap/tests/integration/test_tableeditor.py +4 -4
- umap/tests/integration/test_websocket_sync.py +4 -4
- umap/utils.py +5 -1
- umap/views.py +2 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/METADATA +9 -9
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/RECORD +175 -171
- umap/static/umap/vendors/markercluster/MarkerCluster.Default.css +0 -60
- umap/static/umap/vendors/markercluster/MarkerCluster.css +0 -14
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js +0 -2
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js.map +0 -1
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/WHEEL +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/entry_points.txt +0 -0
- {umap_project-3.2.0.dist-info → umap_project-3.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -298,7 +298,7 @@ export class DataLayer {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
dataChanged() {
|
|
301
|
-
if (!this.isLoaded()) return
|
|
301
|
+
if (!this.isLoaded() || this._batch) return
|
|
302
302
|
this._umap.onDataLayersChanged()
|
|
303
303
|
this.layer.dataChanged()
|
|
304
304
|
}
|
|
@@ -318,7 +318,11 @@ export class DataLayer {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
async fromUmapGeoJSON(geojson) {
|
|
321
|
-
if (geojson._storage)
|
|
321
|
+
if (geojson._storage) {
|
|
322
|
+
// Retrocompat
|
|
323
|
+
geojson._umap_options = geojson._storage
|
|
324
|
+
delete geojson._storage
|
|
325
|
+
}
|
|
322
326
|
if (geojson._umap_options) this.setProperties(geojson._umap_options)
|
|
323
327
|
if (this.isRemoteLayer()) {
|
|
324
328
|
await this.fetchRemoteData()
|
|
@@ -547,22 +551,27 @@ export class DataLayer {
|
|
|
547
551
|
}
|
|
548
552
|
|
|
549
553
|
sortedValues(property) {
|
|
550
|
-
return
|
|
554
|
+
return this.features
|
|
555
|
+
.all()
|
|
551
556
|
.map((feature) => feature.properties[property])
|
|
552
557
|
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
553
558
|
.sort(Utils.naturalSort)
|
|
554
559
|
}
|
|
555
560
|
|
|
556
561
|
addData(geojson, sync) {
|
|
562
|
+
let data = []
|
|
563
|
+
this._batch = true
|
|
557
564
|
try {
|
|
558
565
|
// Do not fail if remote data is somehow invalid,
|
|
559
566
|
// otherwise the layer becomes uneditable.
|
|
560
|
-
|
|
567
|
+
data = this.makeFeatures(geojson, sync)
|
|
561
568
|
} catch (err) {
|
|
562
569
|
console.debug('Error with DataLayer', this.id)
|
|
563
570
|
console.error(err)
|
|
564
|
-
return []
|
|
565
571
|
}
|
|
572
|
+
this._batch = false
|
|
573
|
+
this.dataChanged()
|
|
574
|
+
return data
|
|
566
575
|
}
|
|
567
576
|
|
|
568
577
|
makeFeatures(geojson = {}, sync = true) {
|
|
@@ -821,6 +830,7 @@ export class DataLayer {
|
|
|
821
830
|
const fields = [
|
|
822
831
|
'properties.color',
|
|
823
832
|
'properties.iconClass',
|
|
833
|
+
'properties.iconSize',
|
|
824
834
|
'properties.iconUrl',
|
|
825
835
|
'properties.iconOpacity',
|
|
826
836
|
'properties.opacity',
|
|
@@ -1158,7 +1168,11 @@ export class DataLayer {
|
|
|
1158
1168
|
this.getVersionUrl(version)
|
|
1159
1169
|
)
|
|
1160
1170
|
if (!error) {
|
|
1161
|
-
if (geojson._storage)
|
|
1171
|
+
if (geojson._storage) {
|
|
1172
|
+
// Retrocompat.
|
|
1173
|
+
geojson._umap_options = geojson._storage
|
|
1174
|
+
delete geojson._storage
|
|
1175
|
+
}
|
|
1162
1176
|
if (geojson._umap_options) {
|
|
1163
1177
|
const oldProperties = Utils.CopyJSON(this.properties)
|
|
1164
1178
|
this.setProperties(geojson._umap_options)
|
|
@@ -1176,12 +1190,6 @@ export class DataLayer {
|
|
|
1176
1190
|
})
|
|
1177
1191
|
}
|
|
1178
1192
|
|
|
1179
|
-
featuresToGeoJSON() {
|
|
1180
|
-
const features = []
|
|
1181
|
-
this.features.forEach((feature) => features.push(feature.toGeoJSON()))
|
|
1182
|
-
return features
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
1193
|
async show() {
|
|
1186
1194
|
this._leafletMap.addLayer(this.layer)
|
|
1187
1195
|
if (!this.isLoaded()) await this.fetchData()
|
|
@@ -1194,10 +1202,11 @@ export class DataLayer {
|
|
|
1194
1202
|
}
|
|
1195
1203
|
|
|
1196
1204
|
toggle(force) {
|
|
1197
|
-
// From now on, do not try to how/
|
|
1198
|
-
// automatically this layer
|
|
1199
|
-
|
|
1205
|
+
// From now on, do not try to how/hide
|
|
1206
|
+
// automatically this layer, as user
|
|
1207
|
+
// has taken control on this.
|
|
1200
1208
|
this._forcedVisibility = true
|
|
1209
|
+
let display = force
|
|
1201
1210
|
if (force === undefined) {
|
|
1202
1211
|
if (!this.isVisible()) display = true
|
|
1203
1212
|
else display = false
|
|
@@ -1267,11 +1276,9 @@ export class DataLayer {
|
|
|
1267
1276
|
}
|
|
1268
1277
|
|
|
1269
1278
|
umapGeoJSON() {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
_umap_options: this.properties,
|
|
1274
|
-
}
|
|
1279
|
+
const geojson = this._umap.formatter.toFeatureCollection(this.features.all())
|
|
1280
|
+
geojson._umap_options = this.properties
|
|
1281
|
+
return geojson
|
|
1275
1282
|
}
|
|
1276
1283
|
|
|
1277
1284
|
getDOMOrder() {
|
|
@@ -70,7 +70,11 @@ export class Form extends Utils.WithEvents {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
setter(field, value) {
|
|
73
|
-
|
|
73
|
+
if ('setter' in this.obj) {
|
|
74
|
+
this.obj.setter(field, value)
|
|
75
|
+
} else {
|
|
76
|
+
Utils.setObjectValue(this.obj, field, value)
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
restoreField(field) {
|
|
@@ -104,9 +108,13 @@ export class Form extends Utils.WithEvents {
|
|
|
104
108
|
finish() {}
|
|
105
109
|
|
|
106
110
|
getTemplate(helper) {
|
|
111
|
+
let tpl = helper.getTemplate()
|
|
112
|
+
if (helper.properties.label && !tpl.includes(helper.properties.label)) {
|
|
113
|
+
tpl = `<label>${helper.properties.label}${tpl}</label>`
|
|
114
|
+
}
|
|
107
115
|
return `
|
|
108
116
|
<div class="formbox" data-ref=container>
|
|
109
|
-
${
|
|
117
|
+
${tpl}
|
|
110
118
|
<small class="help-text" data-ref=helpText></small>
|
|
111
119
|
</div>`
|
|
112
120
|
}
|
|
@@ -171,11 +179,7 @@ export class MutatingForm extends Form {
|
|
|
171
179
|
|
|
172
180
|
setter(field, value) {
|
|
173
181
|
const oldValue = this.getter(field)
|
|
174
|
-
|
|
175
|
-
this.obj.setter(field, value)
|
|
176
|
-
} else {
|
|
177
|
-
super.setter(field, value)
|
|
178
|
-
}
|
|
182
|
+
super.setter(field, value)
|
|
179
183
|
if ('render' in this.obj) {
|
|
180
184
|
this.obj.render([field], this)
|
|
181
185
|
}
|
|
@@ -582,6 +582,9 @@ Fields.SlideshowDelay = class extends Fields.IntSelect {
|
|
|
582
582
|
Fields.DataLayerSwitcher = class extends Fields.Select {
|
|
583
583
|
getOptions() {
|
|
584
584
|
const options = []
|
|
585
|
+
if (this.properties.allowEmpty) {
|
|
586
|
+
options.push([null, translate('Import in a new layer')])
|
|
587
|
+
}
|
|
585
588
|
this.builder._umap.datalayers.reverse().map((datalayer) => {
|
|
586
589
|
if (
|
|
587
590
|
datalayer.isLoaded() &&
|
|
@@ -595,7 +598,7 @@ Fields.DataLayerSwitcher = class extends Fields.Select {
|
|
|
595
598
|
}
|
|
596
599
|
|
|
597
600
|
toHTML() {
|
|
598
|
-
return this.obj.datalayer
|
|
601
|
+
return this.obj.datalayer?.id
|
|
599
602
|
}
|
|
600
603
|
|
|
601
604
|
toJS() {
|
|
@@ -604,7 +607,7 @@ Fields.DataLayerSwitcher = class extends Fields.Select {
|
|
|
604
607
|
|
|
605
608
|
set() {
|
|
606
609
|
this.builder._umap.lastUsedDataLayer = this.toJS()
|
|
607
|
-
this.
|
|
610
|
+
this.builder.setter(this.field, this.toJS())
|
|
608
611
|
}
|
|
609
612
|
}
|
|
610
613
|
|
|
@@ -863,6 +866,7 @@ Fields.IconUrl = class extends Fields.BlurInput {
|
|
|
863
866
|
const loadCollection = (name) => {
|
|
864
867
|
icons.innerHTML = ''
|
|
865
868
|
const collection = this.pictogramCollections[name || collectionsNames[0]]
|
|
869
|
+
if (!collection) return
|
|
866
870
|
const sorted = Object.entries(collection.categories).sort(([a], [b]) =>
|
|
867
871
|
Utils.naturalSort(a, b, U.lang)
|
|
868
872
|
)
|
|
@@ -1303,11 +1307,10 @@ Fields.Range = class extends Fields.FloatInput {
|
|
|
1303
1307
|
const step = this.properties.step || 1
|
|
1304
1308
|
const digits = step < 1 ? 1 : 0
|
|
1305
1309
|
const id = `range-${this.properties.label || this.name}`
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
) {
|
|
1310
|
+
const range = this.properties.max - this.properties.min
|
|
1311
|
+
const ticks = this.properties.ticks || Math.min(20, range / step)
|
|
1312
|
+
const tickStep = range / ticks
|
|
1313
|
+
for (let i = this.properties.min; i <= this.properties.max; i += tickStep) {
|
|
1311
1314
|
const ii = i.toFixed(digits)
|
|
1312
1315
|
options += `<option value="${ii}" label="${ii}"></option>`
|
|
1313
1316
|
}
|
|
@@ -17,33 +17,18 @@ const parseTextGeom = async (geom) => {
|
|
|
17
17
|
|
|
18
18
|
export const EXPORT_FORMATS = {
|
|
19
19
|
geojson: {
|
|
20
|
-
formatter: async (umap) => JSON.stringify(umap.toGeoJSON(), null, 2),
|
|
21
20
|
ext: '.geojson',
|
|
22
21
|
filetype: 'application/json',
|
|
23
22
|
},
|
|
24
23
|
gpx: {
|
|
25
|
-
formatter: async (umap) => await umap.formatter.toGPX(umap.toGeoJSON()),
|
|
26
24
|
ext: '.gpx',
|
|
27
25
|
filetype: 'application/gpx+xml',
|
|
28
26
|
},
|
|
29
27
|
kml: {
|
|
30
|
-
formatter: async (umap) => await umap.formatter.toKML(umap.toGeoJSON()),
|
|
31
28
|
ext: '.kml',
|
|
32
29
|
filetype: 'application/vnd.google-earth.kml+xml',
|
|
33
30
|
},
|
|
34
31
|
csv: {
|
|
35
|
-
formatter: async (umap) => {
|
|
36
|
-
const table = []
|
|
37
|
-
umap.eachFeature((feature) => {
|
|
38
|
-
const row = feature.toGeoJSON().properties
|
|
39
|
-
const center = feature.center
|
|
40
|
-
delete row._umap_options
|
|
41
|
-
row.Latitude = center.lat
|
|
42
|
-
row.Longitude = center.lng
|
|
43
|
-
table.push(row)
|
|
44
|
-
})
|
|
45
|
-
return csv2geojson.dsv.csvFormat(table)
|
|
46
|
-
},
|
|
47
32
|
ext: '.csv',
|
|
48
33
|
filetype: 'text/csv',
|
|
49
34
|
},
|
|
@@ -180,17 +165,54 @@ export class Formatter {
|
|
|
180
165
|
}
|
|
181
166
|
}
|
|
182
167
|
|
|
183
|
-
async
|
|
168
|
+
async stringify(features, format) {
|
|
169
|
+
switch (format) {
|
|
170
|
+
case 'csv':
|
|
171
|
+
return await this.toCSV(features)
|
|
172
|
+
case 'gpx':
|
|
173
|
+
return await this.toGPX(features)
|
|
174
|
+
case 'kml':
|
|
175
|
+
return await this.toKML(features)
|
|
176
|
+
case 'geojson':
|
|
177
|
+
return await this.toGeoJSON(features)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async toGPX(features) {
|
|
184
182
|
const togpx = await import('../../vendors/geojson-to-gpx/index.js')
|
|
185
|
-
for (const feature of
|
|
183
|
+
for (const feature of features) {
|
|
186
184
|
feature.properties.desc = feature.properties.description
|
|
187
185
|
}
|
|
188
|
-
const gpx = togpx.default(
|
|
186
|
+
const gpx = togpx.default(this.toFeatureCollection(features))
|
|
189
187
|
return new XMLSerializer().serializeToString(gpx)
|
|
190
188
|
}
|
|
191
189
|
|
|
192
|
-
async toKML(
|
|
190
|
+
async toKML(features) {
|
|
193
191
|
const tokml = await import('../../vendors/tokml/tokml.es.js')
|
|
194
|
-
return tokml.toKML(
|
|
192
|
+
return tokml.toKML(this.toFeatureCollection(features))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
toFeatureCollection(features) {
|
|
196
|
+
return {
|
|
197
|
+
type: 'FeatureCollection',
|
|
198
|
+
features: features.map((f) => f.toGeoJSON()),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async toGeoJSON(features) {
|
|
203
|
+
return JSON.stringify(this.toFeatureCollection(features), null, 2)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async toCSV(features) {
|
|
207
|
+
const table = []
|
|
208
|
+
for (const feature of features) {
|
|
209
|
+
const row = feature.toGeoJSON().properties
|
|
210
|
+
const center = feature.center
|
|
211
|
+
delete row._umap_options
|
|
212
|
+
row.Latitude = center.lat
|
|
213
|
+
row.Longitude = center.lng
|
|
214
|
+
table.push(row)
|
|
215
|
+
}
|
|
216
|
+
return csv2geojson.dsv.csvFormat(table)
|
|
195
217
|
}
|
|
196
218
|
}
|
|
@@ -17,7 +17,7 @@ const TEMPLATE = `
|
|
|
17
17
|
<input type="file" multiple autofocus onchange />
|
|
18
18
|
<textarea onchange placeholder="${translate('Paste your data here')}"></textarea>
|
|
19
19
|
<input class="highlightable" type="url" placeholder="${translate('Provide an URL here')}" onchange />
|
|
20
|
-
<button class="
|
|
20
|
+
<button type=button class="importers" hidden data-ref="importersButton"><i class="icon icon-16 icon-magic"></i>${translate('Import helpers')}</button>
|
|
21
21
|
</fieldset>
|
|
22
22
|
<fieldset class="formbox">
|
|
23
23
|
<legend class="counter" data-help="importFormats">${translate(
|
|
@@ -169,8 +169,13 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
169
169
|
this.onChange()
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
set layer(layer) {
|
|
173
|
+
this._layer = layer
|
|
174
|
+
}
|
|
175
|
+
|
|
172
176
|
get layer() {
|
|
173
177
|
return (
|
|
178
|
+
this._layer ||
|
|
174
179
|
this._umap.datalayers[this.layerId] ||
|
|
175
180
|
this._umap.createDirtyDataLayer({ name: this.layerName })
|
|
176
181
|
)
|
|
@@ -8,11 +8,21 @@ const PORTALS = [
|
|
|
8
8
|
url: 'https://data.ampmetropole.fr',
|
|
9
9
|
platform: 'opendatasoft',
|
|
10
10
|
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Auverge-Rhône-Alpes',
|
|
13
|
+
url: 'https://admin.open-datara.fr',
|
|
14
|
+
platform: 'prodige',
|
|
15
|
+
},
|
|
11
16
|
{
|
|
12
17
|
name: 'Bordeaux Métropole',
|
|
13
18
|
url: 'https://opendata.bordeaux-metropole.fr',
|
|
14
19
|
platform: 'opendatasoft',
|
|
15
20
|
},
|
|
21
|
+
{
|
|
22
|
+
name: 'Nouvelle Aquitaine',
|
|
23
|
+
url: 'https://admin.sigena.fr',
|
|
24
|
+
platform: 'prodige',
|
|
25
|
+
},
|
|
16
26
|
{
|
|
17
27
|
name: 'Région Centre-Val de Loire',
|
|
18
28
|
url: 'https://data.centrevaldeloire.fr',
|
|
@@ -33,6 +43,21 @@ const PORTALS = [
|
|
|
33
43
|
url: 'https://data.iledefrance.fr',
|
|
34
44
|
platform: 'opendatasoft',
|
|
35
45
|
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Martinique',
|
|
48
|
+
url: 'https://admin.geomartinique.fr',
|
|
49
|
+
platform: 'prodige',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Région Pays de la Loire',
|
|
53
|
+
url: 'https://admin.sigloire.fr',
|
|
54
|
+
platform: 'prodige',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Saint-Pierre et Miquelon',
|
|
58
|
+
url: 'https://admin.geospm.com',
|
|
59
|
+
platform: 'prodige',
|
|
60
|
+
},
|
|
36
61
|
{
|
|
37
62
|
name: 'Toulouse Métropole',
|
|
38
63
|
url: 'https://data.toulouse-metropole.fr',
|
|
@@ -57,29 +82,54 @@ const TEMPLATE = `
|
|
|
57
82
|
<option disabled selected value="">${translate('Choose a dataset')}</option>
|
|
58
83
|
</select>
|
|
59
84
|
<input type="hidden" name="geofield" data-ref="geofield">
|
|
60
|
-
<label><input type="checkbox" name="in_bbox">${translate('Limit results to current map view')}</label>
|
|
85
|
+
<label data-ref="in_bbox" hidden><input type="checkbox" name="in_bbox">${translate('Limit results to current map view')}</label>
|
|
61
86
|
</div>
|
|
62
87
|
</div>
|
|
63
88
|
`
|
|
64
89
|
|
|
65
|
-
|
|
66
|
-
constructor(umap,
|
|
90
|
+
class Connector {
|
|
91
|
+
constructor(umap, baseUrl) {
|
|
67
92
|
this.umap = umap
|
|
68
|
-
this.
|
|
69
|
-
this.id = 'opendata'
|
|
70
|
-
this.portals = options.choices || PORTALS
|
|
93
|
+
this.baseUrl = baseUrl
|
|
71
94
|
}
|
|
95
|
+
}
|
|
72
96
|
|
|
73
|
-
|
|
97
|
+
class Prodige extends Connector {
|
|
98
|
+
async datasets() {
|
|
99
|
+
const datasets = []
|
|
100
|
+
const endpoint = this.umap.proxyUrl(
|
|
101
|
+
`${this.baseUrl}/api/ogc-features/collections.json`,
|
|
102
|
+
3600
|
|
103
|
+
)
|
|
104
|
+
const response = await this.umap.request.get(endpoint)
|
|
105
|
+
if (!response?.ok) return datasets
|
|
106
|
+
const data = await response.json()
|
|
107
|
+
for (const dataset of data.collections) {
|
|
108
|
+
let url
|
|
109
|
+
for (const link of dataset.links) {
|
|
110
|
+
if (link.type === 'application/geo+json') url = link.href
|
|
111
|
+
}
|
|
112
|
+
if (!url) continue
|
|
113
|
+
datasets.push({
|
|
114
|
+
label: dataset.title,
|
|
115
|
+
url: this.umap.proxyUrl(url, 3600),
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
return datasets.sort((a, b) => Utils.naturalSort(a.label, b.label, U.lang))
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class OpenDataSoft extends Connector {
|
|
123
|
+
async datasets() {
|
|
74
124
|
let results = []
|
|
75
125
|
let total = null
|
|
76
126
|
const hardLimit = 500
|
|
77
127
|
while (total === null || results.length < total) {
|
|
78
128
|
const offset = results.length
|
|
79
129
|
const response = await this.umap.request.get(
|
|
80
|
-
`${baseUrl}/api/explore/v2.1/catalog/datasets?where=features%20in%20%28%22geo%22%29&limit=100&offset=${offset}&order_by=title asc`
|
|
130
|
+
`${this.baseUrl}/api/explore/v2.1/catalog/datasets?where=features%20in%20%28%22geo%22%29&limit=100&offset=${offset}&order_by=title asc`
|
|
81
131
|
)
|
|
82
|
-
if (!response
|
|
132
|
+
if (!response?.ok) break
|
|
83
133
|
const data = await response.json()
|
|
84
134
|
if (total === null) {
|
|
85
135
|
total = data.total_count
|
|
@@ -87,48 +137,85 @@ export class Importer {
|
|
|
87
137
|
results = results.concat(data.results)
|
|
88
138
|
if (total === null || results.length > hardLimit) break
|
|
89
139
|
}
|
|
90
|
-
|
|
140
|
+
const datasets = []
|
|
141
|
+
for (const result of results) {
|
|
142
|
+
const fields = result.fields.filter((field) => field.type === 'geo_point_2d')
|
|
143
|
+
if (!fields.length) {
|
|
144
|
+
console.debug('No geofield found for', result)
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
if (fields.length > 1) {
|
|
148
|
+
console.debug('More than one geofield found for', result)
|
|
149
|
+
}
|
|
150
|
+
const url = `${this.baseUrl}/api/explore/v2.1/catalog/datasets/${result.dataset_id}/exports/geojson?select=%2A&limit=-1&timezone=UTC&use_labels=false&epsg=4326`
|
|
151
|
+
const bbox_url = `${url}&where=in_bbox%28${fields[0].name}%2C%20{south},{west},{north},{east}%29`
|
|
152
|
+
datasets.push({
|
|
153
|
+
id: result.dataset_id,
|
|
154
|
+
label: `${result.metas.default.title} (${result.metas.default.records_count})`,
|
|
155
|
+
url,
|
|
156
|
+
bbox_url,
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
return datasets
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export class Importer {
|
|
164
|
+
constructor(umap, options = {}) {
|
|
165
|
+
this.umap = umap
|
|
166
|
+
this.name = options.name || 'Open Data'
|
|
167
|
+
this.id = 'opendata'
|
|
168
|
+
this.portals = options.choices || PORTALS
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
resetSelect(select) {
|
|
172
|
+
Array.from(select.children).forEach((option) => {
|
|
173
|
+
if (!option.disabled) {
|
|
174
|
+
option.remove()
|
|
175
|
+
} else {
|
|
176
|
+
option.selected = true
|
|
177
|
+
}
|
|
178
|
+
})
|
|
91
179
|
}
|
|
92
180
|
|
|
93
181
|
async open(importer) {
|
|
94
|
-
|
|
95
|
-
const [container, { portals, datasets, geofield }] =
|
|
182
|
+
const [container, { portals, datasets, in_bbox }] =
|
|
96
183
|
Utils.loadTemplateWithRefs(TEMPLATE)
|
|
184
|
+
datasets.addEventListener('change', (event) => {
|
|
185
|
+
const select = event.target
|
|
186
|
+
const selected = select.options[select.selectedIndex]
|
|
187
|
+
const bbox_url = selected.dataset.bbox_url
|
|
188
|
+
in_bbox.checked = false
|
|
189
|
+
in_bbox.hidden = !bbox_url
|
|
190
|
+
})
|
|
97
191
|
portals.addEventListener('change', async (event) => {
|
|
98
|
-
const
|
|
192
|
+
const select = event.target
|
|
193
|
+
const selected = select.options[select.selectedIndex]
|
|
194
|
+
const platform = selected.dataset.platform
|
|
195
|
+
let connector
|
|
196
|
+
if (platform === 'opendatasoft') {
|
|
197
|
+
connector = new OpenDataSoft(this.umap, selected.value)
|
|
198
|
+
} else if (platform === 'prodige') {
|
|
199
|
+
connector = new Prodige(this.umap, selected.value)
|
|
200
|
+
} else {
|
|
201
|
+
console.error('Unknown platform', platform)
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
const results = await connector.datasets(event.target.value)
|
|
99
205
|
if (results) {
|
|
100
|
-
|
|
101
|
-
Array.from(datasets.children).forEach((option) => {
|
|
102
|
-
if (!option.disabled) {
|
|
103
|
-
option.remove()
|
|
104
|
-
} else {
|
|
105
|
-
option.selected = true
|
|
106
|
-
}
|
|
107
|
-
})
|
|
206
|
+
this.resetSelect(datasets)
|
|
108
207
|
for (const result of results) {
|
|
109
|
-
const fields = result.fields.filter((field) => field.type === 'geo_point_2d')
|
|
110
|
-
if (!fields.length) {
|
|
111
|
-
console.debug('No geofield found for', result)
|
|
112
|
-
continue
|
|
113
|
-
}
|
|
114
|
-
if (fields.length > 1) {
|
|
115
|
-
console.debug('More than one geofield found for', result)
|
|
116
|
-
}
|
|
117
|
-
fields_map[result.dataset_id] = fields[0].name
|
|
118
208
|
const el = Utils.loadTemplate(
|
|
119
|
-
`<option value="${result.
|
|
209
|
+
`<option value="${result.url}" data-url="${result.url}" data-bbox_url="${result.bbox_url || ''}">${result.label}</option>`
|
|
120
210
|
)
|
|
121
211
|
datasets.appendChild(el)
|
|
122
212
|
}
|
|
123
213
|
datasets.hidden = false
|
|
124
214
|
}
|
|
125
215
|
})
|
|
126
|
-
datasets.addEventListener('change', (event) => {
|
|
127
|
-
geofield.value = fields_map[event.target.value]
|
|
128
|
-
})
|
|
129
216
|
for (const instance of this.portals) {
|
|
130
217
|
const el = Utils.loadTemplate(
|
|
131
|
-
`<option value="${instance.url}">${instance.name}</option>`
|
|
218
|
+
`<option value="${instance.url}" data-platform="${instance.platform}">${instance.name}</option>`
|
|
132
219
|
)
|
|
133
220
|
portals.appendChild(el)
|
|
134
221
|
}
|
|
@@ -138,9 +225,10 @@ export class Importer {
|
|
|
138
225
|
Alert.error(translate('Please choose an instance first.'))
|
|
139
226
|
return
|
|
140
227
|
}
|
|
141
|
-
let url =
|
|
228
|
+
let url = form.dataset
|
|
142
229
|
if (form.in_bbox) {
|
|
143
|
-
|
|
230
|
+
const selected = datasets.options[datasets.selectedIndex]
|
|
231
|
+
url = selected.dataset.bbox_url
|
|
144
232
|
}
|
|
145
233
|
importer.url = url
|
|
146
234
|
importer.format = 'geojson'
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
2
|
+
import { translate } from '../i18n.js'
|
|
3
|
+
import * as Utils from '../utils.js'
|
|
4
|
+
import { Form } from '../form/builder.js'
|
|
5
|
+
|
|
6
|
+
export const PROFILES = [
|
|
7
|
+
['foot-walking', translate('Walking')],
|
|
8
|
+
['foot-hiking', translate('Hiking')],
|
|
9
|
+
['driving-car', translate('By car')],
|
|
10
|
+
['cycling-regular', translate('Cycling')],
|
|
11
|
+
['wheelchair', translate('Wheelchair')],
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
export const PREFERENCES = [
|
|
15
|
+
['recommended', translate('Recommended')],
|
|
16
|
+
['fastest', translate('Fastest')],
|
|
17
|
+
['shortest', translate('Shortest')],
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export class Importer {
|
|
21
|
+
constructor(umap) {
|
|
22
|
+
this.umap = umap
|
|
23
|
+
this.id = 'openrouteservice'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async loadORS() {
|
|
27
|
+
const mod = await import('../../../vendors/openrouteservice/ors-js-client.js')
|
|
28
|
+
return mod.default
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async isochrone(latlng) {
|
|
32
|
+
const ORS = await this.loadORS()
|
|
33
|
+
const properties = {
|
|
34
|
+
range: 10,
|
|
35
|
+
lines: 1,
|
|
36
|
+
profile: 'foot-walking',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const metadatas = [
|
|
40
|
+
['datalayer', { handler: 'DataLayerSwitcher', allowEmpty: true }],
|
|
41
|
+
[
|
|
42
|
+
'profile',
|
|
43
|
+
{ handler: 'Select', selectOptions: PROFILES, label: translate('Profile') },
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
'range',
|
|
47
|
+
{
|
|
48
|
+
handler: 'Range',
|
|
49
|
+
min: 5,
|
|
50
|
+
max: 60,
|
|
51
|
+
step: 1,
|
|
52
|
+
ticks: 11,
|
|
53
|
+
label: translate('Max time (in minutes)'),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
[
|
|
57
|
+
'lines',
|
|
58
|
+
{
|
|
59
|
+
handler: 'Range',
|
|
60
|
+
min: 1,
|
|
61
|
+
max: 5,
|
|
62
|
+
step: 1,
|
|
63
|
+
label: translate('Number of lines'),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
]
|
|
67
|
+
const form = new Form(properties, metadatas, { umap: this.umap })
|
|
68
|
+
// Needed for DataLayerSwitcher (which expects to be used with MutatingForm)
|
|
69
|
+
form._umap = this.umap
|
|
70
|
+
|
|
71
|
+
const Isochrones = new ORS.Isochrones({
|
|
72
|
+
api_key: this.umap.properties.ORSAPIKey,
|
|
73
|
+
})
|
|
74
|
+
this.umap.dialog.open({ template: form.build() }).then(async () => {
|
|
75
|
+
try {
|
|
76
|
+
const params = {
|
|
77
|
+
locations: [[latlng.lng, latlng.lat]],
|
|
78
|
+
profile: properties.profile,
|
|
79
|
+
range: [properties.range * 60],
|
|
80
|
+
}
|
|
81
|
+
if (properties.lines !== 1) {
|
|
82
|
+
params.interval = (properties.range / properties.lines) * 60
|
|
83
|
+
}
|
|
84
|
+
const data = await Isochrones.calculate(params)
|
|
85
|
+
this.umap.importer.build()
|
|
86
|
+
this.umap.importer.raw = JSON.stringify(data)
|
|
87
|
+
this.umap.importer.format = 'geojson'
|
|
88
|
+
this.umap.importer.layer = properties.datalayer
|
|
89
|
+
this.umap.importer.submit()
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(err)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async elevation(geometry) {
|
|
97
|
+
const ORS = await this.loadORS()
|
|
98
|
+
|
|
99
|
+
const Elevation = new ORS.Elevation({
|
|
100
|
+
api_key: this.umap.properties.ORSAPIKey,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const data = await Elevation.lineElevation({
|
|
105
|
+
format_in: 'geojson',
|
|
106
|
+
format_out: 'geojson',
|
|
107
|
+
geometry: geometry,
|
|
108
|
+
})
|
|
109
|
+
return data?.geometry
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.debug(`An error occurred: ${err.status}`)
|
|
112
|
+
console.error(err)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async directions(properties) {
|
|
117
|
+
if (!properties?.coordinates || properties.coordinates.length < 2) {
|
|
118
|
+
console.error('Not enough coordinates to compute route', properties)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
const ORS = await this.loadORS()
|
|
122
|
+
const Directions = new ORS.Directions({ api_key: this.umap.properties.ORSAPIKey })
|
|
123
|
+
try {
|
|
124
|
+
const featuresCollection = await Directions.calculate({
|
|
125
|
+
coordinates: properties.coordinates,
|
|
126
|
+
profile: properties.profile,
|
|
127
|
+
preference: properties.preference,
|
|
128
|
+
elevation: properties.elevation,
|
|
129
|
+
geometry_simplify: true,
|
|
130
|
+
instructions: false,
|
|
131
|
+
format: 'geojson',
|
|
132
|
+
})
|
|
133
|
+
const feature = featuresCollection.features[0]
|
|
134
|
+
return feature.geometry
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.debug(`An error occurred: ${err.status}`)
|
|
137
|
+
console.error(err)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|