umap-project 3.3.6__py3-none-any.whl → 3.4.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.
Files changed (239) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +4 -1
  3. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  5. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/da/LC_MESSAGES/django.po +43 -33
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +35 -29
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +35 -29
  11. umap/locale/en/LC_MESSAGES/django.po +47 -41
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +43 -33
  14. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/et/LC_MESSAGES/django.po +58 -54
  16. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  18. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  20. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  22. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  24. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  26. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/is/LC_MESSAGES/django.po +43 -33
  28. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/it/LC_MESSAGES/django.po +43 -33
  30. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  32. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/pl/LC_MESSAGES/django.po +114 -103
  34. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  36. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  38. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  40. umap/management/commands/switch_user.py +2 -2
  41. umap/migrations/0018_datalayer_uuid.py +1 -1
  42. umap/models.py +7 -3
  43. umap/settings/local.py.sample +1 -1
  44. umap/static/umap/base.css +89 -32
  45. umap/static/umap/content.css +129 -33
  46. umap/static/umap/css/bar.css +82 -20
  47. umap/static/umap/css/browser.css +163 -0
  48. umap/static/umap/css/contextmenu.css +15 -0
  49. umap/static/umap/css/dialog.css +36 -16
  50. umap/static/umap/css/form.css +123 -33
  51. umap/static/umap/css/icon.css +46 -3
  52. umap/static/umap/css/panel.css +7 -3
  53. umap/static/umap/css/popup.css +34 -8
  54. umap/static/umap/css/tooltip.css +8 -4
  55. umap/static/umap/img/16-white.svg +26 -8
  56. umap/static/umap/img/16.svg +1 -1
  57. umap/static/umap/img/source/16-white.svg +36 -18
  58. umap/static/umap/img/source/16.svg +1 -1
  59. umap/static/umap/js/components/alerts/alert.css +69 -31
  60. umap/static/umap/js/components/alerts/alert.js +20 -2
  61. umap/static/umap/js/components/base.js +1 -1
  62. umap/static/umap/js/modules/browser.js +69 -61
  63. umap/static/umap/js/modules/caption.js +10 -7
  64. umap/static/umap/js/modules/data/features.js +85 -60
  65. umap/static/umap/js/modules/data/fields.js +446 -0
  66. umap/static/umap/js/modules/data/layer.js +78 -184
  67. umap/static/umap/js/modules/domutils.js +109 -0
  68. umap/static/umap/js/modules/filters.js +780 -0
  69. umap/static/umap/js/modules/form/builder.js +8 -5
  70. umap/static/umap/js/modules/form/fields.js +111 -221
  71. umap/static/umap/js/modules/formatter.js +24 -1
  72. umap/static/umap/js/modules/help.js +4 -3
  73. umap/static/umap/js/modules/i18n.js +1 -1
  74. umap/static/umap/js/modules/importer.js +1 -1
  75. umap/static/umap/js/modules/importers/opendata.js +15 -0
  76. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  77. umap/static/umap/js/modules/managers.js +2 -2
  78. umap/static/umap/js/modules/permissions.js +39 -31
  79. umap/static/umap/js/modules/rendering/controls.js +11 -9
  80. umap/static/umap/js/modules/rendering/icon.js +3 -8
  81. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  82. umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
  83. umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
  84. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  85. umap/static/umap/js/modules/rendering/template.js +50 -23
  86. umap/static/umap/js/modules/rendering/ui.js +29 -23
  87. umap/static/umap/js/modules/rules.js +38 -44
  88. umap/static/umap/js/modules/schema.js +3 -6
  89. umap/static/umap/js/modules/share.js +5 -4
  90. umap/static/umap/js/modules/tableeditor.js +50 -38
  91. umap/static/umap/js/modules/templates.js +2 -3
  92. umap/static/umap/js/modules/ui/bar.js +55 -23
  93. umap/static/umap/js/modules/ui/dialog.js +38 -27
  94. umap/static/umap/js/modules/ui/panel.js +23 -8
  95. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  96. umap/static/umap/js/modules/umap.js +151 -56
  97. umap/static/umap/js/modules/utils.js +24 -2
  98. umap/static/umap/js/umap.core.js +1 -110
  99. umap/static/umap/locale/am_ET.js +52 -17
  100. umap/static/umap/locale/am_ET.json +52 -17
  101. umap/static/umap/locale/ar.js +52 -17
  102. umap/static/umap/locale/ar.json +52 -17
  103. umap/static/umap/locale/ast.js +52 -17
  104. umap/static/umap/locale/ast.json +52 -17
  105. umap/static/umap/locale/bg.js +52 -17
  106. umap/static/umap/locale/bg.json +52 -17
  107. umap/static/umap/locale/br.js +48 -22
  108. umap/static/umap/locale/br.json +48 -22
  109. umap/static/umap/locale/ca.js +52 -17
  110. umap/static/umap/locale/ca.json +52 -17
  111. umap/static/umap/locale/cs_CZ.js +52 -17
  112. umap/static/umap/locale/cs_CZ.json +52 -17
  113. umap/static/umap/locale/da.js +54 -17
  114. umap/static/umap/locale/da.json +54 -17
  115. umap/static/umap/locale/de.js +51 -16
  116. umap/static/umap/locale/de.json +51 -16
  117. umap/static/umap/locale/el.js +52 -17
  118. umap/static/umap/locale/el.json +52 -17
  119. umap/static/umap/locale/en.js +53 -16
  120. umap/static/umap/locale/en.json +53 -16
  121. umap/static/umap/locale/en_US.json +52 -17
  122. umap/static/umap/locale/es.js +54 -17
  123. umap/static/umap/locale/es.json +54 -17
  124. umap/static/umap/locale/et.js +91 -56
  125. umap/static/umap/locale/et.json +91 -56
  126. umap/static/umap/locale/eu.js +84 -49
  127. umap/static/umap/locale/eu.json +84 -49
  128. umap/static/umap/locale/fa_IR.js +52 -17
  129. umap/static/umap/locale/fa_IR.json +52 -17
  130. umap/static/umap/locale/fi.js +52 -17
  131. umap/static/umap/locale/fi.json +52 -17
  132. umap/static/umap/locale/fr.js +53 -16
  133. umap/static/umap/locale/fr.json +53 -16
  134. umap/static/umap/locale/gl.js +52 -17
  135. umap/static/umap/locale/gl.json +52 -17
  136. umap/static/umap/locale/he.js +52 -17
  137. umap/static/umap/locale/he.json +52 -17
  138. umap/static/umap/locale/hr.js +52 -17
  139. umap/static/umap/locale/hr.json +52 -17
  140. umap/static/umap/locale/hu.js +59 -24
  141. umap/static/umap/locale/hu.json +59 -24
  142. umap/static/umap/locale/id.js +52 -17
  143. umap/static/umap/locale/id.json +52 -17
  144. umap/static/umap/locale/is.js +52 -17
  145. umap/static/umap/locale/is.json +52 -17
  146. umap/static/umap/locale/it.js +52 -17
  147. umap/static/umap/locale/it.json +52 -17
  148. umap/static/umap/locale/ja.js +52 -17
  149. umap/static/umap/locale/ja.json +52 -17
  150. umap/static/umap/locale/ko.js +52 -17
  151. umap/static/umap/locale/ko.json +52 -17
  152. umap/static/umap/locale/lt.js +52 -17
  153. umap/static/umap/locale/lt.json +52 -17
  154. umap/static/umap/locale/ms.js +52 -17
  155. umap/static/umap/locale/ms.json +52 -17
  156. umap/static/umap/locale/nl.js +52 -17
  157. umap/static/umap/locale/nl.json +52 -17
  158. umap/static/umap/locale/no.js +52 -17
  159. umap/static/umap/locale/no.json +52 -17
  160. umap/static/umap/locale/pl.js +53 -17
  161. umap/static/umap/locale/pl.json +53 -17
  162. umap/static/umap/locale/pl_PL.json +52 -17
  163. umap/static/umap/locale/pt.js +52 -17
  164. umap/static/umap/locale/pt.json +52 -17
  165. umap/static/umap/locale/pt_BR.js +52 -17
  166. umap/static/umap/locale/pt_BR.json +52 -17
  167. umap/static/umap/locale/pt_PT.js +52 -17
  168. umap/static/umap/locale/pt_PT.json +52 -17
  169. umap/static/umap/locale/ro.js +52 -17
  170. umap/static/umap/locale/ro.json +52 -17
  171. umap/static/umap/locale/ru.js +52 -17
  172. umap/static/umap/locale/ru.json +52 -17
  173. umap/static/umap/locale/si.js +1 -1
  174. umap/static/umap/locale/si.json +1 -1
  175. umap/static/umap/locale/sk_SK.js +52 -17
  176. umap/static/umap/locale/sk_SK.json +52 -17
  177. umap/static/umap/locale/sl.js +52 -17
  178. umap/static/umap/locale/sl.json +52 -17
  179. umap/static/umap/locale/sr.js +52 -17
  180. umap/static/umap/locale/sr.json +52 -17
  181. umap/static/umap/locale/sv.js +52 -17
  182. umap/static/umap/locale/sv.json +52 -17
  183. umap/static/umap/locale/th_TH.js +52 -17
  184. umap/static/umap/locale/th_TH.json +52 -17
  185. umap/static/umap/locale/tr.js +52 -17
  186. umap/static/umap/locale/tr.json +52 -17
  187. umap/static/umap/locale/uk_UA.js +52 -17
  188. umap/static/umap/locale/uk_UA.json +52 -17
  189. umap/static/umap/locale/vi.js +52 -17
  190. umap/static/umap/locale/vi.json +52 -17
  191. umap/static/umap/locale/vi_VN.json +52 -17
  192. umap/static/umap/locale/zh.js +52 -17
  193. umap/static/umap/locale/zh.json +52 -17
  194. umap/static/umap/locale/zh_CN.json +52 -17
  195. umap/static/umap/locale/zh_TW.Big5.json +52 -17
  196. umap/static/umap/locale/zh_TW.js +52 -16
  197. umap/static/umap/locale/zh_TW.json +52 -16
  198. umap/static/umap/map.css +63 -226
  199. umap/static/umap/unittests/utils.js +18 -0
  200. umap/static/umap/vars.css +23 -5
  201. umap/templates/umap/components/alerts/alert.html +32 -29
  202. umap/templates/umap/css.html +2 -1
  203. umap/templates/umap/login_popup_end.html +18 -9
  204. umap/templates/umap/user_map_table.html +7 -2
  205. umap/tests/integration/conftest.py +10 -6
  206. umap/tests/integration/test_anonymous_owned_map.py +90 -37
  207. umap/tests/integration/test_basics.py +25 -1
  208. umap/tests/integration/test_browser.py +37 -0
  209. umap/tests/integration/test_conditional_rules.py +107 -52
  210. umap/tests/integration/test_draw_polygon.py +6 -0
  211. umap/tests/integration/test_draw_polyline.py +11 -0
  212. umap/tests/integration/test_edit_marker.py +1 -1
  213. umap/tests/integration/test_export_map.py +19 -0
  214. umap/tests/integration/test_fields.py +541 -0
  215. umap/tests/integration/test_filters.py +616 -0
  216. umap/tests/integration/test_iframe.py +1 -1
  217. umap/tests/integration/test_import.py +38 -42
  218. umap/tests/integration/test_map_preview.py +1 -1
  219. umap/tests/integration/test_picto.py +1 -1
  220. umap/tests/integration/test_popup.py +31 -0
  221. umap/tests/integration/test_remote_data.py +60 -4
  222. umap/tests/integration/test_save.py +1 -1
  223. umap/tests/integration/test_share.py +4 -4
  224. umap/tests/integration/test_tableeditor.py +31 -7
  225. umap/tests/integration/test_websocket_sync.py +71 -20
  226. umap/tests/test_dashboard.py +11 -1
  227. umap/tests/test_statics.py +2 -2
  228. umap/tests/test_utils.py +19 -2
  229. umap/tests/test_views.py +1 -1
  230. umap/urls.py +1 -0
  231. umap/utils.py +8 -1
  232. umap/views.py +5 -0
  233. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
  234. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
  235. umap/static/umap/js/modules/facets.js +0 -164
  236. umap/tests/integration/test_facets_browser.py +0 -279
  237. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
  238. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
  239. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ export class DataLayerManager extends Object {
7
7
  active() {
8
8
  return Object.values(this)
9
9
  .filter((datalayer) => !datalayer.isDeleted)
10
- .sort((a, b) => a.rank > b.rank)
10
+ .sort((a, b) => a.rank - b.rank)
11
11
  }
12
12
  reverse() {
13
13
  return this.active().reverse()
@@ -59,7 +59,7 @@ export class FeatureManager extends Map {
59
59
  if (this.has(feature.id)) {
60
60
  console.error('Duplicate id', feature, this.get(feature.id))
61
61
  feature.id = Utils.generateId()
62
- feature.datalayer._found_duplicate_id = true
62
+ feature.datalayer._migrated = true
63
63
  }
64
64
  this.set(feature.id, feature)
65
65
  }
@@ -3,6 +3,7 @@ import { uMapAlert as Alert } from '../components/alerts/alert.js'
3
3
  import { MutatingForm } from './form/builder.js'
4
4
  import { translate } from './i18n.js'
5
5
  import * as Utils from './utils.js'
6
+ import * as DOMUtils from './domutils.js'
6
7
 
7
8
  // Dedicated object so we can deal with a separate dirty status, and thus
8
9
  // call the endpoint only when needed, saving one call at each save.
@@ -42,12 +43,42 @@ export class MapPermissions {
42
43
  }
43
44
 
44
45
  isAnonymousMap() {
45
- return !this._umap.properties.permissions.owner
46
+ return !this.properties.owner
47
+ }
48
+
49
+ isDraft() {
50
+ return this.properties.share_status === 0
51
+ }
52
+
53
+ userIsAuth() {
54
+ return Boolean(this._umap.properties.user?.id)
46
55
  }
47
56
 
48
57
  _editAnonymous(container) {
49
- const fields = []
50
58
  if (this.isOwner()) {
59
+ // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
60
+ // Note: real check is made on the back office anyway.
61
+ const template = `
62
+ <div class="anonymous soft-round aplat">
63
+ <h4><i class="icon icon-16 icon-anonymous"></i> ${translate('Anonymous map')}</h4>
64
+ <div data-ref="copiableInput"></div>
65
+ <p data-ref="p" hidden><button type="button" data-ref="button">${translate('Attach the map to my account')}</button></p>
66
+ </div>
67
+ `
68
+ const [root, { button, copiableInput, p }] = Utils.loadTemplateWithRefs(template)
69
+ container.appendChild(root)
70
+ if (this.properties.anonymous_edit_url) {
71
+ DOMUtils.copiableInput(
72
+ copiableInput,
73
+ translate('Secret edit link:'),
74
+ this.properties.anonymous_edit_url
75
+ )
76
+ }
77
+ if (this.userIsAuth()) {
78
+ button.addEventListener('click', () => this.attach())
79
+ p.hidden = false
80
+ }
81
+ const fields = []
51
82
  fields.push([
52
83
  'properties.edit_status',
53
84
  {
@@ -67,31 +98,6 @@ export class MapPermissions {
67
98
  const builder = new MutatingForm(this, fields)
68
99
  const form = builder.build()
69
100
  container.appendChild(form)
70
-
71
- if (this.properties.anonymous_edit_url) {
72
- DomUtil.createCopiableInput(
73
- container,
74
- translate('Secret edit link:'),
75
- this.properties.anonymous_edit_url
76
- )
77
- }
78
-
79
- if (this._umap.properties.user?.id) {
80
- // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
81
- // Note: real check is made on the back office anyway.
82
- const advancedActions = DomUtil.createFieldset(
83
- container,
84
- translate('Advanced actions')
85
- )
86
- const advancedButtons = DomUtil.create('div', 'button-bar', advancedActions)
87
- DomUtil.createButton(
88
- 'button',
89
- advancedButtons,
90
- translate('Attach the map to my account'),
91
- this.attach,
92
- this
93
- )
94
- }
95
101
  }
96
102
  }
97
103
 
@@ -187,6 +193,8 @@ export class MapPermissions {
187
193
  const [data, response, error] = await this._umap.server.post(this.getAttachUrl())
188
194
  if (!error) {
189
195
  this.properties.owner = this._umap.properties.user
196
+ this._umap.properties.user.is_owner = true
197
+ this.render()
190
198
  Alert.success(translate('Map has been attached to your account'))
191
199
  this._umap.editPanel.close()
192
200
  }
@@ -238,6 +246,10 @@ export class MapPermissions {
238
246
  )
239
247
  }
240
248
 
249
+ pull() {
250
+ this.setProperties(this._umap.properties.permissions)
251
+ }
252
+
241
253
  getShareStatusDisplay() {
242
254
  if (this._umap.properties.share_statuses) {
243
255
  return Object.fromEntries(this._umap.properties.share_statuses)[
@@ -245,10 +257,6 @@ export class MapPermissions {
245
257
  ]
246
258
  }
247
259
  }
248
-
249
- isDraft() {
250
- return this.properties.share_status === 0
251
- }
252
260
  }
253
261
 
254
262
  export class DataLayerPermissions {
@@ -112,7 +112,9 @@ export const PermanentCreditsControl = Control.extend({
112
112
  const container = Utils.loadTemplate(
113
113
  `<div class="umap-permanent-credits-container text">${Utils.toHTML(map.options.permanentCredit)}</div>`
114
114
  )
115
- const background = map.options.permanentCreditBackground ? '#FFFFFFB0' : ''
115
+ const background = map._umap.getProperty('permanentCreditBackground')
116
+ ? '#FFFFFFB0'
117
+ : ''
116
118
  container.style.backgroundColor = background
117
119
  return container
118
120
  },
@@ -164,7 +166,7 @@ export const DataLayersControl = BaseButton.extend({
164
166
  },
165
167
 
166
168
  afterAdd: function (container) {
167
- Utils.toggleBadge(container, this._umap.browser?.hasFilters())
169
+ Utils.toggleBadge(container, this._umap.browser?.hasActiveFilters())
168
170
  },
169
171
 
170
172
  onClick: function () {
@@ -177,7 +179,7 @@ export const CaptionControl = BaseButton.extend({
177
179
  position: 'topleft',
178
180
  className: 'umap-control-caption',
179
181
  title: translate('About'),
180
- icon: 'icon-caption',
182
+ icon: 'icon-info',
181
183
  },
182
184
 
183
185
  onClick: function () {
@@ -242,13 +244,13 @@ export const SearchControl = BaseButton.extend({
242
244
  const [container, { input, resultsContainer }] =
243
245
  Utils.loadTemplateWithRefs(template)
244
246
  const id = Math.random()
247
+ this.search = new U.Search(
248
+ this._umap._leafletMap,
249
+ input,
250
+ this.layer,
251
+ this.photonOptions
252
+ )
245
253
  this._umap.panel.open({ content: container }).then(() => {
246
- this.search = new U.Search(
247
- this._umap._leafletMap,
248
- input,
249
- this.layer,
250
- this.photonOptions
251
- )
252
254
  this.search.on('ajax:send', () => {
253
255
  this._umap.fire('dataloading', { id: id })
254
256
  })
@@ -1,12 +1,7 @@
1
- import {
2
- DivIcon,
3
- DomEvent,
4
- DomUtil,
5
- Icon,
6
- Point,
7
- } from '../../../vendors/leaflet/leaflet-src.esm.js'
1
+ import { DivIcon, Icon, Point } from '../../../vendors/leaflet/leaflet-src.esm.js'
8
2
  import { SCHEMA } from '../schema.js'
9
3
  import * as Utils from '../utils.js'
4
+ import * as DOMUtils from '../domutils.js'
10
5
 
11
6
  export function getClass(name) {
12
7
  switch (name) {
@@ -299,7 +294,7 @@ export function setContrast(icon, parent, src, bgcolor) {
299
294
  */
300
295
  if (!icon) return
301
296
 
302
- if (DomUtil.contrastedColor(parent, bgcolor)) {
297
+ if (DOMUtils.contrastedColor(parent, bgcolor)) {
303
298
  // Decide whether to switch svg to white or not, but do it
304
299
  // only for internal SVG, as invert could do weird things
305
300
  if (src.endsWith('.svg') && src !== SCHEMA.iconUrl.default) {
@@ -53,7 +53,7 @@ export const LayerMixin = {
53
53
  },
54
54
 
55
55
  onZoomEnd() {
56
- if (!this.datalayer.autoLoaded) return
56
+ if (!this.datalayer.autoVisibility) return
57
57
  if (!this.datalayer.showAtZoom() && this.datalayer.isVisible()) {
58
58
  this.datalayer.hide()
59
59
  }
@@ -2,11 +2,12 @@ import colorbrewer from '../../../../vendors/colorbrewer/colorbrewer.js'
2
2
  import { DomUtil, FeatureGroup } from '../../../../vendors/leaflet/leaflet-src.esm.js'
3
3
  import { translate } from '../../i18n.js'
4
4
  import * as Utils from '../../utils.js'
5
+ import * as DOMUtils from '../../domutils.js'
5
6
  import { CircleMarker } from '../ui.js'
6
7
  import { LayerMixin } from './base.js'
7
8
 
8
9
  // Layer where each feature color is relative to the others,
9
- // so we need all features before behing able to set one
10
+ // so we need all features before being able to set one
10
11
  // feature layer
11
12
  const ClassifiedMixin = {
12
13
  initialize: function (datalayer) {
@@ -76,15 +77,21 @@ const ClassifiedMixin = {
76
77
 
77
78
  renderLegend: function (container) {
78
79
  if (!this.datalayer.isLoaded()) return
79
- const parent = DomUtil.create('ul', '', container)
80
+ const ul = Utils.loadTemplate('<ul></ul>')
80
81
  const items = this.getLegendItems()
81
82
  for (const [color, label] of items) {
82
- const li = DomUtil.create('li', '', parent)
83
- const colorEl = DomUtil.create('span', 'datalayer-color', li)
84
- colorEl.style.backgroundColor = color
85
- const labelEl = DomUtil.create('span', '', li)
86
- labelEl.textContent = label
83
+ const rgbColor = DOMUtils.hexToRGB(color)
84
+ const opacity = this.datalayer.getOption('fillOpacity')
85
+ const bgColor = `rgba(${rgbColor.join(',')}, ${opacity})`
86
+ const li = Utils.loadTemplate(`
87
+ <li>
88
+ <span class="datalayer-color" style="background-color: ${bgColor};"></span>
89
+ <span>${label}</span>
90
+ </li>
91
+ `)
92
+ ul.appendChild(li)
87
93
  }
94
+ container.appendChild(ul)
88
95
  },
89
96
 
90
97
  getColorSchemes: function (classes) {
@@ -198,7 +205,7 @@ export const Choropleth = FeatureGroup.extend({
198
205
  'properties.choropleth.property',
199
206
  {
200
207
  handler: 'Select',
201
- selectOptions: this.datalayer.fieldKeys,
208
+ selectOptions: this.datalayer.fields.keys(),
202
209
  label: translate('Choropleth property value'),
203
210
  },
204
211
  ],
@@ -307,7 +314,7 @@ export const Circles = FeatureGroup.extend({
307
314
  'properties.circles.property',
308
315
  {
309
316
  handler: 'Select',
310
- selectOptions: this.datalayer.fieldKeys,
317
+ selectOptions: this.datalayer.fields.keys(),
311
318
  label: translate('Property name to compute circles'),
312
319
  },
313
320
  ],
@@ -384,7 +391,7 @@ export const Categorized = FeatureGroup.extend({
384
391
 
385
392
  _getValue: function (feature) {
386
393
  const key =
387
- this.datalayer.properties.categorized.property || this.datalayer.fieldKeys[0]
394
+ this.datalayer.properties.categorized.property || this.datalayer.fields.keys()[0]
388
395
  return feature.properties[key]
389
396
  },
390
397
 
@@ -437,7 +444,7 @@ export const Categorized = FeatureGroup.extend({
437
444
  'properties.categorized.property',
438
445
  {
439
446
  handler: 'Select',
440
- selectOptions: this.datalayer.fieldKeys,
447
+ selectOptions: this.datalayer.fields.keys(),
441
448
  label: translate('Category property'),
442
449
  },
443
450
  ],
@@ -5,11 +5,11 @@ import {
5
5
  Marker,
6
6
  Rectangle,
7
7
  Polyline,
8
- DomUtil,
9
8
  latLngBounds,
10
9
  } from '../../../../vendors/leaflet/leaflet-src.esm.js'
11
10
  import { translate } from '../../i18n.js'
12
11
  import * as Utils from '../../utils.js'
12
+ import * as DOMUtils from '../../domutils.js'
13
13
  import { Cluster as ClusterIcon } from '../icon.js'
14
14
  import { LayerMixin } from './base.js'
15
15
 
@@ -21,7 +21,7 @@ const MarkerCluster = Marker.extend({
21
21
  const bgColor = this.options.icon.options.color
22
22
  const textColor = this.options.icon.options.textColor
23
23
  counter.style.color =
24
- textColor || DomUtil.TextColorFromBackgroundColor(counter, bgColor)
24
+ textColor || DOMUtils.textColorFromBackgroundColor(counter, bgColor)
25
25
  },
26
26
 
27
27
  computeCoverage() {
@@ -281,6 +281,8 @@ export const Cluster = FeatureGroup.extend({
281
281
  ],
282
282
 
283
283
  onEdit: function (field, builder) {
284
- if (field === 'properties.cluster.radius') this.redraw()
284
+ if (field === 'properties.cluster.radius' || field === 'properties.color') {
285
+ this.redraw()
286
+ }
285
287
  },
286
288
  })
@@ -68,27 +68,32 @@ export const Heat = L.HeatLayer.extend({
68
68
  return latLngBounds(this._latlngs)
69
69
  },
70
70
 
71
- getEditableProperties: () => [
72
- [
73
- 'properties.heat.radius',
74
- {
75
- handler: 'Range',
76
- min: 10,
77
- max: 100,
78
- step: 5,
79
- label: translate('Heatmap radius'),
80
- helpText: translate('Override heatmap radius (default 25)'),
81
- },
82
- ],
83
- [
84
- 'properties.heat.intensityProperty',
85
- {
86
- handler: 'BlurInput',
87
- placeholder: translate('Heatmap intensity property'),
88
- helpText: translate('Optional intensity property for heatmap'),
89
- },
90
- ],
91
- ],
71
+ getEditableProperties: function () {
72
+ return [
73
+ [
74
+ 'properties.heat.radius',
75
+ {
76
+ handler: 'Range',
77
+ min: 10,
78
+ max: 100,
79
+ step: 5,
80
+ label: translate('Heatmap radius'),
81
+ helpText: translate('Override heatmap radius (default 25)'),
82
+ },
83
+ ],
84
+ [
85
+ 'properties.heat.intensityProperty',
86
+ {
87
+ handler: 'Select',
88
+ selectOptions: [
89
+ ['', translate('Select field to compute intensity')],
90
+ ...this.datalayer.fields.keys(),
91
+ ],
92
+ helpText: translate('Optional intensity field to compute heatmap'),
93
+ },
94
+ ],
95
+ ]
96
+ },
92
97
 
93
98
  onEdit: function (field, builder) {
94
99
  if (field === 'properties.heat.intensityProperty') {
@@ -97,6 +102,7 @@ export const Heat = L.HeatLayer.extend({
97
102
  }
98
103
  if (field === 'properties.heat.radius') {
99
104
  this.options.radius = this.datalayer.properties.heat.radius
105
+ this.redraw()
100
106
  }
101
107
  this._updateOptions()
102
108
  },
@@ -6,6 +6,7 @@ import {
6
6
  import { getLocale, translate } from '../i18n.js'
7
7
  import { Request } from '../request.js'
8
8
  import * as Utils from '../utils.js'
9
+ import * as DOMUtils from '../domutils.js'
9
10
  import * as Icon from './icon.js'
10
11
 
11
12
  export default async function loadTemplate(name, feature, container) {
@@ -107,29 +108,20 @@ export const TitleMixin = (Base) =>
107
108
  }
108
109
 
109
110
  class Table extends TitleMixin(PopupTemplate) {
110
- getValue(feature, key) {
111
- // TODO, manage links (url, mailto, wikipedia...)
112
- const value = Utils.escapeHTML(feature.properties[key]).trim()
113
- if (value.indexOf('http') === 0) {
114
- return `<a href="${value}" target="_blank">${value}</a>`
115
- }
116
- return value
117
- }
118
-
119
- makeRow(feature, key) {
111
+ makeRow(feature, field) {
120
112
  return Utils.loadTemplate(
121
- `<tr><th>${key}</th><td>${this.getValue(feature, key)}</td></tr>`
113
+ `<tr><th>${field.key}</th><td>${field.render(feature.properties[field.key])}</td></tr>`
122
114
  )
123
115
  }
124
116
 
125
117
  async renderBody(feature) {
126
118
  const table = document.createElement('table')
127
119
 
128
- for (const key in feature.properties) {
129
- if (typeof feature.properties[key] === 'object' || U.LABEL_KEYS.includes(key)) {
120
+ for (const field of feature.fields) {
121
+ if (U.LABEL_KEYS.includes(field.key)) {
130
122
  continue
131
123
  }
132
- table.appendChild(this.makeRow(feature, key))
124
+ table.appendChild(this.makeRow(feature, field))
133
125
  }
134
126
  return table
135
127
  }
@@ -171,7 +163,7 @@ class OSM extends PopupTemplate {
171
163
  const icon = Icon.makeElement(iconUrl, title)
172
164
  DomUtil.addClass(icon, 'icon')
173
165
  Icon.setContrast(icon, title, iconUrl, color)
174
- if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
166
+ if (DOMUtils.contrastedColor(title, color)) title.style.color = 'white'
175
167
  DomUtil.add('span', '', title, this.getName(feature))
176
168
  return title
177
169
  }
@@ -180,7 +172,7 @@ class OSM extends PopupTemplate {
180
172
  const props = feature.properties
181
173
  const locale = getLocale()
182
174
  if (locale && props[`name:${locale}`]) return props[`name:${locale}`]
183
- return props.name
175
+ return props.name || feature.getDisplayName()
184
176
  }
185
177
 
186
178
  async renderBody(feature) {
@@ -192,7 +184,7 @@ class OSM extends PopupTemplate {
192
184
  const row = DomUtil.add('address', 'address', body)
193
185
  const number = props['addr:housenumber']
194
186
  if (number) {
195
- // Poor way to deal with international forms of writting addresses
187
+ // Poor way to deal with international forms of writing addresses
196
188
  DomUtil.add('span', '', row, `${translate('No.')}: ${number}`)
197
189
  DomUtil.add('span', '', row, `${translate('Street')}: ${street}`)
198
190
  } else {
@@ -230,11 +222,31 @@ class OSM extends PopupTemplate {
230
222
  )
231
223
  )
232
224
  }
225
+ if (props.image) {
226
+ body.appendChild(
227
+ Utils.loadTemplate(`<div><img src="${props.image}" alt="" /></div>`)
228
+ )
229
+ }
230
+ if (props.mapillary) {
231
+ body.appendChild(
232
+ Utils.loadTemplate(
233
+ `<div><a href="https://www.mapillary.com/app/?focus=photo&pKey=${props.mapillary}" target="_blank">${translate('Mapillary')}<i class="icon icon-16 icon-external-link"></i></a></div>`
234
+ )
235
+ )
236
+ }
233
237
  const wikipedia = props[`wikipedia:${locale}`] || props.wikipedia
234
238
  if (wikipedia) {
235
239
  body.appendChild(
236
240
  Utils.loadTemplate(
237
- `<div class="wikipedia-link"><a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">${translate('Wikipedia')}</a></div>`
241
+ `<div><a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">${translate('Wikipedia')}<i class="icon icon-16 icon-external-link"></i></a></div>`
242
+ )
243
+ )
244
+ }
245
+ const wikidata = props[`wikidata:${locale}`] || props.wikidata
246
+ if (wikidata) {
247
+ body.appendChild(
248
+ Utils.loadTemplate(
249
+ `<div><a href="https://www.wikidata.org/wiki/${wikidata}" target="_blank">${translate('Wikidata')}<i class="icon icon-16 icon-external-link"></i></a></div>`
238
250
  )
239
251
  )
240
252
  }
@@ -242,7 +254,7 @@ class OSM extends PopupTemplate {
242
254
  if (id) {
243
255
  body.appendChild(
244
256
  Utils.loadTemplate(
245
- `<div class="osm-link"><a href="https://www.openstreetmap.org/${id}">${translate('See on OpenStreetMap')}</a></div>`
257
+ `<div class="osm-link"><a href="https://www.openstreetmap.org/${id}">${translate('See on OpenStreetMap')}<i class="icon icon-16 icon-external-link"></i></a></div>`
246
258
  )
247
259
  )
248
260
  }
@@ -257,7 +269,7 @@ class Wikipedia extends PopupTemplate {
257
269
  if (wikipedia && _WIKIPEDIA_CACHE[wikipedia]) return _WIKIPEDIA_CACHE[wikipedia]
258
270
  // Wikipedia value should be in form of "{locale}:{title}", according to https://wiki.openstreetmap.org/wiki/Key:wikipedia
259
271
  const [locale, page] = wikipedia.split(':')
260
- const url = `https://${locale}.wikipedia.org/w/api.php?action=query&format=json&origin=*&pithumbsize=500&prop=extracts|pageimages&titles=${page}`
272
+ const url = `https://${locale}.wikipedia.org/w/api.php?action=query&format=json&origin=*&pithumbsize=500&exintro=1&prop=extracts|pageimages&titles=${page}`
261
273
  const request = new Request()
262
274
  const response = await request.get(url)
263
275
  if (response?.ok) {
@@ -277,9 +289,21 @@ class Wikipedia extends PopupTemplate {
277
289
  const title = page.title || feature.getDisplayName()
278
290
  const extract = page.extract || ''
279
291
  const thumbnail = page.thumbnail?.source
280
- const [content, { image }] = Utils.loadTemplateWithRefs(
281
- `<div><h3>${Utils.escapeHTML(title)}</h3><img data-ref="image" hidden src="" />${Utils.escapeHTML(extract)}</div>`
282
- )
292
+ const [content, { image }] = Utils.loadTemplateWithRefs(`
293
+ <div>
294
+ <h3>${Utils.escapeHTML(title)}</h3>
295
+ <img data-ref="image" hidden src="" />
296
+ <p>
297
+ ${Utils.escapeHTML(extract)}
298
+ </p>
299
+ <p>
300
+ © ${translate('Wikipedia contributors')} •
301
+ <a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">
302
+ ${translate('See on Wikipedia')}
303
+ <i class="icon icon-16 icon-external-link"></i>
304
+ </a>
305
+ </p>
306
+ </div>`)
283
307
  if (thumbnail) {
284
308
  image.src = thumbnail
285
309
  image.hidden = false
@@ -349,6 +373,9 @@ class Route extends TitleMixin(PopupTemplate) {
349
373
  color: 'orange',
350
374
  }).addTo(map)
351
375
  })
376
+ if (feature.properties.description) {
377
+ root.appendChild(Utils.loadTemplate(`<p>${feature.properties.description}</p>`))
378
+ }
352
379
  return root
353
380
  }
354
381
  }
@@ -111,6 +111,10 @@ const FeatureMixin = {
111
111
  onCommit: function () {
112
112
  this.feature.onCommit()
113
113
  },
114
+
115
+ isVisible() {
116
+ return Boolean(this._map?.hasLayer(this))
117
+ },
114
118
  }
115
119
 
116
120
  const PointMixin = {
@@ -377,6 +381,27 @@ const PathMixin = {
377
381
  // https://github.com/Leaflet/Leaflet/pull/9475
378
382
 
379
383
  this._path.classList.toggle('leaflet-interactive', options.interactive)
384
+
385
+ // Text decoration
386
+ this.setText(null) // Reset.
387
+ const textPath = this.feature.getDynamicOption('textPath')
388
+ if (textPath) {
389
+ const color =
390
+ this.feature.getOption('textPathColor') ||
391
+ this.feature.getDynamicOption('color')
392
+ const textPathOptions = {
393
+ repeat: this.feature.getOption('textPathRepeat'),
394
+ offset: this.feature.getOption('textPathOffset') || undefined,
395
+ position: this.feature.getOption('textPathPosition'),
396
+ attributes: {
397
+ fill: color,
398
+ opacity: this.feature.getDynamicOption('opacity'),
399
+ rotate: this.feature.getOption('textPathRotate'),
400
+ 'font-size': this.feature.getOption('textPathSize'),
401
+ },
402
+ }
403
+ this.setText(textPath, textPathOptions)
404
+ }
380
405
  },
381
406
 
382
407
  _redraw: function () {
@@ -423,29 +448,6 @@ export const LeafletPolyline = Polyline.extend({
423
448
  parentClass: Polyline,
424
449
  includes: [FeatureMixin, PathMixin],
425
450
 
426
- setStyle: function (options) {
427
- PathMixin.setStyle.call(this, options)
428
- this.setText(null) // Reset.
429
- const textPath = this.feature.getDynamicOption('textPath')
430
- if (textPath) {
431
- const color =
432
- this.feature.getOption('textPathColor') ||
433
- this.feature.getDynamicOption('color')
434
- const textPathOptions = {
435
- repeat: this.feature.getOption('textPathRepeat'),
436
- offset: this.feature.getOption('textPathOffset') || undefined,
437
- position: this.feature.getOption('textPathPosition'),
438
- attributes: {
439
- fill: color,
440
- opacity: this.feature.getDynamicOption('opacity'),
441
- rotate: this.feature.getOption('textPathRotate'),
442
- 'font-size': this.feature.getOption('textPathSize'),
443
- },
444
- }
445
- this.setText(textPath, textPathOptions)
446
- }
447
- },
448
-
449
451
  getClass: () => LeafletPolyline,
450
452
 
451
453
  getMeasure: function (shape) {
@@ -632,4 +634,8 @@ export const CircleMarker = BaseCircleMarker.extend({
632
634
  getCenter: function () {
633
635
  return this._latlng
634
636
  },
637
+
638
+ setText() {
639
+ // Dummy function, as it inherits from PathMixin
640
+ },
635
641
  })