umap-project 3.1.2__py3-none-any.whl → 3.3.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 (199) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/en/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/en/LC_MESSAGES/django.po +22 -18
  4. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/fr/LC_MESSAGES/django.po +21 -17
  6. umap/management/commands/export_pictogram.py +29 -0
  7. umap/management/commands/migrate_to_S3.py +5 -1
  8. umap/management/commands/purge_old_versions.py +8 -6
  9. umap/settings/__init__.py +21 -0
  10. umap/settings/base.py +3 -0
  11. umap/static/umap/content.css +7 -2
  12. umap/static/umap/css/contextmenu.css +58 -2
  13. umap/static/umap/css/form.css +175 -45
  14. umap/static/umap/css/icon.css +97 -3
  15. umap/static/umap/css/panel.css +31 -1
  16. umap/static/umap/img/16-white.svg +21 -40
  17. umap/static/umap/img/16.svg +1 -1
  18. umap/static/umap/img/24-white.svg +9 -9
  19. umap/static/umap/img/24.svg +23 -10
  20. umap/static/umap/img/source/16-white.svg +23 -41
  21. umap/static/umap/img/source/16.svg +1 -1
  22. umap/static/umap/img/source/24-white.svg +11 -11
  23. umap/static/umap/img/source/24.svg +25 -12
  24. umap/static/umap/js/modules/browser.js +1 -1
  25. umap/static/umap/js/modules/caption.js +8 -0
  26. umap/static/umap/js/modules/data/features.js +331 -202
  27. umap/static/umap/js/modules/data/layer.js +263 -152
  28. umap/static/umap/js/modules/facets.js +2 -2
  29. umap/static/umap/js/modules/form/builder.js +11 -7
  30. umap/static/umap/js/modules/form/fields.js +66 -26
  31. umap/static/umap/js/modules/formatter.js +78 -28
  32. umap/static/umap/js/modules/importer.js +6 -1
  33. umap/static/umap/js/modules/importers/opendata.js +138 -33
  34. umap/static/umap/js/modules/importers/openrouteservice.js +140 -0
  35. umap/static/umap/js/modules/managers.js +67 -0
  36. umap/static/umap/js/modules/printer.js +107 -0
  37. umap/static/umap/js/modules/rendering/controls.js +78 -2
  38. umap/static/umap/js/modules/rendering/icon.js +116 -87
  39. umap/static/umap/js/modules/rendering/layers/classified.js +8 -7
  40. umap/static/umap/js/modules/rendering/layers/cluster.js +199 -63
  41. umap/static/umap/js/modules/rendering/map.js +6 -2
  42. umap/static/umap/js/modules/rendering/template.js +71 -1
  43. umap/static/umap/js/modules/rendering/ui.js +111 -34
  44. umap/static/umap/js/modules/rules.js +76 -23
  45. umap/static/umap/js/modules/schema.js +27 -0
  46. umap/static/umap/js/modules/share.js +19 -12
  47. umap/static/umap/js/modules/slideshow.js +1 -1
  48. umap/static/umap/js/modules/sync/updaters.js +1 -6
  49. umap/static/umap/js/modules/tableeditor.js +13 -37
  50. umap/static/umap/js/modules/templates.js +7 -6
  51. umap/static/umap/js/modules/ui/bar.js +6 -1
  52. umap/static/umap/js/modules/ui/base.js +24 -9
  53. umap/static/umap/js/modules/ui/contextmenu.js +17 -7
  54. umap/static/umap/js/modules/ui/dialog.js +7 -4
  55. umap/static/umap/js/modules/ui/panel.js +7 -0
  56. umap/static/umap/js/modules/umap.js +84 -67
  57. umap/static/umap/js/modules/utils.js +8 -7
  58. umap/static/umap/js/umap.controls.js +22 -57
  59. umap/static/umap/locale/am_ET.js +81 -9
  60. umap/static/umap/locale/am_ET.json +81 -9
  61. umap/static/umap/locale/ar.js +81 -9
  62. umap/static/umap/locale/ar.json +81 -9
  63. umap/static/umap/locale/ast.js +81 -9
  64. umap/static/umap/locale/ast.json +81 -9
  65. umap/static/umap/locale/bg.js +81 -9
  66. umap/static/umap/locale/bg.json +81 -9
  67. umap/static/umap/locale/br.js +68 -29
  68. umap/static/umap/locale/br.json +68 -29
  69. umap/static/umap/locale/ca.js +88 -16
  70. umap/static/umap/locale/ca.json +88 -16
  71. umap/static/umap/locale/cs_CZ.js +81 -9
  72. umap/static/umap/locale/cs_CZ.json +81 -9
  73. umap/static/umap/locale/da.js +48 -9
  74. umap/static/umap/locale/da.json +48 -9
  75. umap/static/umap/locale/de.js +48 -9
  76. umap/static/umap/locale/de.json +48 -9
  77. umap/static/umap/locale/el.js +58 -13
  78. umap/static/umap/locale/el.json +58 -13
  79. umap/static/umap/locale/en.js +48 -9
  80. umap/static/umap/locale/en.json +48 -9
  81. umap/static/umap/locale/en_US.json +81 -9
  82. umap/static/umap/locale/es.js +48 -9
  83. umap/static/umap/locale/es.json +48 -9
  84. umap/static/umap/locale/et.js +81 -9
  85. umap/static/umap/locale/et.json +81 -9
  86. umap/static/umap/locale/eu.js +97 -25
  87. umap/static/umap/locale/eu.json +97 -25
  88. umap/static/umap/locale/fa_IR.js +81 -9
  89. umap/static/umap/locale/fa_IR.json +81 -9
  90. umap/static/umap/locale/fi.js +81 -9
  91. umap/static/umap/locale/fi.json +81 -9
  92. umap/static/umap/locale/fr.js +48 -9
  93. umap/static/umap/locale/fr.json +48 -9
  94. umap/static/umap/locale/gl.js +81 -9
  95. umap/static/umap/locale/gl.json +81 -9
  96. umap/static/umap/locale/he.js +81 -9
  97. umap/static/umap/locale/he.json +81 -9
  98. umap/static/umap/locale/hr.js +81 -9
  99. umap/static/umap/locale/hr.json +81 -9
  100. umap/static/umap/locale/hu.js +72 -27
  101. umap/static/umap/locale/hu.json +72 -27
  102. umap/static/umap/locale/id.js +81 -9
  103. umap/static/umap/locale/id.json +81 -9
  104. umap/static/umap/locale/is.js +81 -9
  105. umap/static/umap/locale/is.json +81 -9
  106. umap/static/umap/locale/it.js +48 -9
  107. umap/static/umap/locale/it.json +48 -9
  108. umap/static/umap/locale/ja.js +81 -9
  109. umap/static/umap/locale/ja.json +81 -9
  110. umap/static/umap/locale/ko.js +81 -9
  111. umap/static/umap/locale/ko.json +81 -9
  112. umap/static/umap/locale/lt.js +81 -9
  113. umap/static/umap/locale/lt.json +81 -9
  114. umap/static/umap/locale/ms.js +81 -9
  115. umap/static/umap/locale/ms.json +81 -9
  116. umap/static/umap/locale/nl.js +48 -9
  117. umap/static/umap/locale/nl.json +48 -9
  118. umap/static/umap/locale/no.js +81 -9
  119. umap/static/umap/locale/no.json +81 -9
  120. umap/static/umap/locale/pl.js +81 -9
  121. umap/static/umap/locale/pl.json +81 -9
  122. umap/static/umap/locale/pl_PL.json +81 -9
  123. umap/static/umap/locale/pt.js +81 -9
  124. umap/static/umap/locale/pt.json +81 -9
  125. umap/static/umap/locale/pt_BR.js +91 -19
  126. umap/static/umap/locale/pt_BR.json +91 -19
  127. umap/static/umap/locale/pt_PT.js +81 -9
  128. umap/static/umap/locale/pt_PT.json +81 -9
  129. umap/static/umap/locale/ro.js +81 -9
  130. umap/static/umap/locale/ro.json +81 -9
  131. umap/static/umap/locale/ru.js +81 -9
  132. umap/static/umap/locale/ru.json +81 -9
  133. umap/static/umap/locale/sk_SK.js +81 -9
  134. umap/static/umap/locale/sk_SK.json +81 -9
  135. umap/static/umap/locale/sl.js +81 -9
  136. umap/static/umap/locale/sl.json +81 -9
  137. umap/static/umap/locale/sr.js +81 -9
  138. umap/static/umap/locale/sr.json +81 -9
  139. umap/static/umap/locale/sv.js +81 -9
  140. umap/static/umap/locale/sv.json +81 -9
  141. umap/static/umap/locale/th_TH.js +81 -9
  142. umap/static/umap/locale/th_TH.json +81 -9
  143. umap/static/umap/locale/tr.js +81 -9
  144. umap/static/umap/locale/tr.json +81 -9
  145. umap/static/umap/locale/uk_UA.js +81 -9
  146. umap/static/umap/locale/uk_UA.json +81 -9
  147. umap/static/umap/locale/vi.js +81 -9
  148. umap/static/umap/locale/vi.json +81 -9
  149. umap/static/umap/locale/vi_VN.json +81 -9
  150. umap/static/umap/locale/zh.js +81 -9
  151. umap/static/umap/locale/zh.json +81 -9
  152. umap/static/umap/locale/zh_CN.json +81 -9
  153. umap/static/umap/locale/zh_TW.Big5.json +81 -9
  154. umap/static/umap/locale/zh_TW.js +98 -26
  155. umap/static/umap/locale/zh_TW.json +98 -26
  156. umap/static/umap/map.css +325 -102
  157. umap/static/umap/vars.css +1 -0
  158. umap/static/umap/vendors/betterknown/betterknown.mjs +287 -0
  159. umap/static/umap/vendors/editable/Leaflet.Editable.js +3 -1
  160. umap/static/umap/vendors/openrouteservice/ors-js-client.js +521 -0
  161. umap/static/umap/vendors/openrouteservice/ors-js-client.js.map +1 -0
  162. umap/static/umap/vendors/simple-elevation-chart/elevation.js +63 -0
  163. umap/static/umap/vendors/simple-elevation-chart/elevation.svg +8 -0
  164. umap/static/umap/vendors/snapdom/snapdom.min.mjs +3 -0
  165. umap/storage/fs.py +3 -2
  166. umap/storage/staticfiles.py +12 -0
  167. umap/templates/base.html +4 -1
  168. umap/templates/umap/css.html +0 -4
  169. umap/templates/umap/js.html +1 -3
  170. umap/tests/base.py +9 -1
  171. umap/tests/integration/test_basics.py +3 -1
  172. umap/tests/integration/test_conditional_rules.py +79 -37
  173. umap/tests/integration/test_datalayer.py +1 -1
  174. umap/tests/integration/test_draw_polygon.py +3 -5
  175. umap/tests/integration/test_draw_polyline.py +4 -6
  176. umap/tests/integration/test_draw_route.py +178 -0
  177. umap/tests/integration/test_edit_datalayer.py +1 -1
  178. umap/tests/integration/test_edit_map.py +1 -1
  179. umap/tests/integration/test_edit_marker.py +8 -8
  180. umap/tests/integration/test_edit_polygon.py +2 -2
  181. umap/tests/integration/test_export_map.py +84 -10
  182. umap/tests/integration/test_import.py +140 -0
  183. umap/tests/integration/test_map_preview.py +1 -1
  184. umap/tests/integration/test_optimistic_merge.py +72 -12
  185. umap/tests/integration/test_share.py +1 -1
  186. umap/tests/integration/test_tableeditor.py +10 -7
  187. umap/tests/integration/test_websocket_sync.py +4 -4
  188. umap/utils.py +37 -0
  189. umap/views.py +18 -2
  190. umap_project-3.3.0.dist-info/METADATA +76 -0
  191. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/RECORD +194 -188
  192. umap/static/umap/vendors/markercluster/MarkerCluster.Default.css +0 -60
  193. umap/static/umap/vendors/markercluster/MarkerCluster.css +0 -14
  194. umap/static/umap/vendors/markercluster/leaflet.markercluster.js +0 -2
  195. umap/static/umap/vendors/markercluster/leaflet.markercluster.js.map +0 -1
  196. umap_project-3.1.2.dist-info/METADATA +0 -68
  197. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/WHEEL +0 -0
  198. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/entry_points.txt +0 -0
  199. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,178 @@
