umap-project 3.0.5__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.mo +0 -0
- 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 +219 -73
- 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 +15 -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 +34 -1
- umap/static/umap/locale/en.json +34 -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_init.html +3 -1
- 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_basics.py +9 -1
- 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 +65 -19
- {umap_project-3.0.5.dist-info → umap_project-3.1.0.dist-info}/METADATA +15 -15
- {umap_project-3.0.5.dist-info → umap_project-3.1.0.dist-info}/RECORD +145 -132
- {umap_project-3.0.5.dist-info → umap_project-3.1.0.dist-info}/WHEEL +0 -0
- {umap_project-3.0.5.dist-info → umap_project-3.1.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.0.5.dist-info → umap_project-3.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -27,8 +27,8 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
27
27
|
|
|
28
28
|
initialize: function (datalayer) {
|
|
29
29
|
this.datalayer = datalayer
|
|
30
|
-
if (!Utils.isObject(this.datalayer.
|
|
31
|
-
this.datalayer.
|
|
30
|
+
if (!Utils.isObject(this.datalayer.properties.cluster)) {
|
|
31
|
+
this.datalayer.properties.cluster = {}
|
|
32
32
|
}
|
|
33
33
|
const options = {
|
|
34
34
|
polygonOptions: {
|
|
@@ -36,8 +36,8 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
36
36
|
},
|
|
37
37
|
iconCreateFunction: (cluster) => new ClusterIcon(datalayer, cluster),
|
|
38
38
|
}
|
|
39
|
-
if (this.datalayer.
|
|
40
|
-
options.maxClusterRadius = this.datalayer.
|
|
39
|
+
if (this.datalayer.properties.cluster?.radius) {
|
|
40
|
+
options.maxClusterRadius = this.datalayer.properties.cluster.radius
|
|
41
41
|
}
|
|
42
42
|
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
|
43
43
|
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
@@ -81,9 +81,9 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
81
81
|
return L.MarkerClusterGroup.prototype.removeLayer.call(this, layer)
|
|
82
82
|
},
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
getEditableProperties: () => [
|
|
85
85
|
[
|
|
86
|
-
'
|
|
86
|
+
'properties.cluster.radius',
|
|
87
87
|
{
|
|
88
88
|
handler: 'BlurIntInput',
|
|
89
89
|
placeholder: translate('Clustering radius'),
|
|
@@ -91,7 +91,7 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
91
91
|
},
|
|
92
92
|
],
|
|
93
93
|
[
|
|
94
|
-
'
|
|
94
|
+
'properties.cluster.textColor',
|
|
95
95
|
{
|
|
96
96
|
handler: 'TextColorPicker',
|
|
97
97
|
placeholder: translate('Auto'),
|
|
@@ -101,13 +101,20 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
101
101
|
],
|
|
102
102
|
|
|
103
103
|
onEdit: function (field, builder) {
|
|
104
|
-
if (field === '
|
|
104
|
+
if (field === 'properties.cluster.radius') {
|
|
105
105
|
// No way to reset radius of an already instanciated MarkerClusterGroup...
|
|
106
106
|
this.datalayer.resetLayer(true)
|
|
107
107
|
return
|
|
108
108
|
}
|
|
109
|
-
if (field === '
|
|
109
|
+
if (field === 'properties.color') {
|
|
110
110
|
this.options.polygonOptions.color = this.datalayer.getColor()
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
|
+
|
|
114
|
+
_moveChild: (layer, from, to) => {
|
|
115
|
+
// Extend parent method, so to remove remove/addLayer,
|
|
116
|
+
// to let our own dragend event listener be called
|
|
117
|
+
// cf https://github.com/umap-project/umap/issues/2749
|
|
118
|
+
layer._latlng = to
|
|
119
|
+
},
|
|
113
120
|
})
|
|
@@ -20,10 +20,10 @@ export const Heat = L.HeatLayer.extend({
|
|
|
20
20
|
|
|
21
21
|
initialize: function (datalayer) {
|
|
22
22
|
this.datalayer = datalayer
|
|
23
|
-
L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.
|
|
23
|
+
L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.properties.heat)
|
|
24
24
|
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
25
|
-
if (!Utils.isObject(this.datalayer.
|
|
26
|
-
this.datalayer.
|
|
25
|
+
if (!Utils.isObject(this.datalayer.properties.heat)) {
|
|
26
|
+
this.datalayer.properties.heat = {}
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
|
|
@@ -31,9 +31,11 @@ export const Heat = L.HeatLayer.extend({
|
|
|
31
31
|
if (layer instanceof Marker) {
|
|
32
32
|
let latlng = layer.getLatLng()
|
|
33
33
|
let alt
|
|
34
|
-
if (this.datalayer.
|
|
34
|
+
if (this.datalayer.properties.heat?.intensityProperty) {
|
|
35
35
|
alt = Number.parseFloat(
|
|
36
|
-
layer.feature.properties[
|
|
36
|
+
layer.feature.properties[
|
|
37
|
+
this.datalayer.properties.heat.intensityProperty || 0
|
|
38
|
+
]
|
|
37
39
|
)
|
|
38
40
|
latlng = new LatLng(latlng.lat, latlng.lng, alt)
|
|
39
41
|
}
|
|
@@ -66,9 +68,9 @@ export const Heat = L.HeatLayer.extend({
|
|
|
66
68
|
return latLngBounds(this._latlngs)
|
|
67
69
|
},
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
getEditableProperties: () => [
|
|
70
72
|
[
|
|
71
|
-
'
|
|
73
|
+
'properties.heat.radius',
|
|
72
74
|
{
|
|
73
75
|
handler: 'Range',
|
|
74
76
|
min: 10,
|
|
@@ -79,7 +81,7 @@ export const Heat = L.HeatLayer.extend({
|
|
|
79
81
|
},
|
|
80
82
|
],
|
|
81
83
|
[
|
|
82
|
-
'
|
|
84
|
+
'properties.heat.intensityProperty',
|
|
83
85
|
{
|
|
84
86
|
handler: 'BlurInput',
|
|
85
87
|
placeholder: translate('Heatmap intensity property'),
|
|
@@ -89,12 +91,12 @@ export const Heat = L.HeatLayer.extend({
|
|
|
89
91
|
],
|
|
90
92
|
|
|
91
93
|
onEdit: function (field, builder) {
|
|
92
|
-
if (field === '
|
|
94
|
+
if (field === 'properties.heat.intensityProperty') {
|
|
93
95
|
this.datalayer.resetLayer(true) // We need to repopulate the latlngs
|
|
94
96
|
return
|
|
95
97
|
}
|
|
96
|
-
if (field === '
|
|
97
|
-
this.options.radius = this.datalayer.
|
|
98
|
+
if (field === 'properties.heat.radius') {
|
|
99
|
+
this.options.radius = this.datalayer.properties.heat.radius
|
|
98
100
|
}
|
|
99
101
|
this._updateOptions()
|
|
100
102
|
},
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MoreControl,
|
|
22
22
|
PermanentCreditsControl,
|
|
23
23
|
TileLayerChooser,
|
|
24
|
+
LoadTemplateControl,
|
|
24
25
|
} from './controls.js'
|
|
25
26
|
import * as Utils from '../utils.js'
|
|
26
27
|
import * as Icon from './icon.js'
|
|
@@ -50,6 +51,10 @@ const ControlsMixin = {
|
|
|
50
51
|
initControls: function () {
|
|
51
52
|
this._controls = {}
|
|
52
53
|
|
|
54
|
+
if (this._umap.properties.is_template && !this.options.noControl) {
|
|
55
|
+
new LoadTemplateControl(this).addTo(this)
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
if (this._umap.hasEditMode() && !this.options.noControl) {
|
|
54
59
|
new EditControl(this).addTo(this)
|
|
55
60
|
}
|
|
@@ -372,6 +372,29 @@ export const LeafletPolyline = Polyline.extend({
|
|
|
372
372
|
parentClass: Polyline,
|
|
373
373
|
includes: [FeatureMixin, PathMixin],
|
|
374
374
|
|
|
375
|
+
setStyle: function (options) {
|
|
376
|
+
PathMixin.setStyle.call(this, options)
|
|
377
|
+
this.setText(null) // Reset.
|
|
378
|
+
const textPath = this.feature.getDynamicOption('textPath')
|
|
379
|
+
if (textPath) {
|
|
380
|
+
const color =
|
|
381
|
+
this.feature.getOption('textPathColor') ||
|
|
382
|
+
this.feature.getDynamicOption('color')
|
|
383
|
+
const textPathOptions = {
|
|
384
|
+
repeat: this.feature.getOption('textPathRepeat'),
|
|
385
|
+
offset: this.feature.getOption('textPathOffset') || undefined,
|
|
386
|
+
position: this.feature.getOption('textPathPosition'),
|
|
387
|
+
attributes: {
|
|
388
|
+
fill: color,
|
|
389
|
+
opacity: this.feature.getDynamicOption('opacity'),
|
|
390
|
+
rotate: this.feature.getOption('textPathRotate'),
|
|
391
|
+
'font-size': this.feature.getOption('textPathSize'),
|
|
392
|
+
},
|
|
393
|
+
}
|
|
394
|
+
this.setText(textPath, textPathOptions)
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
|
|
375
398
|
getClass: () => LeafletPolyline,
|
|
376
399
|
|
|
377
400
|
getMeasure: function (shape) {
|
|
@@ -17,7 +17,7 @@ class Rule {
|
|
|
17
17
|
this.parse()
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
constructor(umap, condition = '', options = {}) {
|
|
20
|
+
constructor(umap, parent, condition = '', options = {}) {
|
|
21
21
|
// TODO make this public properties when browser coverage is ok
|
|
22
22
|
// cf https://caniuse.com/?search=public%20class%20field
|
|
23
23
|
this._condition = null
|
|
@@ -29,6 +29,7 @@ class Rule {
|
|
|
29
29
|
['!=', this.not_equal],
|
|
30
30
|
['=', this.equal],
|
|
31
31
|
]
|
|
32
|
+
this.parent = parent
|
|
32
33
|
this._umap = umap
|
|
33
34
|
this.active = true
|
|
34
35
|
this.options = options
|
|
@@ -36,7 +37,7 @@ class Rule {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
render(fields) {
|
|
39
|
-
this.
|
|
40
|
+
this.parent.render(fields)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
equal(other) {
|
|
@@ -123,7 +124,7 @@ class Rule {
|
|
|
123
124
|
const container = document.createElement('div')
|
|
124
125
|
container.appendChild(builder.build())
|
|
125
126
|
const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
|
|
126
|
-
const properties = this.
|
|
127
|
+
const properties = this.parent.allProperties()
|
|
127
128
|
autocomplete.suggestions = properties
|
|
128
129
|
autocomplete.input.addEventListener('input', (event) => {
|
|
129
130
|
const value = event.target.value
|
|
@@ -131,9 +132,9 @@ class Rule {
|
|
|
131
132
|
autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
|
|
132
133
|
} else if (value.endsWith('=')) {
|
|
133
134
|
const key = value.split('!')[0].split('=')[0]
|
|
134
|
-
autocomplete.suggestions = this.
|
|
135
|
+
autocomplete.suggestions = this.parent
|
|
135
136
|
.sortedValues(key)
|
|
136
|
-
.map((str) => `${value}${str
|
|
137
|
+
.map((str) => `${value}${str ?? ''}`)
|
|
137
138
|
}
|
|
138
139
|
})
|
|
139
140
|
const backButton = Utils.loadTemplate(`
|
|
@@ -141,8 +142,8 @@ class Rule {
|
|
|
141
142
|
<i class="icon icon-16 icon-back" title="${translate('Back to list')}"></i>
|
|
142
143
|
</button>`)
|
|
143
144
|
backButton.addEventListener('click', () =>
|
|
144
|
-
this.
|
|
145
|
-
|
|
145
|
+
this.parent.edit().then((panel) => {
|
|
146
|
+
panel.container.querySelector('details#rules').open = true
|
|
146
147
|
})
|
|
147
148
|
)
|
|
148
149
|
|
|
@@ -175,40 +176,41 @@ class Rule {
|
|
|
175
176
|
toggle.addEventListener('click', () => {
|
|
176
177
|
this.active = !this.active
|
|
177
178
|
li.classList.toggle('off', !this.active)
|
|
178
|
-
this.
|
|
179
|
+
this.parent.render(['rules'])
|
|
179
180
|
})
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
_delete() {
|
|
183
|
-
this.
|
|
184
|
-
this.
|
|
184
|
+
this.parent.rules.rules = this.parent.rules.rules.filter((rule) => rule !== this)
|
|
185
|
+
this.parent.rules.commit()
|
|
185
186
|
}
|
|
186
187
|
|
|
187
188
|
setter(key, value) {
|
|
188
|
-
const oldRules = Utils.CopyJSON(this.
|
|
189
|
+
const oldRules = Utils.CopyJSON(this.parent.properties.rules || {})
|
|
189
190
|
Utils.setObjectValue(this, key, value)
|
|
190
|
-
this.
|
|
191
|
-
this.
|
|
191
|
+
this.parent.rules.commit()
|
|
192
|
+
this.parent.sync.update('properties.rules', this.parent.properties.rules, oldRules)
|
|
192
193
|
}
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
export default class Rules {
|
|
196
|
-
constructor(umap) {
|
|
197
|
+
constructor(umap, parent) {
|
|
197
198
|
this._umap = umap
|
|
199
|
+
this.parent = parent
|
|
198
200
|
this.load()
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
load() {
|
|
202
204
|
this.rules = []
|
|
203
|
-
if (!this.
|
|
204
|
-
for (const { condition, options } of this.
|
|
205
|
+
if (!this.parent.properties.rules?.length) return
|
|
206
|
+
for (const { condition, options } of this.parent.properties.rules) {
|
|
205
207
|
if (!condition) continue
|
|
206
|
-
this.rules.push(new Rule(this._umap, condition, options))
|
|
208
|
+
this.rules.push(new Rule(this._umap, this.parent, condition, options))
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
|
|
210
212
|
onReorder(src, dst, initialIndex, finalIndex) {
|
|
211
|
-
const oldRules = Utils.CopyJSON(this.
|
|
213
|
+
const oldRules = Utils.CopyJSON(this.parent.properties.rules || {})
|
|
212
214
|
const moved = this.rules.find((rule) => stamp(rule) === +src.dataset.id)
|
|
213
215
|
const reference = this.rules.find((rule) => stamp(rule) === +dst.dataset.id)
|
|
214
216
|
const movedIdx = this.rules.indexOf(moved)
|
|
@@ -222,9 +224,9 @@ export default class Rules {
|
|
|
222
224
|
else if (finalIndex > initialIndex) newIdx = referenceIdx
|
|
223
225
|
else newIdx = referenceIdx + 1
|
|
224
226
|
this.rules.splice(newIdx, 0, moved)
|
|
225
|
-
this.
|
|
227
|
+
this.parent.render(['rules'])
|
|
226
228
|
this.commit()
|
|
227
|
-
this.
|
|
229
|
+
this.parent.sync.update('properties.rules', this.parent.properties.rules, oldRules)
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
edit(container) {
|
|
@@ -249,13 +251,13 @@ export default class Rules {
|
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
addRule() {
|
|
252
|
-
const rule = new Rule(this._umap)
|
|
254
|
+
const rule = new Rule(this._umap, this.parent)
|
|
253
255
|
this.rules.push(rule)
|
|
254
256
|
rule.edit(map)
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
commit() {
|
|
258
|
-
this.
|
|
260
|
+
this.parent.properties.rules = this.rules.map((rule) => {
|
|
259
261
|
return {
|
|
260
262
|
condition: rule.condition,
|
|
261
263
|
options: rule.options,
|
|
@@ -267,7 +269,6 @@ export default class Rules {
|
|
|
267
269
|
for (const rule of this.rules) {
|
|
268
270
|
if (rule.match(feature.properties)) {
|
|
269
271
|
if (Utils.usableOption(rule.options, option)) return rule.options[option]
|
|
270
|
-
break
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
}
|
|
@@ -51,7 +51,6 @@ export const SCHEMA = {
|
|
|
51
51
|
color: {
|
|
52
52
|
type: String,
|
|
53
53
|
impacts: ['data'],
|
|
54
|
-
handler: 'ColorPicker',
|
|
55
54
|
label: translate('color'),
|
|
56
55
|
helpEntries: ['colorValue'],
|
|
57
56
|
inheritable: true,
|
|
@@ -84,7 +83,6 @@ export const SCHEMA = {
|
|
|
84
83
|
type: Boolean,
|
|
85
84
|
impacts: ['ui'],
|
|
86
85
|
nullable: true,
|
|
87
|
-
handler: 'DataLayersControl',
|
|
88
86
|
label: translate('Display the open browser control'),
|
|
89
87
|
default: true,
|
|
90
88
|
},
|
|
@@ -160,7 +158,6 @@ export const SCHEMA = {
|
|
|
160
158
|
fillColor: {
|
|
161
159
|
type: String,
|
|
162
160
|
impacts: ['data'],
|
|
163
|
-
handler: 'ColorPicker',
|
|
164
161
|
label: translate('fill color'),
|
|
165
162
|
helpEntries: ['fillColor'],
|
|
166
163
|
inheritable: true,
|
|
@@ -237,7 +234,6 @@ export const SCHEMA = {
|
|
|
237
234
|
iconUrl: {
|
|
238
235
|
type: String,
|
|
239
236
|
impacts: ['data'],
|
|
240
|
-
handler: 'IconUrl',
|
|
241
237
|
label: translate('Icon symbol'),
|
|
242
238
|
inheritable: true,
|
|
243
239
|
},
|
|
@@ -253,6 +249,12 @@ export const SCHEMA = {
|
|
|
253
249
|
inheritable: true,
|
|
254
250
|
default: true,
|
|
255
251
|
},
|
|
252
|
+
is_template: {
|
|
253
|
+
type: Boolean,
|
|
254
|
+
impacts: ['ui'],
|
|
255
|
+
label: translate('This map is a template'),
|
|
256
|
+
default: false,
|
|
257
|
+
},
|
|
256
258
|
labelDirection: {
|
|
257
259
|
type: String,
|
|
258
260
|
impacts: ['data'],
|
|
@@ -539,6 +541,60 @@ export const SCHEMA = {
|
|
|
539
541
|
team: {
|
|
540
542
|
type: Object,
|
|
541
543
|
},
|
|
544
|
+
textPath: {
|
|
545
|
+
type: String,
|
|
546
|
+
impacts: ['data'],
|
|
547
|
+
label: translate('Add text along path'),
|
|
548
|
+
},
|
|
549
|
+
textPathColor: {
|
|
550
|
+
type: String,
|
|
551
|
+
impacts: ['data'],
|
|
552
|
+
label: translate('Text color'),
|
|
553
|
+
},
|
|
554
|
+
textPathOffset: {
|
|
555
|
+
type: Number,
|
|
556
|
+
label: translate('Text offset'),
|
|
557
|
+
impacts: ['data'],
|
|
558
|
+
default: 1,
|
|
559
|
+
min: -20,
|
|
560
|
+
max: 20,
|
|
561
|
+
step: 1,
|
|
562
|
+
},
|
|
563
|
+
textPathPosition: {
|
|
564
|
+
type: String,
|
|
565
|
+
impacts: ['data'],
|
|
566
|
+
label: translate('Text position'),
|
|
567
|
+
default: 'center',
|
|
568
|
+
choices: [
|
|
569
|
+
['start', translate('start')],
|
|
570
|
+
['center', translate('center')],
|
|
571
|
+
['end', translate('end')],
|
|
572
|
+
],
|
|
573
|
+
},
|
|
574
|
+
textPathRepeat: {
|
|
575
|
+
type: Boolean,
|
|
576
|
+
label: translate('Text repeat'),
|
|
577
|
+
impacts: ['data'],
|
|
578
|
+
default: true,
|
|
579
|
+
},
|
|
580
|
+
textPathRotate: {
|
|
581
|
+
type: Number,
|
|
582
|
+
label: translate('Text rotate'),
|
|
583
|
+
impacts: ['data'],
|
|
584
|
+
default: 0,
|
|
585
|
+
min: 0,
|
|
586
|
+
max: 360,
|
|
587
|
+
step: 1,
|
|
588
|
+
},
|
|
589
|
+
textPathSize: {
|
|
590
|
+
type: Number,
|
|
591
|
+
label: translate('Text size'),
|
|
592
|
+
impacts: ['data'],
|
|
593
|
+
default: 20,
|
|
594
|
+
min: 10,
|
|
595
|
+
max: 30,
|
|
596
|
+
step: 1,
|
|
597
|
+
},
|
|
542
598
|
tilelayer: {
|
|
543
599
|
type: Object,
|
|
544
600
|
impacts: ['background'],
|
|
@@ -52,8 +52,8 @@ export class DataLayerUpdater extends BaseUpdater {
|
|
|
52
52
|
|
|
53
53
|
update({ key, metadata, value }) {
|
|
54
54
|
const datalayer = this.getDataLayerFromID(metadata.id)
|
|
55
|
-
if (key === '
|
|
56
|
-
datalayer.
|
|
55
|
+
if (key === 'properties') {
|
|
56
|
+
datalayer.setProperties(value)
|
|
57
57
|
} else if (Utils.fieldInSchema(key)) {
|
|
58
58
|
Utils.setObjectValue(datalayer, key, value)
|
|
59
59
|
} else {
|
|
@@ -109,7 +109,11 @@ export class FeatureUpdater extends BaseUpdater {
|
|
|
109
109
|
feature.geometry = value
|
|
110
110
|
} else {
|
|
111
111
|
Utils.setObjectValue(feature, key, value)
|
|
112
|
-
|
|
112
|
+
if (key.startsWith('properties')) {
|
|
113
|
+
feature.datalayer.indexProperties(feature)
|
|
114
|
+
const name = key.replace('properties.', '')
|
|
115
|
+
feature.datalayer.checkIndexForProperty(name)
|
|
116
|
+
}
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
feature.render([key])
|
|
@@ -96,7 +96,7 @@ export default class TableEditor extends WithTemplate {
|
|
|
96
96
|
if (inBbox && !feature.isOnScreen(bounds)) return
|
|
97
97
|
const tds = this.properties.map(
|
|
98
98
|
(prop) =>
|
|
99
|
-
`<td tabindex="0" data-property="${prop}">${feature.properties[prop]
|
|
99
|
+
`<td tabindex="0" data-property="${prop}">${feature.properties[prop] ?? ''}</td>`
|
|
100
100
|
)
|
|
101
101
|
html += `<tr data-feature="${feature.id}"><th><input type="checkbox" /></th>${tds.join('')}</tr>`
|
|
102
102
|
})
|
|
@@ -110,28 +110,12 @@ export default class TableEditor extends WithTemplate {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
validateName(name) {
|
|
114
|
-
if (name.includes('.')) {
|
|
115
|
-
U.Alert.error(translate('Name “{name}” should not contain a dot.', { name }))
|
|
116
|
-
return false
|
|
117
|
-
}
|
|
118
|
-
if (this.properties.includes(name)) {
|
|
119
|
-
U.Alert.error(translate('This name already exists: “{name}”', { name }))
|
|
120
|
-
return false
|
|
121
|
-
}
|
|
122
|
-
return true
|
|
123
|
-
}
|
|
124
|
-
|
|
125
113
|
renameProperty(property) {
|
|
126
114
|
this._umap.dialog
|
|
127
115
|
.prompt(translate('Please enter the new name of this property'))
|
|
128
116
|
.then(({ prompt }) => {
|
|
129
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
130
|
-
this.datalayer.
|
|
131
|
-
feature.renameProperty(property, prompt)
|
|
132
|
-
})
|
|
133
|
-
this.datalayer.deindexProperty(property)
|
|
134
|
-
this.datalayer.indexProperty(prompt)
|
|
117
|
+
if (!prompt || !this.datalayer.validateName(prompt)) return
|
|
118
|
+
this.datalayer.renameProperty(property, prompt)
|
|
135
119
|
this.open()
|
|
136
120
|
})
|
|
137
121
|
}
|
|
@@ -142,23 +126,16 @@ export default class TableEditor extends WithTemplate {
|
|
|
142
126
|
translate('Are you sure you want to delete this property on all the features?')
|
|
143
127
|
)
|
|
144
128
|
.then(() => {
|
|
145
|
-
this.datalayer.
|
|
146
|
-
feature.deleteProperty(property)
|
|
147
|
-
})
|
|
148
|
-
this.datalayer.deindexProperty(property)
|
|
129
|
+
this.datalayer.deleteProperty(property)
|
|
149
130
|
this.resetProperties()
|
|
150
131
|
this.open()
|
|
151
132
|
})
|
|
152
133
|
}
|
|
153
134
|
|
|
154
135
|
addProperty() {
|
|
155
|
-
this.
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
if (!prompt || !this.validateName(prompt)) return
|
|
159
|
-
this.datalayer.indexProperty(prompt)
|
|
160
|
-
this.open()
|
|
161
|
-
})
|
|
136
|
+
this.datalayer.addProperty().then(() => {
|
|
137
|
+
this.open()
|
|
138
|
+
})
|
|
162
139
|
}
|
|
163
140
|
|
|
164
141
|
open() {
|
|
@@ -0,0 +1,122 @@
|
|
|
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 TEMPLATE = `
|
|
6
|
+
<div>
|
|
7
|
+
<form data-ref="form">
|
|
8
|
+
<h3><i class="icon icon-24 icon-template"></i>${translate('Load map template')}</h3>
|
|
9
|
+
<p>${translate('Loading a template will apply predefined styles and settings to your map')}.</p>
|
|
10
|
+
<div class="formbox">
|
|
11
|
+
<div class="flat-tabs" data-ref="tabs">
|
|
12
|
+
<button type="button" class="flat" data-value="mine" data-ref="mine">${translate('My templates')}</button>
|
|
13
|
+
<button type="button" class="flat" data-value="staff">${translate('From staff')}</button>
|
|
14
|
+
<button type="button" class="flat" data-value="community">${translate('From community')}</button>
|
|
15
|
+
</div>
|
|
16
|
+
<div data-ref="body" class="body"></div>
|
|
17
|
+
<div class="button-bar half">
|
|
18
|
+
<button type="button" class="primary" data-ref="confirm" disabled>${translate('Load template')}</button>
|
|
19
|
+
<button type="button" data-ref="confirmData" disabled>${translate('Load template with data')}</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</form>
|
|
23
|
+
</div>
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
export default class TemplateImporter {
|
|
27
|
+
constructor(umap) {
|
|
28
|
+
this.umap = umap
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async open() {
|
|
32
|
+
const [root, { tabs, form, body, mine, confirm, confirmData }] =
|
|
33
|
+
Utils.loadTemplateWithRefs(TEMPLATE)
|
|
34
|
+
const uri = this.umap.urls.get('template_list')
|
|
35
|
+
const userIsAuth = Boolean(this.umap.properties.user?.id)
|
|
36
|
+
const defaultTab = userIsAuth ? 'mine' : 'staff'
|
|
37
|
+
mine.hidden = !userIsAuth
|
|
38
|
+
|
|
39
|
+
const loadTemplates = async (source) => {
|
|
40
|
+
const [data, response, error] = await this.umap.server.get(
|
|
41
|
+
`${uri}?source=${source}`
|
|
42
|
+
)
|
|
43
|
+
if (!error) {
|
|
44
|
+
body.innerHTML = ''
|
|
45
|
+
if (!data.templates.length) {
|
|
46
|
+
let message
|
|
47
|
+
switch (source) {
|
|
48
|
+
case 'mine':
|
|
49
|
+
message = translate(
|
|
50
|
+
'You have no registered template yet. You can add one by creating a new map and flagging it as "template".'
|
|
51
|
+
)
|
|
52
|
+
break
|
|
53
|
+
case 'staff':
|
|
54
|
+
message = translate(
|
|
55
|
+
'There is no recommended template yet. Recommended templates are the ones starred by uMap administrators.'
|
|
56
|
+
)
|
|
57
|
+
break
|
|
58
|
+
case 'community':
|
|
59
|
+
message = translate('There is no public template yet.')
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
body.textContent = message
|
|
63
|
+
}
|
|
64
|
+
for (const template of data.templates) {
|
|
65
|
+
const item = Utils.loadTemplate(
|
|
66
|
+
`<dl>
|
|
67
|
+
<dt>
|
|
68
|
+
<label>
|
|
69
|
+
<input type="radio" value="${template.id}" name="template" />${template.name}
|
|
70
|
+
<a href="${template.url}" target="_blank"><nobr>${translate('Explore')}<i class="icon icon-16 icon-external-link"></i></nobr></a>
|
|
71
|
+
</label>
|
|
72
|
+
</dt>
|
|
73
|
+
<dd class="text">${Utils.toHTML(template.description)}</dd>
|
|
74
|
+
</dl>`
|
|
75
|
+
)
|
|
76
|
+
body.appendChild(item)
|
|
77
|
+
}
|
|
78
|
+
tabs.querySelectorAll('button').forEach((el) => el.classList.remove('on'))
|
|
79
|
+
tabs.querySelector(`[data-value="${source}"]`).classList.add('on')
|
|
80
|
+
} else {
|
|
81
|
+
console.error(response)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
loadTemplates(defaultTab)
|
|
85
|
+
tabs
|
|
86
|
+
.querySelectorAll('button')
|
|
87
|
+
.forEach((el) =>
|
|
88
|
+
el.addEventListener('click', () => loadTemplates(el.dataset.value))
|
|
89
|
+
)
|
|
90
|
+
form.addEventListener('change', () => {
|
|
91
|
+
if (form.template.value) {
|
|
92
|
+
confirm.disabled = false
|
|
93
|
+
confirmData.disabled = false
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
const onConfirm = (includeData) => {
|
|
97
|
+
const templateId = form.template.value
|
|
98
|
+
if (!templateId) {
|
|
99
|
+
Alert.error(translate('You must select a template.'))
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
let url = this.umap.urls.get('map_download', {
|
|
103
|
+
map_id: templateId,
|
|
104
|
+
})
|
|
105
|
+
if (!includeData) {
|
|
106
|
+
url = `${url}?include_data=0`
|
|
107
|
+
}
|
|
108
|
+
this.umap.importer.build()
|
|
109
|
+
this.umap.importer.url = url
|
|
110
|
+
this.umap.importer.format = 'umap'
|
|
111
|
+
this.umap.importer.submit()
|
|
112
|
+
this.umap.editPanel.close()
|
|
113
|
+
}
|
|
114
|
+
confirm.addEventListener('click', () => onConfirm(false))
|
|
115
|
+
confirmData.addEventListener('click', () => onConfirm(true))
|
|
116
|
+
|
|
117
|
+
this.umap.editPanel.open({
|
|
118
|
+
content: root,
|
|
119
|
+
highlight: 'templates',
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
}
|