umap-project 2.6.3__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 (137) 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 +54 -1
  35. umap/static/umap/js/modules/caption.js +16 -5
  36. umap/static/umap/js/modules/data/features.js +176 -2
  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 +73 -52
  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/br.js +13 -4
  62. umap/static/umap/locale/br.json +13 -4
  63. umap/static/umap/locale/ca.js +28 -15
  64. umap/static/umap/locale/ca.json +28 -15
  65. umap/static/umap/locale/cs_CZ.js +87 -78
  66. umap/static/umap/locale/cs_CZ.json +87 -78
  67. umap/static/umap/locale/de.js +17 -8
  68. umap/static/umap/locale/de.json +17 -8
  69. umap/static/umap/locale/en.js +13 -2
  70. umap/static/umap/locale/en.json +13 -2
  71. umap/static/umap/locale/es.js +330 -319
  72. umap/static/umap/locale/es.json +330 -319
  73. umap/static/umap/locale/eu.js +10 -3
  74. umap/static/umap/locale/eu.json +10 -3
  75. umap/static/umap/locale/fa_IR.js +11 -4
  76. umap/static/umap/locale/fa_IR.json +11 -4
  77. umap/static/umap/locale/fr.js +15 -4
  78. umap/static/umap/locale/fr.json +15 -4
  79. umap/static/umap/locale/hu.js +10 -3
  80. umap/static/umap/locale/hu.json +10 -3
  81. umap/static/umap/locale/pt.js +17 -8
  82. umap/static/umap/locale/pt.json +17 -8
  83. umap/static/umap/locale/pt_PT.js +13 -4
  84. umap/static/umap/locale/pt_PT.json +13 -4
  85. umap/static/umap/locale/zh_TW.js +13 -4
  86. umap/static/umap/locale/zh_TW.json +13 -4
  87. umap/static/umap/map.css +44 -29
  88. umap/static/umap/unittests/hlc.js +165 -0
  89. umap/static/umap/unittests/sync.js +321 -15
  90. umap/static/umap/unittests/utils.js +47 -0
  91. umap/static/umap/vars.css +2 -1
  92. umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
  93. umap/static/umap/vendors/dompurify/purify.es.js +15 -16
  94. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  95. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  96. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
  97. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
  98. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
  99. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
  100. umap/templates/umap/css.html +0 -2
  101. umap/templates/umap/dashboard_menu.html +4 -2
  102. umap/templates/umap/js.html +0 -5
  103. umap/templates/umap/map_detail.html +2 -2
  104. umap/tests/fixtures/test_upload_data.csv +2 -2
  105. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  106. umap/tests/integration/test_basics.py +1 -1
  107. umap/tests/integration/test_browser.py +69 -7
  108. umap/tests/integration/test_caption.py +3 -3
  109. umap/tests/integration/test_circles_layer.py +12 -0
  110. umap/tests/integration/test_datalayer.py +2 -1
  111. umap/tests/integration/test_draw_polygon.py +17 -9
  112. umap/tests/integration/test_draw_polyline.py +12 -8
  113. umap/tests/integration/test_edit_datalayer.py +5 -8
  114. umap/tests/integration/test_edit_map.py +2 -2
  115. umap/tests/integration/test_edit_marker.py +1 -1
  116. umap/tests/integration/test_facets_browser.py +3 -3
  117. umap/tests/integration/test_import.py +1 -0
  118. umap/tests/integration/test_map.py +1 -0
  119. umap/tests/integration/test_owned_map.py +1 -1
  120. umap/tests/integration/test_view_marker.py +63 -0
  121. umap/tests/integration/test_view_polygon.py +12 -12
  122. umap/tests/integration/test_websocket_sync.py +65 -3
  123. umap/tests/test_clean_tilelayer.py +83 -0
  124. umap/tests/test_datalayer.py +24 -0
  125. umap/tests/test_map_views.py +20 -0
  126. umap/tests/test_purge_purgatory.py +25 -0
  127. umap/tests/test_websocket_server.py +22 -0
  128. umap/urls.py +5 -1
  129. umap/views.py +6 -3
  130. umap/websocket_server.py +130 -27
  131. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/METADATA +18 -14
  132. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/RECORD +135 -127
  133. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  134. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  135. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/WHEEL +0 -0
  136. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/entry_points.txt +0 -0
  137. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,152 @@
