umap-project 2.0.4__py3-none-any.whl → 2.1.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.
Files changed (106) hide show
  1. umap/__init__.py +1 -1
  2. umap/fields.py +3 -1
  3. umap/locale/br/LC_MESSAGES/django.po +76 -71
  4. umap/locale/en/LC_MESSAGES/django.po +41 -41
  5. umap/locale/hu/LC_MESSAGES/django.po +42 -42
  6. umap/locale/it/LC_MESSAGES/django.po +64 -58
  7. umap/locale/ms/LC_MESSAGES/django.po +62 -57
  8. umap/migrations/0018_datalayer_uuid.py +62 -0
  9. umap/migrations/0019_migrate_internal_remote_datalayers.py +52 -0
  10. umap/models.py +20 -3
  11. umap/settings/base.py +1 -0
  12. umap/settings/dev.py +1 -0
  13. umap/static/umap/js/modules/browser.js +2 -2
  14. umap/static/umap/js/modules/global.js +14 -4
  15. umap/static/umap/js/modules/i18n.js +35 -0
  16. umap/static/umap/js/modules/leaflet-configure.js +7 -0
  17. umap/static/umap/js/modules/schema.js +388 -0
  18. umap/static/umap/js/modules/urls.js +17 -2
  19. umap/static/umap/js/modules/utils.js +24 -0
  20. umap/static/umap/js/umap.controls.js +9 -10
  21. umap/static/umap/js/umap.core.js +5 -5
  22. umap/static/umap/js/umap.features.js +23 -9
  23. umap/static/umap/js/umap.forms.js +49 -299
  24. umap/static/umap/js/umap.icon.js +2 -2
  25. umap/static/umap/js/umap.js +26 -129
  26. umap/static/umap/js/umap.layer.js +9 -9
  27. umap/static/umap/js/umap.popup.js +3 -0
  28. umap/static/umap/js/umap.share.js +1 -1
  29. umap/static/umap/locale/am_ET.json +229 -225
  30. umap/static/umap/locale/ar.json +229 -225
  31. umap/static/umap/locale/ast.json +229 -225
  32. umap/static/umap/locale/bg.json +229 -225
  33. umap/static/umap/locale/br.json +237 -233
  34. umap/static/umap/locale/ca.json +229 -225
  35. umap/static/umap/locale/cs_CZ.json +229 -225
  36. umap/static/umap/locale/da.json +229 -225
  37. umap/static/umap/locale/de.json +229 -225
  38. umap/static/umap/locale/el.json +229 -225
  39. umap/static/umap/locale/en.json +230 -233
  40. umap/static/umap/locale/en_US.json +229 -225
  41. umap/static/umap/locale/es.json +229 -225
  42. umap/static/umap/locale/et.json +229 -225
  43. umap/static/umap/locale/eu.json +226 -198
  44. umap/static/umap/locale/fa_IR.json +229 -225
  45. umap/static/umap/locale/fi.json +229 -225
  46. umap/static/umap/locale/fr.json +229 -232
  47. umap/static/umap/locale/gl.json +229 -225
  48. umap/static/umap/locale/he.json +229 -225
  49. umap/static/umap/locale/hr.json +229 -225
  50. umap/static/umap/locale/hu.json +229 -232
  51. umap/static/umap/locale/id.json +229 -225
  52. umap/static/umap/locale/is.json +229 -225
  53. umap/static/umap/locale/it.json +229 -232
  54. umap/static/umap/locale/ja.json +229 -225
  55. umap/static/umap/locale/ko.json +229 -225
  56. umap/static/umap/locale/lt.json +229 -225
  57. umap/static/umap/locale/ms.json +229 -232
  58. umap/static/umap/locale/nl.json +232 -228
  59. umap/static/umap/locale/no.json +229 -225
  60. umap/static/umap/locale/pl.json +229 -225
  61. umap/static/umap/locale/pl_PL.json +229 -225
  62. umap/static/umap/locale/pt.json +229 -225
  63. umap/static/umap/locale/pt_BR.json +229 -225
  64. umap/static/umap/locale/pt_PT.json +229 -225
  65. umap/static/umap/locale/ro.json +229 -225
  66. umap/static/umap/locale/ru.json +229 -225
  67. umap/static/umap/locale/sk_SK.json +229 -225
  68. umap/static/umap/locale/sl.json +229 -225
  69. umap/static/umap/locale/sr.json +229 -225
  70. umap/static/umap/locale/sv.json +229 -225
  71. umap/static/umap/locale/th_TH.json +229 -225
  72. umap/static/umap/locale/tr.json +229 -225
  73. umap/static/umap/locale/uk_UA.json +229 -225
  74. umap/static/umap/locale/vi.json +229 -225
  75. umap/static/umap/locale/vi_VN.json +229 -225
  76. umap/static/umap/locale/zh.json +229 -225
  77. umap/static/umap/locale/zh_CN.json +229 -225
  78. umap/static/umap/locale/zh_TW.Big5.json +229 -225
  79. umap/static/umap/locale/zh_TW.json +229 -232
  80. umap/static/umap/test/index.html +0 -2
  81. umap/static/umap/{test → unittests}/URLs.js +5 -0
  82. umap/static/umap/vendors/leaflet/leaflet-src.esm.js +7064 -7064
  83. umap/static/umap/vendors/photon/leaflet.photon.js +3 -0
  84. umap/templates/umap/js.html +8 -6
  85. umap/templatetags/umap_tags.py +3 -2
  86. umap/tests/integration/test_browser.py +40 -0
  87. umap/tests/integration/test_collaborative_editing.py +72 -3
  88. umap/tests/integration/test_export_map.py +226 -9
  89. umap/tests/integration/test_features_id_generation.py +51 -0
  90. umap/tests/integration/test_owned_map.py +14 -1
  91. umap/tests/integration/test_statics.py +3 -3
  92. umap/tests/integration/test_tilelayer.py +3 -3
  93. umap/tests/settings.py +3 -3
  94. umap/tests/test_datalayer_views.py +77 -20
  95. umap/tests/test_map_views.py +20 -0
  96. umap/tests/test_merge_features.py +25 -5
  97. umap/urls.py +12 -12
  98. umap/utils.py +7 -0
  99. umap/views.py +58 -49
  100. umap/wsgi.py +1 -0
  101. {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/METADATA +9 -9
  102. {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/RECORD +105 -99
  103. umap/static/umap/test/Map.Export.js +0 -106
  104. {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/WHEEL +0 -0
  105. {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/entry_points.txt +0 -0
  106. {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -388,6 +388,9 @@ L.PhotonSearch = L.PhotonBaseSearch.extend({
388
388
  if (this.options.includePosition) {
389
389
  params.lat = this.map.getCenter().lat;
390
390
  params.lon = this.map.getCenter().lng;
391
+ if (this.options.location_bias_scale) {
392
+ params.location_bias_scale = this.options.location_bias_scale;
393
+ }
391
394
  }
392
395
  if (this.options.bbox && this.options.bbox.length === 4) {
393
396
  params.bbox = this.options.bbox.join(',');
@@ -2,11 +2,18 @@
2
2
  <script type="module"
3
3
  src="{% static 'umap/vendors/leaflet/leaflet-src.esm.js' %}"
4
4
  defer></script>
5
+ <script type="module"
6
+ src="{% static 'umap/js/modules/leaflet-configure.js' %}"
7
+ defer></script>
8
+ {% if locale %}
9
+ {% with "umap/locale/"|add:locale|add:".js" as path %}
10
+ <script src="{% static path %}" defer></script>
11
+ {% endwith %}
12
+ {% endif %}
5
13
  <script type="module" src="{% static 'umap/js/modules/global.js' %}" defer></script>
6
14
  <script src="{% static 'umap/vendors/editable/Path.Drag.js' %}" defer></script>
7
15
  <script src="{% static 'umap/vendors/editable/Leaflet.Editable.js' %}" defer></script>
8
16
  <script src="{% static 'umap/vendors/hash/leaflet-hash.js' %}" defer></script>
9
- <script src="{% static 'umap/vendors/i18n/Leaflet.i18n.js' %}" defer></script>
10
17
  <script src="{% static 'umap/vendors/editinosm/Leaflet.EditInOSM.js' %}"
11
18
  defer></script>
12
19
  <script src="{% static 'umap/vendors/minimap/Control.MiniMap.min.js' %}"
@@ -39,11 +46,6 @@
39
46
  <script src="{% static 'umap/vendors/colorbrewer/colorbrewer.js' %}" defer></script>
40
47
  <script src="{% static 'umap/vendors/simple-statistics/simple-statistics.min.js' %}"
41
48
  defer></script>
42
- {% if locale %}
43
- {% with "umap/locale/"|add:locale|add:".js" as path %}
44
- <script src="{% static path %}" defer></script>
45
- {% endwith %}
46
- {% endif %}
47
49
  <script src="{% static 'umap/js/umap.core.js' %}" defer></script>
48
50
  <script src="{% static 'umap/js/umap.autocomplete.js' %}" defer></script>
49
51
  <script src="{% static 'umap/js/umap.popup.js' %}" defer></script>
@@ -1,9 +1,10 @@
1
- import json
2
1
  from copy import copy
3
2
 
4
3
  from django import template
5
4
  from django.conf import settings
6
5
 
6
+ from umap.utils import json_dumps
7
+
7
8
  register = template.Library()
8
9
 
9
10
 
@@ -25,7 +26,7 @@ def map_fragment(map_instance, **kwargs):
25
26
  page = kwargs.pop("page", None) or ""
26
27
  unique_id = prefix + str(page) + "_" + str(map_instance.pk)
27
28
  return {
28
- "map_settings": json.dumps(map_settings),
29
+ "map_settings": json_dumps(map_settings),
29
30
  "map": map_instance,
30
31
  "unique_id": unique_id,
31
32
  }
@@ -291,3 +291,43 @@ def test_should_use_color_variable(live_server, map, page):
291
291
  expect(features.nth(1)).to_have_css("background-color", "rgb(139, 0, 0)")
292
292
  # DarkBlue (default color)
293
293
  expect(features.nth(2)).to_have_css("background-color", "rgb(0, 0, 139)")
294
+
295
+
296
+ def test_should_allow_to_toggle_datalayer_visibility(live_server, map, page, bootstrap):
297
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
298
+ markers = page.locator(".leaflet-marker-icon")
299
+ paths = page.locator(".leaflet-overlay-pane path")
300
+ expect(markers).to_have_count(1)
301
+ expect(paths).to_have_count(2)
302
+ toggle = page.locator("#umap-ui-container").get_by_title("Show/hide layer")
303
+ toggle.click()
304
+ expect(markers).to_have_count(0)
305
+ expect(paths).to_have_count(0)
306
+
307
+
308
+ def test_should_have_edit_buttons_in_edit_mode(live_server, map, page, bootstrap):
309
+ # Faster than doing a login
310
+ map.edit_status = Map.ANONYMOUS
311
+ map.save()
312
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
313
+ browser = page.locator("#umap-ui-container")
314
+ edit_layer = browser.get_by_title("Edit", exact=True)
315
+ in_table = browser.get_by_title("Edit properties in a table")
316
+ delete_layer = browser.get_by_title("Delete layer")
317
+ edit_feature = browser.get_by_title("Edit this feature")
318
+ delete_feature = browser.get_by_title("Delete this feature")
319
+ expect(edit_layer).to_be_hidden()
320
+ expect(in_table).to_be_hidden()
321
+ expect(delete_layer).to_be_hidden()
322
+ # Does not work
323
+ # to_have_count does not seem to case about the elements being visible or not
324
+ # and to_be_hidden is not happy if the selector resolve to more than on element
325
+ # expect(edit_feature).to_have_count(0)
326
+ # expect(delete_feature).to_be_hidden()
327
+ # Switch to edit mode
328
+ page.get_by_role("button", name="Edit").click()
329
+ expect(edit_layer).to_be_visible()
330
+ expect(in_table).to_be_visible()
331
+ expect(delete_layer).to_be_visible()
332
+ expect(edit_feature).to_have_count(3)
333
+ expect(delete_feature).to_have_count(3)
@@ -98,7 +98,7 @@ def test_collaborative_editing_create_markers(context, live_server, tilelayer):
98
98
  "name": "test datalayer",
99
99
  "inCaption": True,
100
100
  "editMode": "advanced",
101
- "id": datalayer.pk,
101
+ "id": str(datalayer.pk),
102
102
  "permissions": {"edit_status": 1},
103
103
  }
104
104
 
@@ -116,7 +116,7 @@ def test_collaborative_editing_create_markers(context, live_server, tilelayer):
116
116
  "name": "test datalayer",
117
117
  "inCaption": True,
118
118
  "editMode": "advanced",
119
- "id": datalayer.pk,
119
+ "id": str(datalayer.pk),
120
120
  "permissions": {"edit_status": 1},
121
121
  }
122
122
  expect(marker_pane_p1).to_have_count(4)
@@ -136,7 +136,7 @@ def test_collaborative_editing_create_markers(context, live_server, tilelayer):
136
136
  "name": "test datalayer",
137
137
  "inCaption": True,
138
138
  "editMode": "advanced",
139
- "id": datalayer.pk,
139
+ "id": str(datalayer.pk),
140
140
  "permissions": {"edit_status": 1},
141
141
  }
142
142
  expect(marker_pane_p2).to_have_count(5)
@@ -196,3 +196,72 @@ def test_empty_datalayers_can_be_merged(context, live_server, tilelayer):
196
196
  sleep(1)
197
197
 
198
198
  expect(marker_pane_p2).to_have_count(2)
199
+
200
+
201
+ def test_same_second_edit_doesnt_conflict(context, live_server, tilelayer):
202
+ # Let's create a new map with an empty datalayer
203
+ map = MapFactory(name="collaborative editing")
204
+ datalayer = DataLayerFactory(map=map, edit_status=DataLayer.ANONYMOUS, data={})
205
+
206
+ # Open the created map on two pages.
207
+ page_one = context.new_page()
208
+ page_one.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
209
+ page_two = context.new_page()
210
+ page_two.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
211
+
212
+ save_p1 = page_one.get_by_role("button", name="Save")
213
+ expect(save_p1).to_be_visible()
214
+
215
+ save_p2 = page_two.get_by_role("button", name="Save")
216
+ expect(save_p2).to_be_visible()
217
+
218
+ # Create a point on the first map
219
+ create_marker_p1 = page_one.get_by_title("Draw a marker")
220
+ expect(create_marker_p1).to_be_visible()
221
+ create_marker_p1.click()
222
+
223
+ # Check no marker is present by default.
224
+ marker_pane_p1 = page_one.locator(".leaflet-marker-pane > div")
225
+ expect(marker_pane_p1).to_have_count(0)
226
+
227
+ # Click on the map, it will place a marker at the given position.
228
+ map_el_p1 = page_one.locator("#map")
229
+ map_el_p1.click(position={"x": 200, "y": 200})
230
+ expect(marker_pane_p1).to_have_count(1)
231
+
232
+ # And add one on the second map as well.
233
+ create_marker_p2 = page_two.get_by_title("Draw a marker")
234
+ expect(create_marker_p2).to_be_visible()
235
+ create_marker_p2.click()
236
+
237
+ marker_pane_p2 = page_two.locator(".leaflet-marker-pane > div")
238
+
239
+ # Click on the map, it will place a marker at the given position.
240
+ map_el_p2 = page_two.locator("#map")
241
+ map_el_p2.click(position={"x": 220, "y": 220})
242
+ expect(marker_pane_p2).to_have_count(1)
243
+
244
+ # Save the two tabs at the same time
245
+ with page_one.expect_response(DATALAYER_UPDATE):
246
+ save_p1.click()
247
+ sleep(0.2) # Needed to avoid having multiple requests coming at the same time.
248
+ save_p2.click()
249
+
250
+ # Now create another marker in the first tab
251
+ create_marker_p1.click()
252
+ map_el_p1.click(position={"x": 150, "y": 150})
253
+ expect(marker_pane_p1).to_have_count(2)
254
+ with page_one.expect_response(DATALAYER_UPDATE):
255
+ save_p1.click()
256
+
257
+ # Should now get the other marker too
258
+ expect(marker_pane_p1).to_have_count(3)
259
+ assert DataLayer.objects.get(pk=datalayer.pk).settings == {
260
+ "browsable": True,
261
+ "displayOnLoad": True,
262
+ "name": "test datalayer",
263
+ "inCaption": True,
264
+ "editMode": "advanced",
265
+ "id": str(datalayer.pk),
266
+ "permissions": {"edit_status": 1},
267
+ }
@@ -4,10 +4,84 @@ from pathlib import Path
4
4
  import pytest
5
5
  from playwright.sync_api import expect
6
6
 
7
+ from ..base import DataLayerFactory
8
+
7
9
  pytestmark = pytest.mark.django_db
8
10
 
11
+ DATALAYER_DATA = {
12
+ "type": "FeatureCollection",
13
+ "features": [
14
+ {
15
+ "type": "Feature",
16
+ "properties": {
17
+ "name": "name poly",
18
+ },
19
+ "id": "gyNzM",
20
+ "geometry": {
21
+ "type": "Polygon",
22
+ "coordinates": [
23
+ [
24
+ [11.25, 53.585984],
25
+ [10.151367, 52.975108],
26
+ [12.689209, 52.167194],
27
+ [14.084473, 53.199452],
28
+ [12.634277, 53.618579],
29
+ [11.25, 53.585984],
30
+ [11.25, 53.585984],
31
+ ],
32
+ ],
33
+ },
34
+ },
35
+ {
36
+ "type": "Feature",
37
+ "properties": {
38
+ "_umap_options": {
39
+ "color": "OliveDrab",
40
+ },
41
+ "name": "test",
42
+ "description": "Some description",
43
+ },
44
+ "id": "QwNjg",
45
+ "geometry": {
46
+ "type": "Point",
47
+ "coordinates": [-0.274658, 52.57635],
48
+ },
49
+ },
50
+ {
51
+ "type": "Feature",
52
+ "properties": {
53
+ "_umap_options": {
54
+ "fill": False,
55
+ "opacity": 0.6,
56
+ },
57
+ "name": "test",
58
+ },
59
+ "id": "YwMTM",
60
+ "geometry": {
61
+ "type": "LineString",
62
+ "coordinates": [
63
+ [-0.571289, 54.476422],
64
+ [0.439453, 54.610255],
65
+ [1.724854, 53.448807],
66
+ [4.163818, 53.988395],
67
+ [5.306396, 53.533778],
68
+ [6.591797, 53.709714],
69
+ [7.042236, 53.350551],
70
+ ],
71
+ },
72
+ },
73
+ ],
74
+ }
75
+
76
+
77
+ @pytest.fixture
78
+ def bootstrap(map, live_server):
79
+ map.settings["properties"]["onLoadPanel"] = "databrowser"
80
+ map.save()
81
+ DataLayerFactory(map=map, data=DATALAYER_DATA)
82
+
9
83
 
10
- def test_umap_export(map, live_server, datalayer, page):
84
+ def test_umap_export(map, live_server, bootstrap, page):
11
85
  page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
12
86
  link = page.get_by_role("link", name="full backup")
13
87
  expect(link).to_be_visible()
@@ -34,16 +108,56 @@ def test_umap_export(map, live_server, datalayer, page):
34
108
  "features": [
35
109
  {
36
110
  "geometry": {
37
- "coordinates": [14.68896484375, 48.55297816440071],
111
+ "coordinates": [
112
+ [
113
+ [11.25, 53.585984],
114
+ [10.151367, 52.975108],
115
+ [12.689209, 52.167194],
116
+ [14.084473, 53.199452],
117
+ [12.634277, 53.618579],
118
+ [11.25, 53.585984],
119
+ [11.25, 53.585984],
120
+ ]
121
+ ],
122
+ "type": "Polygon",
123
+ },
124
+ "id": "gyNzM",
125
+ "properties": {"name": "name poly"},
126
+ "type": "Feature",
127
+ },
128
+ {
129
+ "geometry": {
130
+ "coordinates": [-0.274658, 52.57635],
38
131
  "type": "Point",
39
132
  },
133
+ "id": "QwNjg",
134
+ "properties": {
135
+ "_umap_options": {"color": "OliveDrab"},
136
+ "name": "test",
137
+ "description": "Some description",
138
+ },
139
+ "type": "Feature",
140
+ },
141
+ {
142
+ "geometry": {
143
+ "coordinates": [
144
+ [-0.571289, 54.476422],
145
+ [0.439453, 54.610255],
146
+ [1.724854, 53.448807],
147
+ [4.163818, 53.988395],
148
+ [5.306396, 53.533778],
149
+ [6.591797, 53.709714],
150
+ [7.042236, 53.350551],
151
+ ],
152
+ "type": "LineString",
153
+ },
154
+ "id": "YwMTM",
40
155
  "properties": {
41
- "_umap_options": {"color": "DarkCyan", "iconClass": "Ball"},
42
- "description": "Da place anonymous " "again 755",
43
- "name": "Here",
156
+ "_umap_options": {"fill": False, "opacity": 0.6},
157
+ "name": "test",
44
158
  },
45
159
  "type": "Feature",
46
- }
160
+ },
47
161
  ],
48
162
  "type": "FeatureCollection",
49
163
  }
@@ -66,12 +180,13 @@ def test_umap_export(map, live_server, datalayer, page):
66
180
  "tilelayersControl": True,
67
181
  "zoom": 7,
68
182
  "zoomControl": True,
183
+ "onLoadPanel": "databrowser",
69
184
  },
70
185
  "type": "umap",
71
186
  }
72
187
 
73
188
 
74
- def test_csv_export(map, live_server, datalayer, page):
189
+ def test_csv_export(map, live_server, bootstrap, page):
75
190
  page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
76
191
  button = page.get_by_role("button", name="csv")
77
192
  expect(button).to_be_visible()
@@ -83,6 +198,108 @@ def test_csv_export(map, live_server, datalayer, page):
83
198
  download.save_as(path)
84
199
  assert (
85
200
  path.read_text()
86
- == """name,description,Latitude,Longitude
87
- Here,Da place anonymous again 755,48.55297816440071,14.68896484375"""
201
+ == """name,Latitude,Longitude,description
202
+ name poly,53.0072070131872,12.182431646910137,
203
+ test,52.57635,-0.274658,Some description
204
+ test,53.725145179688646,2.9700064980570517,"""
205
+ )
206
+
207
+
208
+ def test_gpx_export(map, live_server, bootstrap, page):
209
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
210
+ button = page.get_by_role("button", name="gpx")
211
+ expect(button).to_be_visible()
212
+ with page.expect_download() as download_info:
213
+ button.click()
214
+ download = download_info.value
215
+ # FIXME assert mimetype (find no way to access it throught PW)
216
+ assert download.suggested_filename == "test_map.gpx"
217
+ path = Path("/tmp/") / download.suggested_filename
218
+ download.save_as(path)
219
+ assert (
220
+ path.read_text()
221
+ == """<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" creator="togpx"><metadata/><wpt lat="52.57635" lon="-0.274658"><name>test</name><desc>name=test
222
+ description=Some description</desc></wpt><trk><name>name poly</name><desc>name=name poly</desc><trkseg><trkpt lat="53.585984" lon="11.25"/><trkpt lat="52.975108" lon="10.151367"/><trkpt lat="52.167194" lon="12.689209"/><trkpt lat="53.199452" lon="14.084473"/><trkpt lat="53.618579" lon="12.634277"/><trkpt lat="53.585984" lon="11.25"/><trkpt lat="53.585984" lon="11.25"/></trkseg></trk><trk><name>test</name><desc>name=test</desc><trkseg><trkpt lat="54.476422" lon="-0.571289"/><trkpt lat="54.610255" lon="0.439453"/><trkpt lat="53.448807" lon="1.724854"/><trkpt lat="53.988395" lon="4.163818"/><trkpt lat="53.533778" lon="5.306396"/><trkpt lat="53.709714" lon="6.591797"/><trkpt lat="53.350551" lon="7.042236"/></trkseg></trk></gpx>"""
88
223
  )
224
+
225
+
226
+ def test_kml_export(map, live_server, bootstrap, page):
227
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
228
+ button = page.get_by_role("button", name="kml")
229
+ expect(button).to_be_visible()
230
+ with page.expect_download() as download_info:
231
+ button.click()
232
+ download = download_info.value
233
+ assert download.suggested_filename == "test_map.kml"
234
+ path = Path("/tmp/") / download.suggested_filename
235
+ download.save_as(path)
236
+ assert (
237
+ path.read_text()
238
+ == """<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><Document><Placemark><name>name poly</name><ExtendedData><Data name="name"><value>name poly</value></Data></ExtendedData><Polygon><outerBoundaryIs><LinearRing><coordinates>11.25,53.585984 10.151367,52.975108 12.689209,52.167194 14.084473,53.199452 12.634277,53.618579 11.25,53.585984 11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark><name>test</name><description>Some description</description><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data><Data name="description"><value>Some description</value></Data></ExtendedData><Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><LineString><coordinates>-0.571289,54.476422 0.439453,54.610255 1.724854,53.448807 4.163818,53.988395 5.306396,53.533778 6.591797,53.709714 7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>"""
239
+ )
240
+
241
+
242
+ def test_geojson_export(map, live_server, bootstrap, page):
243
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
244
+ button = page.get_by_role("button", name="geojson")
245
+ expect(button).to_be_visible()
246
+ with page.expect_download() as download_info:
247
+ button.click()
248
+ download = download_info.value
249
+ assert download.suggested_filename == "test_map.geojson"
250
+ path = Path("/tmp/") / download.suggested_filename
251
+ download.save_as(path)
252
+ assert json.loads(path.read_text()) == {
253
+ "features": [
254
+ {
255
+ "geometry": {
256
+ "coordinates": [
257
+ [
258
+ [11.25, 53.585984],
259
+ [10.151367, 52.975108],
260
+ [12.689209, 52.167194],
261
+ [14.084473, 53.199452],
262
+ [12.634277, 53.618579],
263
+ [11.25, 53.585984],
264
+ [11.25, 53.585984],
265
+ ]
266
+ ],
267
+ "type": "Polygon",
268
+ },
269
+ "id": "gyNzM",
270
+ "properties": {"name": "name poly"},
271
+ "type": "Feature",
272
+ },
273
+ {
274
+ "geometry": {"coordinates": [-0.274658, 52.57635], "type": "Point"},
275
+ "id": "QwNjg",
276
+ "properties": {
277
+ "_umap_options": {"color": "OliveDrab"},
278
+ "name": "test",
279
+ "description": "Some description",
280
+ },
281
+ "type": "Feature",
282
+ },
283
+ {
284
+ "geometry": {
285
+ "coordinates": [
286
+ [-0.571289, 54.476422],
287
+ [0.439453, 54.610255],
288
+ [1.724854, 53.448807],
289
+ [4.163818, 53.988395],
290
+ [5.306396, 53.533778],
291
+ [6.591797, 53.709714],
292
+ [7.042236, 53.350551],
293
+ ],
294
+ "type": "LineString",
295
+ },
296
+ "id": "YwMTM",
297
+ "properties": {
298
+ "_umap_options": {"fill": False, "opacity": 0.6},
299
+ "name": "test",
300
+ },
301
+ "type": "Feature",
302
+ },
303
+ ],
304
+ "type": "FeatureCollection",
305
+ }
@@ -0,0 +1,51 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+
5
+ def test_ids_generation(page, live_server, tilelayer):
6
+ page.goto(f"{live_server.url}/en/map/new/")
7
+
8
+ # Click on the Draw a line button on a new map.
9
+ create_polyline = page.locator(".leaflet-control-toolbar ").get_by_title(
10
+ "Draw a polyline"
11
+ )
12
+ create_polyline.click()
13
+
14
+ map = page.locator("#map")
15
+ map.click(position={"x": 200, "y": 200})
16
+ map.click(position={"x": 100, "y": 100})
17
+ # Click again to finish
18
+ map.click(position={"x": 100, "y": 100})
19
+
20
+ # Click on the Draw a polygon button on a new map.
21
+ create_polygon = page.locator(".leaflet-control-toolbar ").get_by_title(
22
+ "Draw a polygon"
23
+ )
24
+ create_polygon.click()
25
+
26
+ map = page.locator("#map")
27
+ map.click(position={"x": 300, "y": 300})
28
+ map.click(position={"x": 300, "y": 400})
29
+ map.click(position={"x": 350, "y": 450})
30
+ # Click again to finish
31
+ map.click(position={"x": 350, "y": 450})
32
+
33
+ download_panel = page.get_by_title("Share and download")
34
+ download_panel.click()
35
+
36
+ button = page.get_by_role("button", name="geojson")
37
+
38
+ with page.expect_download() as download_info:
39
+ button.click()
40
+
41
+ download = download_info.value
42
+
43
+ path = Path("/tmp/") / download.suggested_filename
44
+ download.save_as(path)
45
+ downloaded = json.loads(path.read_text())
46
+
47
+ assert "features" in downloaded
48
+ features = downloaded["features"]
49
+ assert len(features) == 2
50
+ assert "id" in features[0]
51
+ assert "id" in features[1]
@@ -134,6 +134,18 @@ def test_owner_has_delete_map_button(map, live_server, login):
134
134
  advanced.click()
135
135
  delete = page.get_by_role("button", name="Delete", exact=True)
136
136
  expect(delete).to_be_visible()
137
+ dialog_shown = False
138
+
139
+ def handle_dialog(dialog):
140
+ dialog.accept()
141
+ nonlocal dialog_shown
142
+ dialog_shown = True
143
+
144
+ page.on("dialog", handle_dialog)
145
+ with page.expect_navigation():
146
+ delete.click()
147
+ assert dialog_shown
148
+ assert Map.objects.all().count() == 0
137
149
 
138
150
 
139
151
  def test_editor_do_not_have_delete_map_button(map, live_server, login, user):
@@ -210,7 +222,8 @@ def test_can_change_owner(map, live_server, login, user):
210
222
  close = page.locator(".umap-field-owner .close")
211
223
  close.click()
212
224
  input = page.locator("input.edit-owner")
213
- input.type(user.username)
225
+ with page.expect_response(re.compile(r".*/agnocomplete/.*")):
226
+ input.type(user.username)
214
227
  input.press("Tab")
215
228
  save = page.get_by_role("button", name="Save")
216
229
  expect(save).to_be_visible()
@@ -15,9 +15,9 @@ def staticfiles(settings):
15
15
  settings.STATIC_ROOT = static_root
16
16
  # Make sure settings are properly reset after the test
17
17
  settings.STORAGES = deepcopy(settings.STORAGES)
18
- settings.STORAGES["staticfiles"][
19
- "BACKEND"
20
- ] = "umap.storage.UmapManifestStaticFilesStorage"
18
+ settings.STORAGES["staticfiles"]["BACKEND"] = (
19
+ "umap.storage.UmapManifestStaticFilesStorage"
20
+ )
21
21
  try:
22
22
  call_command("collectstatic", "--noinput")
23
23
  yield
@@ -101,9 +101,9 @@ def test_map_should_display_custom_tilelayer(map, live_server, tilelayers, page)
101
101
  url_pattern = re.compile(
102
102
  r"https://[abc]{1}.basemaps.cartocdn.com/rastertiles/voyager/\d+/\d+/\d+.png"
103
103
  )
104
- map.settings["properties"]["tilelayer"][
105
- "url_template"
106
- ] = "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
104
+ map.settings["properties"]["tilelayer"]["url_template"] = (
105
+ "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
106
+ )
107
107
  map.settings["properties"]["tilelayersControl"] = True
108
108
  map.save()
109
109
  page.goto(f"{live_server.url}{map.get_absolute_url()}")
umap/tests/settings.py CHANGED
@@ -5,9 +5,9 @@ from umap.settings.base import * # pylint: disable=W0614,W0401
5
5
  SECRET_KEY = "justfortests"
6
6
  DEFAULT_FROM_EMAIL = "test@test.org"
7
7
  EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
8
- STORAGES["staticfiles"][
9
- "BACKEND"
10
- ] = "django.contrib.staticfiles.storage.StaticFilesStorage"
8
+ STORAGES["staticfiles"]["BACKEND"] = (
9
+ "django.contrib.staticfiles.storage.StaticFilesStorage"
10
+ )
11
11
 
12
12
  if os.environ.get("GITHUB_ACTIONS", False) == "true":
13
13
  DATABASES = {