umap-project 3.1.2__py3-none-any.whl → 3.3.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/locale/en/LC_MESSAGES/django.mo +0 -0
- umap/locale/en/LC_MESSAGES/django.po +22 -18
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +21 -17
- umap/management/commands/export_pictogram.py +29 -0
- umap/management/commands/migrate_to_S3.py +5 -1
- umap/management/commands/purge_old_versions.py +8 -6
- umap/settings/__init__.py +21 -0
- umap/settings/base.py +3 -0
- umap/static/umap/content.css +7 -2
- umap/static/umap/css/contextmenu.css +58 -2
- umap/static/umap/css/form.css +175 -45
- umap/static/umap/css/icon.css +97 -3
- umap/static/umap/css/panel.css +31 -1
- umap/static/umap/img/16-white.svg +21 -40
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24-white.svg +9 -9
- umap/static/umap/img/24.svg +23 -10
- umap/static/umap/img/source/16-white.svg +23 -41
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24-white.svg +11 -11
- umap/static/umap/img/source/24.svg +25 -12
- umap/static/umap/js/modules/browser.js +1 -1
- umap/static/umap/js/modules/caption.js +8 -0
- umap/static/umap/js/modules/data/features.js +331 -202
- umap/static/umap/js/modules/data/layer.js +263 -152
- umap/static/umap/js/modules/facets.js +2 -2
- umap/static/umap/js/modules/form/builder.js +11 -7
- umap/static/umap/js/modules/form/fields.js +66 -26
- umap/static/umap/js/modules/formatter.js +78 -28
- umap/static/umap/js/modules/importer.js +6 -1
- umap/static/umap/js/modules/importers/opendata.js +138 -33
- umap/static/umap/js/modules/importers/openrouteservice.js +140 -0
- umap/static/umap/js/modules/managers.js +67 -0
- umap/static/umap/js/modules/printer.js +107 -0
- umap/static/umap/js/modules/rendering/controls.js +78 -2
- umap/static/umap/js/modules/rendering/icon.js +116 -87
- umap/static/umap/js/modules/rendering/layers/classified.js +8 -7
- umap/static/umap/js/modules/rendering/layers/cluster.js +199 -63
- umap/static/umap/js/modules/rendering/map.js +6 -2
- umap/static/umap/js/modules/rendering/template.js +71 -1
- umap/static/umap/js/modules/rendering/ui.js +111 -34
- umap/static/umap/js/modules/rules.js +76 -23
- umap/static/umap/js/modules/schema.js +27 -0
- umap/static/umap/js/modules/share.js +19 -12
- umap/static/umap/js/modules/slideshow.js +1 -1
- umap/static/umap/js/modules/sync/updaters.js +1 -6
- umap/static/umap/js/modules/tableeditor.js +13 -37
- umap/static/umap/js/modules/templates.js +7 -6
- umap/static/umap/js/modules/ui/bar.js +6 -1
- umap/static/umap/js/modules/ui/base.js +24 -9
- umap/static/umap/js/modules/ui/contextmenu.js +17 -7
- umap/static/umap/js/modules/ui/dialog.js +7 -4
- umap/static/umap/js/modules/ui/panel.js +7 -0
- umap/static/umap/js/modules/umap.js +84 -67
- umap/static/umap/js/modules/utils.js +8 -7
- umap/static/umap/js/umap.controls.js +22 -57
- umap/static/umap/locale/am_ET.js +81 -9
- umap/static/umap/locale/am_ET.json +81 -9
- umap/static/umap/locale/ar.js +81 -9
- umap/static/umap/locale/ar.json +81 -9
- umap/static/umap/locale/ast.js +81 -9
- umap/static/umap/locale/ast.json +81 -9
- umap/static/umap/locale/bg.js +81 -9
- umap/static/umap/locale/bg.json +81 -9
- umap/static/umap/locale/br.js +68 -29
- umap/static/umap/locale/br.json +68 -29
- umap/static/umap/locale/ca.js +88 -16
- umap/static/umap/locale/ca.json +88 -16
- umap/static/umap/locale/cs_CZ.js +81 -9
- umap/static/umap/locale/cs_CZ.json +81 -9
- umap/static/umap/locale/da.js +48 -9
- umap/static/umap/locale/da.json +48 -9
- umap/static/umap/locale/de.js +48 -9
- umap/static/umap/locale/de.json +48 -9
- umap/static/umap/locale/el.js +58 -13
- umap/static/umap/locale/el.json +58 -13
- umap/static/umap/locale/en.js +48 -9
- umap/static/umap/locale/en.json +48 -9
- umap/static/umap/locale/en_US.json +81 -9
- umap/static/umap/locale/es.js +48 -9
- umap/static/umap/locale/es.json +48 -9
- umap/static/umap/locale/et.js +81 -9
- umap/static/umap/locale/et.json +81 -9
- umap/static/umap/locale/eu.js +97 -25
- umap/static/umap/locale/eu.json +97 -25
- umap/static/umap/locale/fa_IR.js +81 -9
- umap/static/umap/locale/fa_IR.json +81 -9
- umap/static/umap/locale/fi.js +81 -9
- umap/static/umap/locale/fi.json +81 -9
- umap/static/umap/locale/fr.js +48 -9
- umap/static/umap/locale/fr.json +48 -9
- umap/static/umap/locale/gl.js +81 -9
- umap/static/umap/locale/gl.json +81 -9
- umap/static/umap/locale/he.js +81 -9
- umap/static/umap/locale/he.json +81 -9
- umap/static/umap/locale/hr.js +81 -9
- umap/static/umap/locale/hr.json +81 -9
- umap/static/umap/locale/hu.js +72 -27
- umap/static/umap/locale/hu.json +72 -27
- umap/static/umap/locale/id.js +81 -9
- umap/static/umap/locale/id.json +81 -9
- umap/static/umap/locale/is.js +81 -9
- umap/static/umap/locale/is.json +81 -9
- umap/static/umap/locale/it.js +48 -9
- umap/static/umap/locale/it.json +48 -9
- umap/static/umap/locale/ja.js +81 -9
- umap/static/umap/locale/ja.json +81 -9
- umap/static/umap/locale/ko.js +81 -9
- umap/static/umap/locale/ko.json +81 -9
- umap/static/umap/locale/lt.js +81 -9
- umap/static/umap/locale/lt.json +81 -9
- umap/static/umap/locale/ms.js +81 -9
- umap/static/umap/locale/ms.json +81 -9
- umap/static/umap/locale/nl.js +48 -9
- umap/static/umap/locale/nl.json +48 -9
- umap/static/umap/locale/no.js +81 -9
- umap/static/umap/locale/no.json +81 -9
- umap/static/umap/locale/pl.js +81 -9
- umap/static/umap/locale/pl.json +81 -9
- umap/static/umap/locale/pl_PL.json +81 -9
- umap/static/umap/locale/pt.js +81 -9
- umap/static/umap/locale/pt.json +81 -9
- umap/static/umap/locale/pt_BR.js +91 -19
- umap/static/umap/locale/pt_BR.json +91 -19
- umap/static/umap/locale/pt_PT.js +81 -9
- umap/static/umap/locale/pt_PT.json +81 -9
- umap/static/umap/locale/ro.js +81 -9
- umap/static/umap/locale/ro.json +81 -9
- umap/static/umap/locale/ru.js +81 -9
- umap/static/umap/locale/ru.json +81 -9
- umap/static/umap/locale/sk_SK.js +81 -9
- umap/static/umap/locale/sk_SK.json +81 -9
- umap/static/umap/locale/sl.js +81 -9
- umap/static/umap/locale/sl.json +81 -9
- umap/static/umap/locale/sr.js +81 -9
- umap/static/umap/locale/sr.json +81 -9
- umap/static/umap/locale/sv.js +81 -9
- umap/static/umap/locale/sv.json +81 -9
- umap/static/umap/locale/th_TH.js +81 -9
- umap/static/umap/locale/th_TH.json +81 -9
- umap/static/umap/locale/tr.js +81 -9
- umap/static/umap/locale/tr.json +81 -9
- umap/static/umap/locale/uk_UA.js +81 -9
- umap/static/umap/locale/uk_UA.json +81 -9
- umap/static/umap/locale/vi.js +81 -9
- umap/static/umap/locale/vi.json +81 -9
- umap/static/umap/locale/vi_VN.json +81 -9
- umap/static/umap/locale/zh.js +81 -9
- umap/static/umap/locale/zh.json +81 -9
- umap/static/umap/locale/zh_CN.json +81 -9
- umap/static/umap/locale/zh_TW.Big5.json +81 -9
- umap/static/umap/locale/zh_TW.js +98 -26
- umap/static/umap/locale/zh_TW.json +98 -26
- umap/static/umap/map.css +325 -102
- umap/static/umap/vars.css +1 -0
- umap/static/umap/vendors/betterknown/betterknown.mjs +287 -0
- umap/static/umap/vendors/editable/Leaflet.Editable.js +3 -1
- umap/static/umap/vendors/openrouteservice/ors-js-client.js +521 -0
- umap/static/umap/vendors/openrouteservice/ors-js-client.js.map +1 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.js +63 -0
- umap/static/umap/vendors/simple-elevation-chart/elevation.svg +8 -0
- umap/static/umap/vendors/snapdom/snapdom.min.mjs +3 -0
- umap/storage/fs.py +3 -2
- umap/storage/staticfiles.py +12 -0
- umap/templates/base.html +4 -1
- umap/templates/umap/css.html +0 -4
- umap/templates/umap/js.html +1 -3
- umap/tests/base.py +9 -1
- umap/tests/integration/test_basics.py +3 -1
- umap/tests/integration/test_conditional_rules.py +79 -37
- umap/tests/integration/test_datalayer.py +1 -1
- umap/tests/integration/test_draw_polygon.py +3 -5
- umap/tests/integration/test_draw_polyline.py +4 -6
- umap/tests/integration/test_draw_route.py +178 -0
- umap/tests/integration/test_edit_datalayer.py +1 -1
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_edit_marker.py +8 -8
- umap/tests/integration/test_edit_polygon.py +2 -2
- umap/tests/integration/test_export_map.py +84 -10
- umap/tests/integration/test_import.py +140 -0
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_optimistic_merge.py +72 -12
- umap/tests/integration/test_share.py +1 -1
- umap/tests/integration/test_tableeditor.py +10 -7
- umap/tests/integration/test_websocket_sync.py +4 -4
- umap/utils.py +37 -0
- umap/views.py +18 -2
- umap_project-3.3.0.dist-info/METADATA +76 -0
- {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/RECORD +194 -188
- umap/static/umap/vendors/markercluster/MarkerCluster.Default.css +0 -60
- umap/static/umap/vendors/markercluster/MarkerCluster.css +0 -14
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js +0 -2
- umap/static/umap/vendors/markercluster/leaflet.markercluster.js.map +0 -1
- umap_project-3.1.2.dist-info/METADATA +0 -68
- {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/WHEEL +0 -0
- {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
DomEvent,
|
|
4
4
|
DomUtil,
|
|
5
5
|
Icon,
|
|
6
|
+
Point,
|
|
6
7
|
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
7
8
|
import { SCHEMA } from '../schema.js'
|
|
8
9
|
import * as Utils from '../utils.js'
|
|
@@ -11,6 +12,8 @@ export function getClass(name) {
|
|
|
11
12
|
switch (name) {
|
|
12
13
|
case 'Circle':
|
|
13
14
|
return Circle
|
|
15
|
+
case 'LargeCircle':
|
|
16
|
+
return LargeCircle
|
|
14
17
|
case 'Ball':
|
|
15
18
|
return Ball
|
|
16
19
|
case 'Drop':
|
|
@@ -25,13 +28,13 @@ export function getClass(name) {
|
|
|
25
28
|
export const RECENT = []
|
|
26
29
|
|
|
27
30
|
const BaseIcon = DivIcon.extend({
|
|
31
|
+
default_options: {
|
|
32
|
+
iconSize: null, // Made in css
|
|
33
|
+
iconUrl: SCHEMA.iconUrl.default,
|
|
34
|
+
feature: null,
|
|
35
|
+
},
|
|
28
36
|
initialize: function (options) {
|
|
29
|
-
|
|
30
|
-
iconSize: null, // Made in css
|
|
31
|
-
iconUrl: SCHEMA.iconUrl.default,
|
|
32
|
-
feature: null,
|
|
33
|
-
}
|
|
34
|
-
options = L.Util.extend({}, default_options, options)
|
|
37
|
+
options = { ...this.default_options, ...options }
|
|
35
38
|
Icon.prototype.initialize.call(this, options)
|
|
36
39
|
this.feature = this.options.feature
|
|
37
40
|
if (this.feature?.isReadOnly()) {
|
|
@@ -66,6 +69,10 @@ const BaseIcon = DivIcon.extend({
|
|
|
66
69
|
return color
|
|
67
70
|
},
|
|
68
71
|
|
|
72
|
+
_getSize: function () {
|
|
73
|
+
return this.feature?.getOption('iconSize') || SCHEMA.iconSize.default
|
|
74
|
+
},
|
|
75
|
+
|
|
69
76
|
_getOpacity: function () {
|
|
70
77
|
if (this.feature) return this.feature.getOption('iconOpacity')
|
|
71
78
|
return SCHEMA.iconOpacity.default
|
|
@@ -77,18 +84,33 @@ const BaseIcon = DivIcon.extend({
|
|
|
77
84
|
if (this.feature.isActive()) this.options.className += ' umap-icon-active'
|
|
78
85
|
DivIcon.prototype._setIconStyles.call(this, img, name)
|
|
79
86
|
},
|
|
87
|
+
|
|
88
|
+
createIcon: function () {
|
|
89
|
+
const [root, elements] = Utils.loadTemplateWithRefs(this.getTemplate())
|
|
90
|
+
this.root = root
|
|
91
|
+
this.elements = elements
|
|
92
|
+
this.root.dataset.feature = this.feature?.id
|
|
93
|
+
if (this.elements.container) {
|
|
94
|
+
const src = this._getIconUrl('icon')
|
|
95
|
+
if (src) {
|
|
96
|
+
this.elements.icon = makeElement(src, this.elements.container)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
this._setIconStyles(this.root, 'icon')
|
|
100
|
+
return this.root
|
|
101
|
+
},
|
|
80
102
|
})
|
|
81
103
|
|
|
82
104
|
const DefaultIcon = BaseIcon.extend({
|
|
83
105
|
default_options: {
|
|
84
|
-
iconAnchor: new
|
|
85
|
-
popupAnchor: new
|
|
86
|
-
tooltipAnchor: new
|
|
106
|
+
iconAnchor: new Point(16, 40),
|
|
107
|
+
popupAnchor: new Point(0, -40),
|
|
108
|
+
tooltipAnchor: new Point(16, -24),
|
|
87
109
|
className: 'umap-div-icon',
|
|
88
110
|
},
|
|
89
111
|
|
|
90
112
|
initialize: function (options) {
|
|
91
|
-
options =
|
|
113
|
+
options = { ...this.default_options, ...options }
|
|
92
114
|
BaseIcon.prototype.initialize.call(this, options)
|
|
93
115
|
},
|
|
94
116
|
|
|
@@ -108,92 +130,113 @@ const DefaultIcon = BaseIcon.extend({
|
|
|
108
130
|
setContrast(this.elements.icon, this.elements.container, src, bgcolor)
|
|
109
131
|
},
|
|
110
132
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
this.elements.main.dataset.feature = this.feature?.id
|
|
120
|
-
this.elements.arrow = DomUtil.create('div', 'icon_arrow', this.elements.main)
|
|
121
|
-
const src = this._getIconUrl('icon')
|
|
122
|
-
if (src) {
|
|
123
|
-
this.elements.icon = makeElement(src, this.elements.container)
|
|
124
|
-
}
|
|
125
|
-
this._setIconStyles(this.elements.main, 'icon')
|
|
126
|
-
return this.elements.main
|
|
133
|
+
getTemplate: () => {
|
|
134
|
+
return `
|
|
135
|
+
<div>
|
|
136
|
+
<div class="icon-container" data-ref=container></div>
|
|
137
|
+
<div class="icon-arrow" data-ref=arrow></div>
|
|
138
|
+
</div>
|
|
139
|
+
`
|
|
127
140
|
},
|
|
128
141
|
})
|
|
129
142
|
|
|
130
143
|
const Circle = BaseIcon.extend({
|
|
131
144
|
initialize: function (options) {
|
|
132
145
|
const default_options = {
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
iconSize: new Point(12, 12),
|
|
147
|
+
popupAnchor: new Point(0, -6),
|
|
148
|
+
tooltipAnchor: new Point(6, 0),
|
|
135
149
|
className: 'umap-circle-icon',
|
|
136
150
|
}
|
|
137
|
-
options =
|
|
151
|
+
options = { ...default_options, ...(options || {}) }
|
|
138
152
|
BaseIcon.prototype.initialize.call(this, options)
|
|
139
153
|
},
|
|
140
154
|
|
|
141
155
|
_setIconStyles: function (img, name) {
|
|
142
156
|
BaseIcon.prototype._setIconStyles.call(this, img, name)
|
|
143
|
-
this.
|
|
144
|
-
this.
|
|
157
|
+
this.root.style.backgroundColor = this._getColor()
|
|
158
|
+
this.root.style.opacity = this._getOpacity()
|
|
145
159
|
},
|
|
146
160
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
161
|
+
getTemplate: () => {
|
|
162
|
+
return '<div> </div>'
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const LargeCircle = BaseIcon.extend({
|
|
167
|
+
default_options: {
|
|
168
|
+
className: 'umap-large-circle-icon',
|
|
169
|
+
},
|
|
170
|
+
initialize: function (options) {
|
|
171
|
+
BaseIcon.prototype.initialize.call(this, options)
|
|
172
|
+
const size = this._getSize()
|
|
173
|
+
this.options.popupAnchor = new Point(0, (size / 2) * -1)
|
|
174
|
+
this.options.tooltipAnchor = new Point(size / 2, 0)
|
|
175
|
+
this.options.iconAnchor = new Point(size / 2, size / 2)
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
_setIconStyles: function (img, name) {
|
|
179
|
+
BaseIcon.prototype._setIconStyles.call(this, img, name)
|
|
180
|
+
this.root.style.opacity = this._getOpacity()
|
|
181
|
+
this.root.style.borderColor = this._getColor()
|
|
182
|
+
this.root.style.width = `${this._getSize()}px`
|
|
183
|
+
this.root.style.height = `${this._getSize()}px`
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
getTemplate: () => {
|
|
187
|
+
return '<div data-ref=container></div>'
|
|
154
188
|
},
|
|
155
189
|
})
|
|
156
190
|
|
|
157
191
|
const Raw = DefaultIcon.extend({
|
|
158
192
|
default_options: {
|
|
159
|
-
iconSize: new L.Point(48, 48),
|
|
160
|
-
popupAnchor: new L.Point(0, 0),
|
|
161
|
-
tooltipAnchor: new L.Point(0, 0),
|
|
162
193
|
className: 'umap-raw-icon',
|
|
163
194
|
},
|
|
195
|
+
initialize: function (options) {
|
|
196
|
+
DefaultIcon.prototype.initialize.call(this, options)
|
|
197
|
+
const size = this._getSize()
|
|
198
|
+
this.options.popupAnchor = new Point(0, (size / 2) * -1)
|
|
199
|
+
this.options.tooltipAnchor = new Point(size / 2, 0)
|
|
200
|
+
this.options.iconAnchor = new Point(size / 2, size / 2)
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
_setIconStyles: function (img, name) {
|
|
204
|
+
BaseIcon.prototype._setIconStyles.call(this, img, name)
|
|
205
|
+
this.root.style.width = `${this._getSize()}px`
|
|
206
|
+
this.root.style.height = `${this._getSize()}px`
|
|
207
|
+
},
|
|
164
208
|
|
|
165
209
|
_getColor: () => 'transparent',
|
|
210
|
+
|
|
211
|
+
getTemplate: () => {
|
|
212
|
+
return '<div data-ref=container></div>'
|
|
213
|
+
},
|
|
166
214
|
})
|
|
167
215
|
|
|
168
216
|
const Drop = DefaultIcon.extend({
|
|
169
217
|
default_options: {
|
|
170
|
-
iconAnchor: new
|
|
171
|
-
popupAnchor: new
|
|
172
|
-
tooltipAnchor: new
|
|
218
|
+
iconAnchor: new Point(16, 42),
|
|
219
|
+
popupAnchor: new Point(0, -42),
|
|
220
|
+
tooltipAnchor: new Point(16, -24),
|
|
173
221
|
className: 'umap-drop-icon',
|
|
174
222
|
},
|
|
175
223
|
})
|
|
176
224
|
|
|
177
225
|
const Ball = DefaultIcon.extend({
|
|
178
226
|
default_options: {
|
|
179
|
-
iconAnchor: new
|
|
180
|
-
popupAnchor: new
|
|
181
|
-
tooltipAnchor: new
|
|
227
|
+
iconAnchor: new Point(8, 30),
|
|
228
|
+
popupAnchor: new Point(0, -28),
|
|
229
|
+
tooltipAnchor: new Point(8, -23),
|
|
182
230
|
className: 'umap-ball-icon',
|
|
183
231
|
},
|
|
184
232
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
193
|
-
this.elements.main.dataset.feature = this.feature?.id
|
|
194
|
-
this.elements.arrow = DomUtil.create('div', 'icon_arrow', this.elements.main)
|
|
195
|
-
this._setIconStyles(this.elements.main, 'icon')
|
|
196
|
-
return this.elements.main
|
|
233
|
+
getTemplate: () => {
|
|
234
|
+
return `
|
|
235
|
+
<div>
|
|
236
|
+
<div class="icon-container" data-ref=ball></div>
|
|
237
|
+
<div class="icon-arrow" data-ref=arrow></div>
|
|
238
|
+
</div>
|
|
239
|
+
`
|
|
197
240
|
},
|
|
198
241
|
|
|
199
242
|
_setIconStyles: function (img, name) {
|
|
@@ -207,38 +250,26 @@ const Ball = DefaultIcon.extend({
|
|
|
207
250
|
} else {
|
|
208
251
|
background = `radial-gradient(circle at 6px 38% , white -4px, ${color} 8px) repeat scroll 0 0 transparent`
|
|
209
252
|
}
|
|
210
|
-
this.elements.
|
|
211
|
-
this.elements.
|
|
253
|
+
this.elements.ball.style.background = background
|
|
254
|
+
this.elements.ball.style.opacity = this._getOpacity()
|
|
212
255
|
},
|
|
213
256
|
})
|
|
214
257
|
|
|
215
258
|
export const Cluster = DivIcon.extend({
|
|
216
259
|
options: {
|
|
217
260
|
iconSize: [40, 40],
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
initialize: function (datalayer, cluster) {
|
|
221
|
-
this.datalayer = datalayer
|
|
222
|
-
this.cluster = cluster
|
|
261
|
+
className: 'umap-cluster-icon',
|
|
223
262
|
},
|
|
224
263
|
|
|
225
264
|
createIcon: function () {
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
computeTextColor: function (el) {
|
|
236
|
-
let color
|
|
237
|
-
const backgroundColor = this.datalayer.getColor()
|
|
238
|
-
if (this.datalayer.properties.cluster?.textColor) {
|
|
239
|
-
color = this.datalayer.properties.cluster.textColor
|
|
240
|
-
}
|
|
241
|
-
return color || DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
|
|
265
|
+
const template = '<div><span data-ref=counter></span></div>'
|
|
266
|
+
const [root, { counter }] = Utils.loadTemplateWithRefs(template)
|
|
267
|
+
this.root = root
|
|
268
|
+
this.counter = counter
|
|
269
|
+
this.counter.textContent = this.options.getCounter()
|
|
270
|
+
this.root.style.backgroundColor = this.options.color
|
|
271
|
+
this._setIconStyles(this.root, 'icon')
|
|
272
|
+
return this.root
|
|
242
273
|
},
|
|
243
274
|
})
|
|
244
275
|
|
|
@@ -249,11 +280,9 @@ export function isImg(src) {
|
|
|
249
280
|
export function makeElement(src, parent) {
|
|
250
281
|
let icon
|
|
251
282
|
if (isImg(src)) {
|
|
252
|
-
icon =
|
|
253
|
-
icon.src = src
|
|
283
|
+
icon = Utils.loadTemplate(`<img loading="lazy" src="${src}">`)
|
|
254
284
|
} else {
|
|
255
|
-
icon =
|
|
256
|
-
icon.textContent = src
|
|
285
|
+
icon = Utils.loadTemplate(`<span>${src}</span>`)
|
|
257
286
|
}
|
|
258
287
|
parent.appendChild(icon)
|
|
259
288
|
return icon
|
|
@@ -273,7 +302,7 @@ export function setContrast(icon, parent, src, bgcolor) {
|
|
|
273
302
|
if (DomUtil.contrastedColor(parent, bgcolor)) {
|
|
274
303
|
// Decide whether to switch svg to white or not, but do it
|
|
275
304
|
// only for internal SVG, as invert could do weird things
|
|
276
|
-
if (
|
|
305
|
+
if (src.endsWith('.svg') && src !== SCHEMA.iconUrl.default) {
|
|
277
306
|
// Must be called after icon container is added to the DOM
|
|
278
307
|
// An image
|
|
279
308
|
icon.style.filter = 'invert(1)'
|
|
@@ -67,7 +67,7 @@ const ClassifiedMixin = {
|
|
|
67
67
|
|
|
68
68
|
getValues: function () {
|
|
69
69
|
const values = []
|
|
70
|
-
this.datalayer.
|
|
70
|
+
this.datalayer.features.forEach((feature) => {
|
|
71
71
|
const value = this._getValue(feature)
|
|
72
72
|
if (value !== undefined) values.push(value)
|
|
73
73
|
})
|
|
@@ -198,7 +198,7 @@ export const Choropleth = FeatureGroup.extend({
|
|
|
198
198
|
'properties.choropleth.property',
|
|
199
199
|
{
|
|
200
200
|
handler: 'Select',
|
|
201
|
-
selectOptions: this.datalayer.
|
|
201
|
+
selectOptions: this.datalayer.fieldKeys,
|
|
202
202
|
label: translate('Choropleth property value'),
|
|
203
203
|
},
|
|
204
204
|
],
|
|
@@ -307,7 +307,7 @@ export const Circles = FeatureGroup.extend({
|
|
|
307
307
|
'properties.circles.property',
|
|
308
308
|
{
|
|
309
309
|
handler: 'Select',
|
|
310
|
-
selectOptions: this.datalayer.
|
|
310
|
+
selectOptions: this.datalayer.fieldKeys,
|
|
311
311
|
label: translate('Property name to compute circles'),
|
|
312
312
|
},
|
|
313
313
|
],
|
|
@@ -384,8 +384,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
384
384
|
|
|
385
385
|
_getValue: function (feature) {
|
|
386
386
|
const key =
|
|
387
|
-
this.datalayer.properties.categorized.property ||
|
|
388
|
-
this.datalayer.allProperties()[0]
|
|
387
|
+
this.datalayer.properties.categorized.property || this.datalayer.fieldKeys[0]
|
|
389
388
|
return feature.properties[key]
|
|
390
389
|
},
|
|
391
390
|
|
|
@@ -438,7 +437,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
438
437
|
'properties.categorized.property',
|
|
439
438
|
{
|
|
440
439
|
handler: 'Select',
|
|
441
|
-
selectOptions: this.datalayer.
|
|
440
|
+
selectOptions: this.datalayer.fieldKeys,
|
|
442
441
|
label: translate('Category property'),
|
|
443
442
|
},
|
|
444
443
|
],
|
|
@@ -447,7 +446,7 @@ export const Categorized = FeatureGroup.extend({
|
|
|
447
446
|
{
|
|
448
447
|
handler: 'Select',
|
|
449
448
|
label: translate('Color palette'),
|
|
450
|
-
|
|
449
|
+
getOptions: () => this.getColorSchemes(this._classes),
|
|
451
450
|
},
|
|
452
451
|
],
|
|
453
452
|
[
|
|
@@ -481,6 +480,8 @@ export const Categorized = FeatureGroup.extend({
|
|
|
481
480
|
if (builder) builder.helpers['properties.categorized.mode'].fetch()
|
|
482
481
|
}
|
|
483
482
|
this.compute()
|
|
483
|
+
// Rebuild list of color palettes when aggregation property changes.
|
|
484
|
+
builder?.helpers['properties.categorized.brewer']?.fetch()
|
|
484
485
|
// If user changes the mode
|
|
485
486
|
// then update the categories input value
|
|
486
487
|
if (field === 'properties.categorized.mode') {
|
|
@@ -1,24 +1,100 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
FeatureGroup,
|
|
3
|
+
LayerGroup,
|
|
4
|
+
Point,
|
|
5
|
+
Marker,
|
|
6
|
+
Rectangle,
|
|
7
|
+
Polyline,
|
|
8
|
+
DomUtil,
|
|
9
|
+
latLngBounds,
|
|
10
|
+
} from '../../../../vendors/leaflet/leaflet-src.esm.js'
|
|
4
11
|
import { translate } from '../../i18n.js'
|
|
5
12
|
import * as Utils from '../../utils.js'
|
|
6
13
|
import { Cluster as ClusterIcon } from '../icon.js'
|
|
7
14
|
import { LayerMixin } from './base.js'
|
|
8
15
|
|
|
9
|
-
const MarkerCluster =
|
|
10
|
-
// Custom class so we can call computeTextColor
|
|
11
|
-
// when element is already on the DOM.
|
|
12
|
-
|
|
16
|
+
const MarkerCluster = Marker.extend({
|
|
13
17
|
_initIcon: function () {
|
|
14
|
-
|
|
15
|
-
const
|
|
18
|
+
Marker.prototype._initIcon.call(this)
|
|
19
|
+
const counter = this._icon.querySelector('span')
|
|
16
20
|
// Compute text color only when icon is added to the DOM.
|
|
17
|
-
|
|
21
|
+
const bgColor = this.options.icon.options.color
|
|
22
|
+
const textColor = this.options.icon.options.textColor
|
|
23
|
+
counter.style.color =
|
|
24
|
+
textColor || DomUtil.TextColorFromBackgroundColor(counter, bgColor)
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
computeCoverage() {
|
|
28
|
+
if (this._layers.length < 2) return
|
|
29
|
+
if (!this._coverage) {
|
|
30
|
+
const latlngs = this._layers.map((layer) => layer._latlng)
|
|
31
|
+
const bounds = latLngBounds(latlngs)
|
|
32
|
+
this._coverage = new Rectangle(latlngs, {
|
|
33
|
+
color: this.options.icon.options.color,
|
|
34
|
+
stroke: false,
|
|
35
|
+
})
|
|
36
|
+
this._latlng = bounds.getCenter()
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
async zoomToCoverage() {
|
|
41
|
+
let resolve = undefined
|
|
42
|
+
const promise = new Promise((r) => {
|
|
43
|
+
resolve = r
|
|
44
|
+
})
|
|
45
|
+
if (this._map && this._coverage) {
|
|
46
|
+
this._map.once('moveend', () => resolve())
|
|
47
|
+
this._map.fitBounds(this._coverage.getBounds())
|
|
48
|
+
}
|
|
49
|
+
return promise
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
_spiderfyLatLng: function (center, index) {
|
|
53
|
+
const step = 20
|
|
54
|
+
const maxRadius = 150
|
|
55
|
+
const zoom = this._map.getZoom()
|
|
56
|
+
const angle = (index * step * Math.PI) / 180
|
|
57
|
+
const progress = index / this._layers.length
|
|
58
|
+
const radius = maxRadius * (1 - progress) ** 0.4
|
|
59
|
+
const x = radius * Math.cos(angle)
|
|
60
|
+
const y = radius * Math.sin(angle)
|
|
61
|
+
const point = this._map.project([center.lat, center.lng], zoom)
|
|
62
|
+
const latlng = this._map.unproject(new Point(point.x + x, point.y + y), zoom)
|
|
63
|
+
return latlng
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
spiderfy() {
|
|
67
|
+
if (!this._map) return
|
|
68
|
+
const crs = this._map.options.crs
|
|
69
|
+
if (this._spider && this._map.hasLayer(this._spider)) this.unspiderfy()
|
|
70
|
+
this._spider = new LayerGroup()
|
|
71
|
+
let i = 1
|
|
72
|
+
const center = this.getLatLng()
|
|
73
|
+
for (const layer of this._layers) {
|
|
74
|
+
const latlng = this._spiderfyLatLng(center, i++)
|
|
75
|
+
layer._originalLatLng = layer._latlng
|
|
76
|
+
layer.setLatLng(latlng)
|
|
77
|
+
this._spider.addLayer(layer)
|
|
78
|
+
const line = new Polyline([center, latlng], { color: 'black', weight: 1 })
|
|
79
|
+
this._spider.addLayer(line)
|
|
80
|
+
}
|
|
81
|
+
this._map.addLayer(this._spider)
|
|
82
|
+
this._icon.hidden = true
|
|
83
|
+
this._map.once('click zoomstart', this.unspiderfy, this)
|
|
84
|
+
this.once('remove', this.unspiderfy, this)
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
unspiderfy() {
|
|
88
|
+
if (this._icon) this._icon.hidden = false
|
|
89
|
+
if (this._spider) this._spider.remove()
|
|
90
|
+
for (const layer of this._layers) {
|
|
91
|
+
if (layer._originalLatLng) layer.setLatLng(layer._originalLatLng)
|
|
92
|
+
delete layer._originalLatLng
|
|
93
|
+
}
|
|
18
94
|
},
|
|
19
95
|
})
|
|
20
96
|
|
|
21
|
-
export const Cluster =
|
|
97
|
+
export const Cluster = FeatureGroup.extend({
|
|
22
98
|
statics: {
|
|
23
99
|
NAME: translate('Clustered'),
|
|
24
100
|
TYPE: 'Cluster',
|
|
@@ -27,58 +103,132 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
27
103
|
|
|
28
104
|
initialize: function (datalayer) {
|
|
29
105
|
this.datalayer = datalayer
|
|
106
|
+
this._bucket = []
|
|
30
107
|
if (!Utils.isObject(this.datalayer.properties.cluster)) {
|
|
31
108
|
this.datalayer.properties.cluster = {}
|
|
32
109
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
110
|
+
FeatureGroup.prototype.initialize.call(this)
|
|
111
|
+
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
dataChanged: function () {
|
|
115
|
+
this.redraw()
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
removeClusters() {
|
|
119
|
+
this.hideCoverage()
|
|
120
|
+
if (this._map) {
|
|
121
|
+
for (const cluster of this._clusters) {
|
|
122
|
+
const layer = cluster._layers.length === 1 ? cluster._layers[0] : cluster
|
|
123
|
+
this._map.removeLayer(layer)
|
|
124
|
+
}
|
|
38
125
|
}
|
|
39
|
-
|
|
40
|
-
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
addClusters() {
|
|
129
|
+
if (this._map) {
|
|
130
|
+
for (const cluster of this._clusters) {
|
|
131
|
+
const layer = cluster._layers.length === 1 ? cluster._layers[0] : cluster
|
|
132
|
+
this._map.addLayer(layer)
|
|
133
|
+
}
|
|
41
134
|
}
|
|
42
|
-
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
|
43
|
-
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
|
44
|
-
this._markerCluster = MarkerCluster
|
|
45
|
-
this._layers = []
|
|
46
135
|
},
|
|
47
136
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
137
|
+
redraw: function () {
|
|
138
|
+
this.removeClusters()
|
|
139
|
+
this.compute()
|
|
140
|
+
this.addClusters()
|
|
51
141
|
},
|
|
52
142
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
143
|
+
compute() {
|
|
144
|
+
const radius = this.datalayer.properties.cluster?.radius || 80
|
|
145
|
+
this._clusters = []
|
|
146
|
+
const map = this.datalayer._umap._leafletMap
|
|
147
|
+
const CRS = map.options.crs
|
|
148
|
+
for (const layer of this._bucket) {
|
|
149
|
+
layer._xy = CRS.latLngToPoint(layer._latlng, map.getZoom())
|
|
150
|
+
let cluster = null
|
|
151
|
+
for (const candidate of this._clusters) {
|
|
152
|
+
if (candidate._xy.distanceTo(layer._xy) <= radius) {
|
|
153
|
+
cluster = candidate
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!cluster) {
|
|
158
|
+
const icon = new ClusterIcon({
|
|
159
|
+
color: this.datalayer.getColor(),
|
|
160
|
+
textColor: this.datalayer.properties.cluster?.textColor,
|
|
161
|
+
getCounter: () => cluster._layers.length,
|
|
162
|
+
})
|
|
163
|
+
cluster = new MarkerCluster(layer._latlng, { icon })
|
|
164
|
+
cluster.addEventParent(this)
|
|
165
|
+
cluster._xy ??= layer._xy
|
|
166
|
+
cluster._layers = []
|
|
167
|
+
this._clusters.push(cluster)
|
|
168
|
+
}
|
|
169
|
+
cluster._layers.push(layer)
|
|
170
|
+
layer._cluster = cluster
|
|
171
|
+
}
|
|
172
|
+
for (const cluster of this._clusters) {
|
|
173
|
+
cluster.computeCoverage()
|
|
174
|
+
}
|
|
62
175
|
},
|
|
63
176
|
|
|
64
177
|
addLayer: function (layer) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
178
|
+
if (!layer.getLatLng) return FeatureGroup.prototype.addLayer.call(this, layer)
|
|
179
|
+
// Do not add yet the layer to the map
|
|
180
|
+
// wait for datachanged event, so we can compute breaks only once
|
|
181
|
+
this._bucket.push(layer)
|
|
182
|
+
return this
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
onAdd: function (leafletMap) {
|
|
186
|
+
this.on('click', this.onClick)
|
|
187
|
+
this.on('mouseover', this.onMouseOver)
|
|
188
|
+
this.on('mouseout', this.onMouseOut)
|
|
189
|
+
this.compute()
|
|
190
|
+
LayerMixin.onAdd.call(this, leafletMap)
|
|
191
|
+
leafletMap.on('zoomend', this.redraw, this)
|
|
192
|
+
this.addClusters()
|
|
193
|
+
return FeatureGroup.prototype.onAdd.call(this, leafletMap)
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
onRemove: function (leafletMap) {
|
|
197
|
+
leafletMap.off('zoomend', this.redraw, this)
|
|
198
|
+
this.off('click', this.onClick)
|
|
199
|
+
this.off('mouseover', this.onMouseOver)
|
|
200
|
+
this.off('mouseout', this.onMouseOut)
|
|
201
|
+
LayerMixin.onRemove.call(this, leafletMap)
|
|
202
|
+
this.removeClusters()
|
|
203
|
+
return FeatureGroup.prototype.onRemove.call(this, leafletMap)
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
showCoverage(cluster) {
|
|
207
|
+
if (cluster._coverage) {
|
|
208
|
+
this._shownCoverage = cluster._coverage
|
|
209
|
+
this._map.addLayer(this._shownCoverage)
|
|
76
210
|
}
|
|
77
211
|
},
|
|
78
212
|
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
213
|
+
hideCoverage() {
|
|
214
|
+
if (this._shownCoverage) this._map.removeLayer(this._shownCoverage)
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
onMouseOver(event) {
|
|
218
|
+
event.layer?.computeCoverage?.()
|
|
219
|
+
this.showCoverage(event.layer)
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
onMouseOut(event) {
|
|
223
|
+
this.hideCoverage()
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
onClick(event) {
|
|
227
|
+
if (this._map.getZoom() === this._map.getMaxZoom()) {
|
|
228
|
+
event.layer.spiderfy?.()
|
|
229
|
+
} else {
|
|
230
|
+
event.layer.zoomToCoverage?.()
|
|
231
|
+
}
|
|
82
232
|
},
|
|
83
233
|
|
|
84
234
|
getEditableProperties: () => [
|
|
@@ -101,20 +251,6 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
|
|
101
251
|
],
|
|
102
252
|
|
|
103
253
|
onEdit: function (field, builder) {
|
|
104
|
-
if (field === 'properties.cluster.radius')
|
|
105
|
-
// No way to reset radius of an already instanciated MarkerClusterGroup...
|
|
106
|
-
this.datalayer.resetLayer(true)
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
if (field === 'properties.color') {
|
|
110
|
-
this.options.polygonOptions.color = this.datalayer.getColor()
|
|
111
|
-
}
|
|
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
|
|
254
|
+
if (field === 'properties.cluster.radius') this.redraw()
|
|
119
255
|
},
|
|
120
256
|
})
|