1
+ import json
2
+ import sys
3
+
4
+ from django.core.management.base import BaseCommand
5
+ from django.db import connection
6
+ from psycopg.types.json import Jsonb
7
+
8
+ from umap.models import Map, TileLayer
9
+
10
+
11
+ class Command(BaseCommand):
12
+ help = """Clean tilelayer in database
13
+
14
+ This will simply replace the URL in maps settings:
15
+ umap clean_tilelayer http://my.old/url/template http://my.new/url/template
16
+
17
+ This will replace the whole tilelayer in maps settings by the one with this name:
18
+ umap clean_tilelayer http://my.old/url/template "some string"
19
+
20
+ This will replace the whole tilelayer in maps settings by the one with this id:
21
+ umap clean_tilelayer http://my.old/url/template an_id
22
+
23
+ This will delete the whole tilelayer from maps settings:
24
+ umap clean_tilelayer http://my.old/url/template
25
+
26
+ To get the available tilelayers in db (available for users):
27
+ umap clean_tilelayer --available
28
+
29
+ To get statistics of tilelayers usage in db (including custom ones):
30
+ umap clean_tilelayer --available
31
+ """
32
+
33
+ def add_arguments(self, parser):
34
+ parser.add_argument("old", nargs="?", help="url template we want to clean")
35
+ parser.add_argument(
36
+ "new", help="what to replace this tilelayer with", nargs="?"
37
+ )
38
+ parser.add_argument(
39
+ "--no-input", action="store_true", help="Do not ask for confirm."
40
+ )
41
+ parser.add_argument(
42
+ "--available", action="store_true", help="List known tilelayers."
43
+ )
44
+ parser.add_argument(
45
+ "--stats", action="store_true", help="Display stats on tilelayer usage."
46
+ )
47
+
48
+ def handle(self, *args, **options):
49
+ self.no_input = options["no_input"]
50
+ if options["available"]:
51
+ self.list_available()
52
+ sys.exit()
53
+ if options["stats"]:
54
+ self.stats()
55
+ sys.exit()
56
+ old = options["old"]
57
+ new = options["new"]
58
+ if not old:
59
+ sys.exit("⚠ You must define an url_template")
60
+
61
+ count = Map.objects.filter(
62
+ settings__properties__tilelayer__url_template=old
63
+ ).count()
64
+ if not count:
65
+ self.stdout.write("⚠ No map found. Exiting.")
66
+ sys.exit()
67
+ self.stdout.write(f"{count} maps found.")
68
+ if not new:
69
+ self.delete(old)
70
+ elif new.startswith("http"):
71
+ self.replace_url(old, new)
72
+ else:
73
+ # Let's consider it's a name or an id
74
+ self.replace_tilelayer(old, new)
75
+
76
+ def confirm(self, message):
77
+ if self.no_input:
78
+ return True
79
+ result = input("%s (y/N) " % message) or "n"
80
+ if not result[0].lower() == "y":
81
+ self.stdout.write("⚠ Action cancelled.")
82
+ sys.exit()
83
+ return True
84
+
85
+ def delete(self, old):
86
+ if self.confirm(
87
+ "Are you sure you want to delete the tilelayer key from all those "
88
+ "maps settings ?"
89
+ ):
90
+ with connection.cursor() as cursor:
91
+ ret = cursor.execute(
92
+ "UPDATE umap_map "
93
+ "SET settings['properties'] = (settings->'properties') - 'tilelayer'"
94
+ "WHERE settings->'properties'->'tilelayer'->'url_template' = %s",
95
+ [Jsonb(old)],
96
+ )
97
+ self.stdout.write(f"✔ Deleted {old} from {ret.rowcount} maps.")
98
+
99
+ def replace_url(self, old, new):
100
+ if self.confirm(
101
+ f"Are you sure you want to replace '{old}'' by '{new}'' from all those "
102
+ "map settings ?"
103
+ ):
104
+ with connection.cursor() as cursor:
105
+ ret = cursor.execute(
106
+ "UPDATE umap_map "
107
+ "SET settings['properties']['tilelayer']['url_template'] = %s "
108
+ "WHERE settings->'properties'->'tilelayer'->'url_template' = %s",
109
+ [Jsonb(new), Jsonb(old)],
110
+ )
111
+ self.stdout.write(f"✔ Replaced {old} by {new} in {ret.rowcount} maps.")
112
+
113
+ def replace_tilelayer(self, old, new):
114
+ try:
115
+ tilelayer = TileLayer.objects.get(name=new)
116
+ except TileLayer.DoesNotExist:
117
+ try:
118
+ tilelayer = TileLayer.objects.get(id=new)
119
+ except (TileLayer.DoesNotExist, ValueError):
120
+ sys.exit(f"⚠ Cannot find a TileLayer with name or id = '{new}'.")
121
+ if self.confirm(
122
+ f"Are you sure you want to replace {old} by '{tilelayer.name}' "
123
+ "from all those map settings ?"
124
+ ):
125
+ with connection.cursor() as cursor:
126
+ ret = cursor.execute(
127
+ "UPDATE umap_map "
128
+ "SET settings['properties']['tilelayer'] = %s "
129
+ "WHERE settings->'properties'->'tilelayer'->'url_template' = %s",
130
+ [Jsonb(tilelayer.json), Jsonb(old)],
131
+ )
132
+ self.stdout.write(
133
+ f"✔ Replaced {old} by {tilelayer.name} in {ret.rowcount} maps."
134
+ )
135
+
136
+ def list_available(self):
137
+ tilelayers = TileLayer.objects.all()
138
+ for tilelayer in tilelayers:
139
+ print(f"{tilelayer.pk} '{tilelayer.name}' {tilelayer.url_template}")
140
+
141
+ def stats(self):
142
+ with connection.cursor() as cursor:
143
+ cursor.execute(
144
+ "SELECT COUNT(*) as count, "
145
+ "settings->'properties'->'tilelayer'->'url_template' as url "
146
+ "FROM umap_map "
147
+ "GROUP BY settings->'properties'->'tilelayer'->'url_template' "
148
+ "ORDER BY count DESC"
149
+ )
150
+ res = cursor.fetchall()
151
+ for count, url in res:
152
+ print(f"{count}\t{url}")
@@ -0,0 +1,28 @@
1
+ import time
2
+ from pathlib import Path
3
+
4
+ from django.conf import settings
5
+ from django.core.management.base import BaseCommand
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Remove old files from purgatory. Eg.: umap purge_purgatory --days 7"
10
+
11
+ def add_arguments(self, parser):
12
+ parser.add_argument(
13
+ "--days",
14
+ help="Number of days to consider files for removal",
15
+ default=30,
16
+ type=int,
17
+ )
18
+
19
+ def handle(self, *args, **options):
20
+ days = options["days"]
21
+ root = Path(settings.UMAP_PURGATORY_ROOT)
22
+ threshold = time.time() - days * 86400
23
+ for path in root.iterdir():
24
+ stats = path.stat()
25
+ filestamp = stats.st_mtime
26
+ if filestamp < threshold:
27
+ path.unlink()
28
+ print(f"Removed old file {path}")
umap/models.py CHANGED
@@ -3,6 +3,7 @@ import operator
3
3
  import os
