umap-project 2.7.0b3__py3-none-any.whl → 2.7.2__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/asgi.py +15 -0
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +134 -128
- umap/models.py +2 -1
- umap/settings/base.py +1 -1
- umap/static/umap/css/importers.css +4 -0
- umap/static/umap/img/16.svg +1 -184
- umap/static/umap/img/24-white.svg +1 -0
- umap/static/umap/img/24.svg +1 -0
- umap/static/umap/img/importers/cadastrefr.svg +23 -0
- umap/static/umap/img/source/16.svg +753 -200
- umap/static/umap/img/source/24-white.svg +3 -2
- umap/static/umap/img/source/24.svg +3 -2
- umap/static/umap/js/modules/caption.js +10 -4
- umap/static/umap/js/modules/data/layer.js +26 -14
- umap/static/umap/js/modules/formatter.js +5 -1
- umap/static/umap/js/modules/importer.js +3 -0
- umap/static/umap/js/modules/importers/cadastrefr.js +62 -0
- umap/static/umap/js/modules/importers/communesfr.js +2 -2
- umap/static/umap/js/modules/rendering/layers/classified.js +1 -0
- umap/static/umap/js/modules/rendering/ui.js +3 -1
- umap/static/umap/js/modules/share.js +1 -3
- umap/static/umap/js/modules/slideshow.js +1 -1
- umap/static/umap/js/modules/sync/engine.js +14 -8
- umap/static/umap/js/modules/sync/hlc.js +3 -3
- umap/static/umap/js/modules/sync/updaters.js +14 -2
- umap/static/umap/js/modules/ui/panel.js +12 -1
- umap/static/umap/js/modules/utils.js +24 -4
- umap/static/umap/js/umap.controls.js +42 -11
- umap/static/umap/js/umap.core.js +1 -1
- umap/static/umap/js/umap.js +11 -11
- umap/static/umap/locale/ca.js +8 -4
- umap/static/umap/locale/ca.json +8 -4
- umap/static/umap/locale/en.js +2 -1
- umap/static/umap/locale/en.json +2 -1
- umap/static/umap/locale/es.js +330 -319
- umap/static/umap/locale/es.json +330 -319
- umap/static/umap/locale/fr.js +2 -1
- umap/static/umap/locale/fr.json +2 -1
- umap/static/umap/map.css +27 -7
- umap/static/umap/unittests/hlc.js +2 -0
- umap/static/umap/unittests/utils.js +24 -0
- umap/static/umap/vars.css +2 -1
- umap/static/umap/vendors/csv2geojson/csv2geojson.js +13 -8
- umap/tests/integration/test_caption.py +3 -3
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_import.py +33 -0
- {umap_project-2.7.0b3.dist-info → umap_project-2.7.2.dist-info}/METADATA +9 -6
- {umap_project-2.7.0b3.dist-info → umap_project-2.7.2.dist-info}/RECORD +53 -50
- {umap_project-2.7.0b3.dist-info → umap_project-2.7.2.dist-info}/WHEEL +0 -0
- {umap_project-2.7.0b3.dist-info → umap_project-2.7.2.dist-info}/entry_points.txt +0 -0
- {umap_project-2.7.0b3.dist-info → umap_project-2.7.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
2
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
3
|
|
|
4
|
-
<svg width="252" height="252" viewBox="0 0 66.674992 66.674992" version="1.1" id="svg2876" inkscape:version="1.
|
|
5
|
-
<sodipodi:namedview id="namedview2878" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="
|
|
4
|
+
<svg width="252" height="252" viewBox="0 0 66.674992 66.674992" version="1.1" id="svg2876" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="24-white.svg" inkscape:export-filename="../24-white.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
|
5
|
+
<sodipodi:namedview id="namedview2878" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="5.3566812" inkscape:cx="50.030978" inkscape:cy="170.06799" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="layer1">
|
|
6
6
|
<inkscape:grid type="xygrid" id="grid2997" empspacing="6" originx="0" originy="0" spacingy="0.2645833" spacingx="0.2645833" units="px" visible="true" />
|
|
7
7
|
<inkscape:grid id="grid1" units="px" originx="0" originy="0" spacingx="9.5249989" spacingy="9.5249989" empcolor="#3f3fff" empopacity="0.25098039" color="#3f3fff" opacity="0.1254902" empspacing="1" dotted="false" gridanglex="30" gridanglez="30" visible="true" />
|
|
8
8
|
</sodipodi:namedview>
|
|
@@ -74,5 +74,6 @@
|
|
|
74
74
|
<g id="info" transform="matrix(0.33072916,0,0,0.33072916,-11.906249,-256.10415)" style="stroke-width:0.8">
|
|
75
75
|
<path id="path3762" style="fill:#f2f2f2;fill-opacity:1;stroke:#999999;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1" d="m 107.99999,838.36217 a 8,8 0 0 0 -7.999998,8 8,8 0 0 0 7.999998,8 8,8 0 0 0 8,-8 8,8 0 0 0 -8,-8 z m 0,2.5 a 1.5,1.5 0 0 1 1.5,1.5 1.5,1.5 0 0 1 -1.5,1.5 1.5,1.5 0 0 1 -1.5,-1.5 1.5,1.5 0 0 1 1.5,-1.5 z m -1,4.5 h 2 v 6 h -2 z" />
|
|
76
76
|
</g>
|
|
77
|
+
<path d="m 5.6444435,59.43432 v 1.034991 h 1.0541546 z m 1.1759257,1.612262 h -1.1759257 c -0.3247228,0 -0.5879626,-0.258453 -0.5879626,-0.577271 v -1.154545 h -2.3518514 v 5.195451 h 4.1157397 z m -4.1157397,-2.309089 h 3.0615854 l 1.6421175,1.612259 v 4.160465 c 0,0.318822 -0.2632403,0.577275 -0.5879632,0.577275 h -4.1157397 c -0.3247231,0 -0.5879631,-0.258453 -0.5879631,-0.577275 v -5.195451 c 0,-0.31882 0.26324,-0.577273 0.5879631,-0.577273 z m 1.7638882,3.92135 v -1.323625 h 0.5879632 v 1.323625 l 0.3800861,-0.373177 0.415753,0.408196 -1.0898206,1.070004 -1.0898208,-1.070004 0.4157526,-0.408196 z" fill-rule="evenodd" id="downloadfile" style="fill:#f2f2f2;fill-opacity:1;stroke-width:0.06614583;stroke:#999999;stroke-opacity:0.50980395;stroke-dasharray:none" />
|
|
77
78
|
</g>
|
|
78
79
|
</svg>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
2
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
3
|
|
|
4
|
-
<svg width="252" height="252" viewBox="0 0 66.674999 66.674999" version="1.1" id="svg6237" inkscape:version="1.
|
|
5
|
-
<sodipodi:namedview id="namedview6239" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="3.1596124" inkscape:cx="
|
|
4
|
+
<svg width="252" height="252" viewBox="0 0 66.674999 66.674999" version="1.1" id="svg6237" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="24.svg" inkscape:export-filename="../24.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
|
5
|
+
<sodipodi:namedview id="namedview6239" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="3.1596124" inkscape:cx="118.68544" inkscape:cy="166.47612" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="layer1">
|
|
6
6
|
<inkscape:grid type="xygrid" id="grid6358" empspacing="6" originx="0" originy="0" spacingy="0.26458333" spacingx="0.26458333" units="px" visible="true" />
|
|
7
7
|
<inkscape:grid id="grid1" units="px" originx="0" originy="0" spacingx="9.5249999" spacingy="9.5249999" empcolor="#3f3fff" empopacity="0.25098039" color="#ff0000" opacity="0.83529412" empspacing="0" dotted="false" gridanglex="30" gridanglez="30" visible="true" />
|
|
8
8
|
</sodipodi:namedview>
|
|
@@ -98,5 +98,6 @@
|
|
|
98
98
|
<g id="info" transform="matrix(0.33072916,0,0,0.33072916,-11.90625,-256.10415)" style="fill:#4d4d4d;fill-opacity:1;stroke-width:0.8">
|
|
99
99
|
<path id="path3762" style="fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.8" d="m 108,838.36217 a 8,8 0 0 0 -8,8 8,8 0 0 0 8,8 8,8 0 0 0 8,-8 8,8 0 0 0 -8,-8 z m 0,2.5 a 1.5,1.5 0 0 1 1.5,1.5 1.5,1.5 0 0 1 -1.5,1.5 1.5,1.5 0 0 1 -1.5,-1.5 1.5,1.5 0 0 1 1.5,-1.5 z m -1,4.5 h 2 v 6 h -2 z" />
|
|
100
100
|
</g>
|
|
101
|
+
<path d="m 5.6444435,59.43432 v 1.034991 h 1.0541546 z m 1.1759257,1.612262 h -1.1759257 c -0.3247228,0 -0.5879626,-0.258453 -0.5879626,-0.577271 v -1.154545 h -2.3518514 v 5.195451 h 4.1157397 z m -4.1157397,-2.309089 h 3.0615854 l 1.6421175,1.612259 v 4.160465 c 0,0.318822 -0.2632403,0.577275 -0.5879632,0.577275 h -4.1157397 c -0.3247231,0 -0.5879631,-0.258453 -0.5879631,-0.577275 v -5.195451 c 0,-0.31882 0.26324,-0.577273 0.5879631,-0.577273 z m 1.7638882,3.92135 v -1.323625 h 0.5879632 v 1.323625 l 0.3800861,-0.373177 0.415753,0.408196 -1.0898206,1.070004 -1.0898208,-1.070004 0.4157526,-0.408196 z" fill-rule="evenodd" id="downloadfile" style="fill:#464646;fill-opacity:1;stroke-width:0.192424" />
|
|
101
102
|
</g>
|
|
102
103
|
</svg>
|
|
@@ -40,15 +40,21 @@ export default class Caption {
|
|
|
40
40
|
)
|
|
41
41
|
const creditsContainer = DomUtil.create('div', 'credits-container', container)
|
|
42
42
|
this.addCredits(creditsContainer)
|
|
43
|
-
this.map.panel.open({ content: container })
|
|
43
|
+
this.map.panel.open({ content: container }).then(() => {
|
|
44
|
+
// Create the legend when the panel is actually on the DOM
|
|
45
|
+
this.map.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
|
|
46
|
+
})
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
addDataLayer(datalayer, container) {
|
|
47
50
|
if (!datalayer.options.inCaption) return
|
|
48
|
-
const p = DomUtil.create(
|
|
49
|
-
|
|
51
|
+
const p = DomUtil.create(
|
|
52
|
+
'p',
|
|
53
|
+
`caption-item ${datalayer.cssId}`,
|
|
54
|
+
container
|
|
55
|
+
)
|
|
56
|
+
const legend = DomUtil.create('span', 'datalayer-legend', p)
|
|
50
57
|
const headline = DomUtil.create('strong', '', p)
|
|
51
|
-
datalayer.renderLegend(legend)
|
|
52
58
|
if (datalayer.options.description) {
|
|
53
59
|
DomUtil.element({
|
|
54
60
|
tagName: 'span',
|
|
@@ -115,6 +115,10 @@ export class DataLayer {
|
|
|
115
115
|
return this._isDeleted
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
get cssId() {
|
|
119
|
+
return `datalayer-${stamp(this)}`
|
|
120
|
+
}
|
|
121
|
+
|
|
118
122
|
getSyncMetadata() {
|
|
119
123
|
return {
|
|
120
124
|
subject: 'datalayer',
|
|
@@ -235,6 +239,7 @@ export class DataLayer {
|
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
dataChanged() {
|
|
242
|
+
if (!this.hasDataLoaded()) return
|
|
238
243
|
this.map.onDataLayersChanged()
|
|
239
244
|
this.layer.dataChanged()
|
|
240
245
|
}
|
|
@@ -242,10 +247,15 @@ export class DataLayer {
|
|
|
242
247
|
fromGeoJSON(geojson, sync = true) {
|
|
243
248
|
this.addData(geojson, sync)
|
|
244
249
|
this._geojson = geojson
|
|
245
|
-
this.
|
|
250
|
+
this.onDataLoaded()
|
|
246
251
|
this.dataChanged()
|
|
247
252
|
}
|
|
248
253
|
|
|
254
|
+
onDataLoaded() {
|
|
255
|
+
this._dataloaded = true
|
|
256
|
+
this.renderLegend()
|
|
257
|
+
}
|
|
258
|
+
|
|
249
259
|
async fromUmapGeoJSON(geojson) {
|
|
250
260
|
if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
|
|
251
261
|
if (geojson._umap_options) this.setOptions(geojson._umap_options)
|
|
@@ -384,7 +394,7 @@ export class DataLayer {
|
|
|
384
394
|
this.indexProperties(feature)
|
|
385
395
|
this.map.features_index[feature.getSlug()] = feature
|
|
386
396
|
this.showFeature(feature)
|
|
387
|
-
|
|
397
|
+
this.dataChanged()
|
|
388
398
|
}
|
|
389
399
|
|
|
390
400
|
removeFeature(feature, sync) {
|
|
@@ -395,7 +405,7 @@ export class DataLayer {
|
|
|
395
405
|
feature.disconnectFromDataLayer(this)
|
|
396
406
|
this._index.splice(this._index.indexOf(id), 1)
|
|
397
407
|
delete this._features[id]
|
|
398
|
-
if (this.
|
|
408
|
+
if (this.isVisible()) this.dataChanged()
|
|
399
409
|
}
|
|
400
410
|
|
|
401
411
|
indexProperties(feature) {
|
|
@@ -802,13 +812,12 @@ export class DataLayer {
|
|
|
802
812
|
this
|
|
803
813
|
)
|
|
804
814
|
if (this.umap_id) {
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
)
|
|
815
|
+
const filename = `${Utils.slugify(this.options.name)}.geojson`
|
|
816
|
+
const download = Utils.loadTemplate(`
|
|
817
|
+
<a class="button" href="${this._dataUrl()}" download="${filename}">
|
|
818
|
+
<i class="icon icon-24 icon-download"></i>${translate('Download')}
|
|
819
|
+
</a>`)
|
|
820
|
+
advancedButtons.appendChild(download)
|
|
812
821
|
}
|
|
813
822
|
const backButton = DomUtil.createButtonIcon(
|
|
814
823
|
undefined,
|
|
@@ -1120,10 +1129,13 @@ export class DataLayer {
|
|
|
1120
1129
|
return 'displayName'
|
|
1121
1130
|
}
|
|
1122
1131
|
|
|
1123
|
-
renderLegend(
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1132
|
+
renderLegend() {
|
|
1133
|
+
for (const container of document.querySelectorAll(`.${this.cssId} .datalayer-legend`)) {
|
|
1134
|
+
container.innerHTML = ''
|
|
1135
|
+
if (this.layer.renderLegend) return this.layer.renderLegend(container)
|
|
1136
|
+
const color = DomUtil.create('span', 'datalayer-color', container)
|
|
1137
|
+
color.style.backgroundColor = this.getColor()
|
|
1138
|
+
}
|
|
1127
1139
|
}
|
|
1128
1140
|
|
|
1129
1141
|
renderToolbox(container) {
|
|
@@ -81,6 +81,8 @@ export class Formatter {
|
|
|
81
81
|
{
|
|
82
82
|
delimiter: 'auto',
|
|
83
83
|
includeLatLon: false,
|
|
84
|
+
sexagesimal: false,
|
|
85
|
+
parseLatLon: (raw) => Number.parseFloat(raw.toString().replace(',', '.')),
|
|
84
86
|
},
|
|
85
87
|
(err, result) => {
|
|
86
88
|
// csv2geojson fallback to null geometries when it cannot determine
|
|
@@ -115,7 +117,9 @@ export class Formatter {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
async fromGeoRSS(str) {
|
|
118
|
-
const GeoRSSToGeoJSON = await import(
|
|
120
|
+
const GeoRSSToGeoJSON = await import(
|
|
121
|
+
'../../vendors/georsstogeojson/GeoRSSToGeoJSON.js'
|
|
122
|
+
)
|
|
119
123
|
return GeoRSSToGeoJSON.parse(this.toDom(str))
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -69,6 +69,9 @@ export default class Importer {
|
|
|
69
69
|
case 'communesfr':
|
|
70
70
|
import('./importers/communesfr.js').then(register)
|
|
71
71
|
break
|
|
72
|
+
case 'cadastrefr':
|
|
73
|
+
import('./importers/cadastrefr.js').then(register)
|
|
74
|
+
break
|
|
72
75
|
case 'overpass':
|
|
73
76
|
import('./importers/overpass.js').then(register)
|
|
74
77
|
break
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
|
+
import { BaseAjax, SingleMixin } from '../autocomplete.js'
|
|
3
|
+
import * as Util from '../utils.js'
|
|
4
|
+
import { AutocompleteCommunes } from './communesfr.js'
|
|
5
|
+
|
|
6
|
+
const TEMPLATE = `
|
|
7
|
+
<h3>Cadastre</h3>
|
|
8
|
+
<p>Importer les données cadastrales d’une commune française.</p>
|
|
9
|
+
<select name="theme">
|
|
10
|
+
<option value="batiments">Bâtiments</option>
|
|
11
|
+
<option value="communes">Communes</option>
|
|
12
|
+
<option value="feuilles">Feuilles</option>
|
|
13
|
+
<option value="lieux_dits">Lieux dits</option>
|
|
14
|
+
<option value="parcelles" selected>Parcelles</option>
|
|
15
|
+
<option value="prefixes_sections">Préfixes sections</option>
|
|
16
|
+
<option value="sections">Sections</option>
|
|
17
|
+
<option value="subdivisions_fiscales">Subdivisions fiscales</option>
|
|
18
|
+
</select>
|
|
19
|
+
<label id="boundary">
|
|
20
|
+
</label>
|
|
21
|
+
`
|
|
22
|
+
|
|
23
|
+
export class Importer {
|
|
24
|
+
constructor(map, options) {
|
|
25
|
+
this.name = options.name || 'Cadastre'
|
|
26
|
+
this.id = 'cadastrefr'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async open(importer) {
|
|
30
|
+
let boundary = null
|
|
31
|
+
let boundaryName = null
|
|
32
|
+
const container = DomUtil.create('div')
|
|
33
|
+
container.innerHTML = TEMPLATE
|
|
34
|
+
const select = container.querySelector('select')
|
|
35
|
+
const options = {
|
|
36
|
+
placeholder: 'Nom ou code INSEE…',
|
|
37
|
+
url: 'https://geo.api.gouv.fr/communes?nom={q}&limit=5',
|
|
38
|
+
on_select: (choice) => {
|
|
39
|
+
boundary = choice.item.value
|
|
40
|
+
boundaryName = choice.item.label
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
this.autocomplete = new AutocompleteCommunes(container, options)
|
|
44
|
+
|
|
45
|
+
const confirm = (form) => {
|
|
46
|
+
if (!boundary || !form.theme) {
|
|
47
|
+
Alert.error(translate('Please choose a theme and a boundary first.'))
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
importer.url = `https://cadastre.data.gouv.fr/bundler/cadastre-etalab/communes/${boundary}/geojson/${form.theme}`
|
|
51
|
+
importer.format = 'geojson'
|
|
52
|
+
importer.layerName = `${boundaryName} — ${select.options[select.selectedIndex].textContent}`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
importer.dialog
|
|
56
|
+
.open({
|
|
57
|
+
template: container,
|
|
58
|
+
className: `${this.id} importer dark`,
|
|
59
|
+
})
|
|
60
|
+
.then(confirm)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -2,7 +2,7 @@ import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
|
2
2
|
import { BaseAjax, SingleMixin } from '../autocomplete.js'
|
|
3
3
|
import * as Util from '../utils.js'
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
export class AutocompleteCommunes extends SingleMixin(BaseAjax) {
|
|
6
6
|
createResult(item) {
|
|
7
7
|
return super.createResult({
|
|
8
8
|
value: item.code,
|
|
@@ -46,7 +46,7 @@ export class Importer {
|
|
|
46
46
|
importer.dialog.close()
|
|
47
47
|
},
|
|
48
48
|
}
|
|
49
|
-
this.autocomplete = new
|
|
49
|
+
this.autocomplete = new AutocompleteCommunes(container, options)
|
|
50
50
|
|
|
51
51
|
importer.dialog.open({
|
|
52
52
|
template: container,
|
|
@@ -142,9 +142,7 @@ export default class Share {
|
|
|
142
142
|
async format(mode) {
|
|
143
143
|
const type = EXPORT_FORMATS[mode]
|
|
144
144
|
const content = await type.formatter(this.map)
|
|
145
|
-
|
|
146
|
-
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
147
|
-
const filename = name + type.ext
|
|
145
|
+
const filename = Utils.slugify(this.map.options.name) + type.ext
|
|
148
146
|
return { content, filetype: type.filetype, filename }
|
|
149
147
|
}
|
|
150
148
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import * as Utils from '../utils.js'
|
|
2
|
+
import { HybridLogicalClock } from './hlc.js'
|
|
1
3
|
import { DataLayerUpdater, FeatureUpdater, MapUpdater } from './updaters.js'
|
|
2
4
|
import { WebSocketTransport } from './websocket.js'
|
|
3
|
-
import { HybridLogicalClock } from './hlc.js'
|
|
4
|
-
import * as Utils from '../utils.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The syncEngine exposes an API to sync messages between peers over the network.
|
|
@@ -81,7 +81,7 @@ export class SyncEngine {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
_send(inputMessage) {
|
|
84
|
-
|
|
84
|
+
const message = this._operations.addLocal(inputMessage)
|
|
85
85
|
|
|
86
86
|
if (this.offline) return
|
|
87
87
|
if (this.transport) {
|
|
@@ -101,6 +101,11 @@ export class SyncEngine {
|
|
|
101
101
|
updater.applyMessage(operation)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
getNumberOfConnectedPeers() {
|
|
105
|
+
if (this.peers) return this.peers.length
|
|
106
|
+
return 0
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
/**
|
|
105
110
|
* This is called by the transport layer on new messages,
|
|
106
111
|
* and dispatches the different "on*" methods.
|
|
@@ -146,10 +151,10 @@ export class SyncEngine {
|
|
|
146
151
|
onJoinResponse({ uuid, peers }) {
|
|
147
152
|
debug('received join response', { uuid, peers })
|
|
148
153
|
this.uuid = uuid
|
|
149
|
-
this.peers
|
|
154
|
+
this.onListPeersResponse({ peers })
|
|
150
155
|
|
|
151
156
|
// Get one peer at random
|
|
152
|
-
|
|
157
|
+
const randomPeer = this._getRandomPeer()
|
|
153
158
|
|
|
154
159
|
if (randomPeer) {
|
|
155
160
|
// Retrieve the operations which happened before join.
|
|
@@ -168,6 +173,7 @@ export class SyncEngine {
|
|
|
168
173
|
onListPeersResponse({ peers }) {
|
|
169
174
|
debug('received peerinfo', { peers })
|
|
170
175
|
this.peers = peers
|
|
176
|
+
this.updaters.map.update({ key: 'numberOfConnectedPeers' })
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
/**
|
|
@@ -248,7 +254,7 @@ export class SyncEngine {
|
|
|
248
254
|
* @returns {string|bool} the selected peer uuid, or False if none was found.
|
|
249
255
|
*/
|
|
250
256
|
_getRandomPeer() {
|
|
251
|
-
|
|
257
|
+
const otherPeers = this.peers.filter((p) => p !== this.uuid)
|
|
252
258
|
if (otherPeers.length > 0) {
|
|
253
259
|
const random = Math.floor(Math.random() * otherPeers.length)
|
|
254
260
|
return otherPeers[random]
|
|
@@ -302,7 +308,7 @@ export class Operations {
|
|
|
302
308
|
* @returns {*} clock-aware message
|
|
303
309
|
*/
|
|
304
310
|
addLocal(inputMessage) {
|
|
305
|
-
|
|
311
|
+
const message = { ...inputMessage, hlc: this._hlc.tick() }
|
|
306
312
|
this._operations.push(message)
|
|
307
313
|
return message
|
|
308
314
|
}
|
|
@@ -342,7 +348,7 @@ export class Operations {
|
|
|
342
348
|
*/
|
|
343
349
|
storeRemoteOperations(remoteOperations) {
|
|
344
350
|
// get the highest date from the passed operations
|
|
345
|
-
|
|
351
|
+
const greatestHLC = remoteOperations
|
|
346
352
|
.map((op) => op.hlc)
|
|
347
353
|
.reduce((max, current) => (current > max ? current : max))
|
|
348
354
|
|
|
@@ -30,12 +30,12 @@ export class HybridLogicalClock {
|
|
|
30
30
|
* @returns object
|
|
31
31
|
*/
|
|
32
32
|
parse(raw) {
|
|
33
|
-
|
|
33
|
+
const tokens = raw.split(':')
|
|
34
34
|
|
|
35
35
|
if (tokens.length !== 3) {
|
|
36
36
|
throw new SyntaxError(`Unable to parse ${raw}`)
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
const [walltime, rawNN, id] = tokens
|
|
39
39
|
|
|
40
40
|
let nn = Number.parseInt(rawNN)
|
|
41
41
|
if (Number.isNaN(nn)) {
|
|
@@ -92,7 +92,7 @@ export class HybridLogicalClock {
|
|
|
92
92
|
if (now > local.walltime && now > remote.walltime) {
|
|
93
93
|
nextValue = { ...local, walltime: now }
|
|
94
94
|
} else if (local.walltime == remote.walltime) {
|
|
95
|
-
|
|
95
|
+
const nn = Math.max(local.nn, remote.nn) + 1
|
|
96
96
|
nextValue = { ...local, nn: nn }
|
|
97
97
|
} else if (remote.walltime > local.walltime) {
|
|
98
98
|
nextValue = { ...remote, id: local.id, nn: remote.nn + 1 }
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fieldInSchema } from '../utils.js'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Updaters are classes able to convert messages
|
|
3
5
|
* received from other peers (or from the server) to changes on the map.
|
|
@@ -42,7 +44,10 @@ class BaseUpdater {
|
|
|
42
44
|
|
|
43
45
|
export class MapUpdater extends BaseUpdater {
|
|
44
46
|
update({ key, value }) {
|
|
45
|
-
|
|
47
|
+
if (fieldInSchema(key)) {
|
|
48
|
+
this.updateObjectValue(this.map, key, value)
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
this.map.render([key])
|
|
47
52
|
}
|
|
48
53
|
}
|
|
@@ -56,7 +61,14 @@ export class DataLayerUpdater extends BaseUpdater {
|
|
|
56
61
|
|
|
57
62
|
update({ key, metadata, value }) {
|
|
58
63
|
const datalayer = this.getDataLayerFromID(metadata.id)
|
|
59
|
-
|
|
64
|
+
if (fieldInSchema(key)) {
|
|
65
|
+
this.updateObjectValue(datalayer, key, value)
|
|
66
|
+
} else {
|
|
67
|
+
console.debug(
|
|
68
|
+
'Not applying update for datalayer because key is not in the schema',
|
|
69
|
+
key
|
|
70
|
+
)
|
|
71
|
+
}
|
|
60
72
|
datalayer.render([key])
|
|
61
73
|
}
|
|
62
74
|
}
|
|
@@ -25,6 +25,9 @@ export class Panel {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
open({ content, className, actions = [] } = {}) {
|
|
28
|
+
if (this.isOpen()) {
|
|
29
|
+
this.onClose()
|
|
30
|
+
}
|
|
28
31
|
this.container.className = `with-transition panel window ${this.className} ${
|
|
29
32
|
this.mode || ''
|
|
30
33
|
}`
|
|
@@ -71,10 +74,13 @@ export class Panel {
|
|
|
71
74
|
|
|
72
75
|
close() {
|
|
73
76
|
document.body.classList.remove(`panel-${this.className.split(' ')[0]}-on`)
|
|
77
|
+
this.onClose()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
onClose() {
|
|
74
81
|
if (DomUtil.hasClass(this.container, 'on')) {
|
|
75
82
|
DomUtil.removeClass(this.container, 'on')
|
|
76
83
|
this.map.invalidateSize({ pan: false })
|
|
77
|
-
this.map.editedFeature = null
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
}
|
|
@@ -84,6 +90,11 @@ export class EditPanel extends Panel {
|
|
|
84
90
|
super(map)
|
|
85
91
|
this.className = 'right dark'
|
|
86
92
|
}
|
|
93
|
+
|
|
94
|
+
onClose() {
|
|
95
|
+
super.onClose()
|
|
96
|
+
this.map.editedFeature = null
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
export class FullPanel extends Panel {
|
|
@@ -35,7 +35,7 @@ export function checkId(string) {
|
|
|
35
35
|
* @returns Array[string]
|
|
36
36
|
*/
|
|
37
37
|
export function getImpactsFromSchema(fields, schema) {
|
|
38
|
-
|
|
38
|
+
const current_schema = schema || U.SCHEMA
|
|
39
39
|
const impacted = fields
|
|
40
40
|
.map((field) => {
|
|
41
41
|
// remove the option prefix for fields
|
|
@@ -46,14 +46,30 @@ export function getImpactsFromSchema(fields, schema) {
|
|
|
46
46
|
.reduce((acc, field) => {
|
|
47
47
|
// retrieve the "impacts" field from the schema
|
|
48
48
|
// and merge them together using sets
|
|
49
|
-
const impacts =
|
|
50
|
-
|
|
49
|
+
const impacts = current_schema[field]?.impacts || []
|
|
50
|
+
for (const impact of impacts) {
|
|
51
|
+
acc.add(impact)
|
|
52
|
+
}
|
|
51
53
|
return acc
|
|
52
54
|
}, new Set())
|
|
53
55
|
|
|
54
56
|
return Array.from(impacted)
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Check if a field exists in the schema.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} field
|
|
63
|
+
* @param {object} schema
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function fieldInSchema(field, schema) {
|
|
67
|
+
const current_schema = schema || U.SCHEMA
|
|
68
|
+
if (typeof field !== 'string') return false
|
|
69
|
+
const field_name = field.replace('options.', '').split('.')[0]
|
|
70
|
+
return current_schema[field_name] !== undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
57
73
|
/**
|
|
58
74
|
* Import DOM purify, and initialize it.
|
|
59
75
|
*
|
|
@@ -407,6 +423,10 @@ export class WithTemplate {
|
|
|
407
423
|
}
|
|
408
424
|
}
|
|
409
425
|
|
|
410
|
-
export function deepEqual(object1, object2){
|
|
426
|
+
export function deepEqual(object1, object2) {
|
|
411
427
|
return JSON.stringify(object1) === JSON.stringify(object2)
|
|
412
428
|
}
|
|
429
|
+
|
|
430
|
+
export function slugify(str) {
|
|
431
|
+
return (str || 'data').replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
432
|
+
}
|
|
@@ -660,6 +660,33 @@ const ControlsMixin = {
|
|
|
660
660
|
menu.openBelow(button, actions)
|
|
661
661
|
})
|
|
662
662
|
}
|
|
663
|
+
|
|
664
|
+
const connectedPeers = this.sync.getNumberOfConnectedPeers()
|
|
665
|
+
if (connectedPeers !== 0) {
|
|
666
|
+
const connectedPeersCount = L.DomUtil.createButton(
|
|
667
|
+
'leaflet-control-connected-peers',
|
|
668
|
+
rightContainer,
|
|
669
|
+
''
|
|
670
|
+
)
|
|
671
|
+
L.DomEvent.on(connectedPeersCount, 'mouseover', () => {
|
|
672
|
+
this.tooltip.open({
|
|
673
|
+
content: L._('{connectedPeers} peer(s) currently connected to this map', {
|
|
674
|
+
connectedPeers: connectedPeers,
|
|
675
|
+
}),
|
|
676
|
+
anchor: connectedPeersCount,
|
|
677
|
+
position: 'bottom',
|
|
678
|
+
delay: 500,
|
|
679
|
+
duration: 5000,
|
|
680
|
+
})
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
const updateConnectedPeersCount = () => {
|
|
684
|
+
connectedPeersCount.innerHTML =
|
|
685
|
+
'<span>' + this.sync.getNumberOfConnectedPeers() + '</span>'
|
|
686
|
+
}
|
|
687
|
+
updateConnectedPeersCount()
|
|
688
|
+
}
|
|
689
|
+
|
|
663
690
|
this.help.getStartedLink(rightContainer)
|
|
664
691
|
const controlEditCancel = L.DomUtil.createButton(
|
|
665
692
|
'leaflet-control-edit-cancel',
|
|
@@ -1155,17 +1182,6 @@ U.Editable = L.Editable.extend({
|
|
|
1155
1182
|
initialize: function (map, options) {
|
|
1156
1183
|
L.Editable.prototype.initialize.call(this, map, options)
|
|
1157
1184
|
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
|
1158
|
-
this.on('editable:drawing:end', (event) => {
|
|
1159
|
-
this.map.tooltip.close()
|
|
1160
|
-
// Leaflet.Editable will delete the drawn shape if invalid
|
|
1161
|
-
// (eg. line has only one drawn point)
|
|
1162
|
-
// So let's check if the layer has no more shape
|
|
1163
|
-
if (!event.layer.feature.hasGeom()) {
|
|
1164
|
-
event.layer.feature.del()
|
|
1165
|
-
} else {
|
|
1166
|
-
event.layer.feature.edit()
|
|
1167
|
-
}
|
|
1168
|
-
})
|
|
1169
1185
|
// Layer for items added by users
|
|
1170
1186
|
this.on('editable:drawing:cancel', (event) => {
|
|
1171
1187
|
if (event.layer instanceof U.LeafletMarker) event.layer.feature.del()
|
|
@@ -1295,4 +1311,19 @@ U.Editable = L.Editable.extend({
|
|
|
1295
1311
|
L.DomEvent.stop(e)
|
|
1296
1312
|
e.cancel()
|
|
1297
1313
|
},
|
|
1314
|
+
|
|
1315
|
+
onEscape: function () {
|
|
1316
|
+
this.once('editable:drawing:end', (event) => {
|
|
1317
|
+
this.map.tooltip.close()
|
|
1318
|
+
// Leaflet.Editable will delete the drawn shape if invalid
|
|
1319
|
+
// (eg. line has only one drawn point)
|
|
1320
|
+
// So let's check if the layer has no more shape
|
|
1321
|
+
if (!event.layer.feature.hasGeom()) {
|
|
1322
|
+
event.layer.feature.del()
|
|
1323
|
+
} else {
|
|
1324
|
+
event.layer.feature.edit()
|
|
1325
|
+
}
|
|
1326
|
+
})
|
|
1327
|
+
this.stopDrawing()
|
|
1328
|
+
},
|
|
1298
1329
|
})
|
umap/static/umap/js/umap.core.js
CHANGED
|
@@ -143,7 +143,7 @@ L.DomUtil.createButtonIcon = (parent, className, title, callback, size = 16) =>
|
|
|
143
143
|
|
|
144
144
|
L.DomUtil.createTitle = (parent, text, iconClassName, className = '', tag = 'h3') => {
|
|
145
145
|
const title = L.DomUtil.create(tag, '', parent)
|
|
146
|
-
if (
|
|
146
|
+
if (iconClassName) L.DomUtil.createIcon(title, iconClassName)
|
|
147
147
|
L.DomUtil.add('span', className, title, text)
|
|
148
148
|
return title
|
|
149
149
|
}
|