aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--development.ini1
-rw-r--r--fietsboek/__init__.py9
-rw-r--r--fietsboek/locale/fietslog.pot102
-rw-r--r--fietsboek/routes.py3
-rw-r--r--fietsboek/static/osm-monkeypatch.js352
-rw-r--r--fietsboek/templates/layout.jinja23
-rw-r--r--fietsboek/views/tileproxy.py72
-rw-r--r--poetry.lock214
-rw-r--r--pylint.toml2
-rw-r--r--pyproject.toml2
10 files changed, 705 insertions, 55 deletions
diff --git a/development.ini b/development.ini
index eb33586..3259d3b 100644
--- a/development.ini
+++ b/development.ini
@@ -18,6 +18,7 @@ pyramid.includes =
pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/fietsboek.sqlite
+redis.url = redis://localhost/
fietsboek.data_dir = %(here)s/data
retry.attempts = 3
diff --git a/fietsboek/__init__.py b/fietsboek/__init__.py
index a727230..6dc435a 100644
--- a/fietsboek/__init__.py
+++ b/fietsboek/__init__.py
@@ -2,8 +2,10 @@
For more information, see the README or the included documentation.
"""
+import importlib.metadata
from pathlib import Path
+import redis
from pyramid.config import Configurator
from pyramid.session import SignedCookieSessionFactory
from pyramid.csrf import CookieCSRFStoragePolicy
@@ -16,6 +18,9 @@ from .pages import Pages
from . import jinja2 as fiets_jinja2
+__VERSION__ = importlib.metadata.version('fietsboek')
+
+
def locale_negotiator(request):
"""Negotiates the right locale to use.
@@ -57,6 +62,9 @@ def main(global_config, **settings):
data_dir = request.registry.settings["fietsboek.data_dir"]
return DataManager(Path(data_dir))
+ def redis_(request):
+ return redis.from_url(request.registry.settings["redis.url"])
+
settings['enable_account_registration'] = asbool(
settings.get('enable_account_registration', 'false'))
settings['available_locales'] = aslist(
@@ -90,6 +98,7 @@ def main(global_config, **settings):
config.set_locale_negotiator(locale_negotiator)
config.add_request_method(data_manager, reify=True)
config.add_request_method(pages, reify=True)
+ config.add_request_method(redis_, name="redis", reify=True)
jinja2_env = config.get_jinja2_environment()
jinja2_env.filters['format_decimal'] = fiets_jinja2.filter_format_decimal
diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot
index 91390c8..abb2bfc 100644
--- a/fietsboek/locale/fietslog.pot
+++ b/fietsboek/locale/fietslog.pot
@@ -8,14 +8,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-08-10 13:36+0200\n"
+"POT-Creation-Date: 2022-11-15 23:42+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.10.3\n"
+"Generated-By: Babel 2.11.0\n"
#: fietsboek/util.py:282
msgid "password_constraint.mismatch"
@@ -153,43 +153,43 @@ msgstr ""
msgid "page.browse.synthetic_tooltip"
msgstr ""
-#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:88
+#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:90
msgid "page.details.date"
msgstr ""
-#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:104
msgid "page.details.length"
msgstr ""
-#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:93
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:95
msgid "page.details.start_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:97
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:99
msgid "page.details.end_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:108
msgid "page.details.uphill"
msgstr ""
-#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:112
msgid "page.details.downhill"
msgstr ""
-#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:115
+#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:117
msgid "page.details.moving_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:119
+#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:121
msgid "page.details.stopped_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:123
+#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:125
msgid "page.details.max_speed"
msgstr ""
-#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:127
+#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:129
msgid "page.details.avg_speed"
msgstr ""
@@ -293,40 +293,40 @@ msgstr ""
msgid "page.details.delete.close"
msgstr ""
-#: fietsboek/templates/details.jinja2:69
+#: fietsboek/templates/details.jinja2:70
msgid "page.details.tags"
msgstr ""
-#: fietsboek/templates/details.jinja2:78 fietsboek/templates/edit.jinja2:10
+#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10
#: fietsboek/templates/finish_upload.jinja2:10
msgid "page.noscript"
msgstr ""
-#: fietsboek/templates/details.jinja2:83
+#: fietsboek/templates/details.jinja2:85
msgid "page.details.download"
msgstr ""
-#: fietsboek/templates/details.jinja2:172
+#: fietsboek/templates/details.jinja2:174
msgid "page.details.comments"
msgstr ""
-#: fietsboek/templates/details.jinja2:176
+#: fietsboek/templates/details.jinja2:178
msgid "page.details.comments.author"
msgstr ""
-#: fietsboek/templates/details.jinja2:193
+#: fietsboek/templates/details.jinja2:195
msgid "page.details.comments.new.title"
msgstr ""
-#: fietsboek/templates/details.jinja2:196
+#: fietsboek/templates/details.jinja2:198
msgid "page.details.comments.new.input_title"
msgstr ""
-#: fietsboek/templates/details.jinja2:197
+#: fietsboek/templates/details.jinja2:199
msgid "page.details.comments.new.input_comment"
msgstr ""
-#: fietsboek/templates/details.jinja2:200
+#: fietsboek/templates/details.jinja2:202
msgid "page.details.comments.new.submit"
msgstr ""
@@ -394,40 +394,40 @@ msgstr ""
msgid "page.track.form.tags"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:43
+#: fietsboek/templates/edit_form.jinja2:50
msgid "page.track.form.add_tag"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:48
+#: fietsboek/templates/edit_form.jinja2:55
msgid "page.track.form.tagged_people"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:63
+#: fietsboek/templates/edit_form.jinja2:70
msgid "page.track.form.add_friend"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:83
+#: fietsboek/templates/edit_form.jinja2:90
msgid "page.track.form.badges"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:94
+#: fietsboek/templates/edit_form.jinja2:101
msgid "page.track.form.description"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:101
-#: fietsboek/templates/edit_form.jinja2:115
+#: fietsboek/templates/edit_form.jinja2:108
+#: fietsboek/templates/edit_form.jinja2:122
msgid "page.track.form.remove_image"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:110
+#: fietsboek/templates/edit_form.jinja2:117
msgid "page.track.form.select_images"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:126
+#: fietsboek/templates/edit_form.jinja2:133
msgid "page.track.form.image_description_modal"
msgstr ""
-#: fietsboek/templates/edit_form.jinja2:133
+#: fietsboek/templates/edit_form.jinja2:140
msgid "page.track.form.image_description_modal.save"
msgstr ""
@@ -457,43 +457,43 @@ msgstr ""
msgid "page.navbar.toggle"
msgstr ""
-#: fietsboek/templates/layout.jinja2:46
+#: fietsboek/templates/layout.jinja2:51
msgid "page.navbar.home"
msgstr ""
-#: fietsboek/templates/layout.jinja2:49
+#: fietsboek/templates/layout.jinja2:54
msgid "page.navbar.browse"
msgstr ""
-#: fietsboek/templates/layout.jinja2:53
+#: fietsboek/templates/layout.jinja2:58
msgid "page.navbar.upload"
msgstr ""
-#: fietsboek/templates/layout.jinja2:57
+#: fietsboek/templates/layout.jinja2:67
msgid "page.navbar.user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:61
+#: fietsboek/templates/layout.jinja2:71
msgid "page.navbar.welcome_user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:64
+#: fietsboek/templates/layout.jinja2:74
msgid "page.navbar.logout"
msgstr ""
-#: fietsboek/templates/layout.jinja2:67
+#: fietsboek/templates/layout.jinja2:77
msgid "page.navbar.profile"
msgstr ""
-#: fietsboek/templates/layout.jinja2:71
+#: fietsboek/templates/layout.jinja2:81
msgid "page.navbar.admin"
msgstr ""
-#: fietsboek/templates/layout.jinja2:77
+#: fietsboek/templates/layout.jinja2:87
msgid "page.navbar.login"
msgstr ""
-#: fietsboek/templates/layout.jinja2:81
+#: fietsboek/templates/layout.jinja2:91
msgid "page.navbar.create_account"
msgstr ""
@@ -645,43 +645,43 @@ msgstr ""
msgid "flash.badge_deleted"
msgstr ""
-#: fietsboek/views/default.py:75
+#: fietsboek/views/default.py:96
msgid "flash.invalid_credentials"
msgstr ""
-#: fietsboek/views/default.py:79
+#: fietsboek/views/default.py:100
msgid "flash.account_not_verified"
msgstr ""
-#: fietsboek/views/default.py:82
+#: fietsboek/views/default.py:103
msgid "flash.logged_in"
msgstr ""
-#: fietsboek/views/default.py:96
+#: fietsboek/views/default.py:117
msgid "flash.logged_out"
msgstr ""
-#: fietsboek/views/default.py:127
+#: fietsboek/views/default.py:148
msgid "flash.reset_invalid_email"
msgstr ""
-#: fietsboek/views/default.py:132
+#: fietsboek/views/default.py:153
msgid "flash.password_token_generated"
msgstr ""
-#: fietsboek/views/default.py:137
+#: fietsboek/views/default.py:158
msgid "page.password_reset.email.subject"
msgstr ""
-#: fietsboek/views/default.py:141
+#: fietsboek/views/default.py:162
msgid "page.password_reset.email.body"
msgstr ""
-#: fietsboek/views/default.py:168
+#: fietsboek/views/default.py:189
msgid "flash.email_verified"
msgstr ""
-#: fietsboek/views/default.py:182
+#: fietsboek/views/default.py:203
msgid "flash.password_updated"
msgstr ""
diff --git a/fietsboek/routes.py b/fietsboek/routes.py
index ab1eabf..9286f13 100644
--- a/fietsboek/routes.py
+++ b/fietsboek/routes.py
@@ -54,3 +54,6 @@ def includeme(config):
config.add_route('delete-friend', '/me/delete-friend')
config.add_route('accept-friend', '/me/accept-friend')
config.add_route('json-friends', '/me/friends.json')
+
+ config.add_route('tile-proxy',
+ '/tile/{provider}/{z:\\d+}/{x:\\d+}/{y:\\d+}')
diff --git a/fietsboek/static/osm-monkeypatch.js b/fietsboek/static/osm-monkeypatch.js
new file mode 100644
index 0000000..203a4ca
--- /dev/null
+++ b/fietsboek/static/osm-monkeypatch.js
@@ -0,0 +1,352 @@
+/* We want to override JB.Map to add our own maps instead.
+ * We do this by (ab)using the JS property system to override the setter, so
+ * that JB.Map won't actually set the new function. This means we don't have to
+ * source-patch the gmutils.js file.
+ */
+"use strict";
+
+(() => {
+ let ourMap = function(makemap) {
+ var dieses = this;
+ var id = makemap.id;
+ var mapcanvas = makemap.mapdiv;
+ dieses.id = id;
+ dieses.makemap = makemap;
+ dieses.mapcanvas = mapcanvas;
+ this.cluster_zoomhistory = [];
+
+ // Map anlegen
+
+ const mycp = '<a href="https://www.j-berkemeier.de/GPXViewer" title="GPX Viewer '+JB.GPX2GM.ver+'">GPXViewer</a> | ';
+
+ this.baseLayers = {};
+
+ this.baseLayers["OSM"] = L.tileLayer(BASE_URL + 'tile/osm/{z}/{x}/{y}', {
+ maxZoom: 19,
+ attribution: mycp+'Map data &copy; <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> and contributors <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank">CC-BY-SA</a>'
+ });
+
+ this.baseLayers["Satellit"] = L.tileLayer(BASE_URL + 'tile/satellite/{z}/{x}/{y}', {
+ maxZoom: 21,
+ attribution: mycp+'Map data &copy; <a href="https://www.esri.com/">Esri</a>, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
+ });
+
+ this.baseLayers["OSMDE"] = L.tileLayer(BASE_URL + 'tile/osmde/{z}/{x}/{y}', {
+ maxZoom: 19,
+ attribution: mycp+'Map data &copy; <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> and contributors <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank">CC-BY-SA</a>'
+ });
+
+ this.baseLayers["Open Topo"] = L.tileLayer(BASE_URL + 'tile/opentopo/{z}/{x}/{y}', {
+ maxZoom: 17,
+ attribution: mycp+'Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © <a href="https://opentopomap.org/about">OpenTopoMap</a> (CC-BY-SA)'
+ });
+
+ this.baseLayers["TopPlusOpen"] = L.tileLayer(BASE_URL + 'tile/topplusopen/{z}/{x}/{y}', {
+ maxZoom: 18,
+ attribution: mycp+'Kartendaten: © <a href="https://www.bkg.bund.de/SharedDocs/Produktinformationen/BKG/DE/P-2017/170922-TopPlus-Web-Open.html" target==_blank"">Bundesamt für Kartographie und Geodäsie</a>'
+ });
+
+ // https://tileserver.4umaps.com/${z}/${x}/${y}.png
+ // zoomlevel 16
+ // https://www.4umaps.com/
+
+ if(JB.GPX2GM.OSM_Cycle_Api_Key && JB.GPX2GM.OSM_Cycle_Api_Key.length>0) {
+ this.baseLayers["Cycle"] = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey='+JB.GPX2GM.OSM_Cycle_Api_Key, {
+ maxZoom: 22,
+ attribution: mycp+'Map data &copy; <a href="https://www.thunderforest.com/" target="_blank">OpenCycleMap</a> and contributors <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank">CC-BY-SA</a>'
+ });
+ }
+
+ if(JB.GPX2GM.OSM_Landscape_Api_Key && JB.GPX2GM.OSM_Landscape_Api_Key.length>0) {
+ this.baseLayers["Landscape"] = L.tileLayer('https://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png?apikey='+JB.GPX2GM.OSM_Landscape_Api_Key, {
+ maxZoom: 22,
+ attribution: mycp+'Map data &copy; <a href="https://www.thunderforest.com/" target="_blank">OpenLandscapeMap</a> and contributors <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank">CC-BY-SA</a>'
+ });
+ }
+
+ if(JB.GPX2GM.OSM_Outdoors_Api_Key && JB.GPX2GM.OSM_Outdoors_Api_Key.length>0) {
+ this.baseLayers["Outdoors"] = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey='+JB.GPX2GM.OSM_Outdoors_Api_Key, {
+ maxZoom: 22,
+ attribution: mycp+'Map data &copy; <a href="https://www.thunderforest.com/" target="_blank">OpenOutdoorsMap</a> and contributors <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank">CC-BY-SA</a>'
+ });
+ }
+
+ this.baseLayers[JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].noMap]= L.tileLayer(JB.GPX2GM.Path+"Icons/Grau256x256.png", {
+ maxZoom: 22,
+ attribution: mycp
+ });
+
+ this.overlayLayers = {};
+
+ this.overlayLayers["Open Sea"] = L.tileLayer(BASE_URL + 'tile/opensea/{z}/{x}/{y}', {
+ attribution: 'Kartendaten: © <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
+ });
+
+ this.overlayLayers["Hiking"] = L.tileLayer(BASE_URL + 'tile/hiking/{z}/{x}/{y}', {
+ attribution: '&copy; <a href="http://waymarkedtrails.org">Sarah Hoffmann</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
+ });
+
+ this.overlayLayers["Cycling"] = L.tileLayer(BASE_URL + 'tile/cycling/{z}/{x}/{y}', {
+ attribution: '&copy; <a href="http://waymarkedtrails.org">Sarah Hoffmann</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
+ });
+
+ this.layerNameTranslate = {
+ satellit: "Satellit",
+ satellite: "Satellit",
+ osm: "OSM",
+ osmde: "OSMDE",
+ opentopo: "Open Topo",
+ topplusopen: "TopPlusOpen",
+ cycle: "Cycle",
+ landscape: "Landscape",
+ outdoors: "Outdoors",
+ keinekarte: "Keine Karte",
+ pasdecarte: "Pas de carte",
+ nomap: "No Map",
+ ning\u00FAnmapa: "Ning\u00FAn Mapa",
+ nessunamappa: "Nessuna mappa",
+ opensea: "Open Sea",
+ hiking: "Hiking",
+ cycling: "Cycling",
+ }
+
+ // ['hiking', 'cycling', 'mtb', 'skating', 'slopes', 'riding'];
+
+ var genugplatz = JB.platzgenug(makemap.mapdiv);
+
+ this.map = L.map(mapcanvas, {
+ // layers: osm,
+ closePopupOnClick: false,
+ scrollWheelZoom: genugplatz & makemap.parameters.scrollwheelzoom,
+ tap: genugplatz,
+ keyboard: genugplatz,
+ touchZoom: true,
+ dragging: true,
+ } );
+
+ JB.handle_touch_action(dieses,genugplatz);
+
+ if(makemap.parameters.unit=="si") L.control.scale({imperial:false}).addTo(this.map); // Mit Maßstab km
+ else L.control.scale({metric:false}).addTo(this.map); // Mit Maßstab ml
+
+ var ctrl_layer = null;
+ var showmaptypecontroll_save = makemap.parameters.showmaptypecontroll;
+ JB.onresize(mapcanvas,function(w,h) {
+ makemap.parameters.showmaptypecontroll = (w>200 && h>190 && showmaptypecontroll_save);
+ if(makemap.parameters.showmaptypecontroll) {
+ if(!ctrl_layer) ctrl_layer = L.control.layers(dieses.baseLayers, dieses.overlayLayers).addTo(dieses.map);
+ }
+ else {
+ if(ctrl_layer) {
+ ctrl_layer.remove();
+ ctrl_layer = null;
+ }
+ }
+ },true);
+
+ // Button für Full Screen / normale Größe
+ var fullscreen = false;
+ if(makemap.parameters.fullscreenbutton) {
+ var fsb = document.createElement("button");
+ fsb.style.backgroundColor = "transparent";
+ fsb.style.border = "none";
+ fsb.style.padding = "7px 7px 7px 0";
+ fsb.style.cursor = "pointer";
+ var fsbim = document.createElement("img");
+ fsbim.width = 31;
+ fsbim.height = 31;
+ fsbim.src = JB.GPX2GM.Path+"Icons/fullscreen_p.svg";
+ fsb.title = fsbim.title = fsbim.alt = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].fullScreen;
+ fsbim.large = false;
+ var ele = mapcanvas.parentNode;
+ fsb.onclick = function() {
+ this.blur();
+ if(fsbim.large) {
+ document.body.style.overflow = "";
+ fsbim.src = JB.GPX2GM.Path+"Icons/fullscreen_p.svg";
+ fsb.title = fsbim.title = fsbim.alt = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].fullScreen;
+ ele.style.left = ele.oleft + "px";
+ ele.style.top = ele.otop + "px";
+ ele.style.width = ele.owidth + "px";
+ ele.style.height = ele.oheight + "px";
+ ele.style.margin = ele.omargin;
+ ele.style.padding = ele.opadding;
+ window.setTimeout(function() {
+ JB.removeClass("JBfull",ele);
+ ele.style.position = ele.sposition;
+ ele.style.left = ele.sleft;
+ ele.style.top = ele.stop;
+ ele.style.width = ele.swidth;
+ ele.style.height = ele.sheight;
+ //ele.style.zIndex = ele.szindex;
+ },1000);
+ JB.handle_touch_action(dieses,genugplatz);
+ fullscreen = false;
+ }
+ else {
+ document.body.style.overflow = "hidden";
+ fsbim.src = JB.GPX2GM.Path+"Icons/fullscreen_m.svg";
+ fsb.title = fsbim.title = fsbim.alt = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].normalSize;
+ var scrollY = 0;
+ if(document.documentElement.scrollTop && document.documentElement.scrollTop!=0) scrollY = document.documentElement.scrollTop;
+ else if(document.body.scrollTop && document.body.scrollTop!=0) scrollY = document.body.scrollTop;
+ else if(window.scrollY) scrollY = window.scrollY;
+ else if(window.pageYOffset) scrollY = window.pageYOffset;
+ var rect = JB.getRect(ele);
+ ele.oleft = rect.left;
+ ele.otop = rect.top - scrollY;
+ ele.owidth = rect.width;
+ ele.oheight = rect.height;
+ //ele.szindex = ele.style.zIndex;
+ ele.sposition = ele.style.position;
+ ele.omargin = ele.style.margin;
+ ele.opadding = ele.style.padding;
+ ele.sleft = ele.style.left;
+ ele.stop = ele.style.top;
+ ele.swidth = ele.style.width;
+ ele.sheight = ele.style.height;
+ ele.style.position = "fixed";
+ ele.style.left = ele.oleft+"px";
+ ele.style.top = ele.otop+"px";
+ ele.style.width = ele.owidth+"px";
+ ele.style.height = ele.oheight+"px";
+ //ele.style.zIndex = "1001";
+ window.setTimeout(function() {
+ JB.addClass("JBfull",ele);
+ ele.style.width = "100%";
+ ele.style.height = "100%";
+ ele.style.left = "0px";
+ ele.style.top = "0px";
+ ele.style.margin = "0px";
+ ele.style.padding = "0px";
+ },100);
+ dieses.map.scrollWheelZoom.enable();
+ JB.handle_touch_action(dieses,true);
+ makemap.mapdiv.focus();
+ fullscreen = true;
+ }
+ fsbim.large = !fsbim.large;
+ };
+ fsb.appendChild(fsbim);
+ fsb.index = 0;
+ L.Control.Fsbutton = L.Control.extend({
+ onAdd: function(map) {
+ return fsb;
+ }
+ });
+ var fsbutton = new L.Control.Fsbutton({ position: 'topright' });
+ fsbutton.addTo(this.map);
+ } // fullscreenbutton
+
+ // Button für Traffic-Layer
+ if(makemap.parameters.trafficbutton) {
+ console.warn("Traffic-Layer wird unter Leaflet (noch) nicht unterstützt.");
+ }
+
+ // Button für Anzeige aktuelle Position
+ if(makemap.parameters.currentlocationbutton) {
+ var clb = document.createElement("button");
+ clb.style.backgroundColor = "white";
+ clb.style.border = "none";
+ clb.style.width = "28px";
+ clb.style.height = "28px";
+ clb.style.margin = "10px 10px 0 0";
+ clb.style.borderRadius = "2px";
+ clb.style.cursor = "pointer";
+ clb.title = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].showCurrentLocation;
+ var clbimg = document.createElement("img");
+ clbimg.style.position = "absolute";
+ clbimg.style.top = "50%";
+ clbimg.style.left = "50%";
+ clbimg.style.transform = "translate(-50%, -50%)";
+ clbimg.src = JB.GPX2GM.Path+"Icons/whereami.svg";
+ var wpid = -1, marker = null, first;
+ clb.onclick = function() {
+ this.blur();
+ if (navigator.geolocation) {
+ var geolocpos = function(position) {
+ var lat = position.coords.latitude;
+ var lon = position.coords.longitude;
+ marker.setLatLng([lat,lon]);
+ if(first) {
+ dieses.map.setView([lat,lon]);
+ first = false;
+ }
+ }
+ var geolocerror = function(error) {
+ var errorCodes = ["Permission Denied","Position unavailible","Timeout"];
+ var errorString = (error.code<=3)?errorCodes[error.code-1]:"Error code: "+error.code;
+ JB.Debug_Info("Geolocation-Dienst fehlgeschlagen!",errorString+". "+error.message,true);
+ }
+ first = true;
+ if(!marker) marker = dieses.Marker({lat:0,lon:0},JB.icons.CL)[0];
+ if ( wpid == -1 ) {
+ clb.title = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].hideCurrentLocation;
+ wpid = navigator.geolocation.watchPosition(geolocpos,geolocerror,{enableHighAccuracy:true, timeout: 5000, maximumAge: 60000});
+ marker.addTo(dieses.map);
+ JB.Debug_Info("","Geolocation-Dienst wird eingerichtet.",false);
+ }
+ else {
+ clb.title = JB.GPX2GM.strings[JB.GPX2GM.parameters.doclang].showCurrentLocation;
+ navigator.geolocation.clearWatch(wpid);
+ wpid = -1;
+ marker.remove();
+ JB.Debug_Info("","Geolocation-Dienst wird abgeschaltet.",false);
+ }
+ }
+ else JB.Debug_Info("geolocation","Geolocation wird nicht unterstützt!",true);
+ } // click-Handler
+ clb.appendChild(clbimg);
+ L.Control.Clbutton = L.Control.extend({
+ onAdd: function(map) {
+ return clb;
+ }
+ });
+ var clbutton = new L.Control.Clbutton({ position: 'topright' });
+ clbutton.addTo(this.map);
+ } // currentlocationbutton
+
+ // Scalieren nach MAP-Resize
+ dieses.zoomstatus = {};
+ dieses.zoomstatus.iszoomed = false;
+ dieses.zoomstatus.zoom_changed = function() {
+ dieses.zoomstatus.iszoomed = true;
+ dieses.zoomstatus.level = dieses.map.getZoom();
+ dieses.zoomstatus.w = mapcanvas.offsetWidth;
+ dieses.zoomstatus.h = mapcanvas.offsetHeight;
+ }
+ dieses.zoomstatus.move_end = function() {
+ dieses.zoomstatus.iszoomed = true;
+ dieses.mapcenter = dieses.map.getCenter();
+ }
+ dieses.map.on("moveend", dieses.zoomstatus.move_end);
+ JB.onresize(mapcanvas,function(w,h) {
+ if(w*h==0) return;
+ dieses.map.invalidateSize();
+ dieses.map.setView(dieses.mapcenter);
+ dieses.map.off("zoomend", dieses.zoomstatus.zoom_changed);
+ if(dieses.zoomstatus.iszoomed) {
+ var dz = Math.round(Math.min(Math.log(w/dieses.zoomstatus.w)/Math.LN2,Math.log(h/dieses.zoomstatus.h)/Math.LN2));
+ dieses.map.setZoom(dieses.zoomstatus.level+dz);
+ }
+ else {
+ if(dieses.bounds) {
+ dieses.map.fitBounds(dieses.bounds,{padding:[20,20]});
+ dieses.map.setView(dieses.mapcenter);
+ dieses.zoomstatus.level = dieses.map.getZoom();
+ dieses.zoomstatus.w = w;
+ dieses.zoomstatus.h = h;
+ }
+ }
+ if(!fullscreen) {
+ genugplatz = JB.platzgenug(makemap.mapdiv);
+ JB.handle_touch_action(dieses,genugplatz);
+ }
+ });
+ };
+ window.JB = window.JB || {};
+ Object.defineProperty(window.JB, "Map", {
+ get() { return ourMap; },
+ set(_) {},
+ });
+})();
diff --git a/fietsboek/templates/layout.jinja2 b/fietsboek/templates/layout.jinja2
index 7371052..bbd84f8 100644
--- a/fietsboek/templates/layout.jinja2
+++ b/fietsboek/templates/layout.jinja2
@@ -20,6 +20,7 @@
<script>
const FRIENDS_URL = {{ request.route_url('json-friends') | tojson }};
+const BASE_URL = {{ request.route_url('home') | tojson }};
const LOCALE = {{ request.localizer.locale_name.replace('_', '-') | tojson }};
{% if request.registry.settings.get("thunderforest.api_key") %}
{% set api_key = request.registry.settings.get("thunderforest.api_key") %}
@@ -112,6 +113,8 @@ window.JB.GPX2GM.OSM_Outdoors_Api_Key = {{ api_key | tojson }};
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="{{request.static_url('fietsboek:static/bootstrap.bundle.min.js')}}"></script>
+ <!-- Our patch to the GPX viewer, load before the actual GPX viewer -->
+ <script src="{{request.static_url('fietsboek:static/osm-monkeypatch.js')}}"></script>
<!-- Jürgen Berkemeier's GPX viewer -->
<script src="{{request.static_url('fietsboek:static/GM_Utils/GPX2GM.js')}}"></script>
<script src="{{request.static_url('fietsboek:static/fietsboek.js')}}"></script>
diff --git a/fietsboek/views/tileproxy.py b/fietsboek/views/tileproxy.py
new file mode 100644
index 0000000..16fd4c5
--- /dev/null
+++ b/fietsboek/views/tileproxy.py
@@ -0,0 +1,72 @@
+"""Tile proxying layer.
+
+While this might slow down the initial load (as we now load everything through
+fietsboek), we can cache the OSM tiles per instance, and we can provide better
+access control for services like thunderforest.com.
+
+Additionally, this protects the users' IP, as only fietsboek can see it.
+"""
+import datetime
+import random
+
+from pyramid.view import view_config
+from pyramid.response import Response
+from pyramid.httpexceptions import HTTPBadRequest
+
+import requests
+
+from .. import __VERSION__
+
+
+TILE_SOURCES = {
+ # Main base layers
+ 'osm': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ 'osmde': 'https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png',
+ 'opentopo': 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
+ 'topplusopen': 'https://sgx.geodatenzentrum.de/wmts_topplus_open/tile/'
+ '1.0.0/web/default/WEBMERCATOR/{z}/{y}/{x}.png',
+ 'satellite': 'https://server.arcgisonline.com/ArcGIS/rest/services/'
+ 'World_Imagery/MapServer/tile/{z}/{y}/{x}',
+
+ # Overlay layers
+ 'opensea': 'https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png',
+ 'hiking': 'https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png',
+ 'cycling': 'https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png',
+}
+
+TTL = datetime.timedelta(days=7)
+
+
+@view_config(route_name='tile-proxy', http_cache=3600)
+def tile_proxy(request):
+ """Requests the given tile from the proxy.
+
+ :param request: The Pyramid request.
+ :type request: pyramid.request.Request
+ :return: The HTTP response.
+ :rtype: pyramid.response.Response
+ """
+ provider = request.matchdict['provider']
+ if provider not in TILE_SOURCES:
+ return HTTPBadRequest("Invalid provider")
+
+ x, y, z = (int(request.matchdict['x']), int(request.matchdict['y']),
+ int(request.matchdict['z']))
+ cache_key = f"tile-{provider}-{x}-{y}-{z}"
+ content_type = "image/png"
+
+ cached = request.redis.get(cache_key)
+ if cached is not None:
+ return Response(cached, content_type=content_type)
+
+ url = TILE_SOURCES[provider].format(x=x, y=y, z=z, s=random.choice("abc"))
+ headers = {
+ "user-agent": f"Fietsboek-Tile-Proxy/{__VERSION__}",
+ }
+ from_mail = request.registry.settings.get('email.from')
+ if from_mail:
+ headers["from"] = from_mail
+
+ resp = requests.get(url, headers=headers)
+ request.redis.set(cache_key, resp.content, ex=TTL)
+ return Response(resp.content, content_type=resp.headers.get("Content-type", content_type))
diff --git a/poetry.lock b/poetry.lock
index d9fb521..fc38406 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -16,6 +16,17 @@ SQLAlchemy = ">=1.3.0"
tz = ["python-dateutil"]
[[package]]
+name = "async-timeout"
+version = "4.0.2"
+description = "Timeout context manager for asyncio programs"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
+
+[[package]]
name = "attrs"
version = "22.1.0"
description = "Classes Without Boilerplate"
@@ -72,6 +83,14 @@ css = ["tinycss2 (>=1.1.0,<1.2)"]
dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"]
[[package]]
+name = "certifi"
+version = "2022.9.24"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
name = "cffi"
version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
@@ -83,6 +102,17 @@ python-versions = "*"
pycparser = "*"
[[package]]
+name = "charset-normalizer"
+version = "2.1.1"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.6.0"
+
+[package.extras]
+unicode-backport = ["unicodedata2"]
+
+[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
@@ -136,6 +166,20 @@ ssh = ["bcrypt (>=3.1.5)"]
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
[[package]]
+name = "deprecated"
+version = "1.2.13"
+description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+wrapt = ">=1.10,<2"
+
+[package.extras]
+dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"]
+
+[[package]]
name = "exceptiongroup"
version = "1.0.4"
description = "Backport of PEP 654 (exception groups)"
@@ -179,6 +223,14 @@ docs = ["Sphinx", "pylons-sphinx-themes", "watchdog"]
testing = ["mock", "pytest", "pytest-cov", "watchdog"]
[[package]]
+name = "idna"
+version = "3.4"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
name = "importlib-metadata"
version = "5.0.0"
description = "Read metadata from Python packages"
@@ -276,7 +328,7 @@ name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "main"
-optional = true
+optional = false
python-versions = ">=3.6"
[package.dependencies]
@@ -367,7 +419,7 @@ name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "main"
-optional = true
+optional = false
python-versions = ">=3.6.8"
[package.extras]
@@ -525,6 +577,25 @@ optional = false
python-versions = "*"
[[package]]
+name = "redis"
+version = "4.3.4"
+description = "Python client for Redis database and key-value store"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+async-timeout = ">=4.0.2"
+deprecated = ">=1.2.3"
+importlib-metadata = {version = ">=1.0", markers = "python_version < \"3.8\""}
+packaging = ">=20.4"
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
+[package.extras]
+hiredis = ["hiredis (>=1.0.0)"]
+ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
+
+[[package]]
name = "repoze-lru"
version = "0.7"
description = "A tiny LRU cache implementation and decorator"
@@ -537,6 +608,24 @@ docs = ["Sphinx"]
testing = ["coverage", "nose"]
[[package]]
+name = "requests"
+version = "2.28.1"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=3.7, <4"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = ">=2,<3"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
name = "setuptools"
version = "65.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
@@ -642,6 +731,19 @@ optional = false
python-versions = ">=3.7"
[[package]]
+name = "urllib3"
+version = "1.26.12"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
name = "venusian"
version = "3.0.0"
description = "A library for deferring decorator actions"
@@ -703,6 +805,14 @@ docs = ["Sphinx (>=1.8.1)", "docutils", "pylons-sphinx-themes (>=1.0.8)"]
tests = ["PasteDeploy", "WSGIProxy2", "coverage", "pyquery", "pytest", "pytest-cov"]
[[package]]
+name = "wrapt"
+version = "1.14.1"
+description = "Module for decorators, wrappers and monkey patching."
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[[package]]
name = "zipp"
version = "3.10.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
@@ -768,13 +878,17 @@ testing = ["WebTest", "pytest", "pytest-cov"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "5452a4dee7949b3ed0f47ddae85defaef989f8ffc06b77cfea1e0f3116e6a40c"
+content-hash = "ef5a076f811b957b45dfbac68ef2012ddb94c13aa89e229bf53b3dc0fa6d7cca"
[metadata.files]
alembic = [
{file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"},
{file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"},
]
+async-timeout = [
+ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+]
attrs = [
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
@@ -791,6 +905,10 @@ bleach = [
{file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
{file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
]
+certifi = [
+ {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
+ {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
+]
cffi = [
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
@@ -857,6 +975,10 @@ cffi = [
{file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
{file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
+charset-normalizer = [
+ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
+ {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
+]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
@@ -945,6 +1067,10 @@ cryptography = [
{file = "cryptography-38.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a"},
{file = "cryptography-38.0.3.tar.gz", hash = "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd"},
]
+deprecated = [
+ {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
+ {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
+]
exceptiongroup = [
{file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
{file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
@@ -1018,6 +1144,10 @@ hupper = [
{file = "hupper-1.10.3-py2.py3-none-any.whl", hash = "sha256:f683850d62598c02faf3c7cdaaa727d8cbe3c5a2497a5737a8358386903b2601"},
{file = "hupper-1.10.3.tar.gz", hash = "sha256:cd6f51b72c7587bc9bce8a65ecd025a1e95f1b03284519bfe91284d010316cd9"},
]
+idna = [
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+]
importlib-metadata = [
{file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
{file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
@@ -1152,10 +1282,18 @@ pytz = [
{file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"},
{file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"},
]
+redis = [
+ {file = "redis-4.3.4-py3-none-any.whl", hash = "sha256:a52d5694c9eb4292770084fa8c863f79367ca19884b329ab574d5cb2036b3e54"},
+ {file = "redis-4.3.4.tar.gz", hash = "sha256:ddf27071df4adf3821c4f2ca59d67525c3a82e5f268bed97b813cb4fabf87880"},
+]
repoze-lru = [
{file = "repoze.lru-0.7-py3-none-any.whl", hash = "sha256:f77bf0e1096ea445beadd35f3479c5cff2aa1efe604a133e67150bc8630a62ea"},
{file = "repoze.lru-0.7.tar.gz", hash = "sha256:0429a75e19380e4ed50c0694e26ac8819b4ea7851ee1fc7583c8572db80aff77"},
]
+requests = [
+ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
+ {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
+]
setuptools = [
{file = "setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"},
{file = "setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"},
@@ -1227,6 +1365,10 @@ typing-extensions = [
{file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
{file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
]
+urllib3 = [
+ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
+ {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
+]
venusian = [
{file = "venusian-3.0.0-py3-none-any.whl", hash = "sha256:06e7385786ad3a15c70740b2af8d30dfb063a946a851dcb4159f9e2a2302578f"},
{file = "venusian-3.0.0.tar.gz", hash = "sha256:f6842b7242b1039c0c28f6feef29016e7e7dd3caaeb476a193acf737db31ee38"},
@@ -1247,6 +1389,72 @@ webtest = [
{file = "WebTest-3.0.0-py3-none-any.whl", hash = "sha256:2a001a9efa40d2a7e5d9cd8d1527c75f41814eb6afce2c3d207402547b1e5ead"},
{file = "WebTest-3.0.0.tar.gz", hash = "sha256:54bd969725838d9861a9fa27f8d971f79d275d94ae255f5c501f53bb6d9929eb"},
]
+wrapt = [
+ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"},
+ {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"},
+ {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
+ {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
+ {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"},
+ {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"},
+ {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"},
+ {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"},
+ {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"},
+ {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"},
+ {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"},
+ {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"},
+ {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"},
+ {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"},
+ {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"},
+ {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"},
+ {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"},
+ {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"},
+ {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"},
+ {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"},
+ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"},
+ {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},
+]
zipp = [
{file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"},
{file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"},
diff --git a/pylint.toml b/pylint.toml
index 7495cf7..9e3a63c 100644
--- a/pylint.toml
+++ b/pylint.toml
@@ -149,7 +149,7 @@ function-naming-style = "snake_case"
# function-rgx =
# Good variable names which should always be accepted, separated by a comma.
-good-names = ["i", "j", "k", "ex", "Run", "_", "id"]
+good-names = ["i", "j", "k", "ex", "Run", "_", "id", "x", "y", "z"]
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
diff --git a/pyproject.toml b/pyproject.toml
index 9fab1a6..3504164 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,6 +37,7 @@ SQLAlchemy = "^1.4"
alembic = "^1.8"
transaction = "^3"
"zope.sqlalchemy" = "^1.6"
+redis = "^4.3.4"
importlib_resources = "^5.10"
Babel = "^2.11"
@@ -45,6 +46,7 @@ gpxpy = "^1.5"
markdown = "^3.4"
bleach = "^5"
Click = "^8.1"
+requests = "^2.28.1"
WebTest = {version = "^3", optional = true}
pytest = {version = "^7.2", optional = true}