4
4
  import time
5
5
  import uuid
6
+ from pathlib import Path
6
7
 
7
8
  from django.conf import settings
8
9
  from django.contrib.auth.models import User
@@ -255,6 +256,13 @@ class Map(NamedModel):
255
256
  )
256
257
  return map_settings
257
258
 
259
+ def delete(self, **kwargs):
260
+ # Explicitely call datalayers.delete, so we can deal with removing files
261
+ # (the cascade delete would not call the model delete method)
262
+ for datalayer in self.datalayer_set.all():
263
+ datalayer.delete()
264
+ return super().delete(**kwargs)
265
+
258
266
  def generate_umapjson(self, request):
259
267
  umapjson = self.settings
260
268
  umapjson["type"] = "umap"
@@ -462,7 +470,9 @@ class DataLayer(NamedModel):
462
470
 
463
471
  def save(self, force_insert=False, force_update=False, **kwargs):
464
472
  is_new = not bool(self.pk)
465
- super(DataLayer, self).save(force_insert, force_update, **kwargs)
473
+ super(DataLayer, self).save(
474
+ force_insert=force_insert, force_update=force_update, **kwargs
475
+ )
466
476
 
467
477
  if is_new:
468
478
  force_insert, force_update = False, True
@@ -471,10 +481,25 @@ class DataLayer(NamedModel):
471
481
  new_name = self.geojson.storage.save(filename, self.geojson)
