umap-project 2.7.0b0__py3-none-any.whl → 2.7.0b3__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 (40) hide show
  1. umap/__init__.py +1 -1
  2. umap/static/umap/css/contextmenu.css +1 -1
  3. umap/static/umap/js/modules/browser.js +47 -0
  4. umap/static/umap/js/modules/rendering/layers/classified.js +1 -0
  5. umap/static/umap/js/modules/rendering/ui.js +28 -6
  6. umap/static/umap/js/modules/tableeditor.js +1 -1
  7. umap/static/umap/js/modules/ui/contextmenu.js +18 -2
  8. umap/static/umap/js/umap.controls.js +4 -10
  9. umap/static/umap/js/umap.js +21 -13
  10. umap/static/umap/locale/en.js +4 -1
  11. umap/static/umap/locale/en.json +4 -1
  12. umap/static/umap/locale/fr.js +4 -1
  13. umap/static/umap/locale/fr.json +4 -1
  14. umap/static/umap/map.css +10 -0
  15. umap/static/umap/unittests/hlc.js +8 -3
  16. umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
  17. umap/static/umap/vendors/dompurify/purify.es.js +15 -16
  18. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  19. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
  20. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
  21. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
  22. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
  23. umap/templates/umap/css.html +0 -2
  24. umap/templates/umap/js.html +0 -1
  25. umap/templates/umap/map_detail.html +2 -2
  26. umap/tests/fixtures/test_upload_data.csv +2 -2
  27. umap/tests/integration/test_browser.py +69 -7
  28. umap/tests/integration/test_datalayer.py +1 -5
  29. umap/tests/integration/test_edit_datalayer.py +1 -2
  30. umap/tests/integration/test_edit_marker.py +1 -1
  31. umap/tests/integration/test_facets_browser.py +3 -3
  32. umap/tests/integration/test_import.py +0 -4
  33. umap/tests/integration/test_map.py +0 -4
  34. umap/tests/integration/test_view_marker.py +63 -0
  35. umap/tests/test_map_views.py +19 -0
  36. {umap_project-2.7.0b0.dist-info → umap_project-2.7.0b3.dist-info}/METADATA +8 -7
  37. {umap_project-2.7.0b0.dist-info → umap_project-2.7.0b3.dist-info}/RECORD +40 -40
  38. {umap_project-2.7.0b0.dist-info → umap_project-2.7.0b3.dist-info}/WHEEL +0 -0
  39. {umap_project-2.7.0b0.dist-info → umap_project-2.7.0b3.dist-info}/entry_points.txt +0 -0
  40. {umap_project-2.7.0b0.dist-info → umap_project-2.7.0b3.dist-info}/licenses/LICENSE +0 -0
