umap-project 2.6.3__py3-none-any.whl → 2.7.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.

Files changed (137) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +64 -1
  3. umap/asgi.py +15 -0
  4. umap/context_processors.py +1 -0
  5. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +19 -18
  9. umap/locale/en/LC_MESSAGES/django.po +47 -43
  10. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/es/LC_MESSAGES/django.po +134 -128
  12. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/fr/LC_MESSAGES/django.po +51 -47
  14. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/pt/LC_MESSAGES/django.po +64 -60
  16. umap/management/commands/clean_tilelayer.py +152 -0
  17. umap/management/commands/purge_purgatory.py +28 -0
  18. umap/models.py +27 -2
  19. umap/settings/base.py +3 -1
  20. umap/static/umap/base.css +4 -4
  21. umap/static/umap/css/contextmenu.css +6 -1
  22. umap/static/umap/css/icon.css +7 -2
  23. umap/static/umap/css/importers.css +4 -0
  24. umap/static/umap/img/16-white.svg +9 -2
  25. umap/static/umap/img/16.svg +1 -181
  26. umap/static/umap/img/24-white.svg +1 -0
  27. umap/static/umap/img/24.svg +1 -0
  28. umap/static/umap/img/importers/cadastrefr.svg +23 -0
  29. umap/static/umap/img/source/16-white.svg +10 -3
  30. umap/static/umap/img/source/16.svg +753 -197
  31. umap/static/umap/img/source/24-white.svg +3 -2
  32. umap/static/umap/img/source/24.svg +3 -2
  33. umap/static/umap/js/modules/autocomplete.js +7 -3
  34. umap/static/umap/js/modules/browser.js +54 -1
  35. umap/static/umap/js/modules/caption.js +16 -5
  36. umap/static/umap/js/modules/data/features.js +176 -2
  37. umap/static/umap/js/modules/data/layer.js +57 -40
  38. umap/static/umap/js/modules/formatter.js +3 -2
  39. umap/static/umap/js/modules/global.js +2 -0
  40. umap/static/umap/js/modules/importer.js +3 -0
  41. umap/static/umap/js/modules/importers/cadastrefr.js +62 -0
  42. umap/static/umap/js/modules/importers/communesfr.js +15 -3
  43. umap/static/umap/js/modules/permissions.js +123 -93
  44. umap/static/umap/js/modules/rendering/layers/classified.js +2 -0
  45. umap/static/umap/js/modules/rendering/ui.js +60 -213
  46. umap/static/umap/js/modules/share.js +1 -3
  47. umap/static/umap/js/modules/slideshow.js +1 -1
  48. umap/static/umap/js/modules/sync/engine.js +371 -14
  49. umap/static/umap/js/modules/sync/hlc.js +106 -0
  50. umap/static/umap/js/modules/sync/updaters.js +18 -6
  51. umap/static/umap/js/modules/sync/websocket.js +1 -1
  52. umap/static/umap/js/modules/tableeditor.js +1 -1
  53. umap/static/umap/js/modules/ui/base.js +2 -2
  54. umap/static/umap/js/modules/ui/contextmenu.js +51 -18
  55. umap/static/umap/js/modules/urls.js +5 -1
  56. umap/static/umap/js/modules/utils.js +28 -4
  57. umap/static/umap/js/umap.controls.js +73 -52
  58. umap/static/umap/js/umap.core.js +3 -3
  59. umap/static/umap/js/umap.forms.js +3 -1
  60. umap/static/umap/js/umap.js +115 -124
  61. umap/static/umap/locale/br.js +13 -4
  62. umap/static/umap/locale/br.json +13 -4
  63. umap/static/umap/locale/ca.js +28 -15
  64. umap/static/umap/locale/ca.json +28 -15
  65. umap/static/umap/locale/cs_CZ.js +87 -78
  66. umap/static/umap/locale/cs_CZ.json +87 -78
  67. umap/static/umap/locale/de.js +17 -8
  68. umap/static/umap/locale/de.json +17 -8
  69. umap/static/umap/locale/en.js +13 -2
  70. umap/static/umap/locale/en.json +13 -2
  71. umap/static/umap/locale/es.js +330 -319
  72. umap/static/umap/locale/es.json +330 -319
  73. umap/static/umap/locale/eu.js +10 -3
  74. umap/static/umap/locale/eu.json +10 -3
  75. umap/static/umap/locale/fa_IR.js +11 -4
  76. umap/static/umap/locale/fa_IR.json +11 -4
  77. umap/static/umap/locale/fr.js +15 -4
  78. umap/static/umap/locale/fr.json +15 -4
  79. umap/static/umap/locale/hu.js +10 -3
  80. umap/static/umap/locale/hu.json +10 -3
  81. umap/static/umap/locale/pt.js +17 -8
  82. umap/static/umap/locale/pt.json +17 -8
  83. umap/static/umap/locale/pt_PT.js +13 -4
  84. umap/static/umap/locale/pt_PT.json +13 -4
  85. umap/static/umap/locale/zh_TW.js +13 -4
  86. umap/static/umap/locale/zh_TW.json +13 -4
  87. umap/static/umap/map.css +44 -29
  88. umap/static/umap/unittests/hlc.js +165 -0
  89. umap/static/umap/unittests/sync.js +321 -15
  90. umap/static/umap/unittests/utils.js +47 -0
  91. umap/static/umap/vars.css +2 -1
  92. umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
  93. umap/static/umap/vendors/dompurify/purify.es.js +15 -16
  94. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  95. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  96. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
  97. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
  98. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
  99. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
  100. umap/templates/umap/css.html +0 -2
  101. umap/templates/umap/dashboard_menu.html +4 -2
  102. umap/templates/umap/js.html +0 -5
  103. umap/templates/umap/map_detail.html +2 -2
  104. umap/tests/fixtures/test_upload_data.csv +2 -2
  105. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  106. umap/tests/integration/test_basics.py +1 -1
  107. umap/tests/integration/test_browser.py +69 -7
  108. umap/tests/integration/test_caption.py +3 -3
  109. umap/tests/integration/test_circles_layer.py +12 -0
  110. umap/tests/integration/test_datalayer.py +2 -1
  111. umap/tests/integration/test_draw_polygon.py +17 -9
  112. umap/tests/integration/test_draw_polyline.py +12 -8
  113. umap/tests/integration/test_edit_datalayer.py +5 -8
  114. umap/tests/integration/test_edit_map.py +2 -2
  115. umap/tests/integration/test_edit_marker.py +1 -1
  116. umap/tests/integration/test_facets_browser.py +3 -3
  117. umap/tests/integration/test_import.py +1 -0
  118. umap/tests/integration/test_map.py +1 -0
  119. umap/tests/integration/test_owned_map.py +1 -1
  120. umap/tests/integration/test_view_marker.py +63 -0
  121. umap/tests/integration/test_view_polygon.py +12 -12
  122. umap/tests/integration/test_websocket_sync.py +65 -3
  123. umap/tests/test_clean_tilelayer.py +83 -0
  124. umap/tests/test_datalayer.py +24 -0
  125. umap/tests/test_map_views.py +20 -0
  126. umap/tests/test_purge_purgatory.py +25 -0
  127. umap/tests/test_websocket_server.py +22 -0
  128. umap/urls.py +5 -1
  129. umap/views.py +6 -3
  130. umap/websocket_server.py +130 -27
  131. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/METADATA +18 -14
  132. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/RECORD +135 -127
  133. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  134. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  135. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/WHEEL +0 -0
  136. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/entry_points.txt +0 -0
  137. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,10 +5,14 @@ export default class URLs {
5
5
  this.urls = serverUrls
6
6
  }
