umap-project 2.4.1__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/el/LC_MESSAGES/django.po +145 -90
- umap/locale/en/LC_MESSAGES/django.po +13 -13
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +145 -89
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +100 -50
- umap/static/umap/base.css +5 -2
- umap/static/umap/content.css +2 -2
- umap/static/umap/css/contextmenu.css +11 -0
- umap/static/umap/css/dialog.css +25 -4
- umap/static/umap/css/importers.css +2 -0
- umap/static/umap/css/panel.css +6 -4
- 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 +11 -11
- umap/static/umap/js/components/alerts/alert.js +1 -1
- umap/static/umap/js/modules/autocomplete.js +27 -5
- umap/static/umap/js/modules/browser.js +20 -14
- umap/static/umap/js/modules/caption.js +4 -4
- umap/static/umap/js/modules/dompurify.js +2 -3
- umap/static/umap/js/modules/facets.js +53 -17
- umap/static/umap/js/modules/formatter.js +153 -0
- umap/static/umap/js/modules/global.js +25 -16
- umap/static/umap/js/modules/help.js +26 -26
- umap/static/umap/js/modules/importer.js +10 -10
- 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 +14 -14
- umap/static/umap/js/modules/importers/overpass.js +19 -15
- umap/static/umap/js/modules/orderable.js +2 -2
- umap/static/umap/js/modules/request.js +1 -1
- umap/static/umap/js/modules/rules.js +26 -11
- umap/static/umap/js/modules/schema.js +16 -12
- umap/static/umap/js/{umap.share.js → modules/share.js} +58 -103
- umap/static/umap/js/modules/slideshow.js +141 -0
- umap/static/umap/js/modules/sync/engine.js +3 -3
- umap/static/umap/js/modules/sync/updaters.js +10 -11
- umap/static/umap/js/modules/sync/websocket.js +1 -1
- 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 +7 -5
- umap/static/umap/js/modules/ui/tooltip.js +7 -77
- umap/static/umap/js/modules/urls.js +1 -2
- umap/static/umap/js/modules/utils.js +36 -16
- umap/static/umap/js/umap.controls.js +27 -29
- umap/static/umap/js/umap.core.js +19 -15
- umap/static/umap/js/umap.datalayer.permissions.js +15 -18
- umap/static/umap/js/umap.features.js +113 -131
- umap/static/umap/js/umap.forms.js +203 -228
- umap/static/umap/js/umap.icon.js +17 -22
- umap/static/umap/js/umap.js +117 -107
- umap/static/umap/js/umap.layer.js +374 -324
- umap/static/umap/js/umap.permissions.js +7 -10
- umap/static/umap/js/umap.popup.js +20 -20
- umap/static/umap/locale/am_ET.js +22 -5
- umap/static/umap/locale/am_ET.json +22 -5
- umap/static/umap/locale/ar.js +22 -5
- umap/static/umap/locale/ar.json +22 -5
- umap/static/umap/locale/ast.js +22 -5
- umap/static/umap/locale/ast.json +22 -5
- umap/static/umap/locale/bg.js +22 -5
- umap/static/umap/locale/bg.json +22 -5
- umap/static/umap/locale/br.js +22 -5
- umap/static/umap/locale/br.json +22 -5
- umap/static/umap/locale/ca.js +56 -39
- umap/static/umap/locale/ca.json +56 -39
- umap/static/umap/locale/cs_CZ.js +22 -5
- umap/static/umap/locale/cs_CZ.json +22 -5
- umap/static/umap/locale/da.js +22 -5
- umap/static/umap/locale/da.json +22 -5
- umap/static/umap/locale/de.js +22 -5
- umap/static/umap/locale/de.json +22 -5
- umap/static/umap/locale/el.js +27 -10
- umap/static/umap/locale/el.json +27 -10
- umap/static/umap/locale/en.js +22 -6
- umap/static/umap/locale/en.json +22 -6
- umap/static/umap/locale/en_US.json +22 -5
- umap/static/umap/locale/es.js +22 -6
- umap/static/umap/locale/es.json +22 -6
- umap/static/umap/locale/et.js +22 -5
- umap/static/umap/locale/et.json +22 -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 +22 -5
- umap/static/umap/locale/fi.js +22 -5
- umap/static/umap/locale/fi.json +22 -5
- umap/static/umap/locale/fr.js +22 -6
- umap/static/umap/locale/fr.json +22 -6
- umap/static/umap/locale/gl.js +22 -5
- umap/static/umap/locale/gl.json +22 -5
- umap/static/umap/locale/he.js +22 -5
- umap/static/umap/locale/he.json +22 -5
- umap/static/umap/locale/hr.js +22 -5
- umap/static/umap/locale/hr.json +22 -5
- umap/static/umap/locale/hu.js +89 -72
- umap/static/umap/locale/hu.json +89 -72
- umap/static/umap/locale/id.js +22 -5
- umap/static/umap/locale/id.json +22 -5
- umap/static/umap/locale/is.js +22 -5
- umap/static/umap/locale/is.json +22 -5
- umap/static/umap/locale/it.js +22 -5
- umap/static/umap/locale/it.json +22 -5
- umap/static/umap/locale/ja.js +22 -5
- umap/static/umap/locale/ja.json +22 -5
- umap/static/umap/locale/ko.js +22 -5
- umap/static/umap/locale/ko.json +22 -5
- umap/static/umap/locale/lt.js +22 -5
- umap/static/umap/locale/lt.json +22 -5
- umap/static/umap/locale/ms.js +22 -5
- umap/static/umap/locale/ms.json +22 -5
- umap/static/umap/locale/nl.js +22 -5
- umap/static/umap/locale/nl.json +22 -5
- umap/static/umap/locale/no.js +22 -5
- umap/static/umap/locale/no.json +22 -5
- umap/static/umap/locale/pl.js +22 -5
- umap/static/umap/locale/pl.json +22 -5
- umap/static/umap/locale/pl_PL.json +22 -5
- umap/static/umap/locale/pt.js +22 -6
- umap/static/umap/locale/pt.json +22 -6
- umap/static/umap/locale/pt_BR.js +22 -5
- umap/static/umap/locale/pt_BR.json +22 -5
- umap/static/umap/locale/pt_PT.js +22 -5
- umap/static/umap/locale/pt_PT.json +22 -5
- umap/static/umap/locale/ro.js +22 -5
- umap/static/umap/locale/ro.json +22 -5
- umap/static/umap/locale/ru.js +22 -5
- umap/static/umap/locale/ru.json +22 -5
- umap/static/umap/locale/sk_SK.js +22 -5
- umap/static/umap/locale/sk_SK.json +22 -5
- umap/static/umap/locale/sl.js +22 -5
- umap/static/umap/locale/sl.json +22 -5
- umap/static/umap/locale/sr.js +22 -5
- umap/static/umap/locale/sr.json +22 -5
- umap/static/umap/locale/sv.js +22 -5
- umap/static/umap/locale/sv.json +22 -5
- umap/static/umap/locale/th_TH.js +22 -5
- umap/static/umap/locale/th_TH.json +22 -5
- umap/static/umap/locale/tr.js +22 -5
- umap/static/umap/locale/tr.json +22 -5
- umap/static/umap/locale/uk_UA.js +22 -5
- umap/static/umap/locale/uk_UA.json +22 -5
- umap/static/umap/locale/vi.js +22 -5
- umap/static/umap/locale/vi.json +22 -5
- umap/static/umap/locale/vi_VN.json +22 -5
- umap/static/umap/locale/zh.js +22 -5
- umap/static/umap/locale/zh.json +22 -5
- umap/static/umap/locale/zh_CN.json +22 -5
- umap/static/umap/locale/zh_TW.Big5.json +22 -5
- umap/static/umap/locale/zh_TW.js +22 -5
- umap/static/umap/locale/zh_TW.json +22 -5
- umap/static/umap/map.css +9 -153
- umap/static/umap/vars.css +15 -0
- umap/static/umap/vendors/dompurify/purify.es.js +5 -59
- umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
- umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +410 -428
- umap/static/umap/vendors/geojson-to-gpx/index.js +155 -0
- umap/static/umap/vendors/osmtogeojson/osmtogeojson.js +1 -2
- umap/static/umap/vendors/togeojson/togeojson.es.js +1109 -0
- umap/static/umap/vendors/togeojson/{togeojson.umd.js.map → togeojson.es.mjs.map} +1 -1
- umap/static/umap/vendors/tokml/tokml.es.js +895 -0
- umap/static/umap/vendors/tokml/tokml.es.mjs.map +1 -0
- umap/storage.py +6 -2
- umap/templates/umap/components/alerts/alert.html +3 -3
- umap/templates/umap/css.html +3 -0
- umap/templates/umap/js.html +0 -6
- umap/tests/fixtures/categorized_highway.geojson +1 -0
- umap/tests/fixtures/test_import_osm_relation.json +130 -0
- umap/tests/integration/conftest.py +8 -1
- umap/tests/integration/test_browser.py +3 -2
- umap/tests/integration/test_categorized_layer.py +141 -0
- umap/tests/integration/test_conditional_rules.py +21 -0
- umap/tests/integration/test_datalayer.py +9 -4
- umap/tests/integration/test_edit_datalayer.py +1 -0
- umap/tests/integration/test_edit_polygon.py +1 -1
- umap/tests/integration/test_export_map.py +2 -3
- umap/tests/integration/test_import.py +22 -0
- umap/tests/integration/test_map_preview.py +36 -2
- umap/tests/integration/test_tableeditor.py +158 -4
- umap/tests/integration/test_websocket_sync.py +2 -2
- umap/tests/test_views.py +2 -2
- umap/views.py +3 -2
- {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/METADATA +8 -8
- {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/RECORD +194 -184
- umap/static/umap/js/umap.slideshow.js +0 -165
- 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.1.dist-info → umap_project-2.5.0.dist-info}/WHEEL +0 -0
- {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,11 +15,9 @@ U.Layer = {
|
|
|
15
15
|
return this._layers
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
-
getEditableOptions:
|
|
19
|
-
return []
|
|
20
|
-
},
|
|
18
|
+
getEditableOptions: () => [],
|
|
21
19
|
|
|
22
|
-
onEdit:
|
|
20
|
+
onEdit: () => {},
|
|
23
21
|
|
|
24
22
|
hasDataVisible: function () {
|
|
25
23
|
return !!Object.keys(this._layers).length
|
|
@@ -67,11 +65,9 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
|
|
67
65
|
polygonOptions: {
|
|
68
66
|
color: this.datalayer.getColor(),
|
|
69
67
|
},
|
|
70
|
-
iconCreateFunction:
|
|
71
|
-
return new U.Icon.Cluster(datalayer, cluster)
|
|
72
|
-
},
|
|
68
|
+
iconCreateFunction: (cluster) => new U.Icon.Cluster(datalayer, cluster),
|
|
73
69
|
}
|
|
74
|
-
if (this.datalayer.options.cluster
|
|
70
|
+
if (this.datalayer.options.cluster?.radius) {
|
|
75
71
|
options.maxClusterRadius = this.datalayer.options.cluster.radius
|
|
76
72
|
}
|
|
77
73
|
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
|
@@ -99,26 +95,24 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
|
|
99
95
|
return L.MarkerClusterGroup.prototype.removeLayer.call(this, layer)
|
|
100
96
|
},
|
|
101
97
|
|
|
102
|
-
getEditableOptions:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
]
|
|
121
|
-
},
|
|
98
|
+
getEditableOptions: () => [
|
|
99
|
+
[
|
|
100
|
+
'options.cluster.radius',
|
|
101
|
+
{
|
|
102
|
+
handler: 'BlurIntInput',
|
|
103
|
+
placeholder: L._('Clustering radius'),
|
|
104
|
+
helpText: L._('Override clustering radius (default 80)'),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
'options.cluster.textColor',
|
|
109
|
+
{
|
|
110
|
+
handler: 'TextColorPicker',
|
|
111
|
+
placeholder: L._('Auto'),
|
|
112
|
+
helpText: L._('Text color for the cluster label'),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
],
|
|
122
116
|
|
|
123
117
|
onEdit: function (field, builder) {
|
|
124
118
|
if (field === 'options.cluster.radius') {
|
|
@@ -132,7 +126,77 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
|
|
132
126
|
},
|
|
133
127
|
})
|
|
134
128
|
|
|
135
|
-
|
|
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({
|
|
136
200
|
statics: {
|
|
137
201
|
NAME: L._('Choropleth'),
|
|
138
202
|
TYPE: 'Choropleth',
|
|
@@ -153,42 +217,13 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
153
217
|
manual: L._('Manual'),
|
|
154
218
|
},
|
|
155
219
|
|
|
156
|
-
initialize: function (datalayer) {
|
|
157
|
-
this.datalayer = datalayer
|
|
158
|
-
if (!U.Utils.isObject(this.datalayer.options.choropleth)) {
|
|
159
|
-
this.datalayer.options.choropleth = {}
|
|
160
|
-
}
|
|
161
|
-
L.FeatureGroup.prototype.initialize.call(
|
|
162
|
-
this,
|
|
163
|
-
[],
|
|
164
|
-
this.datalayer.options.choropleth
|
|
165
|
-
)
|
|
166
|
-
this.datalayer.onceDataLoaded(() => {
|
|
167
|
-
this.redraw()
|
|
168
|
-
this.datalayer.on('datachanged', this.redraw, this)
|
|
169
|
-
})
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
redraw: function () {
|
|
173
|
-
this.computeBreaks()
|
|
174
|
-
if (this._map) this.eachLayer(this._map.addLayer, this._map)
|
|
175
|
-
},
|
|
176
|
-
|
|
177
220
|
_getValue: function (feature) {
|
|
178
221
|
const key = this.datalayer.options.choropleth.property || 'value'
|
|
179
|
-
|
|
222
|
+
const value = +feature.properties[key]
|
|
223
|
+
if (!Number.isNaN(value)) return value
|
|
180
224
|
},
|
|
181
225
|
|
|
182
|
-
|
|
183
|
-
const values = []
|
|
184
|
-
this.datalayer.eachLayer((layer) => {
|
|
185
|
-
let value = this._getValue(layer)
|
|
186
|
-
if (!isNaN(value)) values.push(value)
|
|
187
|
-
})
|
|
188
|
-
return values
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
computeBreaks: function () {
|
|
226
|
+
compute: function () {
|
|
192
227
|
const values = this.getValues()
|
|
193
228
|
|
|
194
229
|
if (!values.length) {
|
|
@@ -196,9 +231,9 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
196
231
|
this.options.colors = []
|
|
197
232
|
return
|
|
198
233
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
234
|
+
const mode = this.datalayer.options.choropleth.mode
|
|
235
|
+
let classes = +this.datalayer.options.choropleth.classes || 5
|
|
236
|
+
let breaks
|
|
202
237
|
classes = Math.min(classes, values.length)
|
|
203
238
|
if (mode === 'manual') {
|
|
204
239
|
const manualBreaks = this.datalayer.options.choropleth.breaks
|
|
@@ -206,7 +241,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
206
241
|
breaks = manualBreaks
|
|
207
242
|
.split(',')
|
|
208
243
|
.map((b) => +b)
|
|
209
|
-
.filter((b) => !isNaN(b))
|
|
244
|
+
.filter((b) => !Number.isNaN(b))
|
|
210
245
|
}
|
|
211
246
|
} else if (mode === 'equidistant') {
|
|
212
247
|
breaks = ss.equalIntervalBreaks(values, classes)
|
|
@@ -230,7 +265,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
230
265
|
},
|
|
231
266
|
|
|
232
267
|
getColor: function (feature) {
|
|
233
|
-
if (!feature) return // FIXME
|
|
268
|
+
if (!feature) return // FIXME should not happen
|
|
234
269
|
const featureValue = this._getValue(feature)
|
|
235
270
|
// Find the bucket/step/limit that this value is less than and give it that color
|
|
236
271
|
for (let i = 1; i < this.options.breaks.length; i++) {
|
|
@@ -240,25 +275,6 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
240
275
|
}
|
|
241
276
|
},
|
|
242
277
|
|
|
243
|
-
getOption: function (option, feature) {
|
|
244
|
-
if (feature && option === feature.staticOptions.mainColor) {
|
|
245
|
-
return this.getColor(feature)
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
addLayer: function (layer) {
|
|
250
|
-
// Do not add yet the layer to the map
|
|
251
|
-
// wait for datachanged event, so we want compute breaks once
|
|
252
|
-
var id = this.getLayerId(layer)
|
|
253
|
-
this._layers[id] = layer
|
|
254
|
-
return this
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
onAdd: function (map) {
|
|
258
|
-
this.computeBreaks()
|
|
259
|
-
L.FeatureGroup.prototype.onAdd.call(this, map)
|
|
260
|
-
},
|
|
261
|
-
|
|
262
278
|
onEdit: function (field, builder) {
|
|
263
279
|
// Only compute the breaks if we're dealing with choropleth
|
|
264
280
|
if (!field.startsWith('options.choropleth')) return
|
|
@@ -267,7 +283,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
267
283
|
this.datalayer.options.choropleth.mode = 'manual'
|
|
268
284
|
if (builder) builder.helpers['options.choropleth.mode'].fetch()
|
|
269
285
|
}
|
|
270
|
-
this.
|
|
286
|
+
this.compute()
|
|
271
287
|
// If user changes the mode or the number of classes,
|
|
272
288
|
// then update the breaks input value
|
|
273
289
|
if (field === 'options.choropleth.mode' || field === 'options.choropleth.classes') {
|
|
@@ -276,10 +292,6 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
276
292
|
},
|
|
277
293
|
|
|
278
294
|
getEditableOptions: function () {
|
|
279
|
-
const brewerSchemes = Object.keys(colorbrewer)
|
|
280
|
-
.filter((k) => k !== 'schemeGroups')
|
|
281
|
-
.sort()
|
|
282
|
-
|
|
283
295
|
return [
|
|
284
296
|
[
|
|
285
297
|
'options.choropleth.property',
|
|
@@ -294,7 +306,7 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
294
306
|
{
|
|
295
307
|
handler: 'Select',
|
|
296
308
|
label: L._('Choropleth color palette'),
|
|
297
|
-
selectOptions:
|
|
309
|
+
selectOptions: this.colorSchemes,
|
|
298
310
|
},
|
|
299
311
|
],
|
|
300
312
|
[
|
|
@@ -330,18 +342,139 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|
|
330
342
|
]
|
|
331
343
|
},
|
|
332
344
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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]]
|
|
345
478
|
})
|
|
346
479
|
},
|
|
347
480
|
})
|
|
@@ -364,13 +497,10 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|
|
364
497
|
|
|
365
498
|
addLayer: function (layer) {
|
|
366
499
|
if (layer instanceof L.Marker) {
|
|
367
|
-
let latlng = layer.getLatLng()
|
|
368
|
-
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
this.datalayer.options.heat.intensityProperty
|
|
372
|
-
) {
|
|
373
|
-
alt = parseFloat(
|
|
500
|
+
let latlng = layer.getLatLng()
|
|
501
|
+
let alt
|
|
502
|
+
if (this.datalayer.options.heat?.intensityProperty) {
|
|
503
|
+
alt = Number.parseFloat(
|
|
374
504
|
layer.properties[this.datalayer.options.heat.intensityProperty || 0]
|
|
375
505
|
)
|
|
376
506
|
latlng = new L.LatLng(latlng.lat, latlng.lng, alt)
|
|
@@ -383,37 +513,33 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|
|
383
513
|
this.setLatLngs([])
|
|
384
514
|
},
|
|
385
515
|
|
|
386
|
-
getFeatures:
|
|
387
|
-
return {}
|
|
388
|
-
},
|
|
516
|
+
getFeatures: () => ({}),
|
|
389
517
|
|
|
390
518
|
getBounds: function () {
|
|
391
519
|
return L.latLngBounds(this._latlngs)
|
|
392
520
|
},
|
|
393
521
|
|
|
394
|
-
getEditableOptions:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
]
|
|
416
|
-
},
|
|
522
|
+
getEditableOptions: () => [
|
|
523
|
+
[
|
|
524
|
+
'options.heat.radius',
|
|
525
|
+
{
|
|
526
|
+
handler: 'Range',
|
|
527
|
+
min: 10,
|
|
528
|
+
max: 100,
|
|
529
|
+
step: 5,
|
|
530
|
+
label: L._('Heatmap radius'),
|
|
531
|
+
helpText: L._('Override heatmap radius (default 25)'),
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
[
|
|
535
|
+
'options.heat.intensityProperty',
|
|
536
|
+
{
|
|
537
|
+
handler: 'BlurInput',
|
|
538
|
+
placeholder: L._('Heatmap intensity property'),
|
|
539
|
+
helpText: L._('Optional intensity property for heatmap'),
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
],
|
|
417
543
|
|
|
418
544
|
onEdit: function (field, builder) {
|
|
419
545
|
if (field === 'options.heat.intensityProperty') {
|
|
@@ -440,23 +566,23 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|
|
440
566
|
if (!this._map) {
|
|
441
567
|
return
|
|
442
568
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
569
|
+
const data = []
|
|
570
|
+
const r = this._heat._r
|
|
571
|
+
const size = this._map.getSize()
|
|
572
|
+
const bounds = new L.Bounds(L.point([-r, -r]), size.add([r, r]))
|
|
573
|
+
const cellSize = r / 2
|
|
574
|
+
const grid = []
|
|
575
|
+
const panePos = this._map._getMapPanePos()
|
|
576
|
+
const offsetX = panePos.x % cellSize
|
|
577
|
+
const offsetY = panePos.y % cellSize
|
|
578
|
+
let i
|
|
579
|
+
let len
|
|
580
|
+
let p
|
|
581
|
+
let cell
|
|
582
|
+
let x
|
|
583
|
+
let y
|
|
584
|
+
let j
|
|
585
|
+
let len2
|
|
460
586
|
|
|
461
587
|
this._max = 1
|
|
462
588
|
|
|
@@ -465,7 +591,7 @@ U.Layer.Heat = L.HeatLayer.extend({
|
|
|
465
591
|
x = Math.floor((p.x - offsetX) / cellSize) + 2
|
|
466
592
|
y = Math.floor((p.y - offsetY) / cellSize) + 2
|
|
467
593
|
|
|
468
|
-
|
|
594
|
+
const alt =
|
|
469
595
|
this._latlngs[i].alt !== undefined
|
|
470
596
|
? this._latlngs[i].alt
|
|
471
597
|
: this._latlngs[i][2] !== undefined
|
|
@@ -538,23 +664,20 @@ U.DataLayer = L.Evented.extend({
|
|
|
538
664
|
|
|
539
665
|
let isDirty = false
|
|
540
666
|
let isDeleted = false
|
|
541
|
-
const self = this
|
|
542
667
|
try {
|
|
543
668
|
Object.defineProperty(this, 'isDirty', {
|
|
544
|
-
get:
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
set: function (status) {
|
|
548
|
-
if (!isDirty && status) self.fire('dirty')
|
|
669
|
+
get: () => isDirty,
|
|
670
|
+
set: (status) => {
|
|
671
|
+
if (!isDirty && status) this.fire('dirty')
|
|
549
672
|
isDirty = status
|
|
550
673
|
if (status) {
|
|
551
|
-
|
|
674
|
+
this.map.addDirtyDatalayer(this)
|
|
552
675
|
// A layer can be made dirty by indirect action (like dragging layers)
|
|
553
676
|
// we need to have it loaded before saving it.
|
|
554
|
-
if (!
|
|
677
|
+
if (!this.isLoaded()) this.fetchData()
|
|
555
678
|
} else {
|
|
556
|
-
|
|
557
|
-
|
|
679
|
+
this.map.removeDirtyDatalayer(this)
|
|
680
|
+
this.isDeleted = false
|
|
558
681
|
}
|
|
559
682
|
},
|
|
560
683
|
})
|
|
@@ -563,13 +686,11 @@ U.DataLayer = L.Evented.extend({
|
|
|
563
686
|
}
|
|
564
687
|
try {
|
|
565
688
|
Object.defineProperty(this, 'isDeleted', {
|
|
566
|
-
get:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
set: function (status) {
|
|
570
|
-
if (!isDeleted && status) self.fire('deleted')
|
|
689
|
+
get: () => isDeleted,
|
|
690
|
+
set: (status) => {
|
|
691
|
+
if (!isDeleted && status) this.fire('deleted')
|
|
571
692
|
isDeleted = status
|
|
572
|
-
if (status)
|
|
693
|
+
if (status) this.isDirty = status
|
|
573
694
|
},
|
|
574
695
|
})
|
|
575
696
|
} catch (e) {
|
|
@@ -582,11 +703,11 @@ U.DataLayer = L.Evented.extend({
|
|
|
582
703
|
this.options.remoteData = {}
|
|
583
704
|
}
|
|
584
705
|
// Retrocompat
|
|
585
|
-
if (this.options.remoteData
|
|
706
|
+
if (this.options.remoteData?.from) {
|
|
586
707
|
this.options.fromZoom = this.options.remoteData.from
|
|
587
708
|
delete this.options.remoteData.from
|
|
588
709
|
}
|
|
589
|
-
if (this.options.remoteData
|
|
710
|
+
if (this.options.remoteData?.to) {
|
|
590
711
|
this.options.toZoom = this.options.remoteData.to
|
|
591
712
|
delete this.options.remoteData.to
|
|
592
713
|
}
|
|
@@ -618,9 +739,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
618
739
|
},
|
|
619
740
|
|
|
620
741
|
render: function (fields, builder) {
|
|
621
|
-
|
|
742
|
+
const impacts = U.Utils.getImpactsFromSchema(fields)
|
|
622
743
|
|
|
623
|
-
for (
|
|
744
|
+
for (const impact of impacts) {
|
|
624
745
|
switch (impact) {
|
|
625
746
|
case 'ui':
|
|
626
747
|
this.map.onDataLayersChanged()
|
|
@@ -630,9 +751,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
630
751
|
this.resetLayer()
|
|
631
752
|
}
|
|
632
753
|
this.hide()
|
|
633
|
-
|
|
754
|
+
for (const field of fields) {
|
|
634
755
|
this.layer.onEdit(field, builder)
|
|
635
|
-
}
|
|
756
|
+
}
|
|
636
757
|
this.redraw()
|
|
637
758
|
this.show()
|
|
638
759
|
break
|
|
@@ -745,19 +866,19 @@ U.DataLayer = L.Evented.extend({
|
|
|
745
866
|
}
|
|
746
867
|
},
|
|
747
868
|
|
|
748
|
-
fromGeoJSON: function (geojson) {
|
|
749
|
-
this.addData(geojson)
|
|
869
|
+
fromGeoJSON: function (geojson, sync = true) {
|
|
870
|
+
this.addData(geojson, sync)
|
|
750
871
|
this._geojson = geojson
|
|
751
872
|
this._dataloaded = true
|
|
752
|
-
this.fire('dataloaded')
|
|
753
873
|
this.fire('datachanged')
|
|
874
|
+
this.fire('dataloaded')
|
|
754
875
|
},
|
|
755
876
|
|
|
756
877
|
fromUmapGeoJSON: async function (geojson) {
|
|
757
878
|
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
|
|
758
879
|
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
|
759
880
|
if (this.isRemoteLayer()) await this.fetchRemoteData()
|
|
760
|
-
else this.fromGeoJSON(geojson)
|
|
881
|
+
else this.fromGeoJSON(geojson, false)
|
|
761
882
|
this._loaded = true
|
|
762
883
|
},
|
|
763
884
|
|
|
@@ -787,14 +908,14 @@ U.DataLayer = L.Evented.extend({
|
|
|
787
908
|
},
|
|
788
909
|
|
|
789
910
|
showAtZoom: function () {
|
|
790
|
-
const from = parseInt(this.options.fromZoom, 10)
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
return !((!isNaN(from) && zoom < from) || (!isNaN(to) && zoom > to))
|
|
911
|
+
const from = Number.parseInt(this.options.fromZoom, 10)
|
|
912
|
+
const to = Number.parseInt(this.options.toZoom, 10)
|
|
913
|
+
const zoom = this.map.getZoom()
|
|
914
|
+
return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to))
|
|
794
915
|
},
|
|
795
916
|
|
|
796
917
|
hasDynamicData: function () {
|
|
797
|
-
return !!
|
|
918
|
+
return !!this.options.remoteData?.dynamic
|
|
798
919
|
},
|
|
799
920
|
|
|
800
921
|
fetchRemoteData: async function (force) {
|
|
@@ -806,13 +927,11 @@ U.DataLayer = L.Evented.extend({
|
|
|
806
927
|
url = this.map.proxyUrl(url, this.options.remoteData.ttl)
|
|
807
928
|
}
|
|
808
929
|
const response = await this.map.request.get(url)
|
|
809
|
-
if (response
|
|
930
|
+
if (response?.ok) {
|
|
810
931
|
this.clear()
|
|
811
|
-
this.
|
|
812
|
-
await response.text(),
|
|
813
|
-
this.
|
|
814
|
-
(geojson) => this.fromGeoJSON(geojson)
|
|
815
|
-
)
|
|
932
|
+
this.map.formatter
|
|
933
|
+
.parse(await response.text(), this.options.remoteData.format)
|
|
934
|
+
.then((geojson) => this.fromGeoJSON(geojson))
|
|
816
935
|
}
|
|
817
936
|
},
|
|
818
937
|
|
|
@@ -884,11 +1003,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
884
1003
|
},
|
|
885
1004
|
|
|
886
1005
|
isRemoteLayer: function () {
|
|
887
|
-
return Boolean(
|
|
888
|
-
this.options.remoteData &&
|
|
889
|
-
this.options.remoteData.url &&
|
|
890
|
-
this.options.remoteData.format
|
|
891
|
-
)
|
|
1006
|
+
return Boolean(this.options.remoteData?.url && this.options.remoteData.format)
|
|
892
1007
|
},
|
|
893
1008
|
|
|
894
1009
|
isClustered: function () {
|
|
@@ -911,14 +1026,15 @@ U.DataLayer = L.Evented.extend({
|
|
|
911
1026
|
if (this.hasDataLoaded()) this.fire('datachanged')
|
|
912
1027
|
},
|
|
913
1028
|
|
|
914
|
-
removeLayer: function (feature) {
|
|
1029
|
+
removeLayer: function (feature, sync) {
|
|
915
1030
|
const id = L.stamp(feature)
|
|
1031
|
+
if (sync !== false) feature.sync.delete()
|
|
916
1032
|
this.layer.removeLayer(feature)
|
|
917
1033
|
feature.disconnectFromDataLayer(this)
|
|
918
1034
|
this._index.splice(this._index.indexOf(id), 1)
|
|
919
1035
|
delete this._layers[id]
|
|
920
1036
|
delete this.map.features_index[feature.getSlug()]
|
|
921
|
-
if (this.hasDataLoaded()) this.fire('datachanged')
|
|
1037
|
+
if (this.hasDataLoaded() && this.isVisible()) this.fire('datachanged')
|
|
922
1038
|
},
|
|
923
1039
|
|
|
924
1040
|
indexProperties: function (feature) {
|
|
@@ -931,6 +1047,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
931
1047
|
if (name.indexOf('_') === 0) return
|
|
932
1048
|
if (L.Util.indexOf(this._propertiesIndex, name) !== -1) return
|
|
933
1049
|
this._propertiesIndex.push(name)
|
|
1050
|
+
this._propertiesIndex.sort()
|
|
934
1051
|
},
|
|
935
1052
|
|
|
936
1053
|
deindexProperty: function (name) {
|
|
@@ -938,100 +1055,30 @@ U.DataLayer = L.Evented.extend({
|
|
|
938
1055
|
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
|
|
939
1056
|
},
|
|
940
1057
|
|
|
941
|
-
|
|
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
|
+
|
|
1065
|
+
addData: function (geojson, sync) {
|
|
942
1066
|
try {
|
|
943
1067
|
// Do not fail if remote data is somehow invalid,
|
|
944
1068
|
// otherwise the layer becomes uneditable.
|
|
945
|
-
this.geojsonToFeatures(geojson)
|
|
1069
|
+
this.geojsonToFeatures(geojson, sync)
|
|
946
1070
|
} catch (err) {
|
|
947
1071
|
console.log('Error with DataLayer', this.umap_id)
|
|
948
1072
|
console.error(err)
|
|
949
1073
|
}
|
|
950
1074
|
},
|
|
951
1075
|
|
|
952
|
-
addRawData: function (c, type) {
|
|
953
|
-
this.rawToGeoJSON(c, type, (geojson) => this.addData(geojson))
|
|
954
|
-
},
|
|
955
|
-
|
|
956
|
-
rawToGeoJSON: function (c, type, callback) {
|
|
957
|
-
const toDom = (x) => {
|
|
958
|
-
const doc = new DOMParser().parseFromString(x, 'text/xml')
|
|
959
|
-
const errorNode = doc.querySelector('parsererror')
|
|
960
|
-
if (errorNode) {
|
|
961
|
-
U.Alert.error(L._('Cannot parse data'))
|
|
962
|
-
}
|
|
963
|
-
return doc
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
// TODO add a duck typing guessType
|
|
967
|
-
if (type === 'csv') {
|
|
968
|
-
csv2geojson.csv2geojson(
|
|
969
|
-
c,
|
|
970
|
-
{
|
|
971
|
-
delimiter: 'auto',
|
|
972
|
-
includeLatLon: false,
|
|
973
|
-
},
|
|
974
|
-
(err, result) => {
|
|
975
|
-
// csv2geojson fallback to null geometries when it cannot determine
|
|
976
|
-
// lat or lon columns. This is valid geojson, but unwanted from a user
|
|
977
|
-
// point of view.
|
|
978
|
-
if (result && result.features.length) {
|
|
979
|
-
if (result.features[0].geometry === null) {
|
|
980
|
-
err = {
|
|
981
|
-
type: 'Error',
|
|
982
|
-
message: L._('Cannot determine latitude and longitude columns.'),
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
if (err) {
|
|
987
|
-
let message
|
|
988
|
-
if (err.type === 'Error') {
|
|
989
|
-
message = err.message
|
|
990
|
-
} else {
|
|
991
|
-
message = L._('{count} errors during import: {message}', {
|
|
992
|
-
count: err.length,
|
|
993
|
-
message: err[0].message,
|
|
994
|
-
})
|
|
995
|
-
}
|
|
996
|
-
U.Alert.error(message, 10000)
|
|
997
|
-
console.error(err)
|
|
998
|
-
}
|
|
999
|
-
if (result && result.features.length) {
|
|
1000
|
-
callback(result)
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
)
|
|
1004
|
-
} else if (type === 'gpx') {
|
|
1005
|
-
callback(toGeoJSON.gpx(toDom(c)))
|
|
1006
|
-
} else if (type === 'georss') {
|
|
1007
|
-
callback(GeoRSSToGeoJSON(toDom(c)))
|
|
1008
|
-
} else if (type === 'kml') {
|
|
1009
|
-
callback(toGeoJSON.kml(toDom(c)))
|
|
1010
|
-
} else if (type === 'osm') {
|
|
1011
|
-
let d
|
|
1012
|
-
try {
|
|
1013
|
-
d = JSON.parse(c)
|
|
1014
|
-
} catch (e) {
|
|
1015
|
-
d = toDom(c)
|
|
1016
|
-
}
|
|
1017
|
-
callback(osmtogeojson(d, { flatProperties: true }))
|
|
1018
|
-
} else if (type === 'geojson') {
|
|
1019
|
-
try {
|
|
1020
|
-
const gj = JSON.parse(c)
|
|
1021
|
-
callback(gj)
|
|
1022
|
-
} catch (err) {
|
|
1023
|
-
U.Alert.error(`Invalid JSON file: ${err}`)
|
|
1024
|
-
return
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
},
|
|
1028
|
-
|
|
1029
1076
|
// The choice of the name is not ours, because it is required by Leaflet.
|
|
1030
1077
|
// It is misleading, as the returned objects are uMap objects, and not
|
|
1031
1078
|
// GeoJSON features.
|
|
1032
|
-
geojsonToFeatures: function (geojson) {
|
|
1079
|
+
geojsonToFeatures: function (geojson, sync) {
|
|
1033
1080
|
if (!geojson) return
|
|
1034
|
-
const features = geojson
|
|
1081
|
+
const features = Array.isArray(geojson) ? geojson : geojson.features
|
|
1035
1082
|
let i
|
|
1036
1083
|
let len
|
|
1037
1084
|
|
|
@@ -1045,10 +1092,10 @@ U.DataLayer = L.Evented.extend({
|
|
|
1045
1092
|
|
|
1046
1093
|
const geometry = geojson.type === 'Feature' ? geojson.geometry : geojson
|
|
1047
1094
|
|
|
1048
|
-
|
|
1095
|
+
const feature = this.geoJSONToLeaflet({ geometry, geojson })
|
|
1049
1096
|
if (feature) {
|
|
1050
1097
|
this.addLayer(feature)
|
|
1051
|
-
feature.onCommit()
|
|
1098
|
+
if (sync) feature.onCommit()
|
|
1052
1099
|
return feature
|
|
1053
1100
|
}
|
|
1054
1101
|
},
|
|
@@ -1077,7 +1124,8 @@ U.DataLayer = L.Evented.extend({
|
|
|
1077
1124
|
} = {}) {
|
|
1078
1125
|
if (!geometry) return // null geometry is valid geojson.
|
|
1079
1126
|
const coords = geometry.coordinates
|
|
1080
|
-
let latlng
|
|
1127
|
+
let latlng
|
|
1128
|
+
let latlngs
|
|
1081
1129
|
|
|
1082
1130
|
// Create a default geojson if none is provided
|
|
1083
1131
|
if (geojson === undefined) geojson = { type: 'Feature', geometry: geometry }
|
|
@@ -1154,10 +1202,12 @@ U.DataLayer = L.Evented.extend({
|
|
|
1154
1202
|
return new U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this }, id)
|
|
1155
1203
|
},
|
|
1156
1204
|
|
|
1157
|
-
importRaw: function (raw,
|
|
1158
|
-
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())
|
|
1159
1210
|
this.isDirty = true
|
|
1160
|
-
this.zoomTo()
|
|
1161
1211
|
},
|
|
1162
1212
|
|
|
1163
1213
|
importFromFiles: function (files, type) {
|
|
@@ -1176,7 +1226,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
1176
1226
|
importFromUrl: async function (uri, type) {
|
|
1177
1227
|
uri = this.map.localizeUrl(uri)
|
|
1178
1228
|
const response = await this.map.request.get(uri)
|
|
1179
|
-
if (response
|
|
1229
|
+
if (response?.ok) {
|
|
1180
1230
|
this.importRaw(await response.text(), type)
|
|
1181
1231
|
}
|
|
1182
1232
|
},
|
|
@@ -1222,8 +1272,8 @@ U.DataLayer = L.Evented.extend({
|
|
|
1222
1272
|
const options = U.Utils.CopyJSON(this.options)
|
|
1223
1273
|
options.name = L._('Clone of {name}', { name: this.options.name })
|
|
1224
1274
|
delete options.id
|
|
1225
|
-
const geojson = U.Utils.CopyJSON(this._geojson)
|
|
1226
|
-
|
|
1275
|
+
const geojson = U.Utils.CopyJSON(this._geojson)
|
|
1276
|
+
const datalayer = this.map.createDataLayer(options)
|
|
1227
1277
|
datalayer.fromGeoJSON(geojson)
|
|
1228
1278
|
return datalayer
|
|
1229
1279
|
},
|
|
@@ -1272,28 +1322,28 @@ U.DataLayer = L.Evented.extend({
|
|
|
1272
1322
|
if (!this.map.editEnabled || !this.isLoaded()) {
|
|
1273
1323
|
return
|
|
1274
1324
|
}
|
|
1275
|
-
const container = L.DomUtil.create('div', 'umap-layer-properties-container')
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1325
|
+
const container = L.DomUtil.create('div', 'umap-layer-properties-container')
|
|
1326
|
+
const metadataFields = [
|
|
1327
|
+
'options.name',
|
|
1328
|
+
'options.description',
|
|
1329
|
+
['options.type', { handler: 'LayerTypeChooser', label: L._('Type of layer') }],
|
|
1330
|
+
['options.displayOnLoad', { label: L._('Display on load'), handler: 'Switch' }],
|
|
1331
|
+
[
|
|
1332
|
+
'options.browsable',
|
|
1333
|
+
{
|
|
1334
|
+
label: L._('Data is browsable'),
|
|
1335
|
+
handler: 'Switch',
|
|
1336
|
+
helpEntries: 'browsable',
|
|
1337
|
+
},
|
|
1338
|
+
],
|
|
1339
|
+
[
|
|
1340
|
+
'options.inCaption',
|
|
1341
|
+
{
|
|
1342
|
+
label: L._('Show this layer in the caption'),
|
|
1343
|
+
handler: 'Switch',
|
|
1344
|
+
},
|
|
1345
|
+
],
|
|
1346
|
+
]
|
|
1297
1347
|
L.DomUtil.createTitle(container, L._('Layer properties'), 'icon-layers')
|
|
1298
1348
|
let builder = new U.FormBuilder(this, metadataFields, {
|
|
1299
1349
|
callback: function (e) {
|
|
@@ -1318,7 +1368,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
1318
1368
|
layerProperties.appendChild(builder.build())
|
|
1319
1369
|
}
|
|
1320
1370
|
|
|
1321
|
-
|
|
1371
|
+
const shapeOptions = [
|
|
1322
1372
|
'options.color',
|
|
1323
1373
|
'options.iconClass',
|
|
1324
1374
|
'options.iconUrl',
|
|
@@ -1337,7 +1387,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
1337
1387
|
const shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'))
|
|
1338
1388
|
shapeProperties.appendChild(builder.build())
|
|
1339
1389
|
|
|
1340
|
-
|
|
1390
|
+
const optionsFields = [
|
|
1341
1391
|
'options.smoothFactor',
|
|
1342
1392
|
'options.dashArray',
|
|
1343
1393
|
'options.zoomTo',
|
|
@@ -1484,23 +1534,23 @@ U.DataLayer = L.Evented.extend({
|
|
|
1484
1534
|
},
|
|
1485
1535
|
|
|
1486
1536
|
getOption: function (option, feature) {
|
|
1487
|
-
if (this.layer
|
|
1537
|
+
if (this.layer?.getOption) {
|
|
1488
1538
|
const value = this.layer.getOption(option, feature)
|
|
1489
1539
|
if (typeof value !== 'undefined') return value
|
|
1490
1540
|
}
|
|
1491
1541
|
if (typeof this.getOwnOption(option) !== 'undefined') {
|
|
1492
1542
|
return this.getOwnOption(option)
|
|
1493
|
-
}
|
|
1543
|
+
}
|
|
1544
|
+
if (this.layer?.defaults?.[option]) {
|
|
1494
1545
|
return this.layer.defaults[option]
|
|
1495
|
-
} else {
|
|
1496
|
-
return this.map.getOption(option, feature)
|
|
1497
1546
|
}
|
|
1547
|
+
return this.map.getOption(option, feature)
|
|
1498
1548
|
},
|
|
1499
1549
|
|
|
1500
1550
|
buildVersionsFieldset: async function (container) {
|
|
1501
1551
|
const appendVersion = (data) => {
|
|
1502
|
-
const date = new Date(parseInt(data.at, 10))
|
|
1503
|
-
const content = `${date.toLocaleString(L.lang)} (${parseInt(data.size) / 1000}Kb)`
|
|
1552
|
+
const date = new Date(Number.parseInt(data.at, 10))
|
|
1553
|
+
const content = `${date.toLocaleString(L.lang)} (${Number.parseInt(data.size) / 1000}Kb)`
|
|
1504
1554
|
const el = L.DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
|
|
1505
1555
|
const button = L.DomUtil.createButton(
|
|
1506
1556
|
'',
|
|
@@ -1576,7 +1626,7 @@ U.DataLayer = L.Evented.extend({
|
|
|
1576
1626
|
|
|
1577
1627
|
// Is this layer type browsable in theorie
|
|
1578
1628
|
isBrowsable: function () {
|
|
1579
|
-
return this.layer
|
|
1629
|
+
return this.layer?.browsable
|
|
1580
1630
|
},
|
|
1581
1631
|
|
|
1582
1632
|
// Is this layer browsable in theorie
|
|
@@ -1753,9 +1803,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
1753
1803
|
},
|
|
1754
1804
|
|
|
1755
1805
|
tableEdit: function () {
|
|
1756
|
-
if (
|
|
1806
|
+
if (!this.isVisible()) return
|
|
1757
1807
|
const editor = new U.TableEditor(this)
|
|
1758
|
-
editor.
|
|
1808
|
+
editor.open()
|
|
1759
1809
|
},
|
|
1760
1810
|
|
|
1761
1811
|
getFilterKeys: function () {
|
|
@@ -1763,9 +1813,9 @@ U.DataLayer = L.Evented.extend({
|
|
|
1763
1813
|
// By default, it will we use the "name" property, which is also the one used as label in the features list.
|
|
1764
1814
|
// When map owner has configured another label or sort key, we try to be smart and search in the same keys.
|
|
1765
1815
|
if (this.map.options.filterKey) return this.map.options.filterKey
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1816
|
+
if (this.options.labelKey) return this.options.labelKey
|
|
1817
|
+
if (this.map.options.sortKey) return this.map.options.sortKey
|
|
1818
|
+
return 'name'
|
|
1769
1819
|
},
|
|
1770
1820
|
})
|
|
1771
1821
|
|