umap/__init__.py CHANGED
@@ -1 +1 @@
1
- VERSION = "2.7.0b0"
1
+ VERSION = "2.7.0b3"
@@ -1,7 +1,7 @@
1
1
  .umap-contextmenu {
2
2
  background-color: var(--background-color);
3
3
  padding: calc(var(--box-padding) / 2) var(--box-padding);
4
- position: absolute;
4
+ position: fixed;
5
5
  z-index: var(--zindex-contextmenu);
6
6
  border-radius: var(--border-radius);
7
7
  box-shadow: var(--block-shadow);
@@ -1,6 +1,9 @@
1
1
  import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
2
2
  import { translate } from './i18n.js'
3
3
  import * as Icon from './rendering/icon.js'
4
+ import * as Utils from './utils.js'
5
+ import { EXPORT_FORMATS } from './formatter.js'
6
+ import ContextMenu from './ui/contextmenu.js'
4
7
 
5
8
  export default class Browser {
6
9
  constructor(map) {
@@ -98,6 +101,7 @@ export default class Browser {
98
101
  datalayer.eachFeature((feature) => this.addFeature(feature, container))
99
102
 
100
103
  const total = datalayer.count()
104
+ if (!total) return
101
105
  const current = container.querySelectorAll('li').length
102
106
  const count = total === current ? total : `${current}/${total}`
103
107
  const counter = DomUtil.create('span', 'datalayer-counter', headline)
@@ -164,6 +168,7 @@ export default class Browser {
164
168
  })
165
169
  this.filtersTitle = container.querySelector('summary')
166
170
  this.toggleBadge()
171
+ this.addMainToolbox(container)
167
172
  this.dataContainer = DomUtil.create('div', '', container)
168
173
 
169
174
  let fields = [
@@ -215,6 +220,48 @@ export default class Browser {
215
220
  }
216
221
  }
217
222
 
223
+ addMainToolbox(container) {
224
+ const [toolbox, { toggle, fitBounds, download }] = Utils.loadTemplateWithRefs(`
225
+ <div class="main-toolbox">
226
+ <i class="icon icon-16 icon-eye" title="${translate('show/hide all layers')}" data-ref="toggle"></i>
227
+ <i class="icon icon-16 icon-zoom" title="${translate('zoom to data extent')}" data-ref="fitBounds"></i>
228
+ <i class="icon icon-16 icon-download" title="${translate('download visible data')}" data-ref="download"></i>
229
+ </div>
230
+ `)
231
+ container.appendChild(toolbox)
232
+ toggle.addEventListener('click', () => this.toggleLayers())
233
+ fitBounds.addEventListener('click', () => this.map.fitDataBounds())
234
+ download.addEventListener('click', () => this.downloadVisible(download))
235
+ }
236
+
237
+ downloadVisible(element) {
238
+ const menu = new ContextMenu({ fixed: true })
239
+ const items = []
240
+ for (const format of Object.keys(EXPORT_FORMATS)) {
241
+ items.push({
242
+ label: format,
243
+ action: () => this.map.share.download(format),
244
+ })
245
+ }
246
+ menu.openBelow(element, items)
247
+ }
248
+
249
+ toggleLayers() {
250
+ // If at least one layer is shown, hide it
251
+ // otherwise show all
252
+ let allHidden = true
253
+ this.map.eachBrowsableDataLayer((datalayer) => {
254
+ if (datalayer.isVisible()) allHidden = false
255
+ })
256
+ this.map.eachBrowsableDataLayer((datalayer) => {
257
+ if (allHidden) {
258
+ datalayer.show()
259
+ } else {
260
+ if (datalayer.isVisible()) datalayer.hide()
261
+ }
262
+ })
263
+ }
264
+
218
265
  static backButton(map) {
219
266
  const button = DomUtil.createButtonIcon(
220
267
  DomUtil.create('li', '', undefined),
@@ -3,6 +3,7 @@ import { translate } from '../../i18n.js'
3
3
  import { LayerMixin } from './base.js'
4
4
  import * as Utils from '../../utils.js'
5
5
  import { CircleMarker } from '../ui.js'
6
+ import colorbrewer from '../../../../vendors/colorbrewer/colorbrewer.js'
6
7
 
7
8
  // Layer where each feature color is relative to the others,
8
9
  // so we need all features before behing able to set one
@@ -36,6 +36,12 @@ const FeatureMixin = {
36
36
  }
37
37
  },
38
38
 
39
+ _removeIcon: function () {
40
+ // It may not be in the DOM, and Leaflet does not deal with this
41
+ // situation
42
+ if (this._icon) Marker.prototype._removeIcon.call(this)
43
+ },
44
+
39
45
  addInteractions: function () {
40
46
  this.on('contextmenu editable:vertex:contextmenu', this.onContextMenu)
41
47
  this.on('click', this.onClick)
@@ -86,12 +92,10 @@ const FeatureMixin = {
86
92
 
87
93
  onContextMenu: function (event) {
88
94
  DomEvent.stop(event)
89
- const items = this._map.getContextMenuItems(event)
90
- items.push('-', ...this.feature.getContextMenuItems(event))
91
- this._map.contextmenu.open(
92
- [event.originalEvent.clientX, event.originalEvent.clientY],
93
- items
94
- )
95
+ const items = this.feature
96
+ .getContextMenuItems(event)
97
+ .concat(this._map.getContextMenuItems(event))
98
+ this._map.contextmenu.open(event.originalEvent, items)
95
99
  },
96
100
 
97
101
  onCommit: function () {
@@ -163,6 +167,12 @@ export const LeafletMarker = Marker.extend({
163
167
  return this.setLatLng(latlng)
164
168
  },
165
169
 
170
+ getEvents: function () {
171
+ const events = Marker.prototype.getEvents.call(this)
172
+ events.moveend = this.onMoveEnd
173
+ return events
174
+ },
175
+
166
176
  addInteractions() {
167
177
  PointMixin.addInteractions.call(this)
168
178
  this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
@@ -170,7 +180,19 @@ export const LeafletMarker = Marker.extend({
170
180
  this.on('popupclose', this.resetHighlight)
171
181
  },
172
182
 
183
+ onMoveEnd: function () {
184
+ this._initIcon()
185
+ this.update()
186
+ },
187
+
173
188
  _initIcon: function () {
189
+ if (!this._map.getBounds().contains(this.getCenter())) {
190
+ if (this._icon) this._removeIcon()
191
+ if (this._tooltip && this.isTooltipOpen()) {
192
+ this.unbindTooltip()
193
+ }
194
+ return
195
+ }
174
196
  this.options.icon = this.getIcon()
175
197
  Marker.prototype._initIcon.call(this)
176
198
  // Allow to run code when icon is actually part of the DOM
@@ -64,7 +64,7 @@ export default class TableEditor extends WithTemplate {
64
64
  action: () => this.deleteProperty(property),
65
65
  })
66
66
  }
67
- this.contextmenu.open([event.clientX, event.clientY], actions)
67
+ this.contextmenu.open(event, actions)
68
68
  }
69
69
 
70
70
  renderHeaders() {
@@ -15,7 +15,18 @@ export default class ContextMenu extends Positioned {
15
15
  })
16
16
  }
17
17
 
18
- open([left, top], items) {
18
+ open(event, items) {
19
+ const left = event.clientX
20
+ const top = event.clientY
21
+ this.openAt([left, top], items)
22
+ }
23
+
24
+ openBelow(element, items) {
25
+ const coords = this.getPosition(element)
26
+ this.openAt([coords.left, coords.bottom], items)
27
+ }
28
+
29
+ openAt([left, top], items) {
19
30
  this.container.innerHTML = ''
20
31
  for (const item of items) {
21
32
  if (item === '-') {
@@ -36,7 +47,12 @@ export default class ContextMenu extends Positioned {
36
47
  this.container.appendChild(li)
37
48
  }
38
49
  }
39
- document.body.appendChild(this.container)
50
+ // When adding contextmenu below the map container, clicking on any link will send the
51
+ // "focusout" element on link click, preventing to trigger the click action
52
+ const parent = document
53
+ .elementFromPoint(left, top)
54
+ .closest('.leaflet-container').parentNode
55
+ parent.appendChild(this.container)
40
56
  if (this.options.fixed) {
41
57
  this.setPosition({ left, top })
42
58
  } else {
@@ -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 () {
@@ -661,9 +657,7 @@ const ControlsMixin = {
661
657
  })
662
658
  }
663
659
  button.addEventListener('click', () => {
664
- const x = button.offsetLeft
665
- const y = button.offsetTop + button.offsetHeight
666
- menu.open([x, y], actions)
660
+ menu.openBelow(button, actions)
667
661
  })
668
662
  }
669
663
  this.help.getStartedLink(rightContainer)
@@ -191,15 +191,17 @@ U.Map = L.Map.extend({
191
191
  this.renderEditToolbar()
192
192
  }
193
193
 
194
- this.initShortcuts()
195
- if (!this.options.noControl) this.initCaptionBar()
196
- this.onceDataLoaded(this.setViewFromQueryString)
194
+ if (!this.options.noControl) {
195
+ this.initShortcuts()
196
+ this.initCaptionBar()
197
+ this.on('contextmenu', this.onContextMenu)
198
+ this.onceDataLoaded(this.setViewFromQueryString)
199
+ this.on('click', this.closeInplaceToolbar)
200
+ this.propagate()
201
+ }
197
202
 
198
203
  window.onbeforeunload = () => (this.editEnabled && this.isDirty) || null
199
204
  this.backup()
200
- this.on('click', this.closeInplaceToolbar)
201
- this.on('contextmenu', this.onContextMenu)
202
- this.propagate()
203
205
  },
204
206
 
205
207
  initSyncEngine: async function () {
@@ -1686,7 +1688,7 @@ U.Map = L.Map.extend({
1686
1688
  this.loader.onAdd(this)
1687
1689
  },
1688
1690
 
1689
- getContextMenuItems: function (event) {
1691
+ getOwnContextMenuItems: function (event) {
1690
1692
  const items = []
1691
1693
  if (this.hasEditMode()) {
1692
1694
  if (this.editEnabled) {
@@ -1726,8 +1728,10 @@ U.Map = L.Map.extend({
1726
1728
  })
1727
1729
  }
1728
1730
  }
1731
+ if (items.length) {
1732
+ items.push('-')
1733
+ }
1729
1734
  items.push(
1730
- '-',
1731
1735
  {
1732
1736
  label: L._('Open browser'),
1733
1737
  action: () => this.openBrowser('layers'),
@@ -1750,9 +1754,14 @@ U.Map = L.Map.extend({
1750
1754
  },
1751
1755
  {
1752
1756
  label: this.help.displayLabel('SEARCH'),
1753
- action: () => this.search(event),
1757
+ action: () => this.search(),
1754
1758
  }
1755
1759
  )
1760
+ return items
1761
+ },
1762
+
1763
+ getContextMenuItems: function (event) {
1764
+ const items = []
1756
1765
  if (this.options.urls.routing) {
1757
1766
  items.push('-', {
1758
1767
  label: L._('Directions from here'),
@@ -1769,11 +1778,10 @@ U.Map = L.Map.extend({
1769
1778
  },
1770
1779
 
1771
1780
  onContextMenu: function (event) {
1772
- const items = this.getContextMenuItems(event)
1773
- this.contextmenu.open(
1774
- [event.originalEvent.clientX, event.originalEvent.clientY],
1775
- items
1781
+ const items = this.getOwnContextMenuItems(event).concat(
1782
+ this.getContextMenuItems(event)
1776
1783
  )
1784
+ this.contextmenu.open(event.originalEvent, items)
1777
1785
  },
1778
1786
 
1779
1787
  editInOSM: function (e) {
@@ -513,7 +513,10 @@ const locale = {
513
513
  "Type new owner's username": "Type new owner's username",
514
514
  "Type editor's username": "Type editor's username",
515
515
  "Map": "Map",
516
- "Manage collaborators": "Manage collaborators"
516
+ "Manage collaborators": "Manage collaborators",
517
+ "show/hide all layers": "show/hide all layers",
518
+ "zoom to data extent": "zoom to data extent",
519
+ "download visible data": "download visible data"
517
520
  }
518
521
  L.registerLocale("en", locale)
519
522
  L.setLocale("en")
@@ -513,5 +513,8 @@
513
513
  "Type new owner's username": "Type new owner's username",
514
514
  "Type editor's username": "Type editor's username",
515
515
  "Map": "Map",
516
- "Manage collaborators": "Manage collaborators"
516
+ "Manage collaborators": "Manage collaborators",
517
+ "show/hide all layers": "show/hide all layers",
518
+ "zoom to data extent": "zoom to data extent",
519
+ "download visible data": "download visible data"
517
520
  }
@@ -513,7 +513,10 @@ const locale = {
513
513
  "Type new owner's username": "Nom d'utilisateur du nouveau propriétaire",
514
514
  "Type editor's username": "Nom d'utilisateur de l'éditeur",
515
515
  "Map": "Carte",
516
- "Manage collaborators": "Gérer les collaborateurs"
516
+ "Manage collaborators": "Gérer les collaborateurs",
517
+ "show/hide all layers": "montrer/masquer les calques",
518
+ "zoom to data extent": "zoomer sur les données",
519
+ "download visible data": "télécharger les données affichées"
517
520
  }
518
521
  L.registerLocale("fr", locale)
519
522
  L.setLocale("fr")
@@ -513,5 +513,8 @@
513
513
  "Type new owner's username": "Nom d'utilisateur du nouveau propriétaire",
514
514
  "Type editor's username": "Nom d'utilisateur de l'éditeur",
515
515
  "Map": "Carte",
516
- "Manage collaborators": "Gérer les collaborateurs"
516
+ "Manage collaborators": "Gérer les collaborateurs",
517
+ "show/hide all layers": "montrer/masquer les calques",
518
+ "zoom to data extent": "zoomer sur les données",
519
+ "download visible data": "télécharger les données affichées"
517
520
  }
umap/static/umap/map.css CHANGED
@@ -913,6 +913,16 @@ a.umap-control-caption,
913
913
  .umap-caption .umap-map-author {
914
914
  padding-inline-start: 31px;
915
915
  }
916
+ .umap-browser .main-toolbox {
917
+ padding-left: 4px; /* Align with toolbox below */
918
+ border-top: 1px solid var(--color-mediumGray);
919
+ margin-top: var(--box-margin);
920
+ padding-top: 3px;
921
+ padding-bottom: 3px;
922
+ }
923
+ .umap-browser .main-toolbox i {
924
+ cursor: pointer;
925
+ }
916
926
 
917
927
 
918
928
  /* ********************************* */
@@ -81,7 +81,7 @@ describe('HybridLogicalClock', () => {
81
81
  const now = Date.now()
82
82
  clock = new HybridLogicalClock(now, 5, 'local')
83
83
  const result = clock.receive(`${now}:7:remote`)
84
- expect(result.walltime).to.equal(now)
84
+ expect(result.walltime).to.be.at.least(now)
85
85
  expect(result.nn).to.equal(8)
86
86
  expect(result.id).to.equal('local')
87
87
  })
@@ -100,7 +100,12 @@ describe('HybridLogicalClock', () => {
100
100
  clock = new HybridLogicalClock(now, 5, 'local')
101
101
  const result = clock.receive(`${now - 1000}:7:remote`)
102
102
  expect(result.walltime).to.be.least(now)
103
- expect(result.nn).to.equal(6)
103
+ if (result.walltime > now) {
104
+ expect(result.nn).to.equal(5)
105
+ }
106
+ else {
107
+ expect(result.nn).to.equal(6)
108
+ }
104
109
  expect(result.id).to.equal('local')
105
110
  })
106
111
  })
@@ -117,7 +122,7 @@ describe('HybridLogicalClock', () => {
117
122
  const event2 = hlc.tick()
118
123
 
119
124
  // Simulate receiving a message from another node
120
- const remoteEvent = hlc.receive(`${Date.now() - 50}:5:remote-id`)
125
+ hlc.receive(`${Date.now() - 50}:5:remote-id`)
121
126
 
122
127
  const event3 = hlc.tick()
123
128