umap-project 2.6.2__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 (211) 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 +55 -2
  35. umap/static/umap/js/modules/caption.js +16 -5
  36. umap/static/umap/js/modules/data/features.js +183 -8
  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 +76 -55
  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/am_ET.js +2 -2
  62. umap/static/umap/locale/am_ET.json +2 -2
  63. umap/static/umap/locale/ar.js +2 -2
  64. umap/static/umap/locale/ar.json +2 -2
  65. umap/static/umap/locale/ast.js +2 -2
  66. umap/static/umap/locale/ast.json +2 -2
  67. umap/static/umap/locale/bg.js +2 -2
  68. umap/static/umap/locale/bg.json +2 -2
  69. umap/static/umap/locale/br.js +13 -4
  70. umap/static/umap/locale/br.json +13 -4
  71. umap/static/umap/locale/ca.js +30 -17
  72. umap/static/umap/locale/ca.json +30 -17
  73. umap/static/umap/locale/cs_CZ.js +89 -80
  74. umap/static/umap/locale/cs_CZ.json +89 -80
  75. umap/static/umap/locale/da.js +2 -2
  76. umap/static/umap/locale/da.json +2 -2
  77. umap/static/umap/locale/de.js +17 -8
  78. umap/static/umap/locale/de.json +17 -8
  79. umap/static/umap/locale/el.js +2 -2
  80. umap/static/umap/locale/el.json +2 -2
  81. umap/static/umap/locale/en.js +15 -4
  82. umap/static/umap/locale/en.json +15 -4
  83. umap/static/umap/locale/en_US.json +2 -2
  84. umap/static/umap/locale/es.js +338 -325
  85. umap/static/umap/locale/es.json +338 -325
  86. umap/static/umap/locale/et.js +2 -2
  87. umap/static/umap/locale/et.json +2 -2
  88. umap/static/umap/locale/eu.js +11 -4
  89. umap/static/umap/locale/eu.json +11 -4
  90. umap/static/umap/locale/fa_IR.js +11 -4
  91. umap/static/umap/locale/fa_IR.json +11 -4
  92. umap/static/umap/locale/fi.js +2 -2
  93. umap/static/umap/locale/fi.json +2 -2
  94. umap/static/umap/locale/fr.js +15 -4
  95. umap/static/umap/locale/fr.json +15 -4
  96. umap/static/umap/locale/gl.js +2 -2
  97. umap/static/umap/locale/gl.json +2 -2
  98. umap/static/umap/locale/he.js +2 -2
  99. umap/static/umap/locale/he.json +2 -2
  100. umap/static/umap/locale/hr.js +2 -2
  101. umap/static/umap/locale/hr.json +2 -2
  102. umap/static/umap/locale/hu.js +12 -5
  103. umap/static/umap/locale/hu.json +12 -5
  104. umap/static/umap/locale/id.js +2 -2
  105. umap/static/umap/locale/id.json +2 -2
  106. umap/static/umap/locale/is.js +2 -2
  107. umap/static/umap/locale/is.json +2 -2
  108. umap/static/umap/locale/it.js +2 -2
  109. umap/static/umap/locale/it.json +2 -2
  110. umap/static/umap/locale/ja.js +2 -2
  111. umap/static/umap/locale/ja.json +2 -2
  112. umap/static/umap/locale/ko.js +2 -2
  113. umap/static/umap/locale/ko.json +2 -2
  114. umap/static/umap/locale/lt.js +2 -2
  115. umap/static/umap/locale/lt.json +2 -2
  116. umap/static/umap/locale/ms.js +2 -2
  117. umap/static/umap/locale/ms.json +2 -2
  118. umap/static/umap/locale/nl.js +2 -2
  119. umap/static/umap/locale/nl.json +2 -2
  120. umap/static/umap/locale/no.js +2 -2
  121. umap/static/umap/locale/no.json +2 -2
  122. umap/static/umap/locale/pl.js +2 -2
  123. umap/static/umap/locale/pl.json +2 -2
  124. umap/static/umap/locale/pl_PL.json +2 -2
  125. umap/static/umap/locale/pt.js +19 -10
  126. umap/static/umap/locale/pt.json +19 -10
  127. umap/static/umap/locale/pt_BR.js +2 -2
  128. umap/static/umap/locale/pt_BR.json +2 -2
  129. umap/static/umap/locale/pt_PT.js +13 -4
  130. umap/static/umap/locale/pt_PT.json +13 -4
  131. umap/static/umap/locale/ro.js +2 -2
  132. umap/static/umap/locale/ro.json +2 -2
  133. umap/static/umap/locale/ru.js +2 -2
  134. umap/static/umap/locale/ru.json +2 -2
  135. umap/static/umap/locale/si.js +2 -2
  136. umap/static/umap/locale/si.json +2 -2
  137. umap/static/umap/locale/sk_SK.js +2 -2
  138. umap/static/umap/locale/sk_SK.json +2 -2
  139. umap/static/umap/locale/sl.js +2 -2
  140. umap/static/umap/locale/sl.json +2 -2
  141. umap/static/umap/locale/sr.js +2 -2
  142. umap/static/umap/locale/sr.json +2 -2
  143. umap/static/umap/locale/sv.js +2 -2
  144. umap/static/umap/locale/sv.json +2 -2
  145. umap/static/umap/locale/th_TH.js +2 -2
  146. umap/static/umap/locale/th_TH.json +2 -2
  147. umap/static/umap/locale/tr.js +2 -2
  148. umap/static/umap/locale/tr.json +2 -2
  149. umap/static/umap/locale/uk_UA.js +2 -2
  150. umap/static/umap/locale/uk_UA.json +2 -2
  151. umap/static/umap/locale/vi.js +2 -2
  152. umap/static/umap/locale/vi.json +2 -2
  153. umap/static/umap/locale/vi_VN.json +2 -2
  154. umap/static/umap/locale/zh.js +2 -2
  155. umap/static/umap/locale/zh.json +2 -2
  156. umap/static/umap/locale/zh_CN.json +2 -2
  157. umap/static/umap/locale/zh_TW.Big5.json +2 -2
  158. umap/static/umap/locale/zh_TW.js +13 -4
  159. umap/static/umap/locale/zh_TW.json +13 -4
  160. umap/static/umap/map.css +44 -29
  161. umap/static/umap/unittests/hlc.js +165 -0
  162. umap/static/umap/unittests/sync.js +321 -15
  163. umap/static/umap/unittests/utils.js +47 -0
  164. umap/static/umap/vars.css +2 -1
  165. umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
  166. umap/static/umap/vendors/dompurify/purify.es.js +15 -16
  167. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  168. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  169. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
  170. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
  171. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
  172. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
  173. umap/templates/umap/css.html +0 -2
  174. umap/templates/umap/dashboard_menu.html +4 -2
  175. umap/templates/umap/js.html +0 -5
  176. umap/templates/umap/map_detail.html +2 -2
  177. umap/tests/fixtures/test_upload_data.csv +2 -2
  178. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  179. umap/tests/integration/test_basics.py +1 -1
  180. umap/tests/integration/test_browser.py +69 -7
  181. umap/tests/integration/test_caption.py +3 -3
  182. umap/tests/integration/test_circles_layer.py +12 -0
  183. umap/tests/integration/test_cluster.py +53 -0
  184. umap/tests/integration/test_datalayer.py +2 -1
  185. umap/tests/integration/test_draw_polygon.py +17 -9
  186. umap/tests/integration/test_draw_polyline.py +84 -7
  187. umap/tests/integration/test_edit_datalayer.py +5 -8
  188. umap/tests/integration/test_edit_map.py +2 -2
  189. umap/tests/integration/test_edit_marker.py +1 -1
  190. umap/tests/integration/test_facets_browser.py +3 -3
  191. umap/tests/integration/test_import.py +1 -0
  192. umap/tests/integration/test_map.py +1 -0
  193. umap/tests/integration/test_owned_map.py +1 -1
  194. umap/tests/integration/test_view_marker.py +63 -0
  195. umap/tests/integration/test_view_polygon.py +12 -12
  196. umap/tests/integration/test_websocket_sync.py +65 -3
  197. umap/tests/test_clean_tilelayer.py +83 -0
  198. umap/tests/test_datalayer.py +24 -0
  199. umap/tests/test_map_views.py +20 -0
  200. umap/tests/test_purge_purgatory.py +25 -0
  201. umap/tests/test_websocket_server.py +22 -0
  202. umap/urls.py +5 -1
  203. umap/views.py +6 -3
  204. umap/websocket_server.py +130 -27
  205. {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/METADATA +18 -14
  206. {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/RECORD +209 -200
  207. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  208. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  209. {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/WHEEL +0 -0
  210. {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
1
1
  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
2
  <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
3
 
4
- <svg width="252" height="252" viewBox="0 0 66.674992 66.674992" version="1.1" id="svg2876" inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" sodipodi:docname="24-white.svg" inkscape:export-filename="../24-white.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
5
- <sodipodi:namedview id="namedview2878" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="2.6753614" inkscape:cx="107.64901" inkscape:cy="85.409023" inkscape:window-width="960" inkscape:window-height="1011" inkscape:window-x="20" inkscape:window-y="20" inkscape:window-maximized="0" inkscape:current-layer="layer1">
4
+ <svg width="252" height="252" viewBox="0 0 66.674992 66.674992" version="1.1" id="svg2876" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="24-white.svg" inkscape:export-filename="../24-white.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
5
+ <sodipodi:namedview id="namedview2878" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="5.3566812" inkscape:cx="50.030978" inkscape:cy="170.06799" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="layer1">
6
6
  <inkscape:grid type="xygrid" id="grid2997" empspacing="6" originx="0" originy="0" spacingy="0.2645833" spacingx="0.2645833" units="px" visible="true" />
7
7
  <inkscape:grid id="grid1" units="px" originx="0" originy="0" spacingx="9.5249989" spacingy="9.5249989" empcolor="#3f3fff" empopacity="0.25098039" color="#3f3fff" opacity="0.1254902" empspacing="1" dotted="false" gridanglex="30" gridanglez="30" visible="true" />
8
8
  </sodipodi:namedview>
@@ -74,5 +74,6 @@
74
74
  <g id="info" transform="matrix(0.33072916,0,0,0.33072916,-11.906249,-256.10415)" style="stroke-width:0.8">
75
75
  <path id="path3762" style="fill:#f2f2f2;fill-opacity:1;stroke:#999999;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1" d="m 107.99999,838.36217 a 8,8 0 0 0 -7.999998,8 8,8 0 0 0 7.999998,8 8,8 0 0 0 8,-8 8,8 0 0 0 -8,-8 z m 0,2.5 a 1.5,1.5 0 0 1 1.5,1.5 1.5,1.5 0 0 1 -1.5,1.5 1.5,1.5 0 0 1 -1.5,-1.5 1.5,1.5 0 0 1 1.5,-1.5 z m -1,4.5 h 2 v 6 h -2 z" />
76
76
  </g>
77
+ <path d="m 5.6444435,59.43432 v 1.034991 h 1.0541546 z m 1.1759257,1.612262 h -1.1759257 c -0.3247228,0 -0.5879626,-0.258453 -0.5879626,-0.577271 v -1.154545 h -2.3518514 v 5.195451 h 4.1157397 z m -4.1157397,-2.309089 h 3.0615854 l 1.6421175,1.612259 v 4.160465 c 0,0.318822 -0.2632403,0.577275 -0.5879632,0.577275 h -4.1157397 c -0.3247231,0 -0.5879631,-0.258453 -0.5879631,-0.577275 v -5.195451 c 0,-0.31882 0.26324,-0.577273 0.5879631,-0.577273 z m 1.7638882,3.92135 v -1.323625 h 0.5879632 v 1.323625 l 0.3800861,-0.373177 0.415753,0.408196 -1.0898206,1.070004 -1.0898208,-1.070004 0.4157526,-0.408196 z" fill-rule="evenodd" id="downloadfile" style="fill:#f2f2f2;fill-opacity:1;stroke-width:0.06614583;stroke:#999999;stroke-opacity:0.50980395;stroke-dasharray:none" />
77
78
  </g>
78
79
  </svg>
@@ -1,8 +1,8 @@
1
1
  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
2
  <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
3
 
4
- <svg width="252" height="252" viewBox="0 0 66.674999 66.674999" version="1.1" id="svg6237" inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" sodipodi:docname="24.svg" inkscape:export-filename="../24.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
5
- <sodipodi:namedview id="namedview6239" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="3.1596124" inkscape:cx="117.10297" inkscape:cy="106.50041" inkscape:window-width="960" inkscape:window-height="1011" inkscape:window-x="20" inkscape:window-y="20" inkscape:window-maximized="0" inkscape:current-layer="layer1">
4
+ <svg width="252" height="252" viewBox="0 0 66.674999 66.674999" version="1.1" id="svg6237" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="24.svg" inkscape:export-filename="../24.svg" inkscape:export-xdpi="7.52" inkscape:export-ydpi="7.52" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
5
+ <sodipodi:namedview id="namedview6239" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showgrid="true" showguides="true" inkscape:zoom="3.1596124" inkscape:cx="118.68544" inkscape:cy="166.47612" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="layer1">
6
6
  <inkscape:grid type="xygrid" id="grid6358" empspacing="6" originx="0" originy="0" spacingy="0.26458333" spacingx="0.26458333" units="px" visible="true" />
7
7
  <inkscape:grid id="grid1" units="px" originx="0" originy="0" spacingx="9.5249999" spacingy="9.5249999" empcolor="#3f3fff" empopacity="0.25098039" color="#ff0000" opacity="0.83529412" empspacing="0" dotted="false" gridanglex="30" gridanglez="30" visible="true" />
8
8
  </sodipodi:namedview>
@@ -98,5 +98,6 @@
98
98
  <g id="info" transform="matrix(0.33072916,0,0,0.33072916,-11.90625,-256.10415)" style="fill:#4d4d4d;fill-opacity:1;stroke-width:0.8">
99
99
  <path id="path3762" style="fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.8" d="m 108,838.36217 a 8,8 0 0 0 -8,8 8,8 0 0 0 8,8 8,8 0 0 0 8,-8 8,8 0 0 0 -8,-8 z m 0,2.5 a 1.5,1.5 0 0 1 1.5,1.5 1.5,1.5 0 0 1 -1.5,1.5 1.5,1.5 0 0 1 -1.5,-1.5 1.5,1.5 0 0 1 1.5,-1.5 z m -1,4.5 h 2 v 6 h -2 z" />
100
100
  </g>
101
+ <path d="m 5.6444435,59.43432 v 1.034991 h 1.0541546 z m 1.1759257,1.612262 h -1.1759257 c -0.3247228,0 -0.5879626,-0.258453 -0.5879626,-0.577271 v -1.154545 h -2.3518514 v 5.195451 h 4.1157397 z m -4.1157397,-2.309089 h 3.0615854 l 1.6421175,1.612259 v 4.160465 c 0,0.318822 -0.2632403,0.577275 -0.5879632,0.577275 h -4.1157397 c -0.3247231,0 -0.5879631,-0.258453 -0.5879631,-0.577275 v -5.195451 c 0,-0.31882 0.26324,-0.577273 0.5879631,-0.577273 z m 1.7638882,3.92135 v -1.323625 h 0.5879632 v 1.323625 l 0.3800861,-0.373177 0.415753,0.408196 -1.0898206,1.070004 -1.0898208,-1.070004 0.4157526,-0.408196 z" fill-rule="evenodd" id="downloadfile" style="fill:#464646;fill-opacity:1;stroke-width:0.192424" />
101
102
  </g>
102
103
  </svg>
@@ -249,6 +249,10 @@ export class BaseAjax extends BaseAutocomplete {
249
249
  }
250
250
  }
251
251
 
252
+ buildUrl(value) {
253
+ return Util.template(this.url, { q: encodeURIComponent(value) })
254
+ }
255
+
252
256
  async search() {
253
257
  let val = this.input.value
254
258
  if (val.length < this.options.minChar) {
@@ -258,7 +262,7 @@ export class BaseAjax extends BaseAutocomplete {
258
262
  if (val === this.cache) return
259
263
  this.cache = val
260
264
  val = val.toLowerCase()
261
- const url = Util.template(this.url, { q: encodeURIComponent(val) })
265
+ const url = this.buildUrl(val)
262
266
  this.handleResults(await this._search(url))
263
267
  }
264
268
 
@@ -309,7 +313,7 @@ export const SingleMixin = (Base) =>
309
313
  DomEvent.on(close, 'click', () => {
310
314
  this.selectedContainer.innerHTML = ''
311
315
  this.input.style.display = 'block'
312
- this.options.on_unselect(result)
316
+ this.options.on_unselect?.(result)
313
317
  })
314
318
  this.hide()
315
319
  }
@@ -338,7 +342,7 @@ export const MultipleMixin = (Base) =>
338
342
  })
339
343
  DomEvent.on(close, 'click', () => {
340
344
  this.selectedContainer.removeChild(result_el)
341
- this.options.on_unselect(result)
345
+ this.options.on_unselect?.(result)
342
346
  })
343
347
  this.hide()
344
348
  }
@@ -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) {
@@ -32,7 +35,11 @@ export default class Browser {
32
35
  'show-on-edit icon-delete',
33
36
  translate('Delete this feature')
34
37
  )
35
- const colorBox = DomUtil.create('i', 'icon icon-16 feature-color', row)
38
+ const colorBox = DomUtil.create(
39
+ 'i',
40
+ `icon icon-16 icon-${feature.getClassName()} feature-color`,
41
+ row
42
+ )
36
43
  const title = DomUtil.create('span', 'feature-title', row)
37
44
  const symbol = feature._getIconUrl
38
45
  ? Icon.formatUrl(feature._getIconUrl(), feature)
@@ -43,9 +50,11 @@ export default class Browser {
43
50
  if (symbol && symbol !== U.SCHEMA.iconUrl.default) {
44
51
  const icon = Icon.makeElement(symbol, colorBox)
45
52
  Icon.setContrast(icon, colorBox, symbol, bgcolor)
53
+ } else if (DomUtil.contrastedColor(colorBox, bgcolor)) {
54
+ colorBox.classList.add('icon-white')
46
55
  }
47
56
  const viewFeature = (e) => {
48
- feature.zoomTo({ ...e, callback: feature.view })
57
+ feature.zoomTo({ ...e, callback: () => feature.view() })
49
58
  }
50
59
  DomEvent.on(zoom_to, 'click', viewFeature)
51
60
  DomEvent.on(title, 'click', viewFeature)
@@ -92,6 +101,7 @@ export default class Browser {
92
101
  datalayer.eachFeature((feature) => this.addFeature(feature, container))
93
102
 
94
103
  const total = datalayer.count()
104
+ if (!total) return
95
105
  const current = container.querySelectorAll('li').length
96
106
  const count = total === current ? total : `${current}/${total}`
97
107
  const counter = DomUtil.create('span', 'datalayer-counter', headline)
@@ -158,6 +168,7 @@ export default class Browser {
158
168
  })
159
169
  this.filtersTitle = container.querySelector('summary')
160
170
  this.toggleBadge()
171
+ this.addMainToolbox(container)
161
172
  this.dataContainer = DomUtil.create('div', '', container)
162
173
 
163
174
  let fields = [
@@ -209,6 +220,48 @@ export default class Browser {
209
220
  }
210
221
  }
211
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
+
212
265
  static backButton(map) {
213
266
  const button = DomUtil.createButtonIcon(
214
267
  DomUtil.create('li', '', undefined),
@@ -19,7 +19,12 @@ export default class Caption {
19
19
  open() {
20
20
  const container = DomUtil.create('div', 'umap-caption')
21
21
  const hgroup = DomUtil.element({ tagName: 'hgroup', parent: container })
22
- DomUtil.createTitle(hgroup, this.map.options.name, 'icon-caption icon-block')
22
+ DomUtil.createTitle(
23
+ hgroup,
24
+ this.map.getDisplayName(),
25
+ 'icon-caption icon-block',
26
+ 'map-name'
27
+ )
23
28
  this.map.addAuthorLink('h4', hgroup)
24
29
  if (this.map.options.description) {
25
30
  const description = DomUtil.element({
@@ -35,15 +40,21 @@ export default class Caption {
35
40
  )
36
41
  const creditsContainer = DomUtil.create('div', 'credits-container', container)
37
42
  this.addCredits(creditsContainer)
38
- this.map.panel.open({ content: container })
43
+ this.map.panel.open({ content: container }).then(() => {
44
+ // Create the legend when the panel is actually on the DOM
45
+ this.map.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
46
+ })
39
47
  }
40
48
 
41
49
  addDataLayer(datalayer, container) {
42
50
  if (!datalayer.options.inCaption) return
43
- const p = DomUtil.create('p', 'datalayer-legend', container)
44
- const legend = DomUtil.create('span', '', p)
51
+ const p = DomUtil.create(
52
+ 'p',
53
+ `caption-item ${datalayer.cssId}`,
54
+ container
55
+ )
56
+ const legend = DomUtil.create('span', 'datalayer-legend', p)
45
57
  const headline = DomUtil.create('strong', '', p)
46
- datalayer.renderLegend(legend)
47
58
  if (datalayer.options.description) {
48
59
  DomUtil.element({
49
60
  tagName: 'span',
@@ -103,11 +103,11 @@ class Feature {
103
103
  }
104
104
 
105
105
  pushGeometry() {
106
- this.ui.setLatLngs(this.toLatLngs())
106
+ this._setLatLngs(this.toLatLngs())
107
107
  }
108
108
 
109
109
  pullGeometry(sync = true) {
110
- this.fromLatLngs(this.ui.getLatLngs())
110
+ this.fromLatLngs(this._getLatLngs())
111
111
  if (sync) {
112
112
  this.sync.update('geometry', this.geometry)
113
113
  }
@@ -439,7 +439,7 @@ class Feature {
439
439
 
440
440
  zoomTo({ easing, latlng, callback } = {}) {
441
441
  if (easing === undefined) easing = this.map.getOption('easing')
442
- if (callback) this.map.once('moveend', callback.call(this))
442
+ if (callback) this.map.once('moveend', callback.bind(this))
443
443
  if (easing) {
444
444
  this.map.flyTo(this.center, this.getBestZoom())
445
445
  } else {
@@ -596,6 +596,55 @@ class Feature {
596
596
  }
597
597
  }
598
598
  }
599
+
600
+ getContextMenuItems(event) {
601
+ const permalink = this.getPermalink()
602
+ let items = []
603
+ if (permalink) {
604
+ items.push({
605
+ label: translate('Permalink'),
606
+ action: () => {
607
+ window.open(permalink)
608
+ },
609
+ })
610
+ }
611
+ items.push({
612
+ label: translate('Copy as GeoJSON'),
613
+ action: () => {
614
+ L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON()))
615
+ this.map.tooltip.open({ content: L._('✅ Copied!') })
616
+ },
617
+ })
618
+ if (this.map.editEnabled && !this.isReadOnly()) {
619
+ items = items.concat(this.getContextMenuEditItems(event))
620
+ }
621
+ return items
622
+ }
623
+
624
+ getContextMenuEditItems() {
625
+ let items = ['-']
626
+ if (this.map.editedFeature !== this) {
627
+ items.push({
628
+ label: `${translate('Edit this feature')} (⇧+Click)`,
629
+ action: () => this.edit(),
630
+ })
631
+ }
632
+ items = items.concat(
633
+ {
634
+ label: this.map.help.displayLabel('EDIT_FEATURE_LAYER'),
635
+ action: () => this.datalayer.edit(),
636
+ },
637
+ {
638
+ label: translate('Delete this feature'),
639
+ action: () => this.confirmDelete(),
640
+ },
641
+ {
642
+ label: translate('Clone this feature'),
643
+ action: () => this.clone(),
644
+ }
645
+ )
646
+ return items
647
+ }
599
648
  }
600
649
 
601
650
  export class Point extends Feature {
@@ -607,6 +656,14 @@ export class Point extends Feature {
607
656
  }
608
657
  }
609
658
 
659
+ _getLatLngs() {
660
+ return this.ui.getLatLng()
661
+ }
662
+
663
+ _setLatLngs(latlng) {
664
+ this.ui.setLatLng(latlng)
665
+ }
666
+
610
667
  toLatLngs() {
611
668
  return GeoJSON.coordsToLatLng(this.coordinates)
612
669
  }
@@ -655,6 +712,7 @@ export class Point extends Feature {
655
712
  builder.restoreField('ui._latlng.lat')
656
713
  builder.restoreField('ui._latlng.lng')
657
714
  }
715
+ this.pullGeometry()
658
716
  this.zoomTo({ easing: false })
659
717
  },
660
718
  })
@@ -663,9 +721,9 @@ export class Point extends Feature {
663
721
  }
664
722
 
665
723
  zoomTo(event) {
666
- if (this.datalayer.isClustered() && !this._icon) {
724
+ if (this.datalayer.isClustered() && !this.ui._icon) {
667
725
  // callback is mandatory for zoomToShowLayer
668
- this.datalayer.layer.zoomToShowLayer(this, event.callback || (() => {}))
726
+ this.datalayer.layer.zoomToShowLayer(this.ui, event.callback || (() => {}))
669
727
  } else {
670
728
  super.zoomTo(event)
671
729
  }
@@ -677,6 +735,14 @@ class Path extends Feature {
677
735
  return !this.isEmpty()
678
736
  }
679
737
 
738
+ _getLatLngs() {
739
+ return this.ui.getLatLngs()
740
+ }
741
+
742
+ _setLatLngs(latlngs) {
743
+ this.ui.setLatLngs(latlngs)
744
+ }
745
+
680
746
  connectToDataLayer(datalayer) {
681
747
  super.connectToDataLayer(datalayer)
682
748
  // We keep markers on their own layer on top of the paths.
@@ -767,6 +833,62 @@ class Path extends Feature {
767
833
  }
768
834
  if (callback) callback.call(this)
769
835
  }
836
+
837
+ getContextMenuItems(event) {
838
+ const items = super.getContextMenuItems(event)
839
+ items.push({
840
+ label: translate('Display measure'),
841
+ action: () => Alert.info(this.ui.getMeasure()),
842
+ })
843
+ if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
844
+ items.push(...this.getContextMenuMultiItems(event))
845
+ }
846
+ return items
847
+ }
848
+
849
+ getContextMenuMultiItems(event) {
850
+ const items = [
851
+ '-',
852
+ {
853
+ label: translate('Remove shape from the multi'),
854
+ action: () => {
855
+ this.ui.enableEdit().deleteShapeAt(event.latlng)
856
+ },
857
+ },
858
+ ]
859
+ const shape = this.ui.shapeAt(event.latlng)
860
+ if (this.ui._latlngs.indexOf(shape) > 0) {
861
+ items.push({
862
+ label: translate('Make main shape'),
863
+ action: () => {
864
+ this.ui.enableEdit().deleteShape(shape)
865
+ this.ui.editor.prependShape(shape)
866
+ },
867
+ })
868
+ }
869
+ return items
870
+ }
871
+
872
+ getContextMenuEditItems(event) {
873
+ const items = super.getContextMenuEditItems(event)
874
+ if (this.map?.editedFeature !== this && this.isSameClass(this.map.editedFeature)) {
875
+ items.push({
876
+ label: translate('Transfer shape to edited feature'),
877
+ action: () => {
878
+ this.transferShape(event.latlng, this.map.editedFeature)
879
+ },
880
+ })
881
+ }
882
+ if (this.isMulti()) {
883
+ items.push({
884
+ label: translate('Extract shape to separate feature'),
885
+ action: () => {
886
+ this.ui.isolateShape(event.latlng)
887
+ },
888
+ })
889
+ }
890
+ return items
891
+ }
770
892
  }
771
893
 
772
894
  export class LineString extends Path {
@@ -866,20 +988,56 @@ export class LineString extends Path {
866
988
 
867
989
  mergeShapes() {
868
990
  if (!this.isMulti()) return
869
- const latlngs = this.getLatLngs()
991
+ const latlngs = this.ui.getLatLngs()
870
992
  if (!latlngs.length) return
871
993
  while (latlngs.length > 1) {
872
994
  latlngs.splice(0, 2, this._mergeShapes(latlngs[1], latlngs[0]))
873
995
  }
874
996
  this.ui.setLatLngs(latlngs[0])
875
- if (!this.editEnabled()) this.edit()
876
- this.editor.reset()
997
+ this.pullGeometry()
998
+ if (!this.ui.editEnabled()) this.edit()
999
+ this.ui.editor.reset()
877
1000
  this.isDirty = true
878
1001
  }
879
1002
 
880
1003
  isMulti() {
881
1004
  return !LineUtil.isFlat(this.coordinates) && this.coordinates.length > 1
882
1005
  }
1006
+
1007
+ getContextMenuEditItems(event) {
1008
+ const items = super.getContextMenuEditItems(event)
1009
+ const vertexClicked = event.vertex
1010
+ if (!this.isMulti()) {
1011
+ items.push({
1012
+ label: translate('Transform to polygon'),
1013
+ action: () => this.toPolygon(),
1014
+ })
1015
+ }
1016
+ if (vertexClicked) {
1017
+ const index = event.vertex.getIndex()
1018
+ if (index !== 0 && index !== event.vertex.getLastIndex()) {
1019
+ items.push({
1020
+ label: translate('Split line'),
1021
+ action: () => event.vertex.split(),
1022
+ })
1023
+ } else if (index === 0 || index === event.vertex.getLastIndex()) {
1024
+ items.push({
1025
+ label: this.map.help.displayLabel('CONTINUE_LINE'),
1026
+ action: () => event.vertex.continue(),
1027
+ })
1028
+ }
1029
+ }
1030
+ return items
1031
+ }
1032
+
1033
+ getContextMenuMultiItems(event) {
1034
+ const items = super.getContextMenuMultiItems(event)
1035
+ items.push({
1036
+ label: translate('Merge lines'),
1037
+ action: () => this.mergeShapes(),
1038
+ })
1039
+ return items
1040
+ }
883
1041
  }
884
1042
 
885
1043
  export class Polygon extends Path {
@@ -990,4 +1148,21 @@ export class Polygon extends Path {
990
1148
  items.push(U.CreateHoleAction)
991
1149
  return items
992
1150
  }
1151
+
1152
+ getContextMenuEditItems(event) {
1153
+ const items = super.getContextMenuEditItems(event)
1154
+ const shape = this.ui.shapeAt(event.latlng)
1155
+ // No multi and no holes.
1156
+ if (shape && !this.isMulti() && (LineUtil.isFlat(shape) || shape.length === 1)) {
1157
+ items.push({
1158
+ label: translate('Transform to lines'),
1159
+ action: () => this.toLineString(),
1160
+ })
1161
+ }
1162
+ items.push({
1163
+ label: translate('Start a hole here'),
1164
+ action: () => this.ui.startHole(event),
1165
+ })
1166
+ return items
1167
+ }
993
1168
  }