7
7
 
8
+ has(urlName) {
9
+ return urlName in this.urls
10
+ }
11
+
8
12
  get(urlName, params) {
9
13
  if (typeof this[urlName] === 'function') return this[urlName](params)
10
14
 
11
- if (this.urls.hasOwnProperty(urlName)) {
15
+ if (this.has(urlName)) {
12
16
  return template(this.urls[urlName], params)
13
17
  }
14
18
  throw `Unable to find a URL for route ${urlName}`
@@ -35,7 +35,7 @@ export function checkId(string) {
35
35
  * @returns Array[string]
36
36
  */
37
37
  export function getImpactsFromSchema(fields, schema) {
38
- schema = schema || U.SCHEMA
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 = schema[field]?.impacts || []
50
- impacts.forEach((impact) => acc.add(impact))
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
  *
@@ -178,7 +194,7 @@ export function toHTML(r, options) {
178
194
  }
179
195
 
180
196
  export function isObject(what) {
181
- return typeof what === 'object' && what !== null
197
+ return typeof what === 'object' && what !== null && !Array.isArray(what)
182
198
  }
183
199
 
184
200
  export function CopyJSON(geojson) {
@@ -406,3 +422,11 @@ export class WithTemplate {
406
422
  return this.element
407
423
  }
408
424
  }
425
+
426
+ export function deepEqual(object1, object2) {
427
+ return JSON.stringify(object1) === JSON.stringify(object2)
428
+ }
429
+
430
+ export function slugify(str) {
431
+ return (str || 'data').replace(/[^a-z0-9]/gi, '_').toLowerCase()
432
+ }
@@ -453,17 +453,13 @@ U.PermanentCreditsControl = L.Control.extend({
453
453
  },
454
454
 
455
455
  onAdd: function () {
456
- const paragraphContainer = L.DomUtil.create(
456
+ this.paragraphContainer = L.DomUtil.create(
457
457
  'div',
458
- 'umap-permanent-credits-container'
458
+ 'umap-permanent-credits-container text'
459
459
  )
460
- const creditsParagraph = L.DomUtil.create('p', '', paragraphContainer)
461
-
462
- this.paragraphContainer = paragraphContainer
463
460
  this.setCredits()
464
461
  this.setBackground()
465
-
466
- return paragraphContainer
462
+ return this.paragraphContainer
467
463
  },
468
464
 
469
465
  setCredits: function () {
@@ -578,11 +574,15 @@ const ControlsMixin = {
578
574
  ],
579
575
 
580
576
  renderEditToolbar: function () {
581
- const container = L.DomUtil.create(
582
- 'div',
583
- 'umap-main-edit-toolbox with-transition dark',
584
- this._controlContainer
585
- )
577
+ const className = 'umap-main-edit-toolbox'
578
+ const container =
579
+ document.querySelector(`.${className}`) ||
580
+ L.DomUtil.create(
581
+ 'div',
582
+ `${className} with-transition dark`,
583
+ this._controlContainer
584
+ )
585
+ container.innerHTML = ''
586
586
  const leftContainer = L.DomUtil.create('div', 'umap-left-edit-toolbox', container)
587
587
  const rightContainer = L.DomUtil.create('div', 'umap-right-edit-toolbox', container)
588
588
  const logo = L.DomUtil.create('div', 'logo', leftContainer)
@@ -623,33 +623,70 @@ const ControlsMixin = {
623
623
  },
624
624
  this
625
625
  )
626
- const update = () => {
627
- const status = this.permissions.getShareStatusDisplay()
628
- nameButton.textContent = this.getDisplayName()
629
- // status is not set until map is saved once
630
- if (status) {
631
- shareStatusButton.textContent = L._('Visibility: {status}', {
632
- status: status,
633
- })
634
- }
635
- }
636
- update()
637
- this.once('saved', L.bind(update, this))
638
626
  if (this.options.editMode === 'advanced') {
639
627
  L.DomEvent.on(nameButton, 'click', this.editCaption, this)
640
628
  L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions)
641
629
  }
642
- this.on('postsync', L.bind(update, this))
643
630
  if (this.options.user?.id) {
644
- L.DomUtil.createLink(
645
- 'umap-user',
631
+ const button = U.Utils.loadTemplate(`
632
+ <button class="umap-user flat" type="button">
633
+ <i class="icon icon-16 icon-profile"></i>
634
+ <span>${this.options.user.name}</span>
635
+ </button>
636
+ `)
637
+ rightContainer.appendChild(button)
638
+ const menu = new U.ContextMenu({ className: 'dark', fixed: true })
639
+ const actions = [
640
+ {
641
+ label: L._('New map'),
642
+ action: this.urls.get('map_new'),
643
+ },
644
+ {
645
+ label: L._('My maps'),
646
+ action: this.urls.get('user_dashboard'),
647
+ },
648
+ {
649
+ label: L._('My teams'),
650
+ action: this.urls.get('user_teams'),
651
+ },
652
+ ]
653
+ if (this.urls.has('user_profile')) {
654
+ actions.push({
655
+ label: L._('My profile'),
656
+ action: this.urls.get('user_profile'),
657
+ })
658
+ }
659
+ button.addEventListener('click', () => {
660
+ menu.openBelow(button, actions)
661
+ })
662
+ }
663
+
664
+ const connectedPeers = this.sync.getNumberOfConnectedPeers()
665
+ if (connectedPeers !== 0) {
666
+ const connectedPeersCount = L.DomUtil.createButton(
667
+ 'leaflet-control-connected-peers',
646
668
  rightContainer,
647
- L._('My Dashboard ({username})', {
648
- username: this.options.user.name,
649
- }),
650
- this.options.user.url
669
+ '',
651
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()
652
688
  }
689
+
653
690
  this.help.getStartedLink(rightContainer)
654
691
  const controlEditCancel = L.DomUtil.createButton(
655
692
  'leaflet-control-edit-cancel',
@@ -808,7 +845,8 @@ U.TileLayerControl = L.Control.IconLayers.extend({
808
845
  )
809
846
  const button = L.DomUtil.element({
810
847
  tagName: 'button',
811
- className: 'leaflet-iconLayers-layerCell leaflet-iconLayers-layerCell-plus button',
848
+ className:
849
+ 'leaflet-iconLayers-layerCell leaflet-iconLayers-layerCell-plus button',
812
850
  textContent: '+',
813
851
  parent: lastRow,
814
852
  })
@@ -1140,23 +1178,6 @@ L.Control.Loading.include({
1140
1178
  },
1141
1179
  })
1142
1180
 
1143
- /*
1144
- * Make it dynamic
1145
- */
1146
- U.ContextMenu = L.Map.ContextMenu.extend({
1147
- _createItems: function (e) {
1148
- this._map.setContextMenuItems(e)
1149
- L.Map.ContextMenu.prototype._createItems.call(this)
1150
- },
1151
-
1152
- _showAtPoint: function (pt, e) {
1153
- this._items = []
1154
- this._container.innerHTML = ''
1155
- this._createItems(e)
1156
- L.Map.ContextMenu.prototype._showAtPoint.call(this, pt, e)
1157
- },
1158
- })
1159
-
1160
1181
  U.Editable = L.Editable.extend({
1161
1182
  initialize: function (map, options) {
1162
1183
  L.Editable.prototype.initialize.call(this, map, options)
@@ -1181,9 +1202,9 @@ U.Editable = L.Editable.extend({
1181
1202
  if (this.map.editedFeature !== event.layer) event.layer.feature.edit(event)
1182
1203
  })
1183
1204
  this.on('editable:editing', (event) => {
1184
- const layer = event.layer
1185
- layer.feature.isDirty = true
1186
- layer.feature.fromLatLngs(layer.getLatLngs())
1205
+ const feature = event.layer.feature
1206
+ feature.isDirty = true
1207
+ feature.pullGeometry(false)
1187
1208
  })
1188
1209
  this.on('editable:vertex:ctrlclick', (event) => {
1189
1210
  const index = event.vertex.getIndex()
@@ -141,10 +141,10 @@ L.DomUtil.createButtonIcon = (parent, className, title, callback, size = 16) =>
141
141
  return el
142
142
  }
143
143
 
144
- L.DomUtil.createTitle = (parent, text, className, tag = 'h3') => {
144
+ L.DomUtil.createTitle = (parent, text, iconClassName, className = '', tag = 'h3') => {
145
145
  const title = L.DomUtil.create(tag, '', parent)
146
- if (className) L.DomUtil.createIcon(title, className)
147
- L.DomUtil.add('span', '', title, text)
146
+ if (iconClassName) L.DomUtil.createIcon(title, iconClassName)
147
+ L.DomUtil.add('span', className, title, text)
148
148
  return title
149
149
  }
150
150
 
@@ -1029,6 +1029,7 @@ L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
1029
1029
  const options = {
1030
1030
  className: 'edit-owner',
1031
1031
  on_select: L.bind(this.onSelect, this),
1032
+ placeholder: L._("Type new owner's username"),
1032
1033
  }
1033
1034
  this.autocomplete = new U.AjaxAutocomplete(this.parentNode, options)
1034
1035
  const owner = this.toHTML()
@@ -1058,6 +1059,7 @@ L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
1058
1059
  className: 'edit-editors',
1059
1060
  on_select: L.bind(this.onSelect, this),
1060
1061
  on_unselect: L.bind(this.onUnselect, this),
1062
+ placeholder: L._("Type editor's username"),
1061
1063
  }
1062
1064
  this.autocomplete = new U.AjaxAutocompleteMultiple(this.parentNode, options)
1063
1065
  this._values = this.toHTML()
@@ -1131,7 +1133,7 @@ U.FormBuilder = L.FormBuilder.extend({
1131
1133
  else schema.handler = 'IntInput'
1132
1134
  } else if (schema.choices) {
1133
1135
  const text_length = schema.choices.reduce(
1134
- (acc, [value, label]) => acc + label.length,
1136
+ (acc, [_, label]) => acc + label.length,
1135
1137
  0
1136
1138
  )
1137
1139
  // Try to be smart and use MultiChoice only