472
482
  self.geojson.storage.delete(old_name)
473
483
  self.geojson.name = new_name
474
- super(DataLayer, self).save(force_insert, force_update, **kwargs)
484
+ super(DataLayer, self).save(
485
+ force_insert=force_insert, force_update=force_update, **kwargs
486
+ )
475
487
  self.purge_gzip()
476
488
  self.purge_old_versions()
477
489
 
490
+ def delete(self, **kwargs):
491
+ self.purge_gzip()
492
+ self.to_purgatory()
493
+ return super().delete(**kwargs)
494
+
495
+ def to_purgatory(self):
496
+ dest = Path(settings.UMAP_PURGATORY_ROOT)
497
+ dest.mkdir(parents=True, exist_ok=True)
498
+ src = Path(self.geojson.storage.location) / self.storage_root()
499
+ for version in self.versions:
500
+ name = version["name"]
501
+ (src / name).rename(dest / f"{self.map.pk}_{name}")
502
+
478
503
  def upload_to(self):
479
504
  root = self.storage_root()
480
505
  name = "%s_%s.geojson" % (self.pk, int(time.time() * 1000))
umap/settings/base.py CHANGED
@@ -19,7 +19,7 @@ env = environ.Env()
19
19
  INTERNAL_IPS = env.list("INTERNAL_IPS", default=["127.0.0.1"])
20
20
  ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
21
21
  ADMINS = tuple(parseaddr(email) for email in env.list("ADMINS", default=[]))
22
-
22
+ ASGI_APPLICATION = "umap.asgi.application"
23
23
 
24
24
  DEBUG = env.bool("DEBUG", default=False)
25
25
 
@@ -240,6 +240,7 @@ USER_URL_FIELD = "username"
240
240
  # Miscellaneous project settings
241
241
  # =============================================================================
242
242
  UMAP_ALLOW_ANONYMOUS = env.bool("UMAP_ALLOW_ANONYMOUS", default=False)
243
+ UMAP_ALLOW_EDIT_PROFILE = env.bool("UMAP_ALLOW_EDIT_PROFILE", default=True)
243
244
 
