umap-project 3.4.2__py3-none-any.whl → 3.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- umap/__init__.py +1 -1
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +71 -57
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +20 -16
- umap/locale/en/LC_MESSAGES/django.po +14 -14
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +20 -16
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +32 -27
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +20 -16
- umap/management/commands/clean_tilelayer.py +0 -1
- umap/management/commands/search_maps.py +95 -0
- umap/settings/__init__.py +9 -1
- umap/settings/base.py +7 -6
- umap/static/umap/css/icon.css +8 -0
- umap/static/umap/img/16-white.svg +5 -2
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +7 -4
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/js/modules/autocomplete.js +1 -9
- umap/static/umap/js/modules/browser.js +27 -10
- umap/static/umap/js/modules/data/features.js +3 -2
- umap/static/umap/js/modules/data/fields.js +12 -2
- umap/static/umap/js/modules/data/layer.js +13 -9
- umap/static/umap/js/modules/domutils.js +4 -0
- umap/static/umap/js/modules/filters.js +11 -10
- umap/static/umap/js/modules/form/builder.js +17 -16
- umap/static/umap/js/modules/form/fields.js +16 -16
- umap/static/umap/js/modules/permissions.js +10 -2
- umap/static/umap/js/modules/rendering/controls.js +202 -9
- umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
- umap/static/umap/js/modules/rendering/map.js +45 -35
- umap/static/umap/js/modules/rendering/template.js +12 -6
- umap/static/umap/js/modules/rules.js +1 -1
- umap/static/umap/js/modules/ui/bar.js +2 -1
- umap/static/umap/js/modules/ui/hash.js +36 -0
- umap/static/umap/js/modules/ui/loader.js +26 -0
- umap/static/umap/js/modules/ui/panel.js +7 -0
- umap/static/umap/js/modules/umap.js +6 -0
- umap/static/umap/js/modules/utils.js +5 -4
- umap/static/umap/js/umap.controls.js +0 -182
- umap/static/umap/locale/am_ET.js +2 -5
- umap/static/umap/locale/am_ET.json +2 -5
- umap/static/umap/locale/ar.js +2 -5
- umap/static/umap/locale/ar.json +2 -5
- umap/static/umap/locale/ast.js +2 -5
- umap/static/umap/locale/ast.json +2 -5
- umap/static/umap/locale/bg.js +2 -5
- umap/static/umap/locale/bg.json +2 -5
- umap/static/umap/locale/br.js +40 -43
- umap/static/umap/locale/br.json +40 -43
- umap/static/umap/locale/ca.js +2 -5
- umap/static/umap/locale/ca.json +2 -5
- umap/static/umap/locale/cs_CZ.js +0 -3
- umap/static/umap/locale/cs_CZ.json +0 -3
- umap/static/umap/locale/da.js +1 -4
- umap/static/umap/locale/da.json +1 -4
- umap/static/umap/locale/de.js +27 -30
- umap/static/umap/locale/de.json +27 -30
- umap/static/umap/locale/el.js +0 -3
- umap/static/umap/locale/el.json +0 -3
- umap/static/umap/locale/en.js +0 -3
- umap/static/umap/locale/en.json +0 -3
- umap/static/umap/locale/en_US.json +2 -5
- umap/static/umap/locale/es.js +0 -3
- umap/static/umap/locale/es.json +0 -3
- umap/static/umap/locale/et.js +0 -3
- umap/static/umap/locale/et.json +0 -3
- umap/static/umap/locale/eu.js +0 -3
- umap/static/umap/locale/eu.json +0 -3
- umap/static/umap/locale/fa_IR.js +0 -3
- umap/static/umap/locale/fa_IR.json +0 -3
- umap/static/umap/locale/fi.js +2 -5
- umap/static/umap/locale/fi.json +2 -5
- umap/static/umap/locale/fr.js +3 -6
- umap/static/umap/locale/fr.json +3 -6
- umap/static/umap/locale/gl.js +0 -3
- umap/static/umap/locale/gl.json +0 -3
- umap/static/umap/locale/he.js +2 -5
- umap/static/umap/locale/he.json +2 -5
- umap/static/umap/locale/hr.js +2 -5
- umap/static/umap/locale/hr.json +2 -5
- umap/static/umap/locale/hu.js +7 -10
- umap/static/umap/locale/hu.json +7 -10
- umap/static/umap/locale/id.js +2 -5
- umap/static/umap/locale/id.json +2 -5
- umap/static/umap/locale/is.js +0 -3
- umap/static/umap/locale/is.json +0 -3
- umap/static/umap/locale/it.js +0 -3
- umap/static/umap/locale/it.json +0 -3
- umap/static/umap/locale/ja.js +2 -5
- umap/static/umap/locale/ja.json +2 -5
- umap/static/umap/locale/ko.js +2 -5
- umap/static/umap/locale/ko.json +2 -5
- umap/static/umap/locale/lt.js +2 -5
- umap/static/umap/locale/lt.json +2 -5
- umap/static/umap/locale/ms.js +0 -3
- umap/static/umap/locale/ms.json +0 -3
- umap/static/umap/locale/nl.js +0 -3
- umap/static/umap/locale/nl.json +0 -3
- umap/static/umap/locale/no.js +2 -5
- umap/static/umap/locale/no.json +2 -5
- umap/static/umap/locale/pl.js +2 -5
- umap/static/umap/locale/pl.json +2 -5
- umap/static/umap/locale/pl_PL.json +2 -5
- umap/static/umap/locale/pt.js +0 -3
- umap/static/umap/locale/pt.json +0 -3
- umap/static/umap/locale/pt_BR.js +2 -5
- umap/static/umap/locale/pt_BR.json +2 -5
- umap/static/umap/locale/pt_PT.js +2 -5
- umap/static/umap/locale/pt_PT.json +2 -5
- umap/static/umap/locale/ro.js +2 -5
- umap/static/umap/locale/ro.json +2 -5
- umap/static/umap/locale/ru.js +2 -5
- umap/static/umap/locale/ru.json +2 -5
- umap/static/umap/locale/sk_SK.js +2 -5
- umap/static/umap/locale/sk_SK.json +2 -5
- umap/static/umap/locale/sl.js +2 -5
- umap/static/umap/locale/sl.json +2 -5
- umap/static/umap/locale/sr.js +2 -5
- umap/static/umap/locale/sr.json +2 -5
- umap/static/umap/locale/sv.js +2 -5
- umap/static/umap/locale/sv.json +2 -5
- umap/static/umap/locale/th_TH.js +2 -5
- umap/static/umap/locale/th_TH.json +2 -5
- umap/static/umap/locale/tr.js +2 -5
- umap/static/umap/locale/tr.json +2 -5
- umap/static/umap/locale/uk_UA.js +2 -5
- umap/static/umap/locale/uk_UA.json +2 -5
- umap/static/umap/locale/vi.js +2 -5
- umap/static/umap/locale/vi.json +2 -5
- umap/static/umap/locale/vi_VN.json +2 -5
- umap/static/umap/locale/zh.js +2 -5
- umap/static/umap/locale/zh.json +2 -5
- umap/static/umap/locale/zh_CN.json +2 -5
- umap/static/umap/locale/zh_TW.Big5.json +2 -5
- umap/static/umap/locale/zh_TW.js +1 -4
- umap/static/umap/locale/zh_TW.json +1 -4
- umap/static/umap/map.css +1 -17
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.esm.js +942 -0
- umap/static/umap/vendors/photon/leaflet.photon.esm.js +472 -0
- umap/sync/app.py +4 -1
- umap/templates/umap/css.html +0 -2
- umap/templates/umap/js.html +0 -5
- umap/templates/umap/team_form.html +2 -1
- umap/tests/fixtures/test_upload_data_with_enum.umap +151 -0
- umap/tests/integration/test_edit_map.py +2 -0
- umap/tests/integration/test_filters.py +24 -0
- umap/tests/integration/test_import.py +40 -23
- umap/tests/integration/test_map.py +1 -1
- umap/tests/integration/test_optimistic_merge.py +7 -1
- umap/tests/integration/test_remote_data.py +1 -1
- umap/tests/test_search_maps_command.py +44 -0
- umap/utils.py +9 -3
- umap/views.py +17 -4
- {umap_project-3.4.2.dist-info → umap_project-3.6.1.dist-info}/METADATA +24 -18
- {umap_project-3.4.2.dist-info → umap_project-3.6.1.dist-info}/RECORD +162 -163
- {umap_project-3.4.2.dist-info → umap_project-3.6.1.dist-info}/WHEEL +1 -1
- umap/static/umap/vendors/hash/leaflet-hash.js +0 -162
- umap/static/umap/vendors/loading/Control.Loading.css +0 -26
- umap/static/umap/vendors/loading/Control.Loading.js +0 -351
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css +0 -1
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css.map +0 -1
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +0 -4
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +0 -1
- umap/static/umap/vendors/photon/leaflet.photon.js +0 -487
- {umap_project-3.4.2.dist-info → umap_project-3.6.1.dist-info}/entry_points.txt +0 -0
- {umap_project-3.4.2.dist-info → umap_project-3.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Control,
|
|
3
|
+
LayerGroup,
|
|
4
|
+
latLng,
|
|
5
|
+
Icon,
|
|
6
|
+
Marker,
|
|
7
|
+
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
8
|
+
import {
|
|
9
|
+
PhotonSearch,
|
|
10
|
+
PhotonReverse,
|
|
11
|
+
} from '../../../vendors/photon/leaflet.photon.esm.js'
|
|
2
12
|
import * as Utils from '../utils.js'
|
|
3
13
|
import { translate } from '../i18n.js'
|
|
14
|
+
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
4
15
|
|
|
5
16
|
export const HomeControl = Control.extend({
|
|
6
17
|
options: {
|
|
@@ -9,8 +20,9 @@ export const HomeControl = Control.extend({
|
|
|
9
20
|
|
|
10
21
|
onAdd: (map) => {
|
|
11
22
|
const path = map._umap.getStaticPathFor('home.svg')
|
|
23
|
+
const homeURL = map._umap.urls.get('home')
|
|
12
24
|
const container = Utils.loadTemplate(
|
|
13
|
-
`<a href="
|
|
25
|
+
`<a href="${homeURL}" class="home-button" title="${translate('Back to home')}"><img src="${path}" alt="${translate('Home logo')}" width="38px" height="38px" /></a>`
|
|
14
26
|
)
|
|
15
27
|
return container
|
|
16
28
|
},
|
|
@@ -124,6 +136,7 @@ const BaseButton = Control.extend({
|
|
|
124
136
|
initialize: function (umap, options) {
|
|
125
137
|
this._umap = umap
|
|
126
138
|
Control.prototype.initialize.call(this, options)
|
|
139
|
+
this.afterInit()
|
|
127
140
|
},
|
|
128
141
|
|
|
129
142
|
onAdd: function (map) {
|
|
@@ -153,6 +166,7 @@ const BaseButton = Control.extend({
|
|
|
153
166
|
this.afterRemove(map)
|
|
154
167
|
},
|
|
155
168
|
|
|
169
|
+
afterInit: () => {},
|
|
156
170
|
afterAdd: (container, map) => {},
|
|
157
171
|
afterRemove: (map) => {},
|
|
158
172
|
})
|
|
@@ -244,7 +258,7 @@ export const SearchControl = BaseButton.extend({
|
|
|
244
258
|
const [container, { input, resultsContainer }] =
|
|
245
259
|
Utils.loadTemplateWithRefs(template)
|
|
246
260
|
const id = Math.random()
|
|
247
|
-
this.search = new
|
|
261
|
+
this.search = new Search(
|
|
248
262
|
this._umap._leafletMap,
|
|
249
263
|
input,
|
|
250
264
|
this.layer,
|
|
@@ -252,10 +266,10 @@ export const SearchControl = BaseButton.extend({
|
|
|
252
266
|
)
|
|
253
267
|
this._umap.panel.open({ content: container }).then(() => {
|
|
254
268
|
this.search.on('ajax:send', () => {
|
|
255
|
-
this._umap.
|
|
269
|
+
this._umap.loader.start(id)
|
|
256
270
|
})
|
|
257
271
|
this.search.on('ajax:return', () => {
|
|
258
|
-
this._umap.
|
|
272
|
+
this._umap.loader.stop(id)
|
|
259
273
|
})
|
|
260
274
|
this.search.resultsContainer = resultsContainer
|
|
261
275
|
input.focus()
|
|
@@ -282,19 +296,16 @@ export const AttributionControl = Control.Attribution.extend({
|
|
|
282
296
|
${originalCredits}
|
|
283
297
|
<span data-ref="short"> — ${Utils.toHTML(shortCredit)}</span>
|
|
284
298
|
<a href="#" data-ref="caption"> — ${translate('Open caption')}</a>
|
|
285
|
-
<a href="/" data-ref="home"> — ${translate('Home')}</a>
|
|
286
299
|
<a href="https://umap-project.org/" data-ref="site"> — ${translate('Powered by uMap')}</a>
|
|
287
300
|
<a href="#" class="attribution-toggle"></a>
|
|
288
301
|
</div>
|
|
289
302
|
`
|
|
290
|
-
const [container, { short, caption,
|
|
291
|
-
Utils.loadTemplateWithRefs(template)
|
|
303
|
+
const [container, { short, caption, site }] = Utils.loadTemplateWithRefs(template)
|
|
292
304
|
caption.addEventListener('click', () => this._map._umap.openCaption())
|
|
293
305
|
this._container.appendChild(container)
|
|
294
306
|
short.hidden = !shortCredit
|
|
295
307
|
caption.hidden = !captionMenus
|
|
296
308
|
site.hidden = !captionMenus
|
|
297
|
-
home.hidden = this._map._umap.isEmbed || !captionMenus
|
|
298
309
|
},
|
|
299
310
|
})
|
|
300
311
|
|
|
@@ -359,3 +370,185 @@ export const TileLayerChooser = BaseButton.extend({
|
|
|
359
370
|
return li
|
|
360
371
|
},
|
|
361
372
|
})
|
|
373
|
+
|
|
374
|
+
export const LocateControl = BaseButton.extend({
|
|
375
|
+
options: {
|
|
376
|
+
position: 'topleft',
|
|
377
|
+
title: translate('Center map on your location'),
|
|
378
|
+
icon: 'icon-locate',
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
async start() {
|
|
382
|
+
await this.loadPlugin()
|
|
383
|
+
this._locate.start()
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
stop() {
|
|
387
|
+
this._locate?.stop()
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
async loadPlugin() {
|
|
391
|
+
if (this._locate) return
|
|
392
|
+
const { LocateControl } = await import(
|
|
393
|
+
'../../../vendors/locatecontrol/L.Control.Locate.esm.js'
|
|
394
|
+
)
|
|
395
|
+
this._locate = new LocateControl({
|
|
396
|
+
strings: {
|
|
397
|
+
title: translate('Center map on your location'),
|
|
398
|
+
},
|
|
399
|
+
showPopup: false,
|
|
400
|
+
flyTo: this.options.easing,
|
|
401
|
+
onLocationError: (err) => Alert.error(err.message),
|
|
402
|
+
})
|
|
403
|
+
this._locate._map = this._umap._leafletMap
|
|
404
|
+
this._locate.onAdd(this._umap._leafletMap)
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
async onClick() {
|
|
408
|
+
if (this._locate?._active) {
|
|
409
|
+
this.stop()
|
|
410
|
+
} else {
|
|
411
|
+
this.start()
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
afterRemove() {
|
|
416
|
+
this.stop()
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
afterInit() {
|
|
420
|
+
this._umap._leafletMap.on('locateactivate', () => {
|
|
421
|
+
this._container.classList.add('active')
|
|
422
|
+
})
|
|
423
|
+
this._umap._leafletMap.on('locatedeactivate', () => {
|
|
424
|
+
this._container.classList.remove('active')
|
|
425
|
+
})
|
|
426
|
+
},
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
export const Search = PhotonSearch.extend({
|
|
430
|
+
initialize: function (map, input, layer, options) {
|
|
431
|
+
this.options.placeholder = translate('Type a place name or coordinates')
|
|
432
|
+
this.options.location_bias_scale = 0.5
|
|
433
|
+
PhotonSearch.prototype.initialize.call(this, map, input, options)
|
|
434
|
+
this.options.url = map.options.urls.search
|
|
435
|
+
if (map.options.maxBounds) this.options.bbox = map.options.maxBounds.toBBoxString()
|
|
436
|
+
this.reverse = new PhotonReverse({
|
|
437
|
+
handleResults: (geojson) => {
|
|
438
|
+
this.handleResultsWithReverse(geojson)
|
|
439
|
+
},
|
|
440
|
+
})
|
|
441
|
+
this.layer = layer
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
handleResultsWithReverse: function (geojson) {
|
|
445
|
+
const latlng = this.reverse.latlng
|
|
446
|
+
geojson.features.unshift({
|
|
447
|
+
type: 'Feature',
|
|
448
|
+
geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] },
|
|
449
|
+
properties: {
|
|
450
|
+
name: translate('Go to "{coords}"', { coords: `${latlng.lat} ${latlng.lng}` }),
|
|
451
|
+
},
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
this.handleResults(geojson)
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
search: function () {
|
|
458
|
+
this.layer.clearLayers()
|
|
459
|
+
const pattern = /^(?<lat>[-+]?\d{1,2}[.,]\d+)\s*[ ,]\s*(?<lng>[-+]?\d{1,3}[.,]\d+)$/
|
|
460
|
+
if (pattern.test(this.input.value)) {
|
|
461
|
+
this.hide()
|
|
462
|
+
const { lat, lng } = pattern.exec(this.input.value).groups
|
|
463
|
+
const latlng = latLng(lat, lng)
|
|
464
|
+
if (Utils.LatLngIsValid(latlng)) {
|
|
465
|
+
this.reverse.doReverse(latlng)
|
|
466
|
+
} else {
|
|
467
|
+
Alert.error(translate('Invalid latitude or longitude'))
|
|
468
|
+
}
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
// Only numbers, abort.
|
|
472
|
+
if (/^[\d .,]*$/.test(this.input.value)) return
|
|
473
|
+
// Do normal search
|
|
474
|
+
this.options.includePosition = this.map.getZoom() > 10
|
|
475
|
+
PhotonSearch.prototype.search.call(this)
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
onBlur: function (e) {
|
|
479
|
+
// Overrided because we don't want to hide the results on blur.
|
|
480
|
+
this.fire('blur')
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
formatResult: function (feature, el) {
|
|
484
|
+
const [tools, { point, geom }] = Utils.loadTemplateWithRefs(`
|
|
485
|
+
<span class="search-result-tools">
|
|
486
|
+
<button type="button" title="${translate('Add this geometry to my map')}" data-ref=geom><i class="icon icon-16 icon-polygon-plus"></i></button>
|
|
487
|
+
<button type="button" title="${translate('Add this place to my map')}" data-ref=point><i class="icon icon-16 icon-marker-plus"></i></button>
|
|
488
|
+
</span>
|
|
489
|
+
`)
|
|
490
|
+
geom.hidden = !['R', 'W'].includes(feature.properties.osm_type)
|
|
491
|
+
point.addEventListener('mousedown', (event) => {
|
|
492
|
+
event.stopPropagation()
|
|
493
|
+
const datalayer = this.map._umap.defaultEditDataLayer()
|
|
494
|
+
const marker = datalayer.makeFeature(feature)
|
|
495
|
+
marker.edit()
|
|
496
|
+
})
|
|
497
|
+
geom.addEventListener('mousedown', async (event) => {
|
|
498
|
+
event.stopPropagation()
|
|
499
|
+
const osm_id = feature.properties.osm_id
|
|
500
|
+
const types = {
|
|
501
|
+
R: 'relation',
|
|
502
|
+
W: 'way',
|
|
503
|
+
N: 'node',
|
|
504
|
+
}
|
|
505
|
+
const osm_type = types[feature.properties.osm_type]
|
|
506
|
+
if (!osm_type || !osm_id) return
|
|
507
|
+
const importer = this.map._umap.importer
|
|
508
|
+
importer.build()
|
|
509
|
+
importer.format = 'geojson'
|
|
510
|
+
importer.raw = await this.getOSMObject(osm_type, osm_id)
|
|
511
|
+
importer.submit()
|
|
512
|
+
})
|
|
513
|
+
el.appendChild(tools)
|
|
514
|
+
this._formatResult(feature, el)
|
|
515
|
+
const path = this.map._umap.getStaticPathFor('target.svg')
|
|
516
|
+
const icon = new Icon({
|
|
517
|
+
iconUrl: path,
|
|
518
|
+
iconSize: [24, 24],
|
|
519
|
+
iconAnchor: [12, 12],
|
|
520
|
+
})
|
|
521
|
+
const coords = feature.geometry.coordinates
|
|
522
|
+
const target = new Marker([coords[1], coords[0]], { icon })
|
|
523
|
+
el.addEventListener('mouseover', (event) => {
|
|
524
|
+
target.addTo(this.layer)
|
|
525
|
+
})
|
|
526
|
+
el.addEventListener('mouseout', (event) => {
|
|
527
|
+
target.removeFrom(this.layer)
|
|
528
|
+
})
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
async getOSMObject(osm_type, osm_id) {
|
|
532
|
+
const url = `https://www.openstreetmap.org/api/0.6/${osm_type}/${osm_id}/full`
|
|
533
|
+
const response = await this.map._umap.request.get(url)
|
|
534
|
+
if (response?.ok) {
|
|
535
|
+
const data = await this.map._umap.formatter.fromOSM(await response.text())
|
|
536
|
+
data.features = data.features.filter(
|
|
537
|
+
(feature) => feature.properties.id === `${osm_type}/${osm_id}`
|
|
538
|
+
)
|
|
539
|
+
return JSON.stringify(data)
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
setChoice: function (choice) {
|
|
544
|
+
choice = choice || this.RESULTS[this.CURRENT]
|
|
545
|
+
if (choice) {
|
|
546
|
+
const feature = choice.feature
|
|
547
|
+
const zoom = Math.max(this.map.getZoom(), 14) // Never unzoom.
|
|
548
|
+
this.map.setView(
|
|
549
|
+
[feature.geometry.coordinates[1], feature.geometry.coordinates[0]],
|
|
550
|
+
zoom
|
|
551
|
+
)
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
})
|
|
@@ -80,7 +80,7 @@ const ClassifiedMixin = {
|
|
|
80
80
|
const ul = Utils.loadTemplate('<ul></ul>')
|
|
81
81
|
const items = this.getLegendItems()
|
|
82
82
|
for (const [color, label] of items) {
|
|
83
|
-
const rgbColor = DOMUtils.
|
|
83
|
+
const rgbColor = DOMUtils.colorToRGB(color)
|
|
84
84
|
const opacity = this.datalayer.getOption('fillOpacity')
|
|
85
85
|
const bgColor = `rgba(${rgbColor.join(',')}, ${opacity})`
|
|
86
86
|
const li = Utils.loadTemplate(`
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
latLng,
|
|
8
8
|
LatLng,
|
|
9
9
|
LatLngBounds,
|
|
10
|
+
stamp,
|
|
10
11
|
setOptions,
|
|
11
12
|
TileLayer,
|
|
12
13
|
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
EmbedControl,
|
|
21
22
|
EditControl,
|
|
22
23
|
HomeControl,
|
|
24
|
+
LocateControl,
|
|
23
25
|
MoreControl,
|
|
24
26
|
PermanentCreditsControl,
|
|
25
27
|
TileLayerChooser,
|
|
@@ -69,19 +71,7 @@ const ControlsMixin = {
|
|
|
69
71
|
})
|
|
70
72
|
this._controls.datalayers = new DataLayersControl(this._umap)
|
|
71
73
|
this._controls.caption = new CaptionControl(this._umap)
|
|
72
|
-
this._controls.locate = new
|
|
73
|
-
strings: {
|
|
74
|
-
title: translate('Center map on your location'),
|
|
75
|
-
},
|
|
76
|
-
showPopup: false,
|
|
77
|
-
// We style this control in our own CSS for consistency with other controls,
|
|
78
|
-
// but the control breaks if we don't specify a class here, so a fake class
|
|
79
|
-
// will do.
|
|
80
|
-
icon: 'umap-fake-class',
|
|
81
|
-
iconLoading: 'umap-fake-class',
|
|
82
|
-
flyTo: this.options.easing,
|
|
83
|
-
onLocationError: (err) => U.Alert.error(err.message),
|
|
84
|
-
})
|
|
74
|
+
this._controls.locate = new LocateControl(this._umap)
|
|
85
75
|
this._controls.fullscreen = new Control.Fullscreen({
|
|
86
76
|
title: {
|
|
87
77
|
false: translate('View Fullscreen'),
|
|
@@ -174,7 +164,19 @@ const ManageTilelayerMixin = {
|
|
|
174
164
|
if (tilelayer === this.selectedTilelayer) {
|
|
175
165
|
return
|
|
176
166
|
}
|
|
167
|
+
const onLoading = () => {
|
|
168
|
+
this._umap.loader.start(stamp(tilelayer))
|
|
169
|
+
}
|
|
170
|
+
const onLoad = () => {
|
|
171
|
+
this._umap.loader.stop(stamp(tilelayer))
|
|
172
|
+
}
|
|
177
173
|
try {
|
|
174
|
+
tilelayer.on('loading', onLoading)
|
|
175
|
+
tilelayer.on('load', onLoad)
|
|
176
|
+
tilelayer.on('remove', () => {
|
|
177
|
+
tilelayer.off('loading', onLoading)
|
|
178
|
+
tilelayer.off('load', onLoad)
|
|
179
|
+
})
|
|
178
180
|
this.addLayer(tilelayer)
|
|
179
181
|
this.fire('baselayerchange', { layer: tilelayer })
|
|
180
182
|
if (this.selectedTilelayer) {
|
|
@@ -253,19 +255,13 @@ export const LeafletMap = BaseMap.extend({
|
|
|
253
255
|
|
|
254
256
|
BaseMap.prototype.initialize.call(this, element, options)
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
this.fire('dataloading', event.detail)
|
|
264
|
-
)
|
|
265
|
-
DomEvent.on(document.body, 'dataload', (event) =>
|
|
266
|
-
this.fire('dataload', event.detail)
|
|
267
|
-
)
|
|
268
|
-
}
|
|
258
|
+
document.body.addEventListener('mapview:update', (event) => {
|
|
259
|
+
let { zoom, latlng } = event.detail
|
|
260
|
+
if (!Utils.LatLngIsValid(latlng)) return
|
|
261
|
+
zoom = Math.min(zoom, this.getMaxZoom())
|
|
262
|
+
zoom = Math.max(zoom, this.getMinZoom())
|
|
263
|
+
this.setView(latlng, zoom)
|
|
264
|
+
})
|
|
269
265
|
|
|
270
266
|
this.on('baselayerchange', (e) => {
|
|
271
267
|
if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
|
|
@@ -276,6 +272,21 @@ export const LeafletMap = BaseMap.extend({
|
|
|
276
272
|
this.initControls()
|
|
277
273
|
// Needs locate control and hash to exist
|
|
278
274
|
this.initCenter()
|
|
275
|
+
|
|
276
|
+
// Wait for URL to have been parsed before modifying the hash
|
|
277
|
+
const updateHash = () => {
|
|
278
|
+
const center = this.getCenter()
|
|
279
|
+
document.body.dispatchEvent(
|
|
280
|
+
new CustomEvent('mapview:updated', {
|
|
281
|
+
detail: {
|
|
282
|
+
zoom: this.getZoom(),
|
|
283
|
+
latlng: [center.lat.toFixed(6), center.lng.toFixed(6)],
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
this.on('moveend', updateHash)
|
|
289
|
+
updateHash()
|
|
279
290
|
this.initTileLayers()
|
|
280
291
|
this.renderUI()
|
|
281
292
|
},
|
|
@@ -316,14 +327,13 @@ export const LeafletMap = BaseMap.extend({
|
|
|
316
327
|
this.setView(this.options.center, this.options.zoom)
|
|
317
328
|
},
|
|
318
329
|
|
|
319
|
-
initCenter: function () {
|
|
330
|
+
initCenter: async function () {
|
|
320
331
|
this._setDefaultCenter()
|
|
321
|
-
if (this.options.hash
|
|
322
|
-
if (this.options.hash && this._hash.parseHash(location.hash)) {
|
|
332
|
+
if (this.options.hash && window.location.hash) {
|
|
323
333
|
// FIXME An invalid hash will cause the load to fail
|
|
324
|
-
this.
|
|
334
|
+
this._umap.hash.parse()
|
|
325
335
|
} else if (this.options.defaultView === 'locate' && !this.options.noControl) {
|
|
326
|
-
this._controls.locate.start()
|
|
336
|
+
await this._controls.locate.start()
|
|
327
337
|
} else if (this.options.defaultView === 'data') {
|
|
328
338
|
this._umap.onceDataLoaded(this._umap.fitDataBounds)
|
|
329
339
|
} else if (this.options.defaultView === 'latest') {
|
|
@@ -343,10 +353,10 @@ export const LeafletMap = BaseMap.extend({
|
|
|
343
353
|
},
|
|
344
354
|
|
|
345
355
|
handleLimitBounds: function () {
|
|
346
|
-
const south = Number.parseFloat(this.options.limitBounds
|
|
347
|
-
const west = Number.parseFloat(this.options.limitBounds
|
|
348
|
-
const north = Number.parseFloat(this.options.limitBounds
|
|
349
|
-
const east = Number.parseFloat(this.options.limitBounds
|
|
356
|
+
const south = Number.parseFloat(this.options.limitBounds?.south)
|
|
357
|
+
const west = Number.parseFloat(this.options.limitBounds?.west)
|
|
358
|
+
const north = Number.parseFloat(this.options.limitBounds?.north)
|
|
359
|
+
const east = Number.parseFloat(this.options.limitBounds?.east)
|
|
350
360
|
if (
|
|
351
361
|
!Number.isNaN(south) &&
|
|
352
362
|
!Number.isNaN(west) &&
|
|
@@ -34,8 +34,7 @@ export default async function loadTemplate(name, feature, container) {
|
|
|
34
34
|
class PopupTemplate {
|
|
35
35
|
renderTitle(feature) {}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
const template = feature.getOption('popupContentTemplate')
|
|
37
|
+
toHTML(feature, template) {
|
|
39
38
|
const target = feature.getOption('outlinkTarget')
|
|
40
39
|
const properties = feature.extendedProperties()
|
|
41
40
|
// Resolve properties inside description
|
|
@@ -44,9 +43,15 @@ class PopupTemplate {
|
|
|
44
43
|
properties
|
|
45
44
|
)
|
|
46
45
|
properties.name = properties.name ?? feature.getDisplayName()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const content = Utils.greedyTemplate(template, properties)
|
|
47
|
+
return Utils.toHTML(content, { target })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
renderBody(feature) {
|
|
51
|
+
const template = feature.getOption('popupContentTemplate')
|
|
52
|
+
return Utils.loadTemplate(
|
|
53
|
+
`<div class="umap-popup-container text">${this.toHTML(feature, template)}</div>`
|
|
54
|
+
)
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
renderFooter(feature) {
|
|
@@ -377,7 +382,8 @@ class Route extends TitleMixin(PopupTemplate) {
|
|
|
377
382
|
}).addTo(map)
|
|
378
383
|
})
|
|
379
384
|
if (feature.properties.description) {
|
|
380
|
-
|
|
385
|
+
const content = this.toHTML(feature, feature.properties.description)
|
|
386
|
+
root.appendChild(Utils.loadTemplate(`<p>${content}</p>`))
|
|
381
387
|
}
|
|
382
388
|
return root
|
|
383
389
|
}
|
|
@@ -69,7 +69,7 @@ class Rule {
|
|
|
69
69
|
}
|
|
70
70
|
// TODO: deal with legacy rules on non typed fields
|
|
71
71
|
else {
|
|
72
|
-
this.cast = this.field.parse
|
|
72
|
+
this.cast = (value) => this.field.parse(value)
|
|
73
73
|
if (
|
|
74
74
|
// Special cases where we want to be lousy when checking isNaN without
|
|
75
75
|
// coercing to a Number first because we handle multiple types.
|
|
@@ -9,7 +9,7 @@ import TemplateImporter from '../templates.js'
|
|
|
9
9
|
const TOP_BAR_TEMPLATE = `
|
|
10
10
|
<div class="umap-main-edit-toolbox with-transition dark">
|
|
11
11
|
<div class="umap-left-edit-toolbox" data-ref="left">
|
|
12
|
-
<div class="logo"><a class="" href="
|
|
12
|
+
<div class="logo"><a class="" href="#" title="${translate('Go to the homepage')}" data-ref="home">uMap</a></div>
|
|
13
13
|
<button class="map-name flat truncate" type="button" data-ref="name"></button>
|
|
14
14
|
<button class="flat truncate" type="button" data-ref="share">
|
|
15
15
|
<i class="icon icon-16 icon-draft show-on-draft"></i><span class="share-status"></span>
|
|
@@ -59,6 +59,7 @@ export class TopBar extends WithTemplate {
|
|
|
59
59
|
|
|
60
60
|
setup() {
|
|
61
61
|
this.parent.appendChild(this.element)
|
|
62
|
+
this.elements.home.href = this._umap.urls.get('home')
|
|
62
63
|
this.elements.name.addEventListener('mouseover', () => {
|
|
63
64
|
this._umap.tooltip.open({
|
|
64
65
|
content: translate('Edit the title of the map'),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export default class Hash {
|
|
2
|
+
constructor() {
|
|
3
|
+
document.body.addEventListener('mapview:updated', (event) => {
|
|
4
|
+
this._updating = true
|
|
5
|
+
this.update(event.detail)
|
|
6
|
+
})
|
|
7
|
+
window.addEventListener('hashchange', () => this.parse())
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
update({ zoom, latlng }) {
|
|
11
|
+
const [lat, lng] = latlng
|
|
12
|
+
window.location.hash = `#${zoom}/${lat}/${lng}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
parse() {
|
|
16
|
+
// Do not parse and re-update the map when we change the hash ourselves
|
|
17
|
+
// after a move from the user.
|
|
18
|
+
if (this._updating) {
|
|
19
|
+
this._updating = false
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
let hash = window.location.hash
|
|
23
|
+
if (hash.indexOf('#') === 0) {
|
|
24
|
+
hash = hash.substr(1)
|
|
25
|
+
}
|
|
26
|
+
const args = hash.split('/')
|
|
27
|
+
if (args.length !== 3) return
|
|
28
|
+
const zoom = parseInt(args[0], 10)
|
|
29
|
+
const lat = parseFloat(args[1])
|
|
30
|
+
const lng = parseFloat(args[2])
|
|
31
|
+
if (isNaN(zoom) || isNaN(lat) || isNaN(lng)) return
|
|
32
|
+
document.body.dispatchEvent(
|
|
33
|
+
new CustomEvent('mapview:update', { detail: { zoom, latlng: [lat, lng] } })
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { loadTemplate } from '../domutils.js'
|
|
2
|
+
|
|
3
|
+
export default class Loader {
|
|
4
|
+
constructor(parent) {
|
|
5
|
+
this.parent = parent
|
|
6
|
+
this.element = loadTemplate('<div class="umap-loader"></div>')
|
|
7
|
+
this.parent.appendChild(this.element)
|
|
8
|
+
document.body.addEventListener('dataloading', (event) =>
|
|
9
|
+
this.start(event.detail.id)
|
|
10
|
+
)
|
|
11
|
+
document.body.addEventListener('dataload', (event) => this.stop(event.detail.id))
|
|
12
|
+
this._counter = new Set()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
start(id) {
|
|
16
|
+
this._counter.add(id)
|
|
17
|
+
this.parent.classList.add('umap-loading')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
stop(id) {
|
|
21
|
+
this._counter.delete(id)
|
|
22
|
+
if (!this._counter.size) {
|
|
23
|
+
this.parent.classList.remove('umap-loading')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -98,6 +98,13 @@ export class Panel {
|
|
|
98
98
|
document.body.classList.remove(`panel-${this.className.split(' ')[0]}-on`)
|
|
99
99
|
this.container.dataset.highlight = null
|
|
100
100
|
this.onClose()
|
|
101
|
+
Promise.all(
|
|
102
|
+
this.container.getAnimations?.().map((animation) => animation.finished)
|
|
103
|
+
).then(() => {
|
|
104
|
+
if (!this.isOpen()) {
|
|
105
|
+
this.container.innerHTML = ''
|
|
106
|
+
}
|
|
107
|
+
})
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
onClose() {
|
|
@@ -31,6 +31,8 @@ import * as Utils from './utils.js'
|
|
|
31
31
|
import * as DOMUtils from './domutils.js'
|
|
32
32
|
import { DataLayerManager } from './managers.js'
|
|
33
33
|
import { Importer as OpenRouteService } from './importers/openrouteservice.js'
|
|
34
|
+
import Loader from './ui/loader.js'
|
|
35
|
+
import Hash from './ui/hash.js'
|
|
34
36
|
|
|
35
37
|
export default class Umap {
|
|
36
38
|
constructor(element, geojson) {
|
|
@@ -110,6 +112,10 @@ export default class Umap {
|
|
|
110
112
|
this.properties.homeControl = false
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
this.loader = new Loader(this._leafletMap._container)
|
|
116
|
+
if (this.properties.hash) {
|
|
117
|
+
this.hash = new Hash()
|
|
118
|
+
}
|
|
113
119
|
this._leafletMap.setup()
|
|
114
120
|
|
|
115
121
|
this.panel = new Panel(this, this._leafletMap)
|
|
@@ -683,10 +683,11 @@ export const COLORS = [
|
|
|
683
683
|
]
|
|
684
684
|
|
|
685
685
|
export const LatLngIsValid = (latlng) => {
|
|
686
|
+
const [lat, lng] = Array.isArray(latlng) ? latlng : [latlng.lat, latlng.lng]
|
|
686
687
|
return (
|
|
687
|
-
Number.isFinite(
|
|
688
|
-
Math.abs(
|
|
689
|
-
Number.isFinite(
|
|
690
|
-
Math.abs(
|
|
688
|
+
Number.isFinite(lat) &&
|
|
689
|
+
Math.abs(lat) <= 90 &&
|
|
690
|
+
Number.isFinite(lng) &&
|
|
691
|
+
Math.abs(lng) <= 180
|
|
691
692
|
)
|
|
692
693
|
}
|