umap-project 3.0.6__py3-none-any.whl → 3.1.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.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/forms.py +1 -1
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +219 -72
- umap/locale/ca/LC_MESSAGES/django.mo +0 -0
- umap/locale/ca/LC_MESSAGES/django.po +286 -95
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +211 -65
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +394 -202
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +146 -75
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +125 -59
- umap/locale/en/LC_MESSAGES/django.po +124 -58
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +125 -59
- umap/locale/et/LC_MESSAGES/django.mo +0 -0
- umap/locale/et/LC_MESSAGES/django.po +210 -64
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +212 -65
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +286 -95
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +125 -59
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +212 -66
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +148 -78
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +130 -60
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +125 -59
- umap/locale/ms/LC_MESSAGES/django.mo +0 -0
- umap/locale/ms/LC_MESSAGES/django.po +289 -98
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +128 -61
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +287 -96
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +211 -65
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +212 -66
- umap/management/commands/migrate_to_S3.py +42 -20
- umap/management/commands/purge_old_versions.py +63 -0
- umap/management/commands/switch_user.py +52 -0
- umap/managers.py +29 -2
- umap/middleware.py +1 -1
- umap/migrations/0028_map_is_template.py +21 -0
- umap/models.py +14 -4
- umap/settings/base.py +22 -0
- umap/static/umap/base.css +4 -2
- umap/static/umap/content.css +1 -1
- umap/static/umap/css/dialog.css +5 -2
- umap/static/umap/css/form.css +19 -12
- umap/static/umap/css/icon.css +6 -0
- umap/static/umap/css/importers.css +4 -0
- umap/static/umap/css/panel.css +2 -0
- umap/static/umap/img/16-white.svg +5 -1
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24-white.svg +3 -2
- umap/static/umap/img/24.svg +3 -4
- umap/static/umap/img/importers/opendata.svg +1 -0
- umap/static/umap/img/source/16-white.svg +8 -4
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24-white.svg +5 -4
- umap/static/umap/img/source/24.svg +5 -6
- umap/static/umap/js/components/modal.js +27 -0
- umap/static/umap/js/modules/caption.js +4 -4
- umap/static/umap/js/modules/data/features.js +40 -4
- umap/static/umap/js/modules/data/layer.js +208 -138
- umap/static/umap/js/modules/form/builder.js +6 -14
- umap/static/umap/js/modules/form/fields.js +2 -2
- umap/static/umap/js/modules/help.js +11 -3
- umap/static/umap/js/modules/importer.js +7 -4
- umap/static/umap/js/modules/importers/opendata.js +142 -0
- umap/static/umap/js/modules/permissions.js +3 -3
- umap/static/umap/js/modules/rendering/controls.js +34 -2
- umap/static/umap/js/modules/rendering/icon.js +2 -2
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +55 -49
- umap/static/umap/js/modules/rendering/layers/cluster.js +16 -9
- umap/static/umap/js/modules/rendering/layers/heat.js +13 -11
- umap/static/umap/js/modules/rendering/map.js +5 -0
- umap/static/umap/js/modules/rendering/ui.js +23 -0
- umap/static/umap/js/modules/rules.js +24 -23
- umap/static/umap/js/modules/schema.js +60 -4
- umap/static/umap/js/modules/sync/updaters.js +7 -3
- umap/static/umap/js/modules/tableeditor.js +7 -30
- umap/static/umap/js/modules/templates.js +122 -0
- umap/static/umap/js/modules/ui/bar.js +13 -3
- umap/static/umap/js/modules/ui/panel.js +1 -1
- umap/static/umap/js/modules/umap.js +28 -13
- umap/static/umap/js/umap.controls.js +11 -4
- umap/static/umap/locale/br.js +51 -18
- umap/static/umap/locale/br.json +51 -18
- umap/static/umap/locale/da.js +343 -310
- umap/static/umap/locale/da.json +343 -310
- umap/static/umap/locale/de.js +40 -7
- umap/static/umap/locale/de.json +40 -7
- umap/static/umap/locale/el.js +31 -4
- umap/static/umap/locale/el.json +31 -4
- umap/static/umap/locale/en.js +33 -1
- umap/static/umap/locale/en.json +33 -1
- umap/static/umap/locale/es.js +37 -4
- umap/static/umap/locale/es.json +37 -4
- umap/static/umap/locale/fr.js +34 -1
- umap/static/umap/locale/fr.json +34 -1
- umap/static/umap/locale/hu.js +44 -17
- umap/static/umap/locale/hu.json +44 -17
- umap/static/umap/locale/it.js +74 -41
- umap/static/umap/locale/it.json +74 -41
- umap/static/umap/locale/nl.js +42 -9
- umap/static/umap/locale/nl.json +42 -9
- umap/static/umap/map.css +3 -23
- umap/static/umap/vendors/textpath/leaflet.textpath.js +184 -0
- umap/storage/fs.py +19 -9
- umap/templates/umap/dashboard_menu.html +5 -0
- umap/templates/umap/design_system.html +9 -0
- umap/templates/umap/js.html +3 -0
- umap/templates/umap/map_list.html +2 -2
- umap/templates/umap/map_table.html +18 -18
- umap/templates/umap/user_dashboard.html +9 -58
- umap/templates/umap/user_map_table.html +36 -0
- umap/templates/umap/user_templates.html +19 -0
- umap/templatetags/umap_tags.py +5 -0
- umap/tests/integration/test_conditional_rules.py +57 -0
- umap/tests/integration/test_datalayer.py +16 -9
- umap/tests/integration/test_edit_marker.py +11 -0
- umap/tests/integration/test_tableeditor.py +42 -7
- umap/tests/integration/test_templates.py +44 -0
- umap/tests/test_dashboard.py +19 -0
- umap/tests/test_purge_old_versions.py +91 -0
- umap/tests/test_switch_user.py +31 -0
- umap/tests/test_views.py +67 -0
- umap/urls.py +7 -1
- umap/views.py +64 -18
- {umap_project-3.0.6.dist-info → umap_project-3.1.0.dist-info}/METADATA +14 -14
- {umap_project-3.0.6.dist-info → umap_project-3.1.0.dist-info}/RECORD +142 -129
- {umap_project-3.0.6.dist-info → umap_project-3.1.0.dist-info}/WHEEL +0 -0
- {umap_project-3.0.6.dist-info → umap_project-3.1.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.0.6.dist-info → umap_project-3.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -127,6 +127,12 @@ export class MutatingForm extends Form {
|
|
|
127
127
|
facetKey: 'PropertyInput',
|
|
128
128
|
slugKey: 'PropertyInput',
|
|
129
129
|
labelKey: 'PropertyInput',
|
|
130
|
+
color: 'ColorPicker',
|
|
131
|
+
fillColor: 'ColorPicker',
|
|
132
|
+
textPathColor: 'ColorPicker',
|
|
133
|
+
iconUrl: 'IconUrl',
|
|
134
|
+
licence: 'LicenceChooser',
|
|
135
|
+
datalayersControl: 'DataLayersControl',
|
|
130
136
|
}
|
|
131
137
|
for (const [key, defaults] of Object.entries(SCHEMA)) {
|
|
132
138
|
const properties = Object.assign({}, defaults)
|
|
@@ -153,21 +159,7 @@ export class MutatingForm extends Form {
|
|
|
153
159
|
} else if (properties.type === Number) {
|
|
154
160
|
if (properties.step) properties.handler = 'Range'
|
|
155
161
|
else properties.handler = 'IntInput'
|
|
156
|
-
} else {
|
|
157
|
-
switch (key) {
|
|
158
|
-
case 'color':
|
|
159
|
-
case 'fillColor':
|
|
160
|
-
properties.handler = 'ColorPicker'
|
|
161
|
-
break
|
|
162
|
-
case 'iconUrl':
|
|
163
|
-
properties.handler = 'IconUrl'
|
|
164
|
-
break
|
|
165
|
-
case 'licence':
|
|
166
|
-
properties.handler = 'LicenceChooser'
|
|
167
|
-
break
|
|
168
|
-
}
|
|
169
162
|
}
|
|
170
|
-
|
|
171
163
|
if (customHandlers[key]) {
|
|
172
164
|
properties.handler = customHandlers[key]
|
|
173
165
|
}
|
|
@@ -725,7 +725,7 @@ Fields.IconUrl = class extends Fields.BlurInput {
|
|
|
725
725
|
<button class="flat tab-url" data-ref=url>${translate('URL')}</button>
|
|
726
726
|
</div>
|
|
727
727
|
`)
|
|
728
|
-
this.tabs.appendChild(
|
|
728
|
+
;[recent, symbols, chars, url].forEach((node) => this.tabs.appendChild(node))
|
|
729
729
|
if (Icon.RECENT.length) {
|
|
730
730
|
recent.addEventListener('click', (event) => {
|
|
731
731
|
event.stopPropagation()
|
|
@@ -1269,7 +1269,7 @@ Fields.Range = class extends Fields.FloatInput {
|
|
|
1269
1269
|
for (
|
|
1270
1270
|
let i = this.properties.min;
|
|
1271
1271
|
i <= this.properties.max;
|
|
1272
|
-
i += this.properties.
|
|
1272
|
+
i += (this.properties.max - this.properties.min) / 10
|
|
1273
1273
|
) {
|
|
1274
1274
|
const ii = i.toFixed(digits)
|
|
1275
1275
|
options += `<option value="${ii}" label="${ii}"></option>`
|
|
@@ -223,10 +223,12 @@ export default class Help {
|
|
|
223
223
|
|
|
224
224
|
// Special dynamic case. Do we still think this dialog is useful?
|
|
225
225
|
showGetStarted() {
|
|
226
|
-
const [container, {
|
|
226
|
+
const [container, { getstarted, links }] = Utils.loadTemplateWithRefs(`
|
|
227
227
|
<div>
|
|
228
228
|
<h3><i class="icon icon-16 icon-help"></i>${translate('Where do we go from here?')}</h3>
|
|
229
|
-
<ul data-ref=
|
|
229
|
+
<ul data-ref=getstarted class="umap-getstarted"></ul>
|
|
230
|
+
<h4>${translate('More help resources')}</h4>
|
|
231
|
+
<ul data-ref=links class="umap-help-links"></ul>
|
|
230
232
|
</div>
|
|
231
233
|
`)
|
|
232
234
|
const elements = document.querySelectorAll('[data-getstarted]')
|
|
@@ -234,12 +236,18 @@ export default class Help {
|
|
|
234
236
|
const [node, { button }] = Utils.loadTemplateWithRefs(
|
|
235
237
|
`<li><button data-ref=button type="button" title="${el.title}">${el.innerHTML}${el.title}</button></li>`
|
|
236
238
|
)
|
|
237
|
-
|
|
239
|
+
getstarted.appendChild(node)
|
|
238
240
|
button.addEventListener('click', () => {
|
|
239
241
|
el.click()
|
|
240
242
|
this.dialog.close()
|
|
241
243
|
})
|
|
242
244
|
}
|
|
245
|
+
for (const info of this.umap.properties.help_links) {
|
|
246
|
+
const [node, { button }] = Utils.loadTemplateWithRefs(
|
|
247
|
+
`<li><a href="${info.url}" target="_blank">${info.label} (${info.lang})</a><i class="icon icon-16 icon-external-link"></i></li>`
|
|
248
|
+
)
|
|
249
|
+
links.appendChild(node)
|
|
250
|
+
}
|
|
243
251
|
this.dialog.open({ template: container })
|
|
244
252
|
}
|
|
245
253
|
|
|
@@ -97,6 +97,9 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
97
97
|
case 'banfr':
|
|
98
98
|
import('./importers/banfr.js').then(register)
|
|
99
99
|
break
|
|
100
|
+
case 'opendata':
|
|
101
|
+
import('./importers/opendata.js').then(register)
|
|
102
|
+
break
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
}
|
|
@@ -248,7 +251,7 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
248
251
|
DomUtil.element({
|
|
249
252
|
tagName: 'option',
|
|
250
253
|
parent: layerSelect,
|
|
251
|
-
textContent: datalayer.
|
|
254
|
+
textContent: datalayer.getName(),
|
|
252
255
|
value: datalayer.id,
|
|
253
256
|
})
|
|
254
257
|
}
|
|
@@ -325,13 +328,13 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
325
328
|
return false
|
|
326
329
|
}
|
|
327
330
|
const layer = this.layer
|
|
328
|
-
layer.
|
|
331
|
+
layer.properties.remoteData = {
|
|
329
332
|
url: this.url,
|
|
330
333
|
format: this.format,
|
|
331
334
|
}
|
|
332
335
|
if (this._umap.properties.urls.ajax_proxy) {
|
|
333
|
-
layer.
|
|
334
|
-
layer.
|
|
336
|
+
layer.properties.remoteData.proxy = true
|
|
337
|
+
layer.properties.remoteData.ttl = SCHEMA.ttl.default
|
|
335
338
|
}
|
|
336
339
|
layer.fetchRemoteData(true).then((features) => {
|
|
337
340
|
if (features?.length) {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
2
|
+
import { translate } from '../i18n.js'
|
|
3
|
+
import * as Utils from '../utils.js'
|
|
4
|
+
|
|
5
|
+
const PORTALS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'Aix-Marseille Métropole',
|
|
8
|
+
url: 'https://data.ampmetropole.fr',
|
|
9
|
+
platform: 'opendatasoft',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Bordeaux Métropole',
|
|
13
|
+
url: 'https://opendata.bordeaux-metropole.fr',
|
|
14
|
+
platform: 'opendatasoft',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'Région Centre-Val de Loire',
|
|
18
|
+
url: 'https://data.centrevaldeloire.fr',
|
|
19
|
+
platform: 'opendatasoft',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'Ville de Clermont-Ferrand',
|
|
23
|
+
url: 'https://opendata.clermont-ferrand.fr',
|
|
24
|
+
platform: 'opendatasoft',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Métropole de Dijon',
|
|
28
|
+
url: 'https://data.metropole-dijon.fr',
|
|
29
|
+
platform: 'opendatasoft',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Région Île-de-France',
|
|
33
|
+
url: 'https://data.iledefrance.fr',
|
|
34
|
+
platform: 'opendatasoft',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Toulouse Métropole',
|
|
38
|
+
url: 'https://data.toulouse-metropole.fr',
|
|
39
|
+
platform: 'opendatasoft',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Tours Métropole Val de Loire',
|
|
43
|
+
url: 'https://data.tours-metropole.fr/',
|
|
44
|
+
platform: 'opendatasoft',
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
const TEMPLATE = `
|
|
49
|
+
<div>
|
|
50
|
+
<h3>Open Data</h3>
|
|
51
|
+
<p>${translate('Import data from public open data portals')}.</p>
|
|
52
|
+
<div class="formbox">
|
|
53
|
+
<select name="instance" data-ref="portals">
|
|
54
|
+
<option disabled selected value="">${translate('Choose a portal')}</option>
|
|
55
|
+
</select>
|
|
56
|
+
<select name="dataset" hidden data-ref="datasets">
|
|
57
|
+
<option disabled selected value="">${translate('Choose a dataset')}</option>
|
|
58
|
+
</select>
|
|
59
|
+
<input type="hidden" name="geofield" data-ref="geofield">
|
|
60
|
+
<label><input type="checkbox" name="in_bbox">${translate('Limit results to current map view')}</label>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
export class Importer {
|
|
66
|
+
constructor(umap, options = {}) {
|
|
67
|
+
this.umap = umap
|
|
68
|
+
this.name = options.name || 'Open Data'
|
|
69
|
+
this.id = 'opendata'
|
|
70
|
+
this.portals = options.choices || PORTALS
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async open(importer) {
|
|
74
|
+
let fields_map = {}
|
|
75
|
+
const [container, { portals, datasets, geofield }] =
|
|
76
|
+
Utils.loadTemplateWithRefs(TEMPLATE)
|
|
77
|
+
portals.addEventListener('change', async (event) => {
|
|
78
|
+
const response = await this.umap.request.get(
|
|
79
|
+
`${event.target.value}/api/explore/v2.1/catalog/datasets?where=features%20in%20%28%22geo%22%29&limit=-1&offset=0&timezone=UTC`
|
|
80
|
+
)
|
|
81
|
+
if (response.ok) {
|
|
82
|
+
fields_map = {}
|
|
83
|
+
Array.from(datasets.children).forEach((option) => {
|
|
84
|
+
if (!option.disabled) {
|
|
85
|
+
option.remove()
|
|
86
|
+
} else {
|
|
87
|
+
option.selected = true
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
const data = await response.json()
|
|
91
|
+
for (const result of data.results) {
|
|
92
|
+
const fields = result.fields.filter((field) => field.type === 'geo_point_2d')
|
|
93
|
+
if (!fields.length) {
|
|
94
|
+
console.debug('No geofield found for', result)
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
if (fields.length > 1) {
|
|
98
|
+
console.debug('More than one geofield found for', result)
|
|
99
|
+
}
|
|
100
|
+
fields_map[result.dataset_id] = fields[0].name
|
|
101
|
+
const el = Utils.loadTemplate(
|
|
102
|
+
`<option value="${result.dataset_id}">${result.metas.default.title} (${result.metas.default.records_count})</option>`
|
|
103
|
+
)
|
|
104
|
+
datasets.appendChild(el)
|
|
105
|
+
}
|
|
106
|
+
datasets.hidden = false
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
datasets.addEventListener('change', (event) => {
|
|
110
|
+
geofield.value = fields_map[event.target.value]
|
|
111
|
+
})
|
|
112
|
+
for (const instance of this.portals) {
|
|
113
|
+
const el = Utils.loadTemplate(
|
|
114
|
+
`<option value="${instance.url}">${instance.name}</option>`
|
|
115
|
+
)
|
|
116
|
+
portals.appendChild(el)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const confirm = (form) => {
|
|
120
|
+
if (!form.instance) {
|
|
121
|
+
Alert.error(translate('Please choose an instance first.'))
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
let url = `${form.instance}/api/explore/v2.1/catalog/datasets/${form.dataset}/exports/geojson?select=%2A&limit=-1&timezone=UTC&use_labels=false&epsg=4326`
|
|
125
|
+
if (form.in_bbox) {
|
|
126
|
+
url += `&where=in_bbox%28${form.geofield}%2C%20{south},{west},{north},{east}%29`
|
|
127
|
+
}
|
|
128
|
+
importer.url = url
|
|
129
|
+
importer.format = 'geojson'
|
|
130
|
+
importer.layerName = datasets.options[datasets.selectedIndex].textContent
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
importer.dialog
|
|
134
|
+
.open({
|
|
135
|
+
template: container,
|
|
136
|
+
className: `${this.id} importer dark`,
|
|
137
|
+
accept: translate('Choose this data'),
|
|
138
|
+
cancel: false,
|
|
139
|
+
})
|
|
140
|
+
.then(confirm)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -258,7 +258,7 @@ export class DataLayerPermissions {
|
|
|
258
258
|
{
|
|
259
259
|
edit_status: null,
|
|
260
260
|
},
|
|
261
|
-
datalayer.
|
|
261
|
+
datalayer.properties.permissions
|
|
262
262
|
)
|
|
263
263
|
|
|
264
264
|
this.datalayer = datalayer
|
|
@@ -314,9 +314,9 @@ export class DataLayerPermissions {
|
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
commit() {
|
|
317
|
-
this.datalayer.
|
|
317
|
+
this.datalayer.properties.permissions = Object.assign(
|
|
318
318
|
{},
|
|
319
|
-
this.datalayer.
|
|
319
|
+
this.datalayer.properties.permissions,
|
|
320
320
|
this.properties
|
|
321
321
|
)
|
|
322
322
|
}
|
|
@@ -23,8 +23,8 @@ export const EditControl = Control.extend({
|
|
|
23
23
|
|
|
24
24
|
onAdd: (map) => {
|
|
25
25
|
const template = `
|
|
26
|
-
<div class="edit-enable">
|
|
27
|
-
<button type="button" data-ref="button"
|
|
26
|
+
<div class="edit-enable dark">
|
|
27
|
+
<button type="button" data-ref="button" class="round"><i class="icon icon-16 icon-edit"></i> ${translate('Edit')}</button>
|
|
28
28
|
</div>
|
|
29
29
|
`
|
|
30
30
|
const [container, { button }] = Utils.loadTemplateWithRefs(template)
|
|
@@ -42,6 +42,38 @@ export const EditControl = Control.extend({
|
|
|
42
42
|
},
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
+
export const LoadTemplateControl = Control.extend({
|
|
46
|
+
options: {
|
|
47
|
+
position: 'topright',
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
onAdd: (map) => {
|
|
51
|
+
const template = `
|
|
52
|
+
<div class="load-template dark hide-on-edit">
|
|
53
|
+
<button type="button" data-ref="button" class="round"><i class="icon icon-16 icon-template"></i> ${translate('Reuse this template')}</button>
|
|
54
|
+
</div>
|
|
55
|
+
`
|
|
56
|
+
const [container, { button }] = Utils.loadTemplateWithRefs(template)
|
|
57
|
+
button.addEventListener('click', () => {
|
|
58
|
+
const downloadUrl = map._umap.urls.get('map_download', {
|
|
59
|
+
map_id: map._umap.id,
|
|
60
|
+
})
|
|
61
|
+
const targetUrl = `${map._umap.urls.get('map_new')}?templateUrl=${downloadUrl}`
|
|
62
|
+
window.open(targetUrl)
|
|
63
|
+
})
|
|
64
|
+
button.addEventListener('mouseover', () => {
|
|
65
|
+
map._umap.tooltip.open({
|
|
66
|
+
content: translate('Create a new map using this template'),
|
|
67
|
+
anchor: button,
|
|
68
|
+
position: 'bottom',
|
|
69
|
+
delay: 750,
|
|
70
|
+
duration: 5000,
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
return container
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
45
77
|
export const MoreControl = Control.extend({
|
|
46
78
|
options: {
|
|
47
79
|
position: 'topleft',
|
|
@@ -235,8 +235,8 @@ export const Cluster = DivIcon.extend({
|
|
|
235
235
|
computeTextColor: function (el) {
|
|
236
236
|
let color
|
|
237
237
|
const backgroundColor = this.datalayer.getColor()
|
|
238
|
-
if (this.datalayer.
|
|
239
|
-
color = this.datalayer.
|
|
238
|
+
if (this.datalayer.properties.cluster?.textColor) {
|
|
239
|
+
color = this.datalayer.properties.cluster.textColor
|
|
240
240
|
}
|
|
241
241
|
return color || DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
|
|
242
242
|
},
|
|
@@ -15,11 +15,11 @@ const ClassifiedMixin = {
|
|
|
15
15
|
.filter((k) => k !== 'schemeGroups')
|
|
16
16
|
.sort()
|
|
17
17
|
const key = this.getType().toLowerCase()
|
|
18
|
-
if (!Utils.isObject(this.datalayer.
|
|
19
|
-
this.datalayer.
|
|
18
|
+
if (!Utils.isObject(this.datalayer.properties[key])) {
|
|
19
|
+
this.datalayer.properties[key] = {}
|
|
20
20
|
}
|
|
21
|
-
this.ensureOptions(this.datalayer.
|
|
22
|
-
FeatureGroup.prototype.initialize.call(this, [], this.datalayer.
|
|
21
|
+
this.ensureOptions(this.datalayer.properties[key])
|
|
22
|
+
FeatureGroup.prototype.initialize.call(this, [], this.datalayer.properties[key])
|
|
23
23
|
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
24
24
|
},
|
|
25
25
|
|
|
@@ -117,7 +117,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
117
117
|
},
|
|
118
118
|
|
|
119
119
|
_getValue: function (feature) {
|
|
120
|
-
const key = this.datalayer.
|
|
120
|
+
const key = this.datalayer.properties.choropleth?.property || 'value'
|
|
121
121
|
const value = +feature.properties[key]
|
|
122
122
|
if (!Number.isNaN(value)) return value
|
|
123
123
|
},
|
|
@@ -130,12 +130,12 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
130
130
|
this.options.colors = []
|
|
131
131
|
return
|
|
132
132
|
}
|
|
133
|
-
const mode = this.datalayer.
|
|
134
|
-
let classes = +this.datalayer.
|
|
133
|
+
const mode = this.datalayer.properties.choropleth?.mode
|
|
134
|
+
let classes = +this.datalayer.properties.choropleth?.classes || 5
|
|
135
135
|
let breaks
|
|
136
136
|
classes = Math.min(classes, values.length)
|
|
137
137
|
if (mode === 'manual') {
|
|
138
|
-
const manualBreaks = this.datalayer.
|
|
138
|
+
const manualBreaks = this.datalayer.properties.choropleth?.breaks
|
|
139
139
|
if (manualBreaks) {
|
|
140
140
|
breaks = manualBreaks
|
|
141
141
|
.split(',')
|
|
@@ -154,10 +154,10 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
154
154
|
breaks.push(ss.max(values)) // Needed for computing the legend
|
|
155
155
|
}
|
|
156
156
|
this.options.breaks = breaks || []
|
|
157
|
-
this.datalayer.
|
|
157
|
+
this.datalayer.properties.choropleth.breaks = this.options.breaks
|
|
158
158
|
.map((b) => +b.toFixed(2))
|
|
159
159
|
.join(',')
|
|
160
|
-
let colorScheme = this.datalayer.
|
|
160
|
+
let colorScheme = this.datalayer.properties.choropleth.brewer
|
|
161
161
|
if (!colorbrewer[colorScheme]) colorScheme = 'Blues'
|
|
162
162
|
this.options.colors = colorbrewer[colorScheme][this.options.breaks.length - 1] || []
|
|
163
163
|
},
|
|
@@ -175,24 +175,27 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
175
175
|
|
|
176
176
|
onEdit: function (field, builder) {
|
|
177
177
|
// Only compute the breaks if we're dealing with choropleth
|
|
178
|
-
if (!field.startsWith('
|
|
178
|
+
if (!field.startsWith('properties.choropleth')) return
|
|
179
179
|
// If user touches the breaks, then force manual mode
|
|
180
|
-
if (field === '
|
|
181
|
-
this.datalayer.
|
|
182
|
-
if (builder) builder.helpers['
|
|
180
|
+
if (field === 'properties.choropleth.breaks') {
|
|
181
|
+
this.datalayer.properties.choropleth.mode = 'manual'
|
|
182
|
+
if (builder) builder.helpers['properties.choropleth.mode'].fetch()
|
|
183
183
|
}
|
|
184
184
|
this.compute()
|
|
185
185
|
// If user changes the mode or the number of classes,
|
|
186
186
|
// then update the breaks input value
|
|
187
|
-
if (
|
|
188
|
-
|
|
187
|
+
if (
|
|
188
|
+
field === 'properties.choropleth.mode' ||
|
|
189
|
+
field === 'properties.choropleth.classes'
|
|
190
|
+
) {
|
|
191
|
+
if (builder) builder.helpers['properties.choropleth.breaks'].fetch()
|
|
189
192
|
}
|
|
190
193
|
},
|
|
191
194
|
|
|
192
|
-
|
|
195
|
+
getEditableProperties: function () {
|
|
193
196
|
return [
|
|
194
197
|
[
|
|
195
|
-
'
|
|
198
|
+
'properties.choropleth.property',
|
|
196
199
|
{
|
|
197
200
|
handler: 'Select',
|
|
198
201
|
selectOptions: this.datalayer.allProperties(),
|
|
@@ -200,7 +203,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
200
203
|
},
|
|
201
204
|
],
|
|
202
205
|
[
|
|
203
|
-
'
|
|
206
|
+
'properties.choropleth.brewer',
|
|
204
207
|
{
|
|
205
208
|
handler: 'Select',
|
|
206
209
|
label: translate('Choropleth color palette'),
|
|
@@ -208,7 +211,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
208
211
|
},
|
|
209
212
|
],
|
|
210
213
|
[
|
|
211
|
-
'
|
|
214
|
+
'properties.choropleth.classes',
|
|
212
215
|
{
|
|
213
216
|
handler: 'Range',
|
|
214
217
|
min: 3,
|
|
@@ -219,7 +222,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
219
222
|
},
|
|
220
223
|
],
|
|
221
224
|
[
|
|
222
|
-
'
|
|
225
|
+
'properties.choropleth.breaks',
|
|
223
226
|
{
|
|
224
227
|
handler: 'BlurInput',
|
|
225
228
|
label: translate('Choropleth breakpoints'),
|
|
@@ -229,7 +232,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
229
232
|
},
|
|
230
233
|
],
|
|
231
234
|
[
|
|
232
|
-
'
|
|
235
|
+
'properties.choropleth.mode',
|
|
233
236
|
{
|
|
234
237
|
handler: 'MultiChoice',
|
|
235
238
|
default: 'kmeans',
|
|
@@ -261,13 +264,13 @@ export const Circles = FeatureGroup.extend({
|
|
|
261
264
|
},
|
|
262
265
|
|
|
263
266
|
ensureOptions: function (options) {
|
|
264
|
-
if (!Utils.isObject(this.datalayer.
|
|
265
|
-
this.datalayer.
|
|
267
|
+
if (!Utils.isObject(this.datalayer.properties.circles.radius)) {
|
|
268
|
+
this.datalayer.properties.circles.radius = {}
|
|
266
269
|
}
|
|
267
270
|
},
|
|
268
271
|
|
|
269
272
|
_getValue: function (feature) {
|
|
270
|
-
const key = this.datalayer.
|
|
273
|
+
const key = this.datalayer.properties.circles.property || 'value'
|
|
271
274
|
const value = +feature.properties[key]
|
|
272
275
|
if (!Number.isNaN(value)) return value
|
|
273
276
|
},
|
|
@@ -276,8 +279,8 @@ export const Circles = FeatureGroup.extend({
|
|
|
276
279
|
const values = this.getValues()
|
|
277
280
|
this.options.minValue = Math.sqrt(Math.min(...values))
|
|
278
281
|
this.options.maxValue = Math.sqrt(Math.max(...values))
|
|
279
|
-
this.options.minPX = this.datalayer.
|
|
280
|
-
this.options.maxPX = this.datalayer.
|
|
282
|
+
this.options.minPX = this.datalayer.properties.circles.radius?.min || 2
|
|
283
|
+
this.options.maxPX = this.datalayer.properties.circles.radius?.max || 50
|
|
281
284
|
},
|
|
282
285
|
|
|
283
286
|
onEdit: function (field, builder) {
|
|
@@ -298,10 +301,10 @@ export const Circles = FeatureGroup.extend({
|
|
|
298
301
|
return this._computeRadius(this._getValue(feature))
|
|
299
302
|
},
|
|
300
303
|
|
|
301
|
-
|
|
304
|
+
getEditableProperties: function () {
|
|
302
305
|
return [
|
|
303
306
|
[
|
|
304
|
-
'
|
|
307
|
+
'properties.circles.property',
|
|
305
308
|
{
|
|
306
309
|
handler: 'Select',
|
|
307
310
|
selectOptions: this.datalayer.allProperties(),
|
|
@@ -309,7 +312,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
309
312
|
},
|
|
310
313
|
],
|
|
311
314
|
[
|
|
312
|
-
'
|
|
315
|
+
'properties.circles.radius.min',
|
|
313
316
|
{
|
|
314
317
|
handler: 'Range',
|
|
315
318
|
label: translate('Min circle radius'),
|
|
@@ -319,7 +322,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
319
322
|
},
|
|
320
323
|
],
|
|
321
324
|
[
|
|
322
|
-
'
|
|
325
|
+
'properties.circles.radius.max',
|
|
323
326
|
{
|
|
324
327
|
handler: 'Range',
|
|
325
328
|
label: translate('Max circle radius'),
|
|
@@ -337,7 +340,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
337
340
|
|
|
338
341
|
renderLegend: function (container) {
|
|
339
342
|
const parent = DomUtil.create('ul', 'circles-layer-legend', container)
|
|
340
|
-
const color = this.datalayer.
|
|
343
|
+
const color = this.datalayer.getProperty('color')
|
|
341
344
|
const values = this.getValues()
|
|
342
345
|
if (!values.length) return
|
|
343
346
|
values.sort((a, b) => a - b)
|
|
@@ -355,7 +358,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
355
358
|
circleEl.style.backgroundColor = color
|
|
356
359
|
circleEl.style.height = `${size * 2}px`
|
|
357
360
|
circleEl.style.width = `${size * 2}px`
|
|
358
|
-
circleEl.style.opacity = this.datalayer.
|
|
361
|
+
circleEl.style.opacity = this.datalayer.getProperty('opacity')
|
|
359
362
|
const labelEl = DomUtil.create('span', 'label', li)
|
|
360
363
|
labelEl.textContent = label
|
|
361
364
|
}
|
|
@@ -381,7 +384,8 @@ export const Categorized = FeatureGroup.extend({
|
|
|
381
384
|
|
|
382
385
|
_getValue: function (feature) {
|
|
383
386
|
const key =
|
|
384
|
-
this.datalayer.
|
|
387
|
+
this.datalayer.properties.categorized.property ||
|
|
388
|
+
this.datalayer.allProperties()[0]
|
|
385
389
|
return feature.properties[key]
|
|
386
390
|
},
|
|
387
391
|
|
|
@@ -403,10 +407,10 @@ export const Categorized = FeatureGroup.extend({
|
|
|
403
407
|
this.options.colors = []
|
|
404
408
|
return
|
|
405
409
|
}
|
|
406
|
-
const mode = this.datalayer.
|
|
410
|
+
const mode = this.datalayer.properties.categorized.mode
|
|
407
411
|
let categories = []
|
|
408
412
|
if (mode === 'manual') {
|
|
409
|
-
const manualCategories = this.datalayer.
|
|
413
|
+
const manualCategories = this.datalayer.properties.categorized.categories
|
|
410
414
|
if (manualCategories) {
|
|
411
415
|
categories = manualCategories.split(',')
|
|
412
416
|
}
|
|
@@ -416,8 +420,8 @@ export const Categorized = FeatureGroup.extend({
|
|
|
416
420
|
.sort(Utils.naturalSort)
|
|
417
421
|
}
|
|
418
422
|
this.options.categories = categories
|
|
419
|
-
this.datalayer.
|
|
420
|
-
const colorScheme = this.datalayer.
|
|
423
|
+
this.datalayer.properties.categorized.categories = this.options.categories.join(',')
|
|
424
|
+
const colorScheme = this.datalayer.properties.categorized.brewer
|
|
421
425
|
this._classes = this.options.categories.length
|
|
422
426
|
if (colorbrewer[colorScheme]?.[this._classes]) {
|
|
423
427
|
this.options.colors = colorbrewer[colorScheme][this._classes]
|
|
@@ -428,10 +432,10 @@ export const Categorized = FeatureGroup.extend({
|
|
|
428
432
|
}
|
|
429
433
|
},
|
|
430
434
|
|
|
431
|
-
|
|
435
|
+
getEditableProperties: function () {
|
|
432
436
|
return [
|
|
433
437
|
[
|
|
434
|
-
'
|
|
438
|
+
'properties.categorized.property',
|
|
435
439
|
{
|
|
436
440
|
handler: 'Select',
|
|
437
441
|
selectOptions: this.datalayer.allProperties(),
|
|
@@ -439,7 +443,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
439
443
|
},
|
|
440
444
|
],
|
|
441
445
|
[
|
|
442
|
-
'
|
|
446
|
+
'properties.categorized.brewer',
|
|
443
447
|
{
|
|
444
448
|
handler: 'Select',
|
|
445
449
|
label: translate('Color palette'),
|
|
@@ -447,7 +451,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
447
451
|
},
|
|
448
452
|
],
|
|
449
453
|
[
|
|
450
|
-
'
|
|
454
|
+
'properties.categorized.categories',
|
|
451
455
|
{
|
|
452
456
|
handler: 'BlurInput',
|
|
453
457
|
label: translate('Categories'),
|
|
@@ -455,7 +459,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
455
459
|
},
|
|
456
460
|
],
|
|
457
461
|
[
|
|
458
|
-
'
|
|
462
|
+
'properties.categorized.mode',
|
|
459
463
|
{
|
|
460
464
|
handler: 'MultiChoice',
|
|
461
465
|
default: 'alpha',
|
|
@@ -468,17 +472,19 @@ export const Categorized = FeatureGroup.extend({
|
|
|
468
472
|
|
|
469
473
|
onEdit: function (field, builder) {
|
|
470
474
|
// Only compute the categories if we're dealing with categorized
|
|
471
|
-
if (!field.startsWith('
|
|
475
|
+
if (!field.startsWith('properties.categorized') && field !== 'properties.type') {
|
|
476
|
+
return
|
|
477
|
+
}
|
|
472
478
|
// If user touches the categories, then force manual mode
|
|
473
|
-
if (field === '
|
|
474
|
-
this.datalayer.
|
|
475
|
-
if (builder) builder.helpers['
|
|
479
|
+
if (field === 'properties.categorized.categories') {
|
|
480
|
+
this.datalayer.properties.categorized.mode = 'manual'
|
|
481
|
+
if (builder) builder.helpers['properties.categorized.mode'].fetch()
|
|
476
482
|
}
|
|
477
483
|
this.compute()
|
|
478
484
|
// If user changes the mode
|
|
479
485
|
// then update the categories input value
|
|
480
|
-
if (field === '
|
|
481
|
-
if (builder) builder.helpers['
|
|
486
|
+
if (field === 'properties.categorized.mode') {
|
|
487
|
+
if (builder) builder.helpers['properties.categorized.categories'].fetch()
|
|
482
488
|
}
|
|
483
489
|
},
|
|
484
490
|
|