244
245
  UMAP_EXTRA_URLS = {
245
246
  "routing": "http://www.openstreetmap.org/directions?engine=osrm_car&route={lat},{lng}&locale={locale}#map={zoom}/{lat}/{lng}", # noqa
@@ -266,6 +267,7 @@ UMAP_DEFAULT_FEATURES_HAVE_OWNERS = False
266
267
  UMAP_HOME_FEED = "latest"
267
268
  UMAP_IMPORTERS = {}
268
269
  UMAP_HOST_INFOS = {}
270
+ UMAP_PURGATORY_ROOT = "/tmp/umappurgatory"
269
271
 
270
272
  UMAP_READONLY = env("UMAP_READONLY", default=False)
271
273
  UMAP_GZIP = True
umap/static/umap/base.css CHANGED
@@ -307,9 +307,6 @@ input + .help-text {
307
307
  .formbox.with-switch {
308
308
  padding-top: 2px;
309
309
  }
310
- .formbox select {
311
- width: calc(100% - 14px);
312
- }
313
310
  fieldset.formbox {
314
311
  border: none;
315
312
  border-top: 1px solid var(--color-lightGray);
@@ -386,6 +383,10 @@ fieldset legend {
386
383
  font-size: .9rem;
387
384
  padding: 0 5px;
388
385
  }
386
+ fieldset.separator {
387
+ border: none;
388
+ border-top: 1px solid var(--color-lightGray);
389
+ }
389
390
 
390
391
  [data-badge] {
391
392
  position: relative;
@@ -633,7 +634,6 @@ i.info {
633
634
  .umap-datalayer-container,
634
635
  .umap-layer-properties-container,
635
636
  .umap-browse-data,
636
- .umap-facet-search,
637
637
  .umap-tilelayer-switcher-container {
638
638
  padding: 0 10px;
639
639
  }
@@ -1,7 +1,7 @@
1
1
  .umap-contextmenu {
2
2
  background-color: var(--background-color);
3
3
  padding: calc(var(--box-padding) / 2) var(--box-padding);
4
- position: absolute;
4
+ position: fixed;
5
5
  z-index: var(--zindex-contextmenu);
6
6
  border-radius: var(--border-radius);
7
7
  box-shadow: var(--block-shadow);
@@ -9,3 +9,8 @@
9
9
  .umap-contextmenu li + li {
10
10
  margin-top: var(--text-margin);
11
11
  }
12
+
13
+ .umap-contextmenu hr {
14
+ margin-top: var(--text-margin);
15
+ margin-bottom: var(--text-margin);
16
+ }
@@ -35,9 +35,11 @@ html[dir="rtl"] .icon {
35
35
  .icon-block + span {
36
36
  margin-inline-start: 0;
37
37
  }
38
+ .icon-16.icon-white,
38
39
  .dark .icon-16 {
39
40
  background-image: url('../img/16-white.svg');
40
41
  }
42
+ .icon-24.icon-white,
41
43
  .dark .icon-24 {
42
44
  background-image: url('../img/24-white.svg');
43
45
  }
@@ -105,10 +107,13 @@ html[dir="rtl"] .icon {
105
107
  background-position: calc(var(--tile) * 3) calc(var(--tile) * 5);
106
108
  }
107
109
  .icon-polygon {
108
- background-position: var(--tile) calc(var(--tile) * 5);
110
+ background-position: var(--tile) calc(var(--tile) * 3);
109
111
  }
110
112
  .icon-polyline {
111
- background-position: 0 calc(var(--tile) * 5);
113
+ background-position: 0 calc(var(--tile) * 3);
114
+ }
115
+ .icon-profile {
116
+ background-position: 0 calc(var(--tile) * 4);
112
117
  }
113
118
  .icon-resize {
114
119
  background-position: calc(var(--tile) * 3) calc(var(--tile) * 6);
@@ -43,6 +43,10 @@
43
43
  .importers ul .communesfr:before {
44
44
  background-image: url(../img/importers/communesfr.svg);
45
45
  }
46
+ .importer.cadastrefr h3:before,
47
+ .importers ul .cadastrefr:before {
48
+ background-image: url(../img/importers/cadastrefr.svg);
49
+ }
46
50
  .importer.overpass h3:before,
47
51
  .importers ul .overpass:before {
48
52
  background-image: url(../img/importers/overpass.svg);
@@ -130,8 +130,8 @@
130
130
  <rect id="rect4243-2-5" x="89" y="901.36" width="2" height="2" fill="#f2f2f2"/>
131
131
  <path id="delete-30-7" d="m131 937.36v1c-1.4286 0-3.2857 0.5999-4 2v1h10v-1c-0.71429-1.4001-2.5714-2-4-2v-1zm-4 5 2 9h6l2-9z" fill="#b3b3b3" stroke="#999" stroke-width=".25"/>
132
132
  <path id="path437" d="m113.29 943.47 0.9072 0.5491c0.1786 0.10817 0.2366 0.34196 0.1294 0.52223-0.0319 0.0535-0.0764 0.0983-0.1294 0.13055l-7.0072 4.2412-7.0071-4.2412c-0.17869-0.10817-0.23664-0.34196-0.12942-0.52222 0.0319-0.0536 0.0763-0.0984 0.12942-0.13056l0.90714-0.5491 6.0999 3.6922zm0 3.578 0.9072 0.5491c0.1786 0.1081 0.2366 0.3419 0.1294 0.5222-0.0319 0.0535-0.0764 0.0983-0.1294 0.1306l-6.6189 4.0061c-0.239 0.1447-0.53753 0.1447-0.77652 0l-6.6188-4.0061c-0.17869-0.1082-0.23664-0.342-0.12942-0.5223 0.0319-0.0536 0.0763-0.0984 0.12942-0.1305l0.90714-0.5491 6.0999 3.6921zm-5.7117-10.575 6.6189 4.0062c0.1786 0.10815 0.2366 0.34196 0.1294 0.52222-0.0319 0.0536-0.0764 0.0984-0.1294 0.13056l-7.0072 4.2412-7.0071-4.2412c-0.17869-0.10815-0.23664-0.34196-0.12942-0.52222 0.0319-0.0536 0.0763-0.0984 0.12942-0.13056l6.6188-4.0062c0.23899-0.14465 0.53752-0.14465 0.77652 0z" fill="#f2f2f2" stroke="#999" stroke-width=".178"/>
133
- <path id="linestring" d="m7 937.36v2h1.8828l6.1172 3.0586v0.23438l-6.707 6.707h-1.293v2h2v-1.293l6.707-6.707h1.293v-2h-1.8828l-6.1172-3.0586v-0.94141z" color="#000000" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".2"/>
134
- <path id="polygon" d="m29 936.36v2h0.92773l-0.85547 12h-1.0723v2h2v-1.1992l8-1.6016v0.80078h2v-2h-0.77734l1.5547-7h1.2227v-2h-2v0.72656l-9-2.4531v-1.2734z" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".2"/>
133
+ <path id="linestring" d="m7 889.36v2h1.8828l6.1172 3.0586v0.23438l-6.707 6.707h-1.293v2h2v-1.293l6.707-6.707h1.293v-2h-1.8828l-6.1172-3.0586v-0.94141z" color="#000000" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".2"/>
134
+ <path id="polygon" d="m29 888.36v2h0.92773l-0.85547 12h-1.0723v2h2v-1.1992l8-1.6016v0.80078h2v-2h-0.77734l1.5547-7h1.2227v-2h-2v0.72656l-9-2.4531v-1.2734z" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".2"/>
135
135
  <path id="marker" d="m84 936.36c-2.625 0-5.25 1.7022-6 5.1064 0 2.0426 1.5 6.1277 6 10.894 4.5-4.7659 6-8.851 6-10.894-0.75-3.4042-3.375-5.1064-6-5.1064zm0 2.383c1.6569 0 3 1.2193 3 2.7234s-1.3431 2.7234-3 2.7234c-1.6568 0-3-1.2194-3-2.7234s1.3432-2.7234 3-2.7234z" fill="#f2f2f2" stroke="#999" stroke-width=".25"/>
136
136
  <g id="settings" class="sprite" transform="matrix(.16295 0 0 .16296 27.835 912.23)" fill="#f2f2f2" stroke="#999" stroke-width="1.4974">
137
137
  <path id="path27812" d="m98.762 43.652c-0.186-1.386-1.481-2.582-2.876-2.659l-6.172-0.337c-1.395-0.076-2.899-1.224-3.341-2.55l-2.28-5.518c-0.629-1.249-0.379-3.121 0.553-4.161l4.122-4.6c0.933-1.042 0.962-2.77 0.066-3.842l-8.813-8.813c-1.073-0.897-2.803-0.867-3.845 0.065l-4.598 4.122c-1.039 0.934-2.915 1.182-4.161 0.551l-5.521-2.279c-1.324-0.442-2.472-1.945-2.549-3.34l-0.337-6.17c-0.077-1.396-1.272-2.691-2.659-2.878 0 0-3.252-0.44-6.248-0.44-2.991 0-6.243 0.44-6.243 0.44-1.386 0.188-2.582 1.483-2.658 2.878l-0.338 6.17c-0.076 1.396-1.224 2.898-2.551 3.34l-5.517 2.279c-1.249 0.631-3.122 0.382-4.161-0.551l-4.601-4.122c-1.042-0.932-2.769-0.962-3.842-0.065l-8.813 8.813c-0.897 1.073-0.867 2.801 0.066 3.842l4.122 4.6c0.933 1.041 1.182 2.913 0.551 4.161l-2.279 5.518c-0.442 1.326-1.946 2.474-3.34 2.55l-6.17 0.337c-1.396 0.077-2.691 1.272-2.879 2.659 0 0-0.439 3.253-0.439 6.243s0.44 6.24 0.44 6.24c0.188 1.389 1.483 2.582 2.879 2.659l6.169 0.339c1.396 0.075 2.898 1.225 3.341 2.549l2.278 5.517c0.631 1.248 0.381 3.122-0.551 4.163l-4.122 4.598c-0.933 1.042-0.963 2.771-0.066 3.845l8.814 8.813c1.073 0.896 2.801 0.866 3.842-0.066l4.6-4.122c1.041-0.932 2.913-1.182 4.161-0.553l5.517 2.28c1.327 0.441 2.475 1.946 2.55 3.343l0.338 6.168c0.076 1.395 1.272 2.692 2.658 2.88 0 0 3.252 0.439 6.244 0.439 2.996 0 6.25-0.439 6.25-0.439 1.385-0.188 2.58-1.485 2.657-2.88l0.337-6.168c0.077-1.396 1.225-2.901 2.551-3.343l5.517-2.28c1.248-0.629 3.122-0.379 4.163 0.553l4.598 4.122c1.042 0.933 2.771 0.964 3.843 0.067l8.815-8.814c0.896-1.073 0.866-2.803-0.066-3.845l-4.122-4.598c-0.932-1.041-1.182-2.915-0.553-4.163l2.28-5.517c0.441-1.324 1.946-2.474 3.341-2.549l6.172-0.339c1.395-0.077 2.692-1.271 2.876-2.659 0 0 0.441-3.25 0.441-6.24s-0.441-6.243-0.441-6.243zm-48.658 18.709c-6.886 0-12.468-5.585-12.468-12.467 0-6.885 5.582-12.467 12.468-12.467 6.89 0 12.475 5.582 12.475 12.467-1e-3 6.882-5.586 12.467-12.475 12.467z" fill="#f2f2f2" stroke="#999" stroke-width="1.4974"/>
@@ -189,5 +189,12 @@
189
189
  <path id="path8" transform="translate(0 812.36)" d="m8.1903 37.424c-0.48722-0.17411-0.79231-0.44207-0.99467-0.87363l-0.1908-0.40689-1.3703-0.0034c-1.4983-0.0037-1.5911-0.03214-1.5406-0.47225l0.026557-0.23161 2.8423-0.0451 0.19827-0.3895c0.34789-0.68343 0.86349-1.0091 1.5923-1.0058 0.71771 0.0033 1.3415 0.43242 1.6291 1.1207l0.11636 0.27848h4.6574c4.5203 0 4.66 0.0047 4.7428 0.1594 0.1159 0.21656 0.10767 0.30093-0.04419 0.45278-0.11466 0.11466-0.66063 0.1295-4.7632 0.1295h-4.6337l-0.10498 0.2903c-0.15257 0.42194-0.62749 0.84447-1.1103 0.98785-0.49622 0.14736-0.6615 0.14879-1.0524 0.0091zm0.96896-0.84513c0.698-0.33122 0.698-1.3772 0-1.7084-0.35156-0.16682-0.49258-0.16849-0.82704-0.0098-0.33761 0.16021-0.5445 0.51333-0.5445 0.92936 0 0.36801 0.16007 0.60614 0.53566 0.79693 0.31576 0.16039 0.48419 0.15875 0.83587-0.0081z" fill="#f2f2f2" stroke="#999" stroke-width=".25" style="paint-order:fill markers stroke"/>
190
190
  <path id="path9" transform="translate(0 812.36)" d="m12.773 42.244c-0.4045-0.18728-0.73246-0.52685-0.9029-0.93487l-0.12875-0.30821-7.6214-0.04285-0.026557-0.23161c-0.055311-0.48238-0.16774-0.4688 3.8893-0.46961l3.7189-7.33e-4 0.21287-0.43192c0.65163-1.3222 2.4726-1.2846 3.1257 0.06446l0.17859 0.36892h2.2972c2.2584 0 2.2988 3e-3 2.391 0.17524 0.06808 0.12721 0.06936 0.22886 0.0047 0.37084l-0.08912 0.1956h-4.6108l-0.15493 0.35024c-0.38713 0.87516-1.4373 1.2865-2.2839 0.89448zm1.0632-0.70036c0.37802-0.15795 0.5443-0.42579 0.54116-0.87174-4e-3 -0.57358-0.36338-0.90877-0.9743-0.90877-0.31204 0-0.40386 0.04292-0.64992 0.30383-0.25777 0.27331-0.28248 0.33986-0.2461 0.66262 0.04524 0.40136 0.22819 0.65122 0.5956 0.81339 0.31718 0.14001 0.39991 0.14008 0.73356 6.68e-4z" fill="#f2f2f2" stroke="#999" stroke-width=".25" style="paint-order:fill markers stroke"/>
191
191
  <path id="path1-67-5" d="m154.68 963.54 4.7344 4.7344-5.0508 4.6836-1.3594-1.4668 3.5274-3.2695-3.2656-3.2656z" color="#000000" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".25"/>
192
+ <path id="copy" d="m58 914.36v3.5h4v4h3.5v-7.5zm-4 4v7.5h7.5v-7.5z" fill="#f2f2f2" style="paint-order:fill markers stroke"/>
193
+ <g id="g1-5" transform="translate(.1 .1)">
194
+ <g id="g24" fill="#f2f2f2" stroke="#999" stroke-width=".2">
195
+ <path id="path24" d="m12 912.36c-4.4183 0-8 3.5817-8 8 0.00463 1.6156 0.49829 3.1919 1.416 4.5215 0.19802 0.28729 0.41463 0.56131 0.64844 0.82032 1.5128 1.6866 3.6699 2.6526 5.9355 2.6582 2.2669-6e-3 4.4249-0.97357 5.9375-2.6621 0.23535-0.26077 0.45327-0.53676 0.65234-0.82617 0.91377-1.3275 1.4053-2.9001 1.4102-4.5117 0-4.4183-3.5817-8-8-8zm-5.3242 12.506c1.3285-1.5609 3.2745-2.461 5.3242-2.4629 2.0504 1e-3 3.9972 0.90149 5.3262 2.4629 0 0-0.0035-3e-3 0.0059 6e-3 0 0-0.5371 0.5762-0.83984 0.82812-0.17533 0.14821-0.35784 0.2877-0.54687 0.41797-0.06626 0.0467-0.13333 0.0923-0.20117 0.13672-0.2448 0.15763-0.4992 0.29983-0.76172 0.42578-0.93187 0.44366-1.9503 0.67577-2.9824 0.67969-0.61808-5e-3 -1.2328-0.0915-1.8281-0.25781-0.67532-0.18416-1.3192-0.46858-1.9102-0.84375-0.0685-0.0444-0.13622-0.09-0.20312-0.13672-0.00521-4e-3 -0.010422-8e-3 -0.015625-0.0117-0.18766-0.12974-0.36887-0.26858-0.54297-0.41602-0.29972-0.24977-0.57803-0.52416-0.83203-0.82031m-0.5918-0.81445c-0.69766-1.1081-1.0705-2.3898-1.0762-3.6992 0-3.866 3.134-7 7-7l-4.1e-5 -1.6e-4c3.866 0 7 3.134 7 7-0.0054 1.3023-0.37406 2.5773-1.0645 3.6816-1.5163-1.6794-3.6729-2.6381-5.9355-2.6387-2.2613 8e-3 -4.4134 0.97341-5.9238 2.6562" style="paint-order:fill markers stroke"/>
196
+ <path id="path11" d="m12 914.86a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0-2.5-2.5zm0 1a1.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.5z" style="paint-order:fill markers stroke"/>
197
+ </g>
198
+ </g>
192
199
  </g>
193
200
  </svg>