umap-project 2.4.2__py3-none-any.whl → 2.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- umap/__init__.py +1 -1
- umap/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 +5 -1
- 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.0.dist-info}/METADATA +8 -8
- {umap_project-2.4.2.dist-info → umap_project-2.5.0.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.0.dist-info}/WHEEL +0 -0
- {umap_project-2.4.2.dist-info → umap_project-2.5.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.4.2.dist-info → umap_project-2.5.0.dist-info}/licenses/LICENSE +0 -0
umap/static/umap/js/umap.js
CHANGED
|
@@ -29,7 +29,7 @@ L.Map.mergeOptions({
|
|
|
29
29
|
U.Map = L.Map.extend({
|
|
30
30
|
includes: [ControlsMixin],
|
|
31
31
|
|
|
32
|
-
initialize: function (el, geojson) {
|
|
32
|
+
initialize: async function (el, geojson) {
|
|
33
33
|
this.sync_engine = new U.SyncEngine(this)
|
|
34
34
|
this.sync = this.sync_engine.proxy(this)
|
|
35
35
|
// Locale name (pt_PT, en_US…)
|
|
@@ -56,8 +56,8 @@ U.Map = L.Map.extend({
|
|
|
56
56
|
this.urls = new U.URLs(this.options.urls)
|
|
57
57
|
|
|
58
58
|
this.panel = new U.Panel(this)
|
|
59
|
+
this.dialog = new U.Dialog({ className: 'dark' })
|
|
59
60
|
this.tooltip = new U.Tooltip(this._controlContainer)
|
|
60
|
-
this.dialog = new U.Dialog(this._controlContainer)
|
|
61
61
|
if (this.hasEditMode()) {
|
|
62
62
|
this.editPanel = new U.EditPanel(this)
|
|
63
63
|
this.fullPanel = new U.FullPanel(this)
|
|
@@ -116,6 +116,8 @@ U.Map = L.Map.extend({
|
|
|
116
116
|
// Needed for actions labels
|
|
117
117
|
this.help = new U.Help(this)
|
|
118
118
|
|
|
119
|
+
this.formatter = new U.Formatter(this)
|
|
120
|
+
|
|
119
121
|
this.initControls()
|
|
120
122
|
// Needs locate control and hash to exist
|
|
121
123
|
this.initCenter()
|
|
@@ -174,23 +176,7 @@ U.Map = L.Map.extend({
|
|
|
174
176
|
}
|
|
175
177
|
this._default_extent = true
|
|
176
178
|
this.options.name = L._('Untitled map')
|
|
177
|
-
|
|
178
|
-
const url = new URL(window.location.href)
|
|
179
|
-
const dataUrls = new URLSearchParams(url.search).getAll('dataUrl')
|
|
180
|
-
const dataFormat = L.Util.queryString('dataFormat', 'geojson')
|
|
181
|
-
if (dataUrls.length) {
|
|
182
|
-
for (let dataUrl of dataUrls) {
|
|
183
|
-
dataUrl = decodeURIComponent(dataUrl)
|
|
184
|
-
dataUrl = this.localizeUrl(dataUrl)
|
|
185
|
-
dataUrl = this.proxyUrl(dataUrl)
|
|
186
|
-
const datalayer = this.createDataLayer()
|
|
187
|
-
datalayer.importFromUrl(dataUrl, dataFormat)
|
|
188
|
-
}
|
|
189
|
-
} else if (data) {
|
|
190
|
-
data = decodeURIComponent(data)
|
|
191
|
-
const datalayer = this.createDataLayer()
|
|
192
|
-
datalayer.importRaw(data, dataFormat)
|
|
193
|
-
}
|
|
179
|
+
await this.loadDataFromQueryString()
|
|
194
180
|
}
|
|
195
181
|
|
|
196
182
|
this.slideshow = new U.Slideshow(this, this.options.slideshow)
|
|
@@ -201,6 +187,7 @@ U.Map = L.Map.extend({
|
|
|
201
187
|
}
|
|
202
188
|
|
|
203
189
|
this.initShortcuts()
|
|
190
|
+
if (!this.options.noControl) this.initCaptionBar()
|
|
204
191
|
this.onceDataLoaded(this.setViewFromQueryString)
|
|
205
192
|
|
|
206
193
|
window.onbeforeunload = () => (this.editEnabled && this.isDirty) || null
|
|
@@ -295,9 +282,28 @@ U.Map = L.Map.extend({
|
|
|
295
282
|
}
|
|
296
283
|
},
|
|
297
284
|
|
|
298
|
-
|
|
285
|
+
loadDataFromQueryString: async function () {
|
|
286
|
+
let data = L.Util.queryString('data', null)
|
|
287
|
+
const url = new URL(window.location.href)
|
|
288
|
+
const dataUrls = new URLSearchParams(url.search).getAll('dataUrl')
|
|
289
|
+
const dataFormat = L.Util.queryString('dataFormat', 'geojson')
|
|
290
|
+
if (dataUrls.length) {
|
|
291
|
+
for (let dataUrl of dataUrls) {
|
|
292
|
+
dataUrl = decodeURIComponent(dataUrl)
|
|
293
|
+
dataUrl = this.localizeUrl(dataUrl)
|
|
294
|
+
dataUrl = this.proxyUrl(dataUrl)
|
|
295
|
+
const datalayer = this.createDataLayer()
|
|
296
|
+
await datalayer.importFromUrl(dataUrl, dataFormat)
|
|
297
|
+
}
|
|
298
|
+
} else if (data) {
|
|
299
|
+
data = decodeURIComponent(data)
|
|
300
|
+
const datalayer = this.createDataLayer()
|
|
301
|
+
await datalayer.importRaw(data, dataFormat)
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
setViewFromQueryString: async function () {
|
|
299
306
|
if (this.options.noControl) return
|
|
300
|
-
this.initCaptionBar()
|
|
301
307
|
if (L.Util.queryString('share')) {
|
|
302
308
|
this.share.open()
|
|
303
309
|
} else if (this.options.onLoadPanel === 'databrowser') {
|
|
@@ -539,18 +545,16 @@ U.Map = L.Map.extend({
|
|
|
539
545
|
initShortcuts: function () {
|
|
540
546
|
const globalShortcuts = function (e) {
|
|
541
547
|
if (e.key === 'Escape') {
|
|
542
|
-
if (this.dialog.visible) {
|
|
543
|
-
this.dialog.close()
|
|
544
|
-
} else if (this.importer.dialog.visible) {
|
|
548
|
+
if (this.importer.dialog.visible) {
|
|
545
549
|
this.importer.dialog.close()
|
|
546
550
|
} else if (this.editEnabled && this.editTools.drawing()) {
|
|
547
551
|
this.editTools.stopDrawing()
|
|
548
552
|
} else if (this.measureTools.enabled()) {
|
|
549
553
|
this.measureTools.stopDrawing()
|
|
550
|
-
} else if (this.editPanel?.isOpen()) {
|
|
551
|
-
this.editPanel?.close()
|
|
552
554
|
} else if (this.fullPanel?.isOpen()) {
|
|
553
555
|
this.fullPanel?.close()
|
|
556
|
+
} else if (this.editPanel?.isOpen()) {
|
|
557
|
+
this.editPanel?.close()
|
|
554
558
|
} else if (this.panel.isOpen()) {
|
|
555
559
|
this.panel.close()
|
|
556
560
|
}
|
|
@@ -1465,12 +1469,8 @@ U.Map = L.Map.extend({
|
|
|
1465
1469
|
{ handler: 'Switch', label: L._('Autostart when map is loaded') },
|
|
1466
1470
|
],
|
|
1467
1471
|
]
|
|
1468
|
-
const slideshowHandler = function () {
|
|
1469
|
-
this.slideshow.setOptions(this.options.slideshow)
|
|
1470
|
-
}
|
|
1471
1472
|
const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
|
|
1472
|
-
callback:
|
|
1473
|
-
callbackContext: this,
|
|
1473
|
+
callback: () => this.slideshow.setOptions(this.options.slideshow),
|
|
1474
1474
|
})
|
|
1475
1475
|
slideshow.appendChild(slideshowBuilder.build())
|
|
1476
1476
|
},
|
|
@@ -1642,9 +1642,12 @@ U.Map = L.Map.extend({
|
|
|
1642
1642
|
},
|
|
1643
1643
|
|
|
1644
1644
|
askForReset: function (e) {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1645
|
+
this.dialog
|
|
1646
|
+
.confirm(L._('Are you sure you want to cancel your changes?'))
|
|
1647
|
+
.then(() => {
|
|
1648
|
+
this.reset()
|
|
1649
|
+
this.disableEdit()
|
|
1650
|
+
})
|
|
1648
1651
|
},
|
|
1649
1652
|
|
|
1650
1653
|
startMarker: function () {
|
|
@@ -1888,4 +1891,15 @@ U.Map = L.Map.extend({
|
|
|
1888
1891
|
})
|
|
1889
1892
|
await this.server.post(sendLink, {}, formData)
|
|
1890
1893
|
},
|
|
1894
|
+
|
|
1895
|
+
allProperties: function () {
|
|
1896
|
+
return [].concat(...this.datalayers_index.map((dl) => dl._propertiesIndex))
|
|
1897
|
+
},
|
|
1898
|
+
|
|
1899
|
+
sortedValues: function (property) {
|
|
1900
|
+
return []
|
|
1901
|
+
.concat(...this.datalayers_index.map((dl) => dl.sortedValues(property)))
|
|
1902
|
+
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
1903
|
+
.sort(U.Utils.naturalSort)
|
|
1904
|
+
},
|
|
1891
1905
|
})
|
|
@@ -126,7 +126,77 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
|
|
126
126
|
},
|
|
127
127
|
})
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
// Layer where each feature color is relative to the others,
|
|
130
|
+
// so we need all features before behing able to set one
|
|
131
|
+
// feature layer
|
|
132
|
+
U.RelativeColorLayer = L.FeatureGroup.extend({
|
|
133
|
+
initialize: function (datalayer) {
|
|
134
|
+
this.datalayer = datalayer
|
|
135
|
+
this.colorSchemes = Object.keys(colorbrewer)
|
|
136
|
+
.filter((k) => k !== 'schemeGroups')
|
|
137
|
+
.sort()
|
|
138
|
+
const key = this.getType().toLowerCase()
|
|
139
|
+
if (!U.Utils.isObject(this.datalayer.options[key])) {
|
|
140
|
+
this.datalayer.options[key] = {}
|
|
141
|
+
}
|
|
142
|
+
L.FeatureGroup.prototype.initialize.call(this, [], this.datalayer.options[key])
|
|
143
|
+
this.datalayer.onceDataLoaded(() => {
|
|
144
|
+
this.redraw()
|
|
145
|
+
this.datalayer.on('datachanged', this.redraw, this)
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
redraw: function () {
|
|
150
|
+
this.compute()
|
|
151
|
+
if (this._map) this.eachLayer(this._map.addLayer, this._map)
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
getOption: function (option, feature) {
|
|
155
|
+
if (feature && option === feature.staticOptions.mainColor) {
|
|
156
|
+
return this.getColor(feature)
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
addLayer: function (layer) {
|
|
161
|
+
// Do not add yet the layer to the map
|
|
162
|
+
// wait for datachanged event, so we can compute breaks only once
|
|
163
|
+
const id = this.getLayerId(layer)
|
|
164
|
+
this._layers[id] = layer
|
|
165
|
+
return this
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
onAdd: function (map) {
|
|
169
|
+
this.compute()
|
|
170
|
+
L.FeatureGroup.prototype.onAdd.call(this, map)
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
getValues: function () {
|
|
174
|
+
const values = []
|
|
175
|
+
this.datalayer.eachLayer((layer) => {
|
|
176
|
+
const value = this._getValue(layer)
|
|
177
|
+
if (value !== undefined) values.push(value)
|
|
178
|
+
})
|
|
179
|
+
return values
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
renderLegend: function (container) {
|
|
183
|
+
const parent = L.DomUtil.create('ul', '', container)
|
|
184
|
+
const items = this.getLegendItems()
|
|
185
|
+
for (const [color, label] of items) {
|
|
186
|
+
const li = L.DomUtil.create('li', '', parent)
|
|
187
|
+
const colorEl = L.DomUtil.create('span', 'datalayer-color', li)
|
|
188
|
+
colorEl.style.backgroundColor = color
|
|
189
|
+
const labelEl = L.DomUtil.create('span', '', li)
|
|
190
|
+
labelEl.textContent = label
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
getColorSchemes: function (classes) {
|
|
195
|
+
return this.colorSchemes.filter((scheme) => Boolean(colorbrewer[scheme][classes]))
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
U.Layer.Choropleth = U.RelativeColorLayer.extend({
|
|
130
200
|
statics: {
|
|
131
201
|
NAME: L._('Choropleth'),
|
|
132
202
|
TYPE: 'Choropleth',
|
|
@@ -147,42 +217,13 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
147
217
|
manual: L._('Manual'),
|
|
148
218
|
},
|
|
149
219
|
|
|
150
|
-
initialize: function (datalayer) {
|
|
151
|
-
this.datalayer = datalayer
|
|
152
|
-
if (!U.Utils.isObject(this.datalayer.options.choropleth)) {
|
|
153
|
-
this.datalayer.options.choropleth = {}
|
|
154
|
-
}
|
|
155
|
-
L.FeatureGroup.prototype.initialize.call(
|
|
156
|
-
this,
|
|
157
|
-
[],
|
|
158
|
-
this.datalayer.options.choropleth
|
|
159
|
-
)
|
|
160
|
-
this.datalayer.onceDataLoaded(() => {
|
|
161
|
-
this.redraw()
|
|
162
|
-
this.datalayer.on('datachanged', this.redraw, this)
|
|
163
|
-
})
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
redraw: function () {
|
|
167
|
-
this.computeBreaks()
|
|
168
|
-
if (this._map) this.eachLayer(this._map.addLayer, this._map)
|
|
169
|
-
},
|
|
170
|
-
|
|
171
220
|
_getValue: function (feature) {
|
|
172
221
|
const key = this.datalayer.options.choropleth.property || 'value'
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
getValues: function () {
|
|
177
|
-
const values = []
|
|
178
|
-
this.datalayer.eachLayer((layer) => {
|
|
179
|
-
const value = this._getValue(layer)
|
|
180
|
-
if (!Number.isNaN(value)) values.push(value)
|
|
181
|
-
})
|
|
182
|
-
return values
|
|
222
|
+
const value = +feature.properties[key]
|
|
223
|
+
if (!Number.isNaN(value)) return value
|
|
183
224
|
},
|
|
184
225
|
|
|
185
|
-
|
|
226
|
+
compute: function () {
|
|
186
227
|
const values = this.getValues()
|
|
187
228
|
|
|
188
229
|
if (!values.length) {
|
|
@@ -224,7 +265,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
224
265
|
},
|
|
225
266
|
|
|
226
267
|
getColor: function (feature) {
|
|
227
|
-
if (!feature) return // FIXME
|
|
268
|
+
if (!feature) return // FIXME should not happen
|
|
228
269
|
const featureValue = this._getValue(feature)
|
|
229
270
|
// Find the bucket/step/limit that this value is less than and give it that color
|
|
230
271
|
for (let i = 1; i < this.options.breaks.length; i++) {
|
|
@@ -234,25 +275,6 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
234
275
|
}
|
|
235
276
|
},
|
|
236
277
|
|
|
237
|
-
getOption: function (option, feature) {
|
|
238
|
-
if (feature && option === feature.staticOptions.mainColor) {
|
|
239
|
-
return this.getColor(feature)
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
addLayer: function (layer) {
|
|
244
|
-
// Do not add yet the layer to the map
|
|
245
|
-
// wait for datachanged event, so we want compute breaks once
|
|
246
|
-
const id = this.getLayerId(layer)
|
|
247
|
-
this._layers[id] = layer
|
|
248
|
-
return this
|
|
249
|
-
},
|
|
250
|
-
|
|
251
|
-
onAdd: function (map) {
|
|
252
|
-
this.computeBreaks()
|
|
253
|
-
L.FeatureGroup.prototype.onAdd.call(this, map)
|
|
254
|
-
},
|
|
255
|
-
|
|
256
278
|
onEdit: function (field, builder) {
|
|
257
279
|
// Only compute the breaks if we're dealing with choropleth
|
|
258
280
|
if (!field.startsWith('options.choropleth')) return
|
|
@@ -261,7 +283,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
261
283
|
this.datalayer.options.choropleth.mode = 'manual'
|
|
262
284
|
if (builder) builder.helpers['options.choropleth.mode'].fetch()
|
|
263
285
|
}
|
|
264
|
-
this.
|
|
286
|
+
this.compute()
|
|
265
287
|
// If user changes the mode or the number of classes,
|
|
266
288
|
// then update the breaks input value
|
|
267
289
|
if (field === 'options.choropleth.mode' || field === 'options.choropleth.classes') {
|
|
@@ -270,10 +292,6 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
270
292
|
},
|
|
271
293
|
|
|
272
294
|
getEditableOptions: function () {
|
|
273
|
-
const brewerSchemes = Object.keys(colorbrewer)
|
|
274
|
-
.filter((k) => k !== 'schemeGroups')
|
|
275
|
-
.sort()
|
|
276
|
-
|
|
277
295
|
return [
|
|
278
296
|
[
|
|
279
297
|
'options.choropleth.property',
|
|
@@ -288,7 +306,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
288
306
|
{
|
|
289
307
|
handler: 'Select',
|
|
290
308
|
label: L._('Choropleth color palette'),
|
|
291
|
-
selectOptions:
|
|
309
|
+
selectOptions: this.colorSchemes,
|
|
292
310
|
},
|
|
293
311
|
],
|
|
294
312
|
[
|
|
@@ -324,20 +342,139 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
324
342
|
]
|
|
325
343
|
},
|
|
326
344
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
345
|
+
getLegendItems: function () {
|
|
346
|
+
return this.options.breaks.slice(0, -1).map((el, index) => {
|
|
347
|
+
const from = +this.options.breaks[index].toFixed(1)
|
|
348
|
+
const to = +this.options.breaks[index + 1].toFixed(1)
|
|
349
|
+
return [this.options.colors[index], `${from} - ${to}`]
|
|
350
|
+
})
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
U.Layer.Categorized = U.RelativeColorLayer.extend({
|
|
355
|
+
statics: {
|
|
356
|
+
NAME: L._('Categorized'),
|
|
357
|
+
TYPE: 'Categorized',
|
|
358
|
+
},
|
|
359
|
+
includes: [U.Layer],
|
|
360
|
+
MODES: {
|
|
361
|
+
manual: L._('Manual'),
|
|
362
|
+
alpha: L._('Alphabetical'),
|
|
363
|
+
},
|
|
364
|
+
defaults: {
|
|
365
|
+
color: 'white',
|
|
366
|
+
fillColor: 'red',
|
|
367
|
+
fillOpacity: 0.7,
|
|
368
|
+
weight: 2,
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
_getValue: function (feature) {
|
|
372
|
+
const key =
|
|
373
|
+
this.datalayer.options.categorized.property || this.datalayer._propertiesIndex[0]
|
|
374
|
+
return feature.properties[key]
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
getColor: function (feature) {
|
|
378
|
+
if (!feature) return // FIXME should not happen
|
|
379
|
+
const featureValue = this._getValue(feature)
|
|
380
|
+
for (let i = 0; i < this.options.categories.length; i++) {
|
|
381
|
+
if (featureValue === this.options.categories[i]) {
|
|
382
|
+
return this.options.colors[i]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
compute: function () {
|
|
388
|
+
const values = this.getValues()
|
|
389
|
+
|
|
390
|
+
if (!values.length) {
|
|
391
|
+
this.options.categories = []
|
|
392
|
+
this.options.colors = []
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
const mode = this.datalayer.options.categorized.mode
|
|
396
|
+
let categories = []
|
|
397
|
+
if (mode === 'manual') {
|
|
398
|
+
const manualCategories = this.datalayer.options.categorized.categories
|
|
399
|
+
if (manualCategories) {
|
|
400
|
+
categories = manualCategories.split(',')
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
categories = values
|
|
404
|
+
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
405
|
+
.sort(U.Utils.naturalSort)
|
|
406
|
+
}
|
|
407
|
+
this.options.categories = categories
|
|
408
|
+
this.datalayer.options.categorized.categories = this.options.categories.join(',')
|
|
409
|
+
const fillColor = this.datalayer.getOption('fillColor') || this.defaults.fillColor
|
|
410
|
+
const colorScheme = this.datalayer.options.categorized.brewer
|
|
411
|
+
this._classes = this.options.categories.length
|
|
412
|
+
if (colorbrewer[colorScheme]?.[this._classes]) {
|
|
413
|
+
this.options.colors = colorbrewer[colorScheme][this._classes]
|
|
414
|
+
} else {
|
|
415
|
+
this.options.colors = colorbrewer?.Accent[this._classes]
|
|
416
|
+
? colorbrewer?.Accent[this._classes]
|
|
417
|
+
: U.COLORS
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
getEditableOptions: function () {
|
|
422
|
+
return [
|
|
423
|
+
[
|
|
424
|
+
'options.categorized.property',
|
|
425
|
+
{
|
|
426
|
+
handler: 'Select',
|
|
427
|
+
selectOptions: this.datalayer._propertiesIndex,
|
|
428
|
+
label: L._('Category property'),
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
[
|
|
432
|
+
'options.categorized.brewer',
|
|
433
|
+
{
|
|
434
|
+
handler: 'Select',
|
|
435
|
+
label: L._('Color palette'),
|
|
436
|
+
selectOptions: this.getColorSchemes(this._classes),
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
[
|
|
440
|
+
'options.categorized.categories',
|
|
441
|
+
{
|
|
442
|
+
handler: 'BlurInput',
|
|
443
|
+
label: L._('Categories'),
|
|
444
|
+
helpText: L._('Comma separated list of categories.'),
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
[
|
|
448
|
+
'options.categorized.mode',
|
|
449
|
+
{
|
|
450
|
+
handler: 'MultiChoice',
|
|
451
|
+
default: 'alpha',
|
|
452
|
+
choices: Object.entries(this.MODES),
|
|
453
|
+
label: L._('Categories mode'),
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
]
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
onEdit: function (field, builder) {
|
|
460
|
+
// Only compute the categories if we're dealing with categorized
|
|
461
|
+
if (!field.startsWith('options.categorized')) return
|
|
462
|
+
// If user touches the categories, then force manual mode
|
|
463
|
+
if (field === 'options.categorized.categories') {
|
|
464
|
+
this.datalayer.options.categorized.mode = 'manual'
|
|
465
|
+
if (builder) builder.helpers['options.categorized.mode'].fetch()
|
|
466
|
+
}
|
|
467
|
+
this.compute()
|
|
468
|
+
// If user changes the mode
|
|
469
|
+
// then update the categories input value
|
|
470
|
+
if (field === 'options.categorized.mode') {
|
|
471
|
+
if (builder) builder.helpers['options.categorized.categories'].fetch()
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
getLegendItems: function () {
|
|
476
|
+
return this.options.categories.map((limit, index) => {
|
|
477
|
+
return [this.options.colors[index], this.options.categories[index]]
|
|
341
478
|
})
|
|
342
479
|
},
|
|
343
480
|
})
|
|
@@ -614,9 +751,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
614
751
|
this.resetLayer()
|
|
615
752
|
}
|
|
616
753
|
this.hide()
|
|
617
|
-
|
|
754
|
+
for (const field of fields) {
|
|
618
755
|
this.layer.onEdit(field, builder)
|
|
619
|
-
}
|
|
756
|
+
}
|
|
620
757
|
this.redraw()
|
|
621
758
|
this.show()
|
|
622
759
|
break
|
|
@@ -733,8 +870,8 @@ U.DataLayer = L.Evented.extend({
|
|
|
733
870
|
this.addData(geojson, sync)
|
|
734
871
|
this._geojson = geojson
|
|
735
872
|
this._dataloaded = true
|
|
736
|
-
this.fire('dataloaded')
|
|
737
873
|
this.fire('datachanged')
|
|
874
|
+
this.fire('dataloaded')
|
|
738
875
|
},
|
|
739
876
|
|
|
740
877
|
fromUmapGeoJSON: async function (geojson) {
|
|
@@ -792,11 +929,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
792
929
|
const response = await this.map.request.get(url)
|
|
793
930
|
if (response?.ok) {
|
|
794
931
|
this.clear()
|
|
795
|
-
this.
|
|
796
|
-
await response.text(),
|
|
797
|
-
this.
|
|
798
|
-
(geojson) => this.fromGeoJSON(geojson)
|
|
799
|
-
)
|
|
932
|
+
this.map.formatter
|
|
933
|
+
.parse(await response.text(), this.options.remoteData.format)
|
|
934
|
+
.then((geojson) => this.fromGeoJSON(geojson))
|
|
800
935
|
}
|
|
801
936
|
},
|
|
802
937
|
|
|
@@ -899,7 +1034,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
899
1034
|
this._index.splice(this._index.indexOf(id), 1)
|
|
900
1035
|
delete this._layers[id]
|
|
901
1036
|
delete this.map.features_index[feature.getSlug()]
|
|
902
|
-
if (this.hasDataLoaded()) this.fire('datachanged')
|
|
1037
|
+
if (this.hasDataLoaded() && this.isVisible()) this.fire('datachanged')
|
|
903
1038
|
},
|
|
904
1039
|
|
|
905
1040
|
indexProperties: function (feature) {
|
|
@@ -912,6 +1047,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
912
1047
|
if (name.indexOf('_') === 0) return
|
|
913
1048
|
if (L.Util.indexOf(this._propertiesIndex, name) !== -1) return
|
|
914
1049
|
this._propertiesIndex.push(name)
|
|
1050
|
+
this._propertiesIndex.sort()
|
|
915
1051
|
},
|
|
916
1052
|
|
|
917
1053
|
deindexProperty: function (name) {
|
|
@@ -919,6 +1055,13 @@ U.DataLayer = L.Evented.extend({
|
|
|
919
1055
|
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
|
|
920
1056
|
},
|
|
921
1057
|
|
|
1058
|
+
sortedValues: function (property) {
|
|
1059
|
+
return Object.values(this._layers)
|
|
1060
|
+
.map((feature) => feature.properties[property])
|
|
1061
|
+
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
1062
|
+
.sort(U.Utils.naturalSort)
|
|
1063
|
+
},
|
|
1064
|
+
|
|
922
1065
|
addData: function (geojson, sync) {
|
|
923
1066
|
try {
|
|
924
1067
|
// Do not fail if remote data is somehow invalid,
|
|
@@ -930,83 +1073,6 @@ U.DataLayer = L.Evented.extend({
|
|
|
930
1073
|
}
|
|
931
1074
|
},
|
|
932
1075
|
|
|
933
|
-
addRawData: function (c, type) {
|
|
934
|
-
this.rawToGeoJSON(c, type, (geojson) => this.addData(geojson))
|
|
935
|
-
},
|
|
936
|
-
|
|
937
|
-
rawToGeoJSON: (c, type, callback) => {
|
|
938
|
-
const toDom = (x) => {
|
|
939
|
-
const doc = new DOMParser().parseFromString(x, 'text/xml')
|
|
940
|
-
const errorNode = doc.querySelector('parsererror')
|
|
941
|
-
if (errorNode) {
|
|
942
|
-
U.Alert.error(L._('Cannot parse data'))
|
|
943
|
-
}
|
|
944
|
-
return doc
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// TODO add a duck typing guessType
|
|
948
|
-
if (type === 'csv') {
|
|
949
|
-
csv2geojson.csv2geojson(
|
|
950
|
-
c,
|
|
951
|
-
{
|
|
952
|
-
delimiter: 'auto',
|
|
953
|
-
includeLatLon: false,
|
|
954
|
-
},
|
|
955
|
-
(err, result) => {
|
|
956
|
-
// csv2geojson fallback to null geometries when it cannot determine
|
|
957
|
-
// lat or lon columns. This is valid geojson, but unwanted from a user
|
|
958
|
-
// point of view.
|
|
959
|
-
if (result?.features.length) {
|
|
960
|
-
if (result.features[0].geometry === null) {
|
|
961
|
-
err = {
|
|
962
|
-
type: 'Error',
|
|
963
|
-
message: L._('Cannot determine latitude and longitude columns.'),
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (err) {
|
|
968
|
-
let message
|
|
969
|
-
if (err.type === 'Error') {
|
|
970
|
-
message = err.message
|
|
971
|
-
} else {
|
|
972
|
-
message = L._('{count} errors during import: {message}', {
|
|
973
|
-
count: err.length,
|
|
974
|
-
message: err[0].message,
|
|
975
|
-
})
|
|
976
|
-
}
|
|
977
|
-
U.Alert.error(message, 10000)
|
|
978
|
-
console.error(err)
|
|
979
|
-
}
|
|
980
|
-
if (result?.features.length) {
|
|
981
|
-
callback(result)
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
)
|
|
985
|
-
} else if (type === 'gpx') {
|
|
986
|
-
callback(toGeoJSON.gpx(toDom(c)))
|
|
987
|
-
} else if (type === 'georss') {
|
|
988
|
-
callback(GeoRSSToGeoJSON(toDom(c)))
|
|
989
|
-
} else if (type === 'kml') {
|
|
990
|
-
callback(toGeoJSON.kml(toDom(c)))
|
|
991
|
-
} else if (type === 'osm') {
|
|
992
|
-
let d
|
|
993
|
-
try {
|
|
994
|
-
d = JSON.parse(c)
|
|
995
|
-
} catch (e) {
|
|
996
|
-
d = toDom(c)
|
|
997
|
-
}
|
|
998
|
-
callback(osmtogeojson(d, { flatProperties: true }))
|
|
999
|
-
} else if (type === 'geojson') {
|
|
1000
|
-
try {
|
|
1001
|
-
const gj = JSON.parse(c)
|
|
1002
|
-
callback(gj)
|
|
1003
|
-
} catch (err) {
|
|
1004
|
-
U.Alert.error(`Invalid JSON file: ${err}`)
|
|
1005
|
-
return
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
},
|
|
1009
|
-
|
|
1010
1076
|
// The choice of the name is not ours, because it is required by Leaflet.
|
|
1011
1077
|
// It is misleading, as the returned objects are uMap objects, and not
|
|
1012
1078
|
// GeoJSON features.
|
|
@@ -1136,10 +1202,12 @@ U.DataLayer = L.Evented.extend({
|
|
|
1136
1202
|
return new U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this }, id)
|
|
1137
1203
|
},
|
|
1138
1204
|
|
|
1139
|
-
importRaw: function (raw,
|
|
1140
|
-
this.
|
|
1205
|
+
importRaw: async function (raw, format) {
|
|
1206
|
+
this.map.formatter
|
|
1207
|
+
.parse(raw, format)
|
|
1208
|
+
.then((geojson) => this.addData(geojson))
|
|
1209
|
+
.then(() => this.zoomTo())
|
|
1141
1210
|
this.isDirty = true
|
|
1142
|
-
this.zoomTo()
|
|
1143
1211
|
},
|
|
1144
1212
|
|
|
1145
1213
|
importFromFiles: function (files, type) {
|
|
@@ -1735,9 +1803,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
1735
1803
|
},
|
|
1736
1804
|
|
|
1737
1805
|
tableEdit: function () {
|
|
1738
|
-
if (
|
|
1806
|
+
if (!this.isVisible()) return
|
|
1739
1807
|
const editor = new U.TableEditor(this)
|
|
1740
|
-
editor.
|
|
1808
|
+
editor.open()
|
|
1741
1809
|
},
|
|
1742
1810
|
|
|
1743
1811
|
getFilterKeys: function () {
|
|
@@ -116,7 +116,7 @@ U.MapPermissions = L.Class.extend({
|
|
|
116
116
|
L._('Advanced actions')
|
|
117
117
|
)
|
|
118
118
|
const advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions)
|
|
119
|
-
|
|
119
|
+
L.DomUtil.createButton(
|
|
120
120
|
'button',
|
|
121
121
|
advancedButtons,
|
|
122
122
|
L._('Attach the map to my account'),
|
|
@@ -265,7 +265,7 @@ U.PopupTemplate.OSM = U.PopupTemplate.Default.extend({
|
|
|
265
265
|
const props = this.feature.properties
|
|
266
266
|
const container = L.DomUtil.add('div')
|
|
267
267
|
const title = L.DomUtil.add('h3', 'popup-title', container)
|
|
268
|
-
const color = this.feature.
|
|
268
|
+
const color = this.feature.getPreviewColor()
|
|
269
269
|
title.style.backgroundColor = color
|
|
270
270
|
const iconUrl = this.feature.getDynamicOption('iconUrl')
|
|
271
271
|
const icon = U.Icon.makeIconElement(iconUrl, title)
|