1
+ import pytest
2
+ from playwright.sync_api import expect
3
+
4
+ pytestmark = pytest.mark.django_db
5
+
6
+
7
+ def test_route_button_is_hidden(page, live_server, tilelayer, settings):
8
+ page.goto(f"{live_server.url}/en/map/new/#14/47.7591/2.4134")
9
+ expect(page.get_by_role("button", name="Draw along routes")).to_be_hidden()
10
+
11
+
12
+ def test_draw_route(page, live_server, tilelayer, settings):
13
+ settings.OPENROUTESERVICE_APIKEY = "FOOBAR="
14
+ cycling_response = {
15
+ "type": "FeatureCollection",
16
+ "bbox": [2.367278, 47.768019, 2.375744, 47.774736],
17
+ "features": [
18
+ {
19
+ "bbox": [2.367278, 47.768019, 2.375744, 47.774736],
20
+ "type": "Feature",
21
+ "properties": {
22
+ "way_points": [0, 31],
23
+ "summary": {"distance": 1228.2, "duration": 308.4},
24
+ },
25
+ "geometry": {
26
+ "coordinates": [
27
+ [2.367419, 47.77416],
28
+ [2.367278, 47.774736],
29
+ [2.367914, 47.774734],
30
+ [2.367982, 47.774676],
31
+ [2.368261, 47.774569],
32
+ [2.368397, 47.774488],
33
+ [2.368517, 47.774396],
34
+ [2.368745, 47.77417],
35
+ [2.368963, 47.773917],
36
+ [2.369246, 47.773728],
37
+ [2.369376, 47.773669],
38
+ [2.37024, 47.773401],
39
+ [2.370303, 47.773393],
40
+ [2.370277, 47.7733],
41
+ [2.37047, 47.77325],
42
+ [2.371473, 47.772904],
43
+ [2.371666, 47.772883],
44
+ [2.371669, 47.772587],
45
+ [2.37161, 47.772529],
46
+ [2.371584, 47.772466],
47
+ [2.371589, 47.772401],
48
+ [2.371672, 47.772301],
49
+ [2.371801, 47.77225],
50
+ [2.371875, 47.772172],
51
+ [2.371966, 47.771983],
52
+ [2.372052, 47.77171],
53
+ [2.373877, 47.768035],
54
+ [2.374129, 47.768118],
55
+ [2.374149, 47.768088],
56
+ [2.374132, 47.768019],
57
+ [2.375642, 47.768538],
58
+ [2.375744, 47.768386],
59
+ ],
60
+ "type": "LineString",
61
+ },
62
+ }
63
+ ],
64
+ "metadata": {
65
+ "attribution": "openrouteservice.org | OpenStreetMap contributors",
66
+ "service": "routing",
67
+ "timestamp": 1753716213909,
68
+ "query": {
69
+ "coordinates": [[2.367039, 47.774118], [2.375622, 47.768349]],
70
+ "profile": "cycling-regular",
71
+ "profileName": "cycling-regular",
72
+ "preference": "recommended",
73
+ "format": "geojson",
74
+ "geometry_simplify": True,
75
+ },
76
+ "engine": {
77
+ "version": "9.3.0",
78
+ "build_date": "2025-06-06T15:39:25Z",
79
+ "graph_date": "2025-06-22T07:54:02Z",
80
+ "osm_date": "1970-01-01T00:00:00Z",
81
+ },
82
+ },
83
+ }
84
+
85
+ car_response = {
86
+ "type": "FeatureCollection",
87
+ "bbox": [2.367278, 47.768035, 2.375458, 47.774736],
88
+ "features": [
89
+ {
90
+ "bbox": [2.367278, 47.768035, 2.375458, 47.774736],
91
+ "type": "Feature",
92
+ "properties": {
93
+ "way_points": [0, 17],
94
+ "summary": {"distance": 1283.8, "duration": 160.8},
95
+ },
96
+ "geometry": {
97
+ "coordinates": [
98
+ [2.367419, 47.77416],
99
+ [2.367278, 47.774736],
100
+ [2.367476, 47.774736],
101
+ [2.367495, 47.774429],
102
+ [2.367601, 47.773963],
103
+ [2.367799, 47.773307],
104
+ [2.368261, 47.772056],
105
+ [2.370046, 47.772198],
106
+ [2.371213, 47.772406],
107
+ [2.371469, 47.772383],
108
+ [2.371625, 47.772341],
109
+ [2.371672, 47.772301],
110
+ [2.371801, 47.77225],
111
+ [2.371875, 47.772172],
112
+ [2.371966, 47.771983],
113
+ [2.372052, 47.77171],
114
+ [2.373877, 47.768035],
115
+ [2.375458, 47.768569],
116
+ ],
117
+ "type": "LineString",
118
+ },
119
+ }
120
+ ],
121
+ "metadata": {
122
+ "attribution": "openrouteservice.org | OpenStreetMap contributors",
123
+ "service": "routing",
124
+ "timestamp": 1753717466321,
125
+ "query": {
126
+ "coordinates": [[2.367039, 47.774118], [2.375622, 47.768349]],
127
+ "profile": "driving-car",
128
+ "profileName": "driving-car",
129
+ "preference": "recommended",
130
+ "format": "geojson",
131
+ "geometry_simplify": True,
132
+ },
133
+ "engine": {
134
+ "version": "9.3.0",
135
+ "build_date": "2025-06-06T15:39:25Z",
136
+ "graph_date": "2025-06-23T11:47:36Z",
137
+ "osm_date": "1970-01-01T00:00:00Z",
138
+ },
139
+ },
140
+ }
141
+
142
+ def handle_cycling(route):
143
+ route.fulfill(json=cycling_response)
144
+
145
+ def handle_car(route):
146
+ route.fulfill(json=car_response)
147
+
148
+ # Intercept the route
149
+ page.route(
150
+ "https://api.openrouteservice.org/v2/directions/cycling-regular/geojson",
151
+ handle_cycling,
152
+ )
153
+ page.route(
154
+ "https://api.openrouteservice.org/v2/directions/driving-car/geojson",
155
+ handle_car,
156
+ )
157
+ page.goto(f"{live_server.url}/en/map/new/#14/47.7591/2.4134")
158
+ expect(page.locator("path")).to_be_hidden()
159
+ expect(page.locator(".leaflet-vertex-icon")).to_be_hidden()
160
+ page.get_by_role("button", name="Draw along routes").click()
161
+ page.locator('select[name="profile"]').select_option("cycling-regular")
162
+ page.get_by_role("button", name="OK").click()
163
+ page.locator("#map").click(position={"x": 100, "y": 100})
164
+ page.locator("#map").click(position={"x": 200, "y": 200})
165
+ page.locator("#map").click(position={"x": 200, "y": 200})
166
+ expect(page.locator("path")).to_be_visible()
167
+ expect(page.locator(".leaflet-vertex-icon")).to_have_count(2)
168
+ page.get_by_text("Advanced actions").click()
169
+ page.get_by_role("button", name="Transform to regular line").click()
170
+ expect(page.locator(".leaflet-vertex-icon")).to_have_count(32)
171
+ page.get_by_text("Advanced actions").click()
172
+ page.get_by_role("button", name="Restore route").click()
173
+ expect(page.locator(".leaflet-vertex-icon")).to_have_count(2)
174
+ page.locator('#edit-route select[name="profile"]').select_option("driving-car")
175
+ page.get_by_role("button", name="Compute route").click()
176
+ page.get_by_text("Advanced actions").click()
177
+ page.get_by_role("button", name="Transform to regular line").click()
178
+ expect(page.locator(".leaflet-vertex-icon")).to_have_count(18)
@@ -221,7 +221,7 @@ def test_deleting_datalayer_should_remove_from_caption(
221
221
  def test_can_edit_datalayer_name_in_list(live_server, openmap, datalayer, page):
222
222
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
223
223
  page.get_by_role("button", name="Manage layers").click()
224
- page.get_by_text("test datalayer").click()
224
+ page.get_by_text("test datalayer").dblclick()
225
225
  page.get_by_text("test datalayer").fill("test datalayer foobar")
226
226
  page.get_by_role("button", name="Open browser").click()
227
227
  expect(
@@ -99,7 +99,7 @@ def test_map_color_impacts_data(live_server, page, tilelayer):
99
99
  page.get_by_title("Lime", exact=True).click()
100
100
 
101
101
  # Assert the new color was used
102
- marker_style = page.locator(".leaflet-marker-icon .icon_container").get_attribute(
102
+ marker_style = page.locator(".leaflet-marker-icon .icon-container").get_attribute(
103
103
  "style"
104
104
  )
105
105
  assert "lime" in marker_style
@@ -47,11 +47,11 @@ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstr
47
47
  page.locator(".panel").get_by_title("Edit", exact=True).click()
48
48
  page.get_by_text("Shape properties").click()
49
49
  page.locator(".umap-field-color .define").click()
50
- expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
50
+ expect(page.locator(".leaflet-marker-icon .icon-container")).to_have_css(
51
51
  "background-color", "rgb(0, 0, 139)"
52
52
  )
53
53
  page.get_by_title("DarkRed").first.click()
54
- expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
54
+ expect(page.locator(".leaflet-marker-icon .icon-container")).to_have_css(
55
55
  "background-color", "rgb(139, 0, 0)"
56
56
  )
57
57
 
@@ -60,7 +60,7 @@ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstr
60
60
  page.get_by_text("Shape properties").click()
61
61
  page.locator("#umap-feature-shape-properties").get_by_text("define").first.click()
62
62
  page.get_by_title("GoldenRod", exact=True).click()
63
- expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
63
+ expect(page.locator(".leaflet-marker-icon .icon-container")).to_have_css(
64
64
  "background-color", "rgb(218, 165, 32)"
65
65
  )
66
66
 
@@ -70,14 +70,14 @@ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstr
70
70
  page.get_by_text("Shape properties").click()
71
71
  page.locator(".umap-field-color input").click()
72
72
  page.get_by_title("DarkViolet").first.click()
73
- expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
73
+ expect(page.locator(".leaflet-marker-icon .icon-container")).to_have_css(
74
74
  "background-color", "rgb(218, 165, 32)"
75
75
  )
76
76
 
77
77
 
78
78
  def test_should_open_an_edit_toolbar_on_click(live_server, openmap, page, bootstrap):
79
79
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
80
- page.locator(".leaflet-marker-icon").click()
80
+ page.locator(".leaflet-marker-icon").click(button="right")
81
81
  expect(page.get_by_role("button", name="Toggle edit mode")).to_be_visible()
82
82
  expect(page.get_by_role("button", name="Delete this feature")).to_be_visible()
83
83
 
@@ -111,10 +111,10 @@ def test_should_follow_datalayer_style_when_changing_datalayer(
111
111
  },
112
112
  )
113
113
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
114
- marker = page.locator(".leaflet-marker-icon .icon_container")
114
+ marker = page.locator(".leaflet-marker-icon .icon-container")
115
115
  expect(marker).to_have_css("background-color", "rgb(0, 139, 139)")
116
116
  # Change datalayer
117
- marker.click()
117
+ marker.click(button="right")
118
118
  page.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
119
119
  page.locator(".umap-field-datalayer select").select_option(label="other datalayer")
120
120
  expect(marker).to_have_css("background-color", "rgb(148, 0, 211)")
@@ -125,7 +125,7 @@ def test_add_property_from_feature_properties_panel(
125
125
  ):
126
126
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
127
127
  page.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
128
- page.get_by_role("button", name="Add a new property").click()
128
+ page.get_by_role("button", name="Add a new field").click()
129
129
  page.locator('input[name="prompt"]').fill("newprop")
130
130
  page.get_by_role("button", name="OK").click()
131
131
  expect(page.locator(".panel.right").get_by_text("newprop")).to_be_visible()
@@ -87,7 +87,7 @@ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstr
87
87
 
88
88
  def test_should_open_an_edit_toolbar_on_click(live_server, openmap, page, bootstrap):
89
89
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
90
- page.locator("path").click()
90
+ page.locator("path").click(button="right")
91
91
  expect(page.get_by_role("button", name="Toggle edit mode")).to_be_visible()
92
92
  expect(page.get_by_role("button", name="Delete this feature")).to_be_visible()
93
93
 
@@ -97,7 +97,7 @@ def test_can_remove_stroke(live_server, openmap, page, bootstrap):
97
97
  expect(page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")).to_have_count(
98
98
  1
99
99
  )
100
- page.locator("path").click()
100
+ page.locator("path").click(button="right")
101
101
  page.get_by_role("button", name="Toggle edit mode").click()
102
102
  page.get_by_text("Shape properties").click()
103
103
  page.locator(".umap-field-stroke .define").first.click()
@@ -38,7 +38,7 @@ DATALAYER_DATA = {
38
38
  "_umap_options": {
39
39
  "color": "OliveDrab",
40
40
  },
41
- "name": "test",
41
+ "name": "test one",
42
42
  "description": "Some description",
43
43
  },
44
44
  "id": "QwNjg",
@@ -54,7 +54,7 @@ DATALAYER_DATA = {
54
54
  "fill": False,
55
55
  "opacity": 0.6,
56
56
  },
57
- "name": "test",
57
+ "name": "test two",
58
58
  },
59
59
  "id": "YwMTM",
60
60
  "geometry": {
@@ -104,6 +104,16 @@ def test_umap_export(map, live_server, bootstrap, page):
104
104
  "browsable": True,
105
105
  "displayOnLoad": True,
106
106
  "name": "test datalayer",
107
+ "fields": [
108
+ {
109
+ "key": "name",
110
+ "type": "String",
111
+ },
112
+ {
113
+ "key": "description",
114
+ "type": "Text",
115
+ },
116
+ ],
107
117
  },
108
118
  "features": [
109
119
  {
@@ -133,7 +143,7 @@ def test_umap_export(map, live_server, bootstrap, page):
133
143
  "id": "QwNjg",
134
144
  "properties": {
135
145
  "_umap_options": {"color": "OliveDrab"},
136
- "name": "test",
146
+ "name": "test one",
137
147
  "description": "Some description",
138
148
  },
139
149
  "type": "Feature",
@@ -154,7 +164,7 @@ def test_umap_export(map, live_server, bootstrap, page):
154
164
  "id": "YwMTM",
155
165
  "properties": {
156
166
  "_umap_options": {"fill": False, "opacity": 0.6},
157
- "name": "test",
167
+ "name": "test two",
158
168
  },
159
169
  "type": "Feature",
160
170
  },
@@ -200,8 +210,8 @@ def test_csv_export(map, live_server, bootstrap, page):
200
210
  path.read_text()
201
211
  == """name,Latitude,Longitude,description
202
212
  name poly,53.0072070131872,12.182431646910137,
203
- test,52.57635,-0.274658,Some description
204
- test,53.725145179688646,2.9700064980570517,"""
213
+ test one,52.57635,-0.274658,Some description
214
+ test two,53.725145179688646,2.9700064980570517,"""
205
215
  )
206
216
 
207
217
 
@@ -218,7 +228,7 @@ def test_gpx_export(map, live_server, bootstrap, page):
218
228
  download.save_as(path)
219
229
  assert (
220
230
  path.read_text()
221
- == """<?xml version="1.0" encoding="UTF-8"?><gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="@dwayneparton/geojson-to-gpx"><wpt lat="52.57635" lon="-0.274658"><name>test</name><desc>Some description</desc></wpt><trk><name>test</name><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>"""
231
+ == """<?xml version="1.0" encoding="UTF-8"?><gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="@dwayneparton/geojson-to-gpx"><wpt lat="52.57635" lon="-0.274658"><name>test one</name><desc>Some description</desc></wpt><trk><name>test two</name><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>"""
222
232
  )
223
233
 
224
234
 
@@ -234,7 +244,7 @@ def test_kml_export(map, live_server, bootstrap, page):
234
244
  download.save_as(path)
235
245
  assert (
236
246
  path.read_text()
237
- == """<kml xmlns="http://www.opengis.net/kml/2.2"><Document>\n<Placemark id="gyNzM">\n<name>name poly</name><ExtendedData></ExtendedData>\n <Polygon>\n<outerBoundaryIs>\n <LinearRing><coordinates>11.25,53.585984\n10.151367,52.975108\n12.689209,52.167194\n14.084473,53.199452\n12.634277,53.618579\n11.25,53.585984\n11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark>\n<Placemark id="QwNjg">\n<name>test</name><description>Some description</description><ExtendedData>\n <Data name="_umap_options"><value>{"color":"OliveDrab"}</value></Data></ExtendedData>\n <Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark>\n<Placemark id="YwMTM">\n<name>test</name><ExtendedData>\n <Data name="_umap_options"><value>{"fill":false,"opacity":0.6}</value></Data></ExtendedData>\n <LineString><coordinates>-0.571289,54.476422\n0.439453,54.610255\n1.724854,53.448807\n4.163818,53.988395\n5.306396,53.533778\n6.591797,53.709714\n7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>"""
247
+ == """<kml xmlns="http://www.opengis.net/kml/2.2"><Document>\n<Placemark id="gyNzM">\n<name>name poly</name><ExtendedData></ExtendedData>\n <Polygon>\n<outerBoundaryIs>\n <LinearRing><coordinates>11.25,53.585984\n10.151367,52.975108\n12.689209,52.167194\n14.084473,53.199452\n12.634277,53.618579\n11.25,53.585984\n11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark>\n<Placemark id="QwNjg">\n<name>test one</name><description>Some description</description><ExtendedData>\n <Data name="_umap_options"><value>{"color":"OliveDrab"}</value></Data></ExtendedData>\n <Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark>\n<Placemark id="YwMTM">\n<name>test two</name><ExtendedData>\n <Data name="_umap_options"><value>{"fill":false,"opacity":0.6}</value></Data></ExtendedData>\n <LineString><coordinates>-0.571289,54.476422\n0.439453,54.610255\n1.724854,53.448807\n4.163818,53.988395\n5.306396,53.533778\n6.591797,53.709714\n7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>"""
238
248
  )
239
249
 
240
250
 
@@ -274,7 +284,58 @@ def test_geojson_export(map, live_server, bootstrap, page):
274
284
  "id": "QwNjg",
275
285
  "properties": {
276
286
  "_umap_options": {"color": "OliveDrab"},
277
- "name": "test",
287
+ "name": "test one",
288
+ "description": "Some description",
289
+ },
290
+ "type": "Feature",
291
+ },
292
+ {
293
+ "geometry": {
294
+ "coordinates": [
295
+ [-0.571289, 54.476422],
296
+ [0.439453, 54.610255],
297
+ [1.724854, 53.448807],
298
+ [4.163818, 53.988395],
299
+ [5.306396, 53.533778],
300
+ [6.591797, 53.709714],
301
+ [7.042236, 53.350551],
302
+ ],
303
+ "type": "LineString",
304
+ },
305
+ "id": "YwMTM",
306
+ "properties": {
307
+ "_umap_options": {"fill": False, "opacity": 0.6},
308
+ "name": "test two",
309
+ },
310
+ "type": "Feature",
311
+ },
312
+ ],
313
+ "type": "FeatureCollection",
314
+ }
315
+
316
+
317
+ def test_export_should_respect_filters(map, live_server, bootstrap, page):
318
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
319
+ page.locator("summary").filter(has_text="Filters").locator("i").click()
320
+ page.get_by_role("textbox", name="Search map features…").fill("test")
321
+ page.wait_for_timeout(300) # Wait for debounce
322
+ page.get_by_role("button", name="Share and download").click()
323
+ button = page.get_by_role("button", name="geojson")
324
+ expect(button).to_be_visible()
325
+ with page.expect_download() as download_info:
326
+ button.click()
327
+ download = download_info.value
328
+ assert download.suggested_filename == "test_map.geojson"
329
+ path = Path("/tmp/") / download.suggested_filename
330
+ download.save_as(path)
331
+ assert json.loads(path.read_text()) == {
332
+ "features": [
333
+ {
334
+ "geometry": {"coordinates": [-0.274658, 52.57635], "type": "Point"},
335
+ "id": "QwNjg",
336
+ "properties": {
337
+ "_umap_options": {"color": "OliveDrab"},
338
+ "name": "test one",
278
339
  "description": "Some description",
279
340
  },
280
341
  "type": "Feature",
@@ -295,10 +356,23 @@ def test_geojson_export(map, live_server, bootstrap, page):
295
356
  "id": "YwMTM",
296
357
  "properties": {
297
358
  "_umap_options": {"fill": False, "opacity": 0.6},
298
- "name": "test",
359
+ "name": "test two",
299
360
  },
300
361
  "type": "Feature",
301
362
  },
302
363
  ],
303
364
  "type": "FeatureCollection",
304
365
  }
366
+
367
+
368
+ def test_png_export(map, live_server, bootstrap, page):
369
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?share#6/53.406/6.757")
370
+ page.get_by_role("button", name="png").click()
371
+ with page.expect_download() as download_info:
372
+ page.get_by_role("button", name="Download", exact=True).click()
373
+ download = download_info.value
374
+ assert download.suggested_filename == "test_map.png"
375
+ path = Path("/tmp/") / download.suggested_filename
376
+ download.save_as(path)
377
+ # Something has been saved…
378
+ assert path.read_bytes()
@@ -557,6 +557,38 @@ def test_import_multipolyline(live_server, page, tilelayer):
557
557
  expect(paths).to_have_count(1)
558
558
 
559
559
 
560
+ def test_import_false_multipoint(live_server, page, tilelayer):
561
+ data = {
562
+ "type": "FeatureCollection",
563
+ "features": [
564
+ {
565
+ "type": "Feature",
566
+ "geometry": {
567
+ "coordinates": [[1.43661447777346, 43.59853484073553]],
568
+ "type": "MultiPoint",
569
+ },
570
+ "properties": {"name": "foo bar"},
571
+ }
572
+ ],
573
+ }
574
+ page.goto(f"{live_server.url}/map/new/")
575
+ page.get_by_title("Open browser").click()
576
+ layers = page.locator(".umap-browser .datalayer")
577
+ markers = page.locator(".leaflet-marker-icon")
578
+ expect(markers).to_have_count(0)
579
+ expect(layers).to_have_count(0)
580
+ button = page.get_by_title("Import data")
581
+ expect(button).to_be_visible()
582
+ button.click()
583
+ textarea = page.locator(".umap-import textarea")
584
+ textarea.fill(json.dumps(data))
585
+ page.locator('select[name="format"]').select_option("geojson")
586
+ page.get_by_role("button", name="Import data", exact=True).click()
587
+ # A layer has been created
588
+ expect(layers).to_have_count(1)
589
+ expect(markers).to_have_count(1)
590
+
591
+
560
592
  def test_should_not_import_empty_coordinates(live_server, page, tilelayer):
561
593
  data = {
562
594
  "type": "FeatureCollection",
@@ -670,6 +702,114 @@ def test_import_csv_with_commas_in_latlon(tilelayer, live_server, page, settings
670
702
  }
671
703
 
672
704
 
705
+ def test_import_csv_with_wkt_geom(tilelayer, live_server, page, settings):
706
+ settings.UMAP_ALLOW_ANONYMOUS = True
707
+ page.goto(f"{live_server.url}/map/new/")
708
+ page.get_by_title("Open browser").click()
709
+ layers = page.locator(".umap-browser .datalayer")
710
+ markers = page.locator(".leaflet-marker-icon")
711
+ paths = page.locator("path")
712
+ page.get_by_title("Import data").click()
713
+ textarea = page.locator(".umap-import textarea")
714
+ textarea.fill(
715
+ "geom;foobar\nPOLYGON ((-64.8 32.3, -65.5 18.3, -80.3 25.2, -64.8 32.3));mypoly\nPOINT(48.35 12.23);mypoint"
716
+ )
717
+ page.locator('select[name="format"]').select_option("csv")
718
+ page.get_by_role("button", name="Import data", exact=True).click()
719
+ expect(layers).to_have_count(1)
720
+ expect(markers).to_have_count(1)
721
+ expect(paths).to_have_count(1)
722
+ with page.expect_response(re.compile(r".*/datalayer/create/.*")):
723
+ page.get_by_role("button", name="Save").click()
724
+ datalayer = DataLayer.objects.last()
725
+ saved_data = json.loads(Path(datalayer.geojson.path).read_text())
726
+ assert saved_data["features"][0]["geometry"] == {
727
+ "coordinates": [
728
+ 48.35,
729
+ 12.23,
730
+ ],
731
+ "type": "Point",
732
+ }
733
+ assert saved_data["features"][1]["geometry"] == {
734
+ "coordinates": [
735
+ [
736
+ [
737
+ -64.8,
738
+ 32.3,
739
+ ],
740
+ [
741
+ -65.5,
742
+ 18.3,
743
+ ],
744
+ [
745
+ -80.3,
746
+ 25.2,
747
+ ],
748
+ [
749
+ -64.8,
750
+ 32.3,
751
+ ],
752
+ ],
753
+ ],
754
+ "type": "Polygon",
755
+ }
756
+
757
+
758
+ def test_import_csv_with_geojson_geom(tilelayer, live_server, page, settings):
759
+ settings.UMAP_ALLOW_ANONYMOUS = True
760
+ page.goto(f"{live_server.url}/map/new/")
761
+ page.get_by_title("Open browser").click()
762
+ layers = page.locator(".umap-browser .datalayer")
763
+ markers = page.locator(".leaflet-marker-icon")
764
+ paths = page.locator("path")
765
+ page.get_by_title("Import data").click()
766
+ textarea = page.locator(".umap-import textarea")
767
+ textarea.fill(
768
+ "geojson;foobar\n"
769
+ '{"coordinates": [[[-64.8,32.3],[-65.5,18.3],[-80.3,25.2],[-64.8,32.3]]],"type": "Polygon"};mypoly\n'
770
+ '{"coordinates": [48.35,12.23],"type": "Point"};mypoint'
771
+ )
772
+ page.locator('select[name="format"]').select_option("csv")
773
+ page.get_by_role("button", name="Import data", exact=True).click()
774
+ expect(layers).to_have_count(1)
775
+ expect(markers).to_have_count(1)
776
+ expect(paths).to_have_count(1)
777
+ with page.expect_response(re.compile(r".*/datalayer/create/.*")):
778
+ page.get_by_role("button", name="Save").click()
779
+ datalayer = DataLayer.objects.last()
780
+ saved_data = json.loads(Path(datalayer.geojson.path).read_text())
781
+ assert saved_data["features"][0]["geometry"] == {
782
+ "coordinates": [
783
+ 48.35,
784
+ 12.23,
785
+ ],
786
+ "type": "Point",
787
+ }
788
+ assert saved_data["features"][1]["geometry"] == {
789
+ "coordinates": [
790
+ [
791
+ [
792
+ -64.8,
793
+ 32.3,
794
+ ],
795
+ [
796
+ -65.5,
797
+ 18.3,
798
+ ],
799
+ [
800
+ -80.3,
801
+ 25.2,
802
+ ],
803
+ [
804
+ -64.8,
805
+ 32.3,
806
+ ],
807
+ ],
808
+ ],
809
+ "type": "Polygon",
810
+ }
811
+
812
+
673
813
  def test_create_remote_data(page, live_server, tilelayer):
674
814
  def handle(route):
675
815
  route.fulfill(
@@ -104,7 +104,7 @@ def test_map_preview_can_load_csv_in_querystring(page, live_server, tilelayer):
104
104
 
105
105
  def test_map_preview_can_change_styling_from_querystring(page, live_server, tilelayer):
106
106
  page.goto(f"{live_server.url}/map/?data={quote(json.dumps(GEOJSON))}&color=DarkRed")
107
- markers = page.locator(".leaflet-marker-icon .icon_container")
107
+ markers = page.locator(".leaflet-marker-icon .icon-container")
108
108
  expect(markers).to_have_count(1)
109
109
  expect(markers).to_have_css("background-color", "rgb(139, 0, 0)")
110
110