umap-project 2.6.3__py3-none-any.whl → 2.7.0b0__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.
- umap/__init__.py +1 -1
- umap/admin.py +64 -1
- umap/context_processors.py +1 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +19 -18
- umap/locale/en/LC_MESSAGES/django.po +47 -43
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +51 -47
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +64 -60
- umap/management/commands/clean_tilelayer.py +152 -0
- umap/management/commands/purge_purgatory.py +28 -0
- umap/models.py +27 -2
- umap/settings/base.py +2 -0
- umap/static/umap/base.css +4 -4
- umap/static/umap/css/contextmenu.css +5 -0
- umap/static/umap/css/icon.css +7 -2
- umap/static/umap/img/16-white.svg +9 -2
- umap/static/umap/img/16.svg +3 -0
- umap/static/umap/img/source/16-white.svg +10 -3
- umap/static/umap/img/source/16.svg +4 -1
- umap/static/umap/js/modules/autocomplete.js +7 -3
- umap/static/umap/js/modules/browser.js +7 -1
- umap/static/umap/js/modules/caption.js +6 -1
- umap/static/umap/js/modules/data/features.js +176 -2
- umap/static/umap/js/modules/data/layer.js +31 -26
- umap/static/umap/js/modules/formatter.js +3 -2
- umap/static/umap/js/modules/global.js +2 -0
- umap/static/umap/js/modules/importers/communesfr.js +13 -1
- umap/static/umap/js/modules/permissions.js +123 -93
- umap/static/umap/js/modules/rendering/ui.js +37 -212
- umap/static/umap/js/modules/sync/engine.js +365 -14
- umap/static/umap/js/modules/sync/hlc.js +106 -0
- umap/static/umap/js/modules/sync/updaters.js +4 -4
- umap/static/umap/js/modules/sync/websocket.js +1 -1
- umap/static/umap/js/modules/ui/base.js +2 -2
- umap/static/umap/js/modules/ui/contextmenu.js +34 -17
- umap/static/umap/js/modules/urls.js +5 -1
- umap/static/umap/js/modules/utils.js +5 -1
- umap/static/umap/js/umap.controls.js +47 -47
- umap/static/umap/js/umap.core.js +3 -3
- umap/static/umap/js/umap.forms.js +3 -1
- umap/static/umap/js/umap.js +95 -112
- umap/static/umap/locale/br.js +13 -4
- umap/static/umap/locale/br.json +13 -4
- umap/static/umap/locale/ca.js +21 -12
- umap/static/umap/locale/ca.json +21 -12
- umap/static/umap/locale/cs_CZ.js +87 -78
- umap/static/umap/locale/cs_CZ.json +87 -78
- umap/static/umap/locale/de.js +17 -8
- umap/static/umap/locale/de.json +17 -8
- umap/static/umap/locale/en.js +9 -2
- umap/static/umap/locale/en.json +9 -2
- umap/static/umap/locale/eu.js +10 -3
- umap/static/umap/locale/eu.json +10 -3
- umap/static/umap/locale/fa_IR.js +11 -4
- umap/static/umap/locale/fa_IR.json +11 -4
- umap/static/umap/locale/fr.js +11 -4
- umap/static/umap/locale/fr.json +11 -4
- umap/static/umap/locale/hu.js +10 -3
- umap/static/umap/locale/hu.json +10 -3
- umap/static/umap/locale/pt.js +17 -8
- umap/static/umap/locale/pt.json +17 -8
- umap/static/umap/locale/pt_PT.js +13 -4
- umap/static/umap/locale/pt_PT.json +13 -4
- umap/static/umap/locale/zh_TW.js +13 -4
- umap/static/umap/locale/zh_TW.json +13 -4
- umap/static/umap/map.css +7 -22
- umap/static/umap/unittests/hlc.js +158 -0
- umap/static/umap/unittests/sync.js +321 -15
- umap/static/umap/unittests/utils.js +23 -0
- umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
- umap/templates/umap/dashboard_menu.html +4 -2
- umap/templates/umap/js.html +0 -4
- umap/tests/integration/test_anonymous_owned_map.py +1 -0
- umap/tests/integration/test_basics.py +1 -1
- umap/tests/integration/test_circles_layer.py +12 -0
- umap/tests/integration/test_datalayer.py +5 -0
- umap/tests/integration/test_draw_polygon.py +17 -9
- umap/tests/integration/test_draw_polyline.py +12 -8
- umap/tests/integration/test_edit_datalayer.py +4 -6
- umap/tests/integration/test_edit_map.py +1 -1
- umap/tests/integration/test_import.py +5 -0
- umap/tests/integration/test_map.py +5 -0
- umap/tests/integration/test_owned_map.py +1 -1
- umap/tests/integration/test_view_polygon.py +12 -12
- umap/tests/integration/test_websocket_sync.py +65 -3
- umap/tests/test_clean_tilelayer.py +83 -0
- umap/tests/test_datalayer.py +24 -0
- umap/tests/test_map_views.py +1 -0
- umap/tests/test_purge_purgatory.py +25 -0
- umap/tests/test_websocket_server.py +22 -0
- umap/urls.py +5 -1
- umap/views.py +6 -3
- umap/websocket_server.py +130 -27
- {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/METADATA +9 -9
- {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/RECORD +102 -97
- umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
- umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
- {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/WHEEL +0 -0
- {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,7 +10,7 @@ msgid ""
|
|
|
10
10
|
msgstr ""
|
|
11
11
|
"Project-Id-Version: uMap\n"
|
|
12
12
|
"Report-Msgid-Bugs-To: \n"
|
|
13
|
-
"POT-Creation-Date: 2024-
|
|
13
|
+
"POT-Creation-Date: 2024-10-04 16:35+0000\n"
|
|
14
14
|
"PO-Revision-Date: 2013-11-22 14:00+0000\n"
|
|
15
15
|
"Last-Translator: lecalam, 2024\n"
|
|
16
16
|
"Language-Team: Portuguese (http://app.transifex.com/openstreetmap/umap/language/pt/)\n"
|
|
@@ -20,6 +20,10 @@ msgstr ""
|
|
|
20
20
|
"Language: pt\n"
|
|
21
21
|
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
|
22
22
|
|
|
23
|
+
#: admin.py:16
|
|
24
|
+
msgid "CSV Export"
|
|
25
|
+
msgstr ""
|
|
26
|
+
|
|
23
27
|
#: forms.py:44 forms.py:70
|
|
24
28
|
msgid "Only editable with secret edit link"
|
|
25
29
|
msgstr "Unicamente editável através de hiperligação secreta"
|
|
@@ -28,7 +32,7 @@ msgstr "Unicamente editável através de hiperligação secreta"
|
|
|
28
32
|
msgid "Everyone can edit"
|
|
29
33
|
msgstr "Todos podem editar"
|
|
30
34
|
|
|
31
|
-
#: forms.py:69 models.py:
|
|
35
|
+
#: forms.py:69 models.py:441
|
|
32
36
|
msgid "Inherit"
|
|
33
37
|
msgstr "Herdado"
|
|
34
38
|
|
|
@@ -36,111 +40,111 @@ msgstr "Herdado"
|
|
|
36
40
|
msgid "Site is readonly for maintenance"
|
|
37
41
|
msgstr "O site está em modo de leitura para manutenção"
|
|
38
42
|
|
|
39
|
-
#: models.py:
|
|
43
|
+
#: models.py:55 models.py:74
|
|
40
44
|
msgid "name"
|
|
41
45
|
msgstr "nome"
|
|
42
46
|
|
|
43
|
-
#: models.py:
|
|
47
|
+
#: models.py:57 models.py:451
|
|
44
48
|
msgid "description"
|
|
45
49
|
msgstr "descrição"
|
|
46
50
|
|
|
47
|
-
#: models.py:
|
|
51
|
+
#: models.py:105
|
|
48
52
|
msgid "details"
|
|
49
53
|
msgstr "detalhes"
|
|
50
54
|
|
|
51
|
-
#: models.py:
|
|
55
|
+
#: models.py:106
|
|
52
56
|
msgid "Link to a page where the licence is detailed."
|
|
53
57
|
msgstr "Hiperligação para uma página detalhando a licença."
|
|
54
58
|
|
|
55
|
-
#: models.py:
|
|
59
|
+
#: models.py:116
|
|
56
60
|
msgid "URL template using OSM tile format"
|
|
57
61
|
msgstr "Modelo de URL no formato de mosaicos OSM"
|
|
58
62
|
|
|
59
|
-
#: models.py:
|
|
63
|
+
#: models.py:122
|
|
60
64
|
msgid "Order of the tilelayers in the edit box"
|
|
61
65
|
msgstr "Ordem das camadas de mosaicos na caixa de edição"
|
|
62
66
|
|
|
63
|
-
#: models.py:
|
|
67
|
+
#: models.py:168 models.py:442
|
|
64
68
|
msgid "Everyone"
|
|
65
69
|
msgstr "Todos"
|
|
66
70
|
|
|
67
|
-
#: models.py:
|
|
71
|
+
#: models.py:169 models.py:175 models.py:443
|
|
68
72
|
msgid "Editors and team only"
|
|
69
|
-
msgstr ""
|
|
73
|
+
msgstr "Apenas editores e equipa"
|
|
70
74
|
|
|
71
|
-
#: models.py:
|
|
75
|
+
#: models.py:170 models.py:444
|
|
72
76
|
msgid "Owner only"
|
|
73
77
|
msgstr "Apenas o proprietário"
|
|
74
78
|
|
|
75
|
-
#: models.py:
|
|
79
|
+
#: models.py:173
|
|
76
80
|
msgid "Everyone (public)"
|
|
77
81
|
msgstr "Todos (público)"
|
|
78
82
|
|
|
79
|
-
#: models.py:
|
|
83
|
+
#: models.py:174
|
|
80
84
|
msgid "Anyone with link"
|
|
81
85
|
msgstr "Quem tiver a hiperligação"
|
|
82
86
|
|
|
83
|
-
#: models.py:
|
|
87
|
+
#: models.py:176
|
|
84
88
|
msgid "Blocked"
|
|
85
89
|
msgstr "Bloqueado"
|
|
86
90
|
|
|
87
|
-
#: models.py:
|
|
91
|
+
#: models.py:179
|
|
88
92
|
msgid "center"
|
|
89
93
|
msgstr "centro"
|
|
90
94
|
|
|
91
|
-
#: models.py:
|
|
95
|
+
#: models.py:180
|
|
92
96
|
msgid "zoom"
|
|
93
97
|
msgstr "zoom"
|
|
94
98
|
|
|
95
|
-
#: models.py:
|
|
99
|
+
#: models.py:182
|
|
96
100
|
msgid "locate"
|
|
97
101
|
msgstr "localizar"
|
|
98
102
|
|
|
99
|
-
#: models.py:
|
|
103
|
+
#: models.py:182
|
|
100
104
|
msgid "Locate user on load?"
|
|
101
105
|
msgstr "Localizar utilizador no início?"
|
|
102
106
|
|
|
103
|
-
#: models.py:
|
|
107
|
+
#: models.py:186
|
|
104
108
|
msgid "Choose the map licence."
|
|
105
109
|
msgstr "Escolha uma licença para o mapa."
|
|
106
110
|
|
|
107
|
-
#: models.py:
|
|
111
|
+
#: models.py:187
|
|
108
112
|
msgid "licence"
|
|
109
113
|
msgstr "licença"
|
|
110
114
|
|
|
111
|
-
#: models.py:
|
|
115
|
+
#: models.py:198
|
|
112
116
|
msgid "owner"
|
|
113
117
|
msgstr "proprietário"
|
|
114
118
|
|
|
115
|
-
#: models.py:
|
|
119
|
+
#: models.py:202
|
|
116
120
|
msgid "editors"
|
|
117
121
|
msgstr "editores"
|
|
118
122
|
|
|
119
|
-
#: models.py:
|
|
123
|
+
#: models.py:208
|
|
120
124
|
msgid "team"
|
|
121
|
-
msgstr ""
|
|
125
|
+
msgstr "equipa"
|
|
122
126
|
|
|
123
|
-
#: models.py:
|
|
127
|
+
#: models.py:214 models.py:465
|
|
124
128
|
msgid "edit status"
|
|
125
129
|
msgstr "editar estado"
|
|
126
130
|
|
|
127
|
-
#: models.py:
|
|
131
|
+
#: models.py:219
|
|
128
132
|
msgid "share status"
|
|
129
133
|
msgstr "partilhar estado"
|
|
130
134
|
|
|
131
|
-
#: models.py:
|
|
135
|
+
#: models.py:222 models.py:460
|
|
132
136
|
msgid "settings"
|
|
133
137
|
msgstr "parâmetros"
|
|
134
138
|
|
|
135
|
-
#: models.py:
|
|
139
|
+
#: models.py:382
|
|
136
140
|
msgid "Clone of"
|
|
137
141
|
msgstr "Clone de"
|
|
138
142
|
|
|
139
|
-
#: models.py:
|
|
143
|
+
#: models.py:455
|
|
140
144
|
msgid "display on load"
|
|
141
145
|
msgstr "mostrar no início"
|
|
142
146
|
|
|
143
|
-
#: models.py:
|
|
147
|
+
#: models.py:456
|
|
144
148
|
msgid "Display this layer on load."
|
|
145
149
|
msgstr "Mostrar esta camada ao carregar."
|
|
146
150
|
|
|
@@ -324,19 +328,19 @@ msgstr "Esta é uma versão de demonstração, utilizada para testes e pré-lan
|
|
|
324
328
|
|
|
325
329
|
#: templates/umap/content_footer.html:5
|
|
326
330
|
msgid "An OpenStreetMap project"
|
|
327
|
-
msgstr ""
|
|
331
|
+
msgstr "Um projeto OpenStreetMap"
|
|
328
332
|
|
|
329
333
|
#: templates/umap/content_footer.html:6
|
|
330
334
|
msgid "version"
|
|
331
|
-
msgstr ""
|
|
335
|
+
msgstr "versão"
|
|
332
336
|
|
|
333
337
|
#: templates/umap/content_footer.html:7
|
|
334
338
|
msgid "Hosted by"
|
|
335
|
-
msgstr ""
|
|
339
|
+
msgstr "Alojado por"
|
|
336
340
|
|
|
337
341
|
#: templates/umap/content_footer.html:8
|
|
338
342
|
msgid "Contact"
|
|
339
|
-
msgstr ""
|
|
343
|
+
msgstr "Contacto"
|
|
340
344
|
|
|
341
345
|
#: templates/umap/content_footer.html:9 templates/umap/navigation.html:25
|
|
342
346
|
msgid "Help"
|
|
@@ -351,13 +355,13 @@ msgstr "Meus mapas (%(count)s)"
|
|
|
351
355
|
msgid "My Maps"
|
|
352
356
|
msgstr "Meus mapas"
|
|
353
357
|
|
|
354
|
-
#: templates/umap/dashboard_menu.html:
|
|
358
|
+
#: templates/umap/dashboard_menu.html:12
|
|
355
359
|
msgid "My profile"
|
|
356
360
|
msgstr "Meu perfil"
|
|
357
361
|
|
|
358
|
-
#: templates/umap/dashboard_menu.html:
|
|
362
|
+
#: templates/umap/dashboard_menu.html:15
|
|
359
363
|
msgid "My teams"
|
|
360
|
-
msgstr ""
|
|
364
|
+
msgstr "Minhas equipas"
|
|
361
365
|
|
|
362
366
|
#: templates/umap/home.html:14
|
|
363
367
|
msgid "Map of the uMaps"
|
|
@@ -371,11 +375,11 @@ msgstr "Inspire-se, explore os mapas"
|
|
|
371
375
|
msgid "You are logged in. Continuing..."
|
|
372
376
|
msgstr "Sucesso na identificação. Continuando..."
|
|
373
377
|
|
|
374
|
-
#: templates/umap/map_list.html:
|
|
378
|
+
#: templates/umap/map_list.html:11 views.py:433
|
|
375
379
|
msgid "by"
|
|
376
380
|
msgstr "por"
|
|
377
381
|
|
|
378
|
-
#: templates/umap/map_list.html:
|
|
382
|
+
#: templates/umap/map_list.html:20
|
|
379
383
|
msgid "More"
|
|
380
384
|
msgstr "Mais"
|
|
381
385
|
|
|
@@ -552,20 +556,20 @@ msgstr "Procurar"
|
|
|
552
556
|
#: templates/umap/team_detail.html:10
|
|
553
557
|
#, python-format
|
|
554
558
|
msgid "Browse %(current_team)s's maps"
|
|
555
|
-
msgstr ""
|
|
559
|
+
msgstr "Ver mapas de %(current_team)s"
|
|
556
560
|
|
|
557
561
|
#: templates/umap/team_detail.html:22
|
|
558
562
|
#, python-format
|
|
559
563
|
msgid "%(current_team)s has no public maps."
|
|
560
|
-
msgstr ""
|
|
564
|
+
msgstr "%(current_team)s não tem mapas públicos."
|
|
561
565
|
|
|
562
566
|
#: templates/umap/team_form.html:24
|
|
563
567
|
msgid "Delete this team"
|
|
564
|
-
msgstr ""
|
|
568
|
+
msgstr "Eliminar esta equipa"
|
|
565
569
|
|
|
566
570
|
#: templates/umap/team_form.html:47
|
|
567
571
|
msgid "Add user"
|
|
568
|
-
msgstr ""
|
|
572
|
+
msgstr "Adicionar utilizador"
|
|
569
573
|
|
|
570
574
|
#: templates/umap/user_dashboard.html:9 templates/umap/user_dashboard.html:25
|
|
571
575
|
msgid "Search my maps"
|
|
@@ -586,76 +590,76 @@ msgstr "Ainda não tem nenhum mapa."
|
|
|
586
590
|
|
|
587
591
|
#: templates/umap/user_teams.html:17
|
|
588
592
|
msgid "Users"
|
|
589
|
-
msgstr ""
|
|
593
|
+
msgstr "Utilizadores"
|
|
590
594
|
|
|
591
595
|
#: templates/umap/user_teams.html:48
|
|
592
596
|
msgid "New team"
|
|
593
|
-
msgstr ""
|
|
597
|
+
msgstr "Nova equipa"
|
|
594
598
|
|
|
595
599
|
#: views.py:235
|
|
596
600
|
msgid "Cannot delete a team with more than one member"
|
|
597
|
-
msgstr ""
|
|
601
|
+
msgstr "Não é possível eliminar uma equipa com mais de um membro"
|
|
598
602
|
|
|
599
603
|
#: views.py:239
|
|
600
604
|
#, python-format
|
|
601
605
|
msgid "Team “%(name)s” has been deleted"
|
|
602
|
-
msgstr ""
|
|
606
|
+
msgstr "A equipa “%(name)s” foi eliminada"
|
|
603
607
|
|
|
604
608
|
#: views.py:438
|
|
605
609
|
msgid "View the map"
|
|
606
610
|
msgstr "Ver o mapa"
|
|
607
611
|
|
|
608
|
-
#: views.py:
|
|
612
|
+
#: views.py:818
|
|
609
613
|
msgid "See full screen"
|
|
610
614
|
msgstr "Ver em ecrã inteiro"
|
|
611
615
|
|
|
612
|
-
#: views.py:
|
|
616
|
+
#: views.py:950
|
|
613
617
|
msgid "Map editors updated with success!"
|
|
614
618
|
msgstr "Os editores do mapa foram atualizados com sucesso!"
|
|
615
619
|
|
|
616
|
-
#: views.py:
|
|
620
|
+
#: views.py:986
|
|
617
621
|
#, python-format
|
|
618
622
|
msgid "The uMap edit link for your map: %(map_name)s"
|
|
619
623
|
msgstr "A hiperligação de edição do uMap para o seu mapa: %(map_name)s"
|
|
620
624
|
|
|
621
|
-
#: views.py:
|
|
625
|
+
#: views.py:989
|
|
622
626
|
#, python-format
|
|
623
627
|
msgid "Here is your secret edit link: %(link)s"
|
|
624
628
|
msgstr "Aqui está a hiperligação de edição secreta: %(link)s"
|
|
625
629
|
|
|
626
|
-
#: views.py:
|
|
630
|
+
#: views.py:996
|
|
627
631
|
#, python-format
|
|
628
632
|
msgid "Can't send email to %(email)s"
|
|
629
633
|
msgstr "Não é possível enviar o email para %(email)s"
|
|
630
634
|
|
|
631
|
-
#: views.py:
|
|
635
|
+
#: views.py:999
|
|
632
636
|
#, python-format
|
|
633
637
|
msgid "Email sent to %(email)s"
|
|
634
638
|
msgstr "Email enviado para %(email)s"
|
|
635
639
|
|
|
636
|
-
#: views.py:
|
|
640
|
+
#: views.py:1010
|
|
637
641
|
msgid "Only its owner can delete the map."
|
|
638
642
|
msgstr "Só o proprietário pode eliminar o mapa."
|
|
639
643
|
|
|
640
|
-
#: views.py:
|
|
644
|
+
#: views.py:1013
|
|
641
645
|
msgid "Map successfully deleted."
|
|
642
646
|
msgstr "Mapa eliminado com sucesso."
|
|
643
647
|
|
|
644
|
-
#: views.py:
|
|
648
|
+
#: views.py:1039
|
|
645
649
|
#, python-format
|
|
646
650
|
msgid ""
|
|
647
651
|
"Your map has been cloned! If you want to edit this map from another "
|
|
648
652
|
"computer, please use this link: %(anonymous_url)s"
|
|
649
653
|
msgstr "O seu mapa foi clonado! Se quiser editar este mapa noutro computador, por favor utilize esta hiperligação: %(anonymous_url)s"
|
|
650
654
|
|
|
651
|
-
#: views.py:
|
|
655
|
+
#: views.py:1044
|
|
652
656
|
msgid "Congratulations, your map has been cloned!"
|
|
653
657
|
msgstr "Parabéns, o seu mapa foi clonado!"
|
|
654
658
|
|
|
655
|
-
#: views.py:
|
|
659
|
+
#: views.py:1277
|
|
656
660
|
msgid "Layer successfully deleted."
|
|
657
661
|
msgstr "Camada eliminada com sucesso."
|
|
658
662
|
|
|
659
|
-
#: views.py:
|
|
663
|
+
#: views.py:1299
|
|
660
664
|
msgid "Permissions updated with success!"
|
|
661
665
|
msgstr "Permissões atualizadas com sucesso!"
|
|
@@ -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(
|
|
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(
|
|
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
|
@@ -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
|
}
|
umap/static/umap/css/icon.css
CHANGED
|
@@ -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) *
|
|
110
|
+
background-position: var(--tile) calc(var(--tile) * 3);
|
|
109
111
|
}
|
|
110
112
|
.icon-polyline {
|
|
111
|
-
background-position: 0 calc(var(--tile) *
|
|
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);
|