geo-activity-playground 0.30.0__tar.gz → 0.34.1__tar.gz
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.
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/PKG-INFO +5 -6
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/__main__.py +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/activities.py +11 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/config.py +5 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/heatmap.py +61 -15
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/paths.py +3 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/tiles.py +8 -5
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/explorer/video.py +2 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/directory.py +6 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/strava_checkout.py +8 -2
- geo_activity_playground-0.34.1/geo_activity_playground/webui/activity/blueprint.py +116 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/controller.py +127 -43
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/templates/activity/day.html.j2 +9 -5
- geo_activity_playground-0.34.1/geo_activity_playground/webui/activity/templates/activity/edit.html.j2 +42 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/templates/activity/lines.html.j2 +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/templates/activity/name.html.j2 +5 -4
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/templates/activity/show.html.j2 +9 -7
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/app.py +29 -6
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/entry_controller.py +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/equipment/controller.py +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/explorer/templates/explorer/index.html.j2 +20 -44
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/heatmap/blueprint.py +5 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/heatmap/heatmap_controller.py +22 -10
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/search/templates/search/index.html.j2 +2 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/blueprint.py +44 -33
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2 +11 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2 +1 -1
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2 +1 -1
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/Leaflet.fullscreen.min.js +1 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/MarkerCluster.Default.css +60 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/MarkerCluster.css +14 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/bootstrap.min.css +6 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/fullscreen.png +0 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/fullscreen@2x.png +0 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/leaflet.css +661 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/leaflet.fullscreen.css +40 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/leaflet.js +6 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/leaflet.markercluster.js +3 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/table-sort.min.js +8 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/vega-embed@6 +7 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/vega-lite@4 +2 -0
- geo_activity_playground-0.34.1/geo_activity_playground/webui/static/vega@5 +2 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/summary/controller.py +20 -25
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/summary/templates/summary/index.html.j2 +19 -6
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/templates/home.html.j2 +6 -5
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/templates/page.html.j2 +14 -16
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/tile/blueprint.py +3 -2
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/tile/controller.py +7 -3
- geo_activity_playground-0.30.0/geo_activity_playground/webui/upload/controller.py → geo_activity_playground-0.34.1/geo_activity_playground/webui/upload_blueprint.py +41 -35
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/pyproject.toml +8 -8
- geo_activity_playground-0.30.0/geo_activity_playground/webui/activity/blueprint.py +0 -57
- geo_activity_playground-0.30.0/geo_activity_playground/webui/upload/__init__.py +0 -0
- geo_activity_playground-0.30.0/geo_activity_playground/webui/upload/blueprint.py +0 -44
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/LICENSE +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/coordinates.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/enrichment.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/heart_rate.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/privacy_zones.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/similarity.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/tasks.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/test_tiles.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/test_time_conversion.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/core/time_conversion.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/explorer/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/explorer/grid_file.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/explorer/tile_visits.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/activity_parsers.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/csv_parser.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/strava_api.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/test_csv_parser.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/test_directory.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/importers/test_strava_api.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/activity/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/auth/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/auth/templates/auth/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/authenticator.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/calendar/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/calendar/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/calendar/controller.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/calendar/templates/calendar/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/calendar/templates/calendar/month.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/eddington/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/eddington/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/eddington/controller.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/equipment/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/equipment/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/equipment/templates/equipment/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/explorer/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/explorer/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/explorer/controller.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/heatmap/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/plot_util.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/search/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/controller.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/admin-password.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/equipment-offsets.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/heart-rate.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/kind-renames.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/kinds-without-achievements.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/metadata-extraction.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/segmentation.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/sharepic.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/settings/templates/settings/strava.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/square_planner/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/square_planner/blueprint.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/square_planner/controller.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/bootstrap-dark-mode.js +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/favicon-48x48.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/favicon.ico +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/favicon.svg +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/site.webmanifest +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/web-app-manifest-192x192.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/static/web-app-manifest-512x512.png +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/summary/__init__.py +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/summary/blueprint.py +0 -0
- {geo_activity_playground-0.30.0/geo_activity_playground/webui/upload → geo_activity_playground-0.34.1/geo_activity_playground/webui}/templates/upload/index.html.j2 +0 -0
- {geo_activity_playground-0.30.0/geo_activity_playground/webui/upload → geo_activity_playground-0.34.1/geo_activity_playground/webui}/templates/upload/reload.html.j2 +0 -0
- {geo_activity_playground-0.30.0 → geo_activity_playground-0.34.1}/geo_activity_playground/webui/tile/__init__.py +0 -0
@@ -1,17 +1,17 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: geo-activity-playground
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.34.1
|
4
4
|
Summary: Analysis of geo data activities like rides, runs or hikes.
|
5
5
|
License: MIT
|
6
6
|
Author: Martin Ueding
|
7
7
|
Author-email: mu@martin-ueding.de
|
8
|
-
Requires-Python: >=3.10,<3.
|
8
|
+
Requires-Python: >=3.10,<3.14
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
|
-
Requires-Dist: Pillow (>=
|
14
|
+
Requires-Dist: Pillow (>=11.0.0,<12.0.0)
|
15
15
|
Requires-Dist: altair (>=5.1.2,<6.0.0)
|
16
16
|
Requires-Dist: appdirs (>=1.4.4,<2.0.0)
|
17
17
|
Requires-Dist: charset-normalizer (>=3.3.2,<4.0.0)
|
@@ -22,12 +22,11 @@ Requires-Dist: geojson (>=3.0.1,<4.0.0)
|
|
22
22
|
Requires-Dist: gpxpy (>=1.5.0,<2.0.0)
|
23
23
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
24
24
|
Requires-Dist: matplotlib (>=3.6.3,<4.0.0)
|
25
|
-
Requires-Dist: numpy (>=1.
|
25
|
+
Requires-Dist: numpy (>=2.1.3,<3.0.0)
|
26
26
|
Requires-Dist: pandas (>=2.2,<3.0)
|
27
|
-
Requires-Dist: pyarrow (>=
|
27
|
+
Requires-Dist: pyarrow (>=18.1.0,<19.0.0)
|
28
28
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
29
29
|
Requires-Dist: requests (>=2.28.1,<3.0.0)
|
30
|
-
Requires-Dist: scipy (>=1.8.1,<2.0.0)
|
31
30
|
Requires-Dist: shapely (>=2.0.5,<3.0.0)
|
32
31
|
Requires-Dist: stravalib (>=2.0,<3.0)
|
33
32
|
Requires-Dist: tcxreader (>=0.4.5,<0.5.0)
|
@@ -13,7 +13,7 @@ from geo_activity_playground.core.config import import_old_strava_config
|
|
13
13
|
from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
|
14
14
|
from geo_activity_playground.explorer.video import explorer_video_main
|
15
15
|
from geo_activity_playground.webui.app import web_ui_main
|
16
|
-
from geo_activity_playground.webui.
|
16
|
+
from geo_activity_playground.webui.upload_blueprint import scan_for_activities
|
17
17
|
|
18
18
|
logger = logging.getLogger(__name__)
|
19
19
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import datetime
|
2
2
|
import functools
|
3
|
+
import json
|
3
4
|
import logging
|
4
5
|
import pickle
|
5
6
|
from typing import Any
|
@@ -16,6 +17,7 @@ from tqdm import tqdm
|
|
16
17
|
from geo_activity_playground.core.paths import activities_file
|
17
18
|
from geo_activity_playground.core.paths import activity_enriched_meta_dir
|
18
19
|
from geo_activity_playground.core.paths import activity_enriched_time_series_dir
|
20
|
+
from geo_activity_playground.core.paths import activity_meta_override_dir
|
19
21
|
|
20
22
|
logger = logging.getLogger(__name__)
|
21
23
|
|
@@ -83,7 +85,12 @@ def build_activity_meta() -> None:
|
|
83
85
|
rows = []
|
84
86
|
for new_id in tqdm(new_ids, desc="Register new activities"):
|
85
87
|
with open(activity_enriched_meta_dir() / f"{new_id}.pickle", "rb") as f:
|
86
|
-
|
88
|
+
data = pickle.load(f)
|
89
|
+
override_file = activity_meta_override_dir() / f"{new_id}.json"
|
90
|
+
if override_file.exists():
|
91
|
+
with open(override_file) as f:
|
92
|
+
data.update(json.load(f))
|
93
|
+
rows.append(data)
|
87
94
|
|
88
95
|
if rows:
|
89
96
|
new_shard = pd.DataFrame(rows)
|
@@ -140,7 +147,6 @@ class ActivityRepository:
|
|
140
147
|
if not dropna or not pd.isna(row["start"]):
|
141
148
|
yield row
|
142
149
|
|
143
|
-
@functools.lru_cache()
|
144
150
|
def get_activity_by_id(self, id: int) -> ActivityMeta:
|
145
151
|
activity = self.meta.loc[id]
|
146
152
|
assert isinstance(activity["name"], str), activity["name"]
|
@@ -158,6 +164,9 @@ class ActivityRepository:
|
|
158
164
|
|
159
165
|
return df
|
160
166
|
|
167
|
+
def save(self) -> None:
|
168
|
+
self.meta.to_parquet(activities_file())
|
169
|
+
|
161
170
|
|
162
171
|
def make_geojson_from_time_series(time_series: pd.DataFrame) -> str:
|
163
172
|
fc = geojson.FeatureCollection(
|
@@ -21,14 +21,16 @@ logger = logging.getLogger(__name__)
|
|
21
21
|
@dataclasses.dataclass
|
22
22
|
class Config:
|
23
23
|
birth_year: Optional[int] = None
|
24
|
-
color_scheme_for_counts: str = "
|
24
|
+
color_scheme_for_counts: str = "teals"
|
25
25
|
color_scheme_for_kind: str = "category10"
|
26
|
+
color_scheme_for_heatmap: str = "hot"
|
26
27
|
equipment_offsets: dict[str, float] = dataclasses.field(default_factory=dict)
|
27
28
|
explorer_zoom_levels: list[int] = dataclasses.field(
|
28
29
|
default_factory=lambda: [14, 17]
|
29
30
|
)
|
30
31
|
heart_rate_resting: int = 0
|
31
32
|
heart_rate_maximum: Optional[int] = None
|
33
|
+
ignore_suffixes: list[str] = dataclasses.field(default_factory=list)
|
32
34
|
kind_renames: dict[str, str] = dataclasses.field(default_factory=dict)
|
33
35
|
kinds_without_achievements: list[str] = dataclasses.field(default_factory=list)
|
34
36
|
metadata_extraction_regexes: list[str] = dataclasses.field(default_factory=list)
|
@@ -42,6 +44,8 @@ class Config:
|
|
42
44
|
strava_client_code: Optional[str] = None
|
43
45
|
time_diff_threshold_seconds: Optional[int] = 30
|
44
46
|
upload_password: Optional[str] = None
|
47
|
+
map_tile_url: str = "https://tile.openstreetmap.org/{zoom}/{x}/{y}.png"
|
48
|
+
map_tile_attribution: str = '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> | <a href="https://www.openstreetmap.org/fixthemap">Correct Map</a>'
|
45
49
|
|
46
50
|
|
47
51
|
class ConfigAccessor:
|
@@ -6,6 +6,7 @@ import logging
|
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
|
9
|
+
from geo_activity_playground.core.config import Config
|
9
10
|
from geo_activity_playground.core.tiles import compute_tile_float
|
10
11
|
from geo_activity_playground.core.tiles import get_tile
|
11
12
|
from geo_activity_playground.core.tiles import get_tile_upper_left_lat_lon
|
@@ -123,21 +124,66 @@ def get_sensible_zoom_level(
|
|
123
124
|
)
|
124
125
|
|
125
126
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
127
|
+
def build_map_from_tiles_around_center(
|
128
|
+
center: tuple[float, float],
|
129
|
+
zoom: int,
|
130
|
+
target: tuple[int, int],
|
131
|
+
inner_target: tuple[int, int],
|
132
|
+
config: Config,
|
133
|
+
) -> np.ndarray:
|
134
|
+
background = np.zeros((target[1], target[0], 3))
|
135
|
+
|
136
|
+
# We will work with the center point and have it in terms of tiles `t` and also in terms of pixels `p`. At the start we know that the tile center must be in the middle of the image.
|
137
|
+
t = np.array(center)
|
138
|
+
p = np.array([inner_target[0] / 2, inner_target[1] / 2])
|
139
|
+
|
140
|
+
# Shift both such that they are in the top-left corner of an even tile.
|
141
|
+
t_offset = np.array([center[0] % 1, center[1] % 1])
|
142
|
+
t -= t_offset
|
143
|
+
p -= t_offset * OSM_TILE_SIZE
|
144
|
+
|
145
|
+
# Shift until we have left the image.
|
146
|
+
shift = np.ceil(p / OSM_TILE_SIZE)
|
147
|
+
p -= shift * OSM_TILE_SIZE
|
148
|
+
t -= shift
|
149
|
+
|
150
|
+
num_tiles = np.ceil(np.array(target) / OSM_TILE_SIZE) + 1
|
151
|
+
|
152
|
+
for x in range(int(t[0]), int(t[0] + num_tiles[0])):
|
153
|
+
for y in range(int(t[1]), int(t[1]) + int(num_tiles[1])):
|
154
|
+
source_x_min = 0
|
155
|
+
source_y_min = 0
|
156
|
+
source_x_max = source_x_min + OSM_TILE_SIZE
|
157
|
+
source_y_max = source_y_min + OSM_TILE_SIZE
|
158
|
+
|
159
|
+
target_x_min = (x - int(t[0])) * OSM_TILE_SIZE + int(p[0])
|
160
|
+
target_y_min = (y - int(t[1])) * OSM_TILE_SIZE + int(p[1])
|
161
|
+
target_x_max = target_x_min + OSM_TILE_SIZE
|
162
|
+
target_y_max = target_y_min + OSM_TILE_SIZE
|
163
|
+
|
164
|
+
if target_x_min < 0:
|
165
|
+
source_x_min -= target_x_min
|
166
|
+
target_x_min = 0
|
167
|
+
if target_y_min < 0:
|
168
|
+
source_y_min -= target_y_min
|
169
|
+
target_y_min = 0
|
170
|
+
if target_x_max > target[0]:
|
171
|
+
a = target_x_max - target[0]
|
172
|
+
target_x_max -= a
|
173
|
+
source_x_max -= a
|
174
|
+
if target_y_max > target[1]:
|
175
|
+
a = target_y_max - target[1]
|
176
|
+
target_y_max -= a
|
177
|
+
source_y_max -= a
|
178
|
+
|
179
|
+
if source_x_max < 0 or source_y_max < 0:
|
180
|
+
continue
|
181
|
+
|
182
|
+
tile = np.array(get_tile(zoom, x, y, config.map_tile_url)) / 255
|
183
|
+
|
184
|
+
background[target_y_min:target_y_max, target_x_min:target_x_max] = tile[
|
185
|
+
source_y_min:source_y_max, source_x_min:source_x_max, :3
|
186
|
+
]
|
141
187
|
|
142
188
|
return background
|
143
189
|
|
@@ -53,6 +53,8 @@ _strava_last_activity_date_path = _cache_dir / "strava-last-activity-date.json"
|
|
53
53
|
|
54
54
|
_new_config_file = pathlib.Path("config.json")
|
55
55
|
|
56
|
+
_activity_meta_override_dir = pathlib.Path("Metadata Override")
|
57
|
+
|
56
58
|
|
57
59
|
cache_dir = dir_wrapper(_cache_dir)
|
58
60
|
|
@@ -63,6 +65,7 @@ activity_enriched_meta_dir = dir_wrapper(_activity_enriched_meta_dir)
|
|
63
65
|
activity_enriched_time_series_dir = dir_wrapper(_activity_enriched_time_series_dir)
|
64
66
|
tiles_per_time_series = dir_wrapper(_tiles_per_time_series)
|
65
67
|
strava_api_dir = dir_wrapper(_strava_api_dir)
|
68
|
+
activity_meta_override_dir = dir_wrapper(_activity_meta_override_dir)
|
66
69
|
|
67
70
|
activities_file = file_wrapper(_activities_file)
|
68
71
|
strava_dynamic_config_path = file_wrapper(_strava_dynamic_config_path)
|
@@ -3,6 +3,7 @@ import logging
|
|
3
3
|
import math
|
4
4
|
import pathlib
|
5
5
|
import time
|
6
|
+
import urllib.parse
|
6
7
|
from typing import Iterator
|
7
8
|
from typing import Optional
|
8
9
|
|
@@ -13,8 +14,10 @@ from PIL import Image
|
|
13
14
|
logger = logging.getLogger(__name__)
|
14
15
|
|
15
16
|
|
16
|
-
def osm_tile_path(x: int, y: int, zoom: int) -> pathlib.Path:
|
17
|
-
|
17
|
+
def osm_tile_path(x: int, y: int, zoom: int, url_template: str) -> pathlib.Path:
|
18
|
+
base_dir = pathlib.Path("Open Street Map Tiles")
|
19
|
+
dir_for_source = base_dir / urllib.parse.quote_plus(url_template)
|
20
|
+
path = dir_for_source / f"{zoom}/{x}/{y}.png"
|
18
21
|
path.parent.mkdir(parents=True, exist_ok=True)
|
19
22
|
return path
|
20
23
|
|
@@ -62,11 +65,11 @@ def download_file(url: str, destination: pathlib.Path):
|
|
62
65
|
|
63
66
|
|
64
67
|
@functools.lru_cache()
|
65
|
-
def get_tile(zoom: int, x: int, y: int) -> Image.Image:
|
66
|
-
destination = osm_tile_path(x, y, zoom)
|
68
|
+
def get_tile(zoom: int, x: int, y: int, url_template: str) -> Image.Image:
|
69
|
+
destination = osm_tile_path(x, y, zoom, url_template)
|
67
70
|
if not destination.exists():
|
68
71
|
logger.info(f"Downloading OSM tile {x=}, {y=}, {zoom=} …")
|
69
|
-
url =
|
72
|
+
url = url_template.format(x=x, y=y, zoom=zoom)
|
70
73
|
download_file(url, destination)
|
71
74
|
with Image.open(destination) as image:
|
72
75
|
image.load()
|
@@ -9,13 +9,14 @@ from typing import Tuple
|
|
9
9
|
|
10
10
|
import numpy as np
|
11
11
|
import pandas as pd
|
12
|
-
import scipy.interpolate
|
13
12
|
from PIL import Image
|
14
13
|
from PIL import ImageEnhance
|
15
14
|
from tqdm import tqdm
|
16
15
|
|
17
16
|
from ..core.tiles import get_tile
|
18
17
|
|
18
|
+
# import scipy.interpolate
|
19
|
+
|
19
20
|
|
20
21
|
def build_image(
|
21
22
|
center_x: float,
|
@@ -10,6 +10,7 @@ from typing import Optional
|
|
10
10
|
from tqdm import tqdm
|
11
11
|
|
12
12
|
from geo_activity_playground.core.activities import ActivityMeta
|
13
|
+
from geo_activity_playground.core.config import Config
|
13
14
|
from geo_activity_playground.core.paths import activity_extracted_dir
|
14
15
|
from geo_activity_playground.core.paths import activity_extracted_meta_dir
|
15
16
|
from geo_activity_playground.core.paths import activity_extracted_time_series_dir
|
@@ -24,13 +25,16 @@ ACTIVITY_DIR = pathlib.Path("Activities")
|
|
24
25
|
|
25
26
|
|
26
27
|
def import_from_directory(
|
27
|
-
metadata_extraction_regexes: list[str], num_processes: Optional[int]
|
28
|
+
metadata_extraction_regexes: list[str], num_processes: Optional[int], config: Config
|
28
29
|
) -> None:
|
29
30
|
|
30
31
|
activity_paths = [
|
31
32
|
path
|
32
33
|
for path in ACTIVITY_DIR.rglob("*.*")
|
33
|
-
if path.is_file()
|
34
|
+
if path.is_file()
|
35
|
+
and path.suffixes
|
36
|
+
and not path.stem.startswith(".")
|
37
|
+
and not path.suffix in config.ignore_suffixes
|
34
38
|
]
|
35
39
|
work_tracker = WorkTracker(activity_extracted_dir() / "work-tracker-extract.pickle")
|
36
40
|
new_activity_paths = work_tracker.filter(activity_paths)
|
@@ -146,7 +146,7 @@ def float_with_comma_or_period(x: str) -> Optional[float]:
|
|
146
146
|
|
147
147
|
def import_from_strava_checkout() -> None:
|
148
148
|
checkout_path = pathlib.Path("Strava Export")
|
149
|
-
with open(checkout_path / "activities.csv") as f:
|
149
|
+
with open(checkout_path / "activities.csv", encoding="utf-8") as f:
|
150
150
|
rows = parse_csv(f.read())
|
151
151
|
header = rows[0]
|
152
152
|
|
@@ -159,9 +159,15 @@ def import_from_strava_checkout() -> None:
|
|
159
159
|
|
160
160
|
if header[0] == EXPECTED_COLUMNS[0]:
|
161
161
|
dayfirst = False
|
162
|
-
|
162
|
+
elif header[0] == "Aktivitäts-ID":
|
163
163
|
header = EXPECTED_COLUMNS
|
164
164
|
dayfirst = True
|
165
|
+
else:
|
166
|
+
logger.error(
|
167
|
+
f"You are trying to import a Strava checkout where the `activities.csv` contains an unexpected header format. In order to import this, we need to map these to the English ones. Unfortunately Strava often changes the number of columns. This means that the program needs to be updated to match the new Strava export format. Please go to https://github.com/martin-ueding/geo-activity-playground/issues and open a new issue and share the following output in the ticket:"
|
168
|
+
)
|
169
|
+
print(header)
|
170
|
+
sys.exit(1)
|
165
171
|
|
166
172
|
table = {
|
167
173
|
header[i]: [rows[r][i] for r in range(1, len(rows))] for i in range(len(header))
|
@@ -0,0 +1,116 @@
|
|
1
|
+
import json
|
2
|
+
import urllib.parse
|
3
|
+
from collections.abc import Collection
|
4
|
+
|
5
|
+
from flask import Blueprint
|
6
|
+
from flask import redirect
|
7
|
+
from flask import render_template
|
8
|
+
from flask import request
|
9
|
+
from flask import Response
|
10
|
+
from flask import url_for
|
11
|
+
|
12
|
+
from ...core.activities import ActivityRepository
|
13
|
+
from ...explorer.tile_visits import TileVisitAccessor
|
14
|
+
from .controller import ActivityController
|
15
|
+
from geo_activity_playground.core.config import Config
|
16
|
+
from geo_activity_playground.core.paths import activity_meta_override_dir
|
17
|
+
from geo_activity_playground.core.privacy_zones import PrivacyZone
|
18
|
+
from geo_activity_playground.webui.authenticator import Authenticator
|
19
|
+
from geo_activity_playground.webui.authenticator import needs_authentication
|
20
|
+
|
21
|
+
|
22
|
+
def make_activity_blueprint(
|
23
|
+
repository: ActivityRepository,
|
24
|
+
tile_visit_accessor: TileVisitAccessor,
|
25
|
+
config: Config,
|
26
|
+
authenticator: Authenticator,
|
27
|
+
) -> Blueprint:
|
28
|
+
blueprint = Blueprint("activity", __name__, template_folder="templates")
|
29
|
+
|
30
|
+
activity_controller = ActivityController(repository, tile_visit_accessor, config)
|
31
|
+
|
32
|
+
@blueprint.route("/all")
|
33
|
+
def all():
|
34
|
+
return render_template(
|
35
|
+
"activity/lines.html.j2", **activity_controller.render_all()
|
36
|
+
)
|
37
|
+
|
38
|
+
@blueprint.route("/<id>")
|
39
|
+
def show(id: str):
|
40
|
+
return render_template(
|
41
|
+
"activity/show.html.j2", **activity_controller.render_activity(int(id))
|
42
|
+
)
|
43
|
+
|
44
|
+
@blueprint.route("/<id>/sharepic.png")
|
45
|
+
def sharepic(id: str):
|
46
|
+
return Response(
|
47
|
+
activity_controller.render_sharepic(int(id)),
|
48
|
+
mimetype="image/png",
|
49
|
+
)
|
50
|
+
|
51
|
+
@blueprint.route("/day/<year>/<month>/<day>")
|
52
|
+
def day(year: str, month: str, day: str):
|
53
|
+
return render_template(
|
54
|
+
"activity/day.html.j2",
|
55
|
+
**activity_controller.render_day(int(year), int(month), int(day)),
|
56
|
+
)
|
57
|
+
|
58
|
+
@blueprint.route("/day-sharepic/<year>/<month>/<day>/sharepic.png")
|
59
|
+
def day_sharepic(year: str, month: str, day: str):
|
60
|
+
return Response(
|
61
|
+
activity_controller.render_day_sharepic(int(year), int(month), int(day)),
|
62
|
+
mimetype="image/png",
|
63
|
+
)
|
64
|
+
|
65
|
+
@blueprint.route("/name/<name>")
|
66
|
+
def name(name: str):
|
67
|
+
return render_template(
|
68
|
+
"activity/name.html.j2",
|
69
|
+
**activity_controller.render_name(urllib.parse.unquote(name)),
|
70
|
+
)
|
71
|
+
|
72
|
+
@blueprint.route("/edit/<id>", methods=["GET", "POST"])
|
73
|
+
@needs_authentication(authenticator)
|
74
|
+
def edit(id: str):
|
75
|
+
activity_id = int(id)
|
76
|
+
activity = repository.get_activity_by_id(activity_id)
|
77
|
+
override_file = activity_meta_override_dir() / f"{activity_id}.json"
|
78
|
+
if override_file.exists():
|
79
|
+
with open(override_file) as f:
|
80
|
+
override = json.load(f)
|
81
|
+
else:
|
82
|
+
override = {}
|
83
|
+
|
84
|
+
if request.method == "POST":
|
85
|
+
override = {}
|
86
|
+
if value := request.form.get("name"):
|
87
|
+
override["name"] = value
|
88
|
+
repository.meta.loc[activity_id, "name"] = value
|
89
|
+
if value := request.form.get("kind"):
|
90
|
+
override["kind"] = value
|
91
|
+
repository.meta.loc[activity_id, "kind"] = value
|
92
|
+
if value := request.form.get("equipment"):
|
93
|
+
override["equipment"] = value
|
94
|
+
repository.meta.loc[activity_id, "equipment"] = value
|
95
|
+
if value := request.form.get("commute"):
|
96
|
+
override["commute"] = True
|
97
|
+
repository.meta.loc[activity_id, "commute"] = True
|
98
|
+
if value := request.form.get("consider_for_achievements"):
|
99
|
+
override["consider_for_achievements"] = True
|
100
|
+
repository.meta.loc[activity_id, "consider_for_achievements"] = True
|
101
|
+
|
102
|
+
with open(override_file, "w") as f:
|
103
|
+
json.dump(override, f, ensure_ascii=False, indent=4, sort_keys=True)
|
104
|
+
|
105
|
+
repository.save()
|
106
|
+
|
107
|
+
return redirect(url_for(".show", id=activity_id))
|
108
|
+
|
109
|
+
return render_template(
|
110
|
+
"activity/edit.html.j2",
|
111
|
+
activity_id=activity_id,
|
112
|
+
activity=activity,
|
113
|
+
override=override,
|
114
|
+
)
|
115
|
+
|
116
|
+
return blueprint
|