umap-project 1.12.1__py3-none-any.whl → 1.13.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 (159) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/br/LC_MESSAGES/django.po +82 -54
  4. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/cs_CZ/LC_MESSAGES/django.po +82 -54
  6. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/el/LC_MESSAGES/django.po +86 -58
  8. umap/locale/en/LC_MESSAGES/django.po +79 -51
  9. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/es/LC_MESSAGES/django.po +82 -54
  11. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/fr/LC_MESSAGES/django.po +84 -56
  13. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/hu/LC_MESSAGES/django.po +108 -80
  15. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/it/LC_MESSAGES/django.po +82 -54
  17. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/ms/LC_MESSAGES/django.po +82 -54
  19. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/pl/LC_MESSAGES/django.po +82 -54
  21. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  22. umap/locale/sv/LC_MESSAGES/django.po +82 -54
  23. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  24. umap/locale/zh_TW/LC_MESSAGES/django.po +86 -58
  25. umap/models.py +29 -0
  26. umap/static/umap/base.css +33 -5
  27. umap/static/umap/content.css +41 -2
  28. umap/static/umap/img/16.svg +12 -2
  29. umap/static/umap/img/source/16.svg +165 -820
  30. umap/static/umap/js/umap.browser.js +9 -3
  31. umap/static/umap/js/umap.controls.js +60 -222
  32. umap/static/umap/js/umap.core.js +66 -30
  33. umap/static/umap/js/umap.forms.js +18 -31
  34. umap/static/umap/js/umap.icon.js +44 -21
  35. umap/static/umap/js/umap.js +11 -27
  36. umap/static/umap/js/umap.layer.js +68 -25
  37. umap/static/umap/js/umap.popup.js +87 -0
  38. umap/static/umap/js/umap.share.js +254 -0
  39. umap/static/umap/js/umap.ui.js +4 -2
  40. umap/static/umap/locale/am_ET.js +19 -9
  41. umap/static/umap/locale/am_ET.json +19 -9
  42. umap/static/umap/locale/ar.js +19 -9
  43. umap/static/umap/locale/ar.json +19 -9
  44. umap/static/umap/locale/ast.js +19 -9
  45. umap/static/umap/locale/ast.json +19 -9
  46. umap/static/umap/locale/bg.js +19 -9
  47. umap/static/umap/locale/bg.json +19 -9
  48. umap/static/umap/locale/br.js +22 -12
  49. umap/static/umap/locale/br.json +22 -12
  50. umap/static/umap/locale/ca.js +19 -9
  51. umap/static/umap/locale/ca.json +19 -9
  52. umap/static/umap/locale/cs_CZ.js +19 -9
  53. umap/static/umap/locale/cs_CZ.json +19 -9
  54. umap/static/umap/locale/da.js +19 -9
  55. umap/static/umap/locale/da.json +19 -9
  56. umap/static/umap/locale/de.js +30 -20
  57. umap/static/umap/locale/de.json +30 -20
  58. umap/static/umap/locale/el.js +19 -9
  59. umap/static/umap/locale/el.json +19 -9
  60. umap/static/umap/locale/en.js +19 -9
  61. umap/static/umap/locale/en.json +19 -9
  62. umap/static/umap/locale/en_US.json +19 -9
  63. umap/static/umap/locale/es.js +19 -9
  64. umap/static/umap/locale/es.json +19 -9
  65. umap/static/umap/locale/et.js +19 -9
  66. umap/static/umap/locale/et.json +19 -9
  67. umap/static/umap/locale/fa_IR.js +19 -9
  68. umap/static/umap/locale/fa_IR.json +19 -9
  69. umap/static/umap/locale/fi.js +19 -9
  70. umap/static/umap/locale/fi.json +19 -9
  71. umap/static/umap/locale/fr.js +21 -11
  72. umap/static/umap/locale/fr.json +21 -11
  73. umap/static/umap/locale/gl.js +19 -9
  74. umap/static/umap/locale/gl.json +19 -9
  75. umap/static/umap/locale/he.js +19 -9
  76. umap/static/umap/locale/he.json +19 -9
  77. umap/static/umap/locale/hr.js +19 -9
  78. umap/static/umap/locale/hr.json +19 -9
  79. umap/static/umap/locale/hu.js +19 -9
  80. umap/static/umap/locale/hu.json +19 -9
  81. umap/static/umap/locale/id.js +19 -9
  82. umap/static/umap/locale/id.json +19 -9
  83. umap/static/umap/locale/is.js +19 -9
  84. umap/static/umap/locale/is.json +19 -9
  85. umap/static/umap/locale/it.js +19 -9
  86. umap/static/umap/locale/it.json +19 -9
  87. umap/static/umap/locale/ja.js +19 -9
  88. umap/static/umap/locale/ja.json +19 -9
  89. umap/static/umap/locale/ko.js +19 -9
  90. umap/static/umap/locale/ko.json +19 -9
  91. umap/static/umap/locale/lt.js +19 -9
  92. umap/static/umap/locale/lt.json +19 -9
  93. umap/static/umap/locale/ms.js +19 -9
  94. umap/static/umap/locale/ms.json +19 -9
  95. umap/static/umap/locale/nl.js +20 -10
  96. umap/static/umap/locale/nl.json +20 -10
  97. umap/static/umap/locale/no.js +19 -9
  98. umap/static/umap/locale/no.json +19 -9
  99. umap/static/umap/locale/pl.js +19 -9
  100. umap/static/umap/locale/pl.json +19 -9
  101. umap/static/umap/locale/pl_PL.json +19 -9
  102. umap/static/umap/locale/pt.js +19 -9
  103. umap/static/umap/locale/pt.json +19 -9
  104. umap/static/umap/locale/pt_BR.js +19 -9
  105. umap/static/umap/locale/pt_BR.json +19 -9
  106. umap/static/umap/locale/pt_PT.js +19 -9
  107. umap/static/umap/locale/pt_PT.json +19 -9
  108. umap/static/umap/locale/ro.js +19 -9
  109. umap/static/umap/locale/ro.json +19 -9
  110. umap/static/umap/locale/ru.js +19 -9
  111. umap/static/umap/locale/ru.json +19 -9
  112. umap/static/umap/locale/sk_SK.js +19 -9
  113. umap/static/umap/locale/sk_SK.json +19 -9
  114. umap/static/umap/locale/sl.js +19 -9
  115. umap/static/umap/locale/sl.json +19 -9
  116. umap/static/umap/locale/sr.js +19 -9
  117. umap/static/umap/locale/sr.json +19 -9
  118. umap/static/umap/locale/sv.js +19 -9
  119. umap/static/umap/locale/sv.json +19 -9
  120. umap/static/umap/locale/th_TH.js +19 -9
  121. umap/static/umap/locale/th_TH.json +19 -9
  122. umap/static/umap/locale/tr.js +19 -9
  123. umap/static/umap/locale/tr.json +19 -9
  124. umap/static/umap/locale/uk_UA.js +19 -9
  125. umap/static/umap/locale/uk_UA.json +19 -9
  126. umap/static/umap/locale/vi.js +19 -9
  127. umap/static/umap/locale/vi.json +19 -9
  128. umap/static/umap/locale/vi_VN.json +19 -9
  129. umap/static/umap/locale/zh.js +19 -9
  130. umap/static/umap/locale/zh.json +19 -9
  131. umap/static/umap/locale/zh_CN.json +19 -9
  132. umap/static/umap/locale/zh_TW.Big5.json +19 -9
  133. umap/static/umap/locale/zh_TW.js +55 -45
  134. umap/static/umap/locale/zh_TW.json +55 -45
  135. umap/static/umap/map.css +77 -10
  136. umap/static/umap/test/Map.Export.js +3 -3
  137. umap/static/umap/test/Polygon.js +2 -2
  138. umap/static/umap/test/Polyline.js +2 -0
  139. umap/static/umap/test/index.html +1 -0
  140. umap/static/umap/vendors/leaflet/leaflet-src.esm.js +7055 -7054
  141. umap/static/umap/vendors/leaflet/leaflet-src.js +7144 -7144
  142. umap/static/umap/vendors/toolbar/leaflet.toolbar-src.js +1 -1
  143. umap/templates/auth/user_form.html +1 -1
  144. umap/templates/umap/content.html +1 -1
  145. umap/templates/umap/js.html +1 -0
  146. umap/templates/umap/map_list.html +1 -1
  147. umap/templates/umap/map_table.html +67 -35
  148. umap/templates/umap/user_dashboard.html +23 -1
  149. umap/templatetags/umap_tags.py +7 -27
  150. umap/tests/integration/test_edit_datalayer.py +45 -0
  151. umap/tests/integration/test_export_map.py +2 -3
  152. umap/tests/integration/test_facets_browser.py +86 -0
  153. umap/utils.py +17 -0
  154. umap/views.py +7 -18
  155. {umap_project-1.12.1.dist-info → umap_project-1.13.0.dist-info}/METADATA +4 -11
  156. {umap_project-1.12.1.dist-info → umap_project-1.13.0.dist-info}/RECORD +159 -156
  157. {umap_project-1.12.1.dist-info → umap_project-1.13.0.dist-info}/WHEEL +0 -0
  158. {umap_project-1.12.1.dist-info → umap_project-1.13.0.dist-info}/entry_points.txt +0 -0
  159. {umap_project-1.12.1.dist-info → umap_project-1.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -13,7 +13,7 @@ L.U.Browser = L.Class.extend({
13
13
  zoom_to = L.DomUtil.create('i', 'feature-zoom_to', feature_li),
14
14
  edit = L.DomUtil.create('i', 'show-on-edit feature-edit', feature_li),
15
15
  del = L.DomUtil.create('i', 'show-on-edit feature-delete', feature_li),
16
- color = L.DomUtil.create('i', 'feature-color', feature_li),
16
+ colorBox = L.DomUtil.create('i', 'feature-color', feature_li),
17
17
  title = L.DomUtil.create('span', 'feature-title', feature_li),
18
18
  symbol = feature._getIconUrl
19
19
  ? L.U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
@@ -22,8 +22,12 @@ L.U.Browser = L.Class.extend({
22
22
  edit.title = L._('Edit this feature')
23
23
  del.title = L._('Delete this feature')
24
24
  title.textContent = feature.getDisplayName() || '—'
25
- color.style.backgroundColor = feature.getOption('color')
26
- if (symbol) color.style.backgroundImage = `url(${symbol})`
25
+ const bgcolor = feature.getOption('color')
26
+ colorBox.style.backgroundColor = bgcolor
27
+ if (symbol && symbol !== this.map.options.default_iconUrl) {
28
+ const icon = L.U.Icon.makeIconElement(symbol, colorBox)
29
+ L.U.Icon.setIconContrast(icon, colorBox, symbol, bgcolor)
30
+ }
27
31
  L.DomEvent.on(
28
32
  zoom_to,
29
33
  'click',
@@ -58,6 +62,8 @@ L.U.Browser = L.Class.extend({
58
62
  container.id = `browse_data_datalayer_${datalayer.umap_id}`
59
63
  datalayer.renderToolbox(headline)
60
64
  L.DomUtil.add('span', '', headline, datalayer.options.name)
65
+ const counter = L.DomUtil.add('span', 'datalayer-counter', headline, datalayer.count())
66
+ counter.title = L._('{count} features in this layer', {count: datalayer.count()})
61
67
  const ul = L.DomUtil.create('ul', '', container)
62
68
  L.DomUtil.classIf(container, 'off', !datalayer.isVisible())
63
69
 
@@ -409,9 +409,9 @@ L.Control.Embed = L.Control.extend({
409
409
  const shareButton = L.DomUtil.createButton(
410
410
  '',
411
411
  container,
412
- L._('Share, download and embed this map'),
413
- map.renderShareBox,
414
- map
412
+ L._('Share and download'),
413
+ map.share.open,
414
+ map.share
415
415
  )
416
416
  L.DomEvent.on(shareButton, 'dblclick', L.DomEvent.stopPropagation)
417
417
  return container
@@ -632,12 +632,14 @@ L.U.DataLayersControl = L.Control.extend({
632
632
  function (e) {
633
633
  const layer = this.map.datalayers[e.src.dataset.id],
634
634
  other = this.map.datalayers[e.dst.dataset.id],
635
- minIndex = Math.min(e.initialIndex, e.finalIndex)
635
+ minIndex = Math.min(layer.getRank(), other.getRank()),
636
+ maxIndex = Math.max(layer.getRank(), other.getRank())
636
637
  if (e.finalIndex === 0) layer.bringToTop()
637
638
  else if (e.finalIndex > e.initialIndex) layer.insertBefore(other)
638
639
  else layer.insertAfter(other)
639
640
  this.map.eachDataLayerReverse((datalayer) => {
640
- if (datalayer.getRank() >= minIndex) datalayer.isDirty = true
641
+ if (datalayer.getRank() >= minIndex && datalayer.getRank() <= maxIndex)
642
+ datalayer.isDirty = true
641
643
  })
642
644
  this.map.indexDatalayers()
643
645
  },
@@ -894,46 +896,6 @@ L.U.Map.include({
894
896
  this.ui.openPanel({ data: { html: container }, actions: actions })
895
897
  },
896
898
 
897
- EXPORT_TYPES: {
898
- geojson: {
899
- formatter: function (map) {
900
- return JSON.stringify(map.toGeoJSON(), null, 2)
901
- },
902
- ext: '.geojson',
903
- filetype: 'application/json',
904
- },
905
- gpx: {
906
- formatter: function (map) {
907
- return togpx(map.toGeoJSON())
908
- },
909
- ext: '.gpx',
910
- filetype: 'application/gpx+xml',
911
- },
912
- kml: {
913
- formatter: function (map) {
914
- return tokml(map.toGeoJSON())
915
- },
916
- ext: '.kml',
917
- filetype: 'application/vnd.google-earth.kml+xml',
918
- },
919
- csv: {
920
- formatter: function (map) {
921
- const table = []
922
- map.eachFeature((feature) => {
923
- const row = feature.toGeoJSON()['properties'],
924
- center = feature.getCenter()
925
- delete row['_umap_options']
926
- row['Latitude'] = center.lat
927
- row['Longitude'] = center.lng
928
- table.push(row)
929
- })
930
- return csv2geojson.dsv.csvFormat(table)
931
- },
932
- ext: '.csv',
933
- filetype: 'text/csv',
934
- },
935
- },
936
-
937
899
  renderEditToolbar: function () {
938
900
  const container = L.DomUtil.create(
939
901
  'div',
@@ -1081,107 +1043,6 @@ L.U.Map.include({
1081
1043
  this
1082
1044
  )
1083
1045
  },
1084
-
1085
- renderShareBox: function () {
1086
- const container = L.DomUtil.create('div', 'umap-share')
1087
- const title = L.DomUtil.create('h3', '', container)
1088
- title.textContent = L._('Share, download and embed this map')
1089
-
1090
- const embedTitle = L.DomUtil.add('h4', '', container, L._('Embed the map'))
1091
- const iframe = L.DomUtil.create('textarea', 'umap-share-iframe', container)
1092
- const urlTitle = L.DomUtil.add('h4', '', container, L._('Direct link'))
1093
- const exportUrl = L.DomUtil.create('input', 'umap-share-url', container)
1094
- let option
1095
- exportUrl.type = 'text'
1096
- const UIFields = [
1097
- ['dimensions.width', { handler: 'Input', label: L._('width') }],
1098
- ['dimensions.height', { handler: 'Input', label: L._('height') }],
1099
- [
1100
- 'options.includeFullScreenLink',
1101
- { handler: 'Switch', label: L._('Include full screen link?') },
1102
- ],
1103
- [
1104
- 'options.currentView',
1105
- { handler: 'Switch', label: L._('Current view instead of default map view?') },
1106
- ],
1107
- [
1108
- 'options.keepCurrentDatalayers',
1109
- { handler: 'Switch', label: L._('Keep current visible layers') },
1110
- ],
1111
- [
1112
- 'options.viewCurrentFeature',
1113
- { handler: 'Switch', label: L._('Open current feature on load') },
1114
- ],
1115
- 'queryString.moreControl',
1116
- 'queryString.scrollWheelZoom',
1117
- 'queryString.miniMap',
1118
- 'queryString.scaleControl',
1119
- 'queryString.onLoadPanel',
1120
- 'queryString.captionBar',
1121
- 'queryString.captionMenus',
1122
- ]
1123
- for (let i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
1124
- UIFields.push(`queryString.${this.HIDDABLE_CONTROLS[i]}Control`)
1125
- }
1126
- const iframeExporter = new L.U.IframeExporter(this)
1127
- const buildIframeCode = () => {
1128
- iframe.innerHTML = iframeExporter.build()
1129
- exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
1130
- }
1131
- buildIframeCode()
1132
- const builder = new L.U.FormBuilder(iframeExporter, UIFields, {
1133
- callback: buildIframeCode,
1134
- })
1135
- const iframeOptions = L.DomUtil.createFieldset(container, L._('Export options'))
1136
- iframeOptions.appendChild(builder.build())
1137
- if (this.options.shortUrl) {
1138
- L.DomUtil.create('hr', '', container)
1139
- L.DomUtil.add('h4', '', container, L._('Short URL'))
1140
- const shortUrl = L.DomUtil.create('input', 'umap-short-url', container)
1141
- shortUrl.type = 'text'
1142
- shortUrl.value = this.options.shortUrl
1143
- }
1144
- L.DomUtil.create('hr', '', container)
1145
- L.DomUtil.add('h4', '', container, L._('Backup data'))
1146
- const downloadUrl = L.Util.template(this.options.urls.map_download, {
1147
- map_id: this.options.umap_id,
1148
- })
1149
- const link = L.DomUtil.createLink(
1150
- 'button',
1151
- container,
1152
- L._('Download full data'),
1153
- downloadUrl
1154
- )
1155
- let name = this.options.name || 'data'
1156
- name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
1157
- link.setAttribute('download', `${name}.umap`)
1158
- L.DomUtil.create('hr', '', container)
1159
- L.DomUtil.add('h4', '', container, L._('Download data'))
1160
- const typeInput = L.DomUtil.create('select', '', container)
1161
- typeInput.name = 'format'
1162
- const exportCaveat = L.DomUtil.add(
1163
- 'small',
1164
- 'help-text',
1165
- container,
1166
- L._('Only visible features will be downloaded.')
1167
- )
1168
- for (const key in this.EXPORT_TYPES) {
1169
- if (this.EXPORT_TYPES.hasOwnProperty(key)) {
1170
- option = L.DomUtil.create('option', '', typeInput)
1171
- option.value = key
1172
- option.textContent = this.EXPORT_TYPES[key].name || key
1173
- if (this.EXPORT_TYPES[key].selected) option.selected = true
1174
- }
1175
- }
1176
- L.DomUtil.createButton(
1177
- 'button',
1178
- container,
1179
- L._('Download data'),
1180
- () => this.download(typeInput.value),
1181
- this
1182
- )
1183
- this.ui.openPanel({ data: { html: container } })
1184
- },
1185
1046
  })
1186
1047
 
1187
1048
  /* Used in view mode to define the current tilelayer */
@@ -1199,11 +1060,21 @@ L.U.TileLayerControl = L.Control.IconLayers.extend({
1199
1060
  if (!layers) {
1200
1061
  layers = []
1201
1062
  this.map.eachTileLayer((layer) => {
1202
- layers.push({
1203
- title: layer.options.name,
1204
- layer: layer,
1205
- icon: L.Util.template(layer.options.url_template, this.map.demoTileInfos),
1206
- })
1063
+ try {
1064
+ // We'd like to use layer.getTileUrl, but this method will only work
1065
+ // when the tilelayer is actually added to the map (needs this._tileZoom
1066
+ // to be defined)
1067
+ // Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
1068
+ const icon = L.Util.template(layer.options.url_template, this.map.demoTileInfos)
1069
+ layers.push({
1070
+ title: layer.options.name,
1071
+ layer: layer,
1072
+ icon: icon,
1073
+ })
1074
+ } catch (e) {
1075
+ // Skip this tilelayer
1076
+ console.error(e)
1077
+ }
1207
1078
  })
1208
1079
  }
1209
1080
  const maxShown = 10
@@ -1353,9 +1224,47 @@ L.U.StarControl = L.Control.extend({
1353
1224
 
1354
1225
  L.U.Search = L.PhotonSearch.extend({
1355
1226
  initialize: function (map, input, options) {
1227
+ this.options.placeholder = L._('Type a place name or coordinates')
1356
1228
  L.PhotonSearch.prototype.initialize.call(this, map, input, options)
1357
1229
  this.options.url = map.options.urls.search
1358
1230
  if (map.options.maxBounds) this.options.bbox = map.options.maxBounds.toBBoxString()
1231
+ this.reverse = new L.PhotonReverse({
1232
+ handleResults: (geojson) => {
1233
+ this.handleResultsWithReverse(geojson)
1234
+ },
1235
+ })
1236
+ },
1237
+
1238
+ handleResultsWithReverse: function (geojson) {
1239
+ const latlng = this.reverse.latlng
1240
+ geojson.features.unshift({
1241
+ type: 'Feature',
1242
+ geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] },
1243
+ properties: {
1244
+ name: L._('Go to "{coords}"', { coords: `${latlng.lat} ${latlng.lng}` }),
1245
+ },
1246
+ })
1247
+
1248
+ this.handleResults(geojson)
1249
+ },
1250
+
1251
+ search: function () {
1252
+ const pattern = /^(?<lat>[-+]?\d{1,2}[.,]\d+)\s*[ ,]\s*(?<lng>[-+]?\d{1,3}[.,]\d+)$/
1253
+ if (pattern.test(this.input.value)) {
1254
+ this.hide()
1255
+ const { lat, lng } = pattern.exec(this.input.value).groups
1256
+ const latlng = L.latLng(lat, lng)
1257
+ if (latlng.isValid()) {
1258
+ this.reverse.doReverse(latlng)
1259
+ } else {
1260
+ this.map.ui.alert({ content: 'Invalid latitude or longitude', mode: 'error' })
1261
+ }
1262
+ return
1263
+ }
1264
+ // Only numbers, abort.
1265
+ if (/^[\d .,]*$/.test(this.input.value)) return
1266
+ // Do normal search
1267
+ L.PhotonSearch.prototype.search.call(this)
1359
1268
  },
1360
1269
 
1361
1270
  onBlur: function (e) {
@@ -1503,77 +1412,6 @@ L.U.ContextMenu = L.Map.ContextMenu.extend({
1503
1412
  },
1504
1413
  })
1505
1414
 
1506
- L.U.IframeExporter = L.Evented.extend({
1507
- options: {
1508
- includeFullScreenLink: true,
1509
- currentView: false,
1510
- keepCurrentDatalayers: false,
1511
- viewCurrentFeature: false,
1512
- },
1513
-
1514
- queryString: {
1515
- scaleControl: false,
1516
- miniMap: false,
1517
- scrollWheelZoom: false,
1518
- zoomControl: true,
1519
- editMode: 'disabled',
1520
- moreControl: true,
1521
- searchControl: null,
1522
- tilelayersControl: null,
1523
- embedControl: null,
1524
- datalayersControl: true,
1525
- onLoadPanel: 'none',
1526
- captionBar: false,
1527
- captionMenus: true,
1528
- },
1529
-
1530
- dimensions: {
1531
- width: '100%',
1532
- height: '300px',
1533
- },
1534
-
1535
- initialize: function (map) {
1536
- this.map = map
1537
- this.baseUrl = L.Util.getBaseUrl()
1538
- // Use map default, not generic default
1539
- this.queryString.onLoadPanel = this.map.options.onLoadPanel
1540
- },
1541
-
1542
- getMap: function () {
1543
- return this.map
1544
- },
1545
-
1546
- buildUrl: function (options) {
1547
- const datalayers = []
1548
- if (this.options.viewCurrentFeature && this.map.currentFeature) {
1549
- this.queryString.feature = this.map.currentFeature.getSlug()
1550
- }
1551
- if (this.options.keepCurrentDatalayers) {
1552
- this.map.eachDataLayer((datalayer) => {
1553
- if (datalayer.isVisible() && datalayer.umap_id) {
1554
- datalayers.push(datalayer.umap_id)
1555
- }
1556
- })
1557
- this.queryString.datalayers = datalayers.join(',')
1558
- } else {
1559
- delete this.queryString.datalayers
1560
- }
1561
- const currentView = this.options.currentView ? window.location.hash : ''
1562
- const queryString = L.extend({}, this.queryString, options)
1563
- return `${this.baseUrl}?${L.Util.buildQueryString(queryString)}${currentView}`
1564
- },
1565
-
1566
- build: function () {
1567
- const iframeUrl = this.buildUrl()
1568
- let code = `<iframe width="${this.dimensions.width}" height="${this.dimensions.height}" frameborder="0" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>`
1569
- if (this.options.includeFullScreenLink) {
1570
- const fullUrl = this.buildUrl({ scrollWheelZoom: true })
1571
- code += `<p><a href="${fullUrl}">${L._('See full screen')}</a></p>`
1572
- }
1573
- return code
1574
- },
1575
- })
1576
-
1577
1415
  L.U.Editable = L.Editable.extend({
1578
1416
  initialize: function (map, options) {
1579
1417
  L.Editable.prototype.initialize.call(this, map, options)
@@ -262,6 +262,18 @@ L.Util.hasVar = (value) => {
262
262
  return typeof value === 'string' && value.indexOf('{') != -1
263
263
  }
264
264
 
265
+ L.Util.isPath = function (value) {
266
+ return value && value.length && value.startsWith('/')
267
+ }
268
+
269
+ L.Util.isRemoteUrl = function (value) {
270
+ return value && value.length && value.startsWith('http')
271
+ }
272
+
273
+ L.Util.isDataImage = function (value) {
274
+ return value && value.length && value.startsWith('data:image')
275
+ }
276
+
265
277
  L.Util.copyToClipboard = function (textToCopy) {
266
278
  // https://stackoverflow.com/a/65996386
267
279
  // Navigator clipboard api needs a secure context (https)
@@ -346,6 +358,24 @@ L.DomUtil.createLink = (className, container, content, url, target, title) => {
346
358
  return el
347
359
  }
348
360
 
361
+ L.DomUtil.createCopiableInput = (parent, label, value) => {
362
+ const wrapper = L.DomUtil.add('div', 'copiable-input', parent)
363
+ const labelEl = L.DomUtil.add('label', '', wrapper, label)
364
+ const input = L.DomUtil.add('input', '', labelEl)
365
+ input.type = 'text'
366
+ input.readOnly = true
367
+ input.value = value
368
+ const button = L.DomUtil.createButton(
369
+ '',
370
+ wrapper,
371
+ '',
372
+ () => L.Util.copyToClipboard(input.value),
373
+ this
374
+ )
375
+ button.title = L._('copy')
376
+ return input
377
+ }
378
+
349
379
  L.DomUtil.classIf = (el, className, bool) => {
350
380
  if (bool) L.DomUtil.addClass(el, className)
351
381
  else L.DomUtil.removeClass(el, className)
@@ -373,8 +403,8 @@ L.DomUtil.after = (target, el) => {
373
403
  }
374
404
 
375
405
  L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
376
- L.DomUtil.TextColorFromBackgroundColor = (el) => {
377
- return L.DomUtil.contrastedColor(el) ? '#ffffff' : '#000000'
406
+ L.DomUtil.TextColorFromBackgroundColor = (el, bgcolor) => {
407
+ return L.DomUtil.contrastedColor(el, bgcolor) ? '#ffffff' : '#000000'
378
408
  }
379
409
  const _CACHE_CONSTRAST = {}
380
410
  L.DomUtil.contrastedColor = (el, bgcolor) => {
@@ -436,60 +466,62 @@ L.U.Keys = {
436
466
  }
437
467
 
438
468
  L.U.Help = L.Class.extend({
439
-
440
469
  SHORTCUTS: {
441
470
  DRAW_MARKER: {
442
- shortcut:'Modifier+M',
443
- label: L._('Draw a marker')
471
+ shortcut: 'Modifier+M',
472
+ label: L._('Draw a marker'),
444
473
  },
445
474
  DRAW_LINE: {
446
- shortcut:'Modifier+L',
447
- label: L._('Draw a polyline')
475
+ shortcut: 'Modifier+L',
476
+ label: L._('Draw a polyline'),
448
477
  },
449
478
  DRAW_POLYGON: {
450
- shortcut:'Modifier+P',
451
- label: L._('Draw a polygon')
479
+ shortcut: 'Modifier+P',
480
+ label: L._('Draw a polygon'),
452
481
  },
453
482
  TOGGLE_EDIT: {
454
- shortcut:'Modifier+E',
455
- label: L._('Toggle edit mode')
483
+ shortcut: 'Modifier+E',
484
+ label: L._('Toggle edit mode'),
456
485
  },
457
486
  STOP_EDIT: {
458
- shortcut:'Modifier+E',
459
- label: L._('Stop editing')
487
+ shortcut: 'Modifier+E',
488
+ label: L._('Stop editing'),
460
489
  },
461
490
  SAVE_MAP: {
462
- shortcut:'Modifier+S',
463
- label: L._('Save map')
491
+ shortcut: 'Modifier+S',
492
+ label: L._('Save map'),
464
493
  },
465
494
  IMPORT_PANEL: {
466
- shortcut:'Modifier+I',
467
- label: L._('Import data')
495
+ shortcut: 'Modifier+I',
496
+ label: L._('Import data'),
468
497
  },
469
498
  SEARCH: {
470
- shortcut:'Modifier+F',
471
- label: L._('Search location')
499
+ shortcut: 'Modifier+F',
500
+ label: L._('Search location'),
472
501
  },
473
502
  CANCEL: {
474
- shortcut:'Modifier+Z',
475
- label: L._('Cancel edits')
503
+ shortcut: 'Modifier+Z',
504
+ label: L._('Cancel edits'),
476
505
  },
477
506
  PREVIEW: {
478
- shortcut:'Modifier+E',
479
- label: L._('Back to preview')
507
+ shortcut: 'Modifier+E',
508
+ label: L._('Back to preview'),
480
509
  },
481
510
  SAVE: {
482
- shortcut:'Modifier+S',
483
- label: L._('Save current edits')
511
+ shortcut: 'Modifier+S',
512
+ label: L._('Save current edits'),
484
513
  },
485
514
  },
486
515
 
487
- displayLabel: function (action, withKbdTag=true) {
488
- let {shortcut, label} = this.SHORTCUTS[action]
516
+ displayLabel: function (action, withKbdTag = true) {
517
+ let { shortcut, label } = this.SHORTCUTS[action]
489
518
  const modifier = this.isMacOS ? 'Cmd' : 'Ctrl'
490
519
  shortcut = shortcut.replace('Modifier', modifier)
491
520
  if (withKbdTag) {
492
- shortcut = shortcut.split('+').map((el) => `<kbd>${el}</kbd>`).join('+')
521
+ shortcut = shortcut
522
+ .split('+')
523
+ .map((el) => `<kbd>${el}</kbd>`)
524
+ .join('+')
493
525
  label += ` ${shortcut}`
494
526
  } else {
495
527
  label += ` (${shortcut})`
@@ -515,7 +547,9 @@ L.U.Help = L.Class.extend({
515
547
  const label = L.DomUtil.create('span', '', closeButton)
516
548
  label.title = label.textContent = L._('Close')
517
549
  this.content = L.DomUtil.create('div', 'umap-help-content', this.box)
518
- this.isMacOS = /mac/i.test(navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform)
550
+ this.isMacOS = /mac/i.test(
551
+ navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
552
+ )
519
553
  },
520
554
 
521
555
  onKeyDown: function (e) {
@@ -708,7 +742,9 @@ L.U.Help = L.Class.extend({
708
742
  facetKey: L._(
709
743
  'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key)'
710
744
  ),
711
- interactive: L._('If false, the polygon or line will act as a part of the underlying map.'),
745
+ interactive: L._(
746
+ 'If false, the polygon or line will act as a part of the underlying map.'
747
+ ),
712
748
  outlink: L._('Define link to open in a new window on polygon click.'),
713
749
  dynamicRemoteData: L._('Fetch data each time map view changes.'),
714
750
  proxyRemoteData: L._("To use if remote server doesn't allow cross domain (slower)"),
@@ -372,16 +372,20 @@ L.FormBuilder.PopupContent = L.FormBuilder.Select.extend({
372
372
  ['Table', L._('Table')],
373
373
  ['GeoRSSImage', L._('GeoRSS (title + image)')],
374
374
  ['GeoRSSLink', L._('GeoRSS (only link)')],
375
+ ['OSM', L._('OpenStreetMap')],
375
376
  ],
376
377
  })
377
378
 
378
379
  L.FormBuilder.LayerTypeChooser = L.FormBuilder.Select.extend({
379
- selectOptions: [
380
- ['Default', L._('Default')],
381
- ['Cluster', L._('Clustered')],
382
- ['Heat', L._('Heatmap')],
383
- ['Choropleth', L._('Choropleth')],
384
- ],
380
+ getOptions: function () {
381
+ const layer_classes = [
382
+ L.U.Layer.Default,
383
+ L.U.Layer.Cluster,
384
+ L.U.Layer.Heat,
385
+ L.U.Layer.Choropleth,
386
+ ]
387
+ return layer_classes.map((class_) => [class_.TYPE, class_.NAME])
388
+ },
385
389
  })
386
390
 
387
391
  L.FormBuilder.SlideshowDelay = L.FormBuilder.IntSelect.extend({
@@ -538,8 +542,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
538
542
  this.footer.innerHTML = ''
539
543
  this.buildTabs()
540
544
  const value = this.value()
541
- if (!value || value.startsWith('/')) this.showSymbolsTab()
542
- else if (value.startsWith('http')) this.showURLTab()
545
+ if (!value || L.Util.isPath(value)) this.showSymbolsTab()
546
+ else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
543
547
  else this.showCharsTab()
544
548
  const closeButton = L.DomUtil.createButton(
545
549
  'button action-button',
@@ -596,20 +600,6 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
596
600
  this.body.innerHTML = ''
597
601
  },
598
602
 
599
- isPath: function () {
600
- const value = this.value()
601
- return value && value.length && value.startsWith('/')
602
- },
603
-
604
- isRemoteUrl: function () {
605
- const value = this.value()
606
- return value && value.length && value.startsWith('http')
607
- },
608
-
609
- isImg: function () {
610
- return this.isPath() || this.isRemoteUrl()
611
- },
612
-
613
603
  updatePreview: function () {
614
604
  this.buttons.innerHTML = ''
615
605
  if (this.isDefault()) return
@@ -617,13 +607,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
617
607
  // Do not try to render URL with variables
618
608
  const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
619
609
  L.DomEvent.on(box, 'click', this.onDefine, this)
620
- if (this.isImg()) {
621
- const img = L.DomUtil.create('img', '', box)
622
- img.src = this.value()
623
- } else {
624
- const el = L.DomUtil.create('span', '', box)
625
- el.textContent = this.value()
626
- }
610
+ const icon = L.U.Icon.makeIconElement(this.value(), box)
627
611
  }
628
612
  this.button = L.DomUtil.createButton(
629
613
  'button action-button',
@@ -723,7 +707,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
723
707
 
724
708
  showCharsTab: function () {
725
709
  this.openTab('chars')
726
- const value = !this.isImg() ? this.value() : null
710
+ const value = !L.U.Icon.isImg(this.value()) ? this.value() : null
727
711
  const input = this.buildInput(this.body, value)
728
712
  input.placeholder = L._('Type char or paste emoji')
729
713
  input.type = 'text'
@@ -731,7 +715,10 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
731
715
 
732
716
  showURLTab: function () {
733
717
  this.openTab('url')
734
- const value = this.isRemoteUrl() ? this.value() : null
718
+ const value =
719
+ L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
720
+ ? this.value()
721
+ : null
735
722
  const input = this.buildInput(this.body, value)
736
723
  input.placeholder = L._('Add image URL')
737
724
  input.type = 'url'