geo-activity-playground 0.31.0__py3-none-any.whl → 0.33.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geo_activity_playground/core/config.py +5 -1
- geo_activity_playground/core/heatmap.py +61 -15
- geo_activity_playground/core/tiles.py +8 -5
- geo_activity_playground/importers/directory.py +6 -2
- geo_activity_playground/webui/activity/controller.py +50 -36
- geo_activity_playground/webui/activity/templates/activity/day.html.j2 +1 -1
- geo_activity_playground/webui/activity/templates/activity/lines.html.j2 +1 -1
- geo_activity_playground/webui/activity/templates/activity/name.html.j2 +3 -2
- geo_activity_playground/webui/activity/templates/activity/show.html.j2 +2 -2
- geo_activity_playground/webui/app.py +14 -2
- geo_activity_playground/webui/explorer/templates/explorer/index.html.j2 +20 -44
- geo_activity_playground/webui/heatmap/blueprint.py +5 -2
- geo_activity_playground/webui/heatmap/heatmap_controller.py +14 -4
- geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +1 -1
- geo_activity_playground/webui/settings/blueprint.py +44 -33
- geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2 +11 -2
- geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2 +1 -1
- geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2 +1 -1
- geo_activity_playground/webui/summary/controller.py +9 -15
- geo_activity_playground/webui/summary/templates/summary/index.html.j2 +18 -5
- geo_activity_playground/webui/templates/home.html.j2 +1 -1
- geo_activity_playground/webui/tile/blueprint.py +3 -2
- geo_activity_playground/webui/tile/controller.py +7 -3
- geo_activity_playground/webui/upload/controller.py +1 -2
- {geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/METADATA +1 -1
- {geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/RECORD +29 -29
- {geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/entry_points.txt +0 -0
@@ -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
|
|
@@ -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()
|
@@ -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)
|
@@ -21,11 +21,9 @@ from geo_activity_playground.core.activities import make_geojson_from_time_serie
|
|
21
21
|
from geo_activity_playground.core.activities import make_speed_color_bar
|
22
22
|
from geo_activity_playground.core.config import Config
|
23
23
|
from geo_activity_playground.core.heart_rate import HeartRateZoneComputer
|
24
|
-
from geo_activity_playground.core.heatmap import
|
25
|
-
from geo_activity_playground.core.heatmap import build_map_from_tiles
|
24
|
+
from geo_activity_playground.core.heatmap import build_map_from_tiles_around_center
|
26
25
|
from geo_activity_playground.core.heatmap import GeoBounds
|
27
|
-
from geo_activity_playground.core.heatmap import
|
28
|
-
from geo_activity_playground.core.heatmap import get_sensible_zoom_level
|
26
|
+
from geo_activity_playground.core.heatmap import OSM_MAX_ZOOM
|
29
27
|
from geo_activity_playground.core.heatmap import OSM_TILE_SIZE
|
30
28
|
from geo_activity_playground.core.heatmap import PixelBounds
|
31
29
|
from geo_activity_playground.core.heatmap import TileBounds
|
@@ -128,7 +126,7 @@ class ActivityController:
|
|
128
126
|
if len(time_series) == 0:
|
129
127
|
time_series = self._repository.get_time_series(id)
|
130
128
|
return make_sharepic(
|
131
|
-
activity, time_series, self._config.sharepic_suppressed_fields
|
129
|
+
activity, time_series, self._config.sharepic_suppressed_fields, self._config
|
132
130
|
)
|
133
131
|
|
134
132
|
def render_day(self, year: int, month: int, day: int) -> dict:
|
@@ -458,43 +456,58 @@ def make_sharepic(
|
|
458
456
|
activity: ActivityMeta,
|
459
457
|
time_series: pd.DataFrame,
|
460
458
|
sharepic_suppressed_fields: list[str],
|
459
|
+
config: Config,
|
461
460
|
) -> bytes:
|
462
|
-
|
461
|
+
tile_x = time_series["x"]
|
462
|
+
tile_y = time_series["y"]
|
463
|
+
tile_width = tile_x.max() - tile_x.min()
|
464
|
+
tile_height = tile_y.max() - tile_y.min()
|
465
|
+
|
466
|
+
target_width = 600
|
467
|
+
target_height = 600
|
468
|
+
footer_height = 100
|
469
|
+
target_map_height = target_height - footer_height
|
470
|
+
|
471
|
+
zoom = int(
|
472
|
+
min(
|
473
|
+
np.log2(target_width / tile_width / OSM_TILE_SIZE),
|
474
|
+
np.log2(target_map_height / tile_height / OSM_TILE_SIZE),
|
475
|
+
OSM_MAX_ZOOM,
|
476
|
+
)
|
477
|
+
)
|
463
478
|
|
464
|
-
|
465
|
-
|
466
|
-
tile_bounds = get_sensible_zoom_level(geo_bounds, (1500, 1500))
|
467
|
-
tile_bounds = make_tile_bounds_square(tile_bounds)
|
468
|
-
background = build_map_from_tiles(tile_bounds)
|
469
|
-
# background = convert_to_grayscale(background)
|
479
|
+
tile_xz = tile_x * 2**zoom
|
480
|
+
tile_yz = tile_y * 2**zoom
|
470
481
|
|
471
|
-
|
472
|
-
|
482
|
+
tile_xz_center = (
|
483
|
+
(tile_xz.max() + tile_xz.min()) / 2,
|
484
|
+
(tile_yz.max() + tile_yz.min()) / 2,
|
485
|
+
)
|
473
486
|
|
474
|
-
background =
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
487
|
+
background = build_map_from_tiles_around_center(
|
488
|
+
tile_xz_center,
|
489
|
+
zoom,
|
490
|
+
(target_width, target_height),
|
491
|
+
(target_width, target_map_height),
|
492
|
+
config,
|
493
|
+
)
|
479
494
|
|
480
495
|
img = Image.fromarray((background * 255).astype("uint8"), "RGB")
|
481
496
|
draw = ImageDraw.Draw(img, mode="RGBA")
|
482
497
|
|
483
498
|
for _, group in time_series.groupby("segment_id"):
|
484
|
-
xs, ys = compute_tile_float(
|
485
|
-
group["latitude"], group["longitude"], tile_bounds.zoom
|
486
|
-
)
|
487
499
|
yx = list(
|
488
|
-
(
|
489
|
-
|
490
|
-
|
500
|
+
zip(
|
501
|
+
(tile_xz - tile_xz_center[0]) * OSM_TILE_SIZE + target_width / 2,
|
502
|
+
(tile_yz - tile_xz_center[1]) * OSM_TILE_SIZE + target_map_height / 2,
|
491
503
|
)
|
492
|
-
for x, y in zip(xs, ys)
|
493
504
|
)
|
494
505
|
|
495
506
|
draw.line(yx, fill="red", width=4)
|
496
507
|
|
497
|
-
draw.rectangle(
|
508
|
+
draw.rectangle(
|
509
|
+
[0, img.height - footer_height, img.width, img.height], fill=(0, 0, 0, 180)
|
510
|
+
)
|
498
511
|
|
499
512
|
facts = {
|
500
513
|
"kind": f"{activity['kind']}",
|
@@ -515,19 +528,20 @@ def make_sharepic(
|
|
515
528
|
if not key in sharepic_suppressed_fields
|
516
529
|
}
|
517
530
|
|
518
|
-
draw.text(
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
531
|
+
draw.text(
|
532
|
+
(35, img.height - footer_height + 10),
|
533
|
+
" ".join(facts.values()),
|
534
|
+
font_size=20,
|
535
|
+
)
|
523
536
|
|
524
|
-
|
525
|
-
|
526
|
-
|
537
|
+
draw.text(
|
538
|
+
(img.width - 250, img.height - 20),
|
539
|
+
"Map: © Open Street Map Contributors",
|
540
|
+
font_size=14,
|
541
|
+
)
|
527
542
|
|
528
543
|
f = io.BytesIO()
|
529
544
|
img.save(f, format="png")
|
530
|
-
# pl.imsave(f, background, format="png")
|
531
545
|
return bytes(f.getbuffer())
|
532
546
|
|
533
547
|
|
@@ -17,7 +17,7 @@
|
|
17
17
|
});
|
18
18
|
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
19
19
|
maxZoom: 19,
|
20
|
-
attribution: '
|
20
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
21
21
|
}).addTo(map);
|
22
22
|
|
23
23
|
let geojson = L.geoJSON({{ geojson| safe }}, {
|
@@ -21,7 +21,7 @@
|
|
21
21
|
});
|
22
22
|
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
23
23
|
maxZoom: 19,
|
24
|
-
attribution: '
|
24
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
25
25
|
}).addTo(map);
|
26
26
|
|
27
27
|
let geojson = L.geoJSON({{ geojson| safe }}, {
|
@@ -17,7 +17,7 @@
|
|
17
17
|
});
|
18
18
|
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
19
19
|
maxZoom: 19,
|
20
|
-
attribution: '
|
20
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
21
21
|
}).addTo(map);
|
22
22
|
|
23
23
|
let geojson = L.geoJSON({{ geojson| safe }}, {
|
@@ -64,7 +64,8 @@
|
|
64
64
|
<tbody>
|
65
65
|
{% for activity in activities %}
|
66
66
|
<tr>
|
67
|
-
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
67
|
+
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
68
|
+
href="{{ url_for('activity.show', id=activity.id) }}">{{
|
68
69
|
activity.name }}</a></td>
|
69
70
|
<td>{{ activity.start|dt }}</td>
|
70
71
|
<td>{{ activity.distance_km | round(1) }}</td>
|
@@ -52,7 +52,7 @@
|
|
52
52
|
});
|
53
53
|
L.tileLayer('/tile/pastel/{z}/{x}/{y}.png', {
|
54
54
|
maxZoom: 19,
|
55
|
-
attribution: '
|
55
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
56
56
|
}).addTo(map);
|
57
57
|
|
58
58
|
let geojson = L.geoJSON({{ color_line_geojson| safe }}, {
|
@@ -160,7 +160,7 @@
|
|
160
160
|
})
|
161
161
|
L.tileLayer('/tile/color/{z}/{x}/{y}.png', {
|
162
162
|
maxZoom: 19,
|
163
|
-
attribution: '
|
163
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
164
164
|
}).addTo(map)
|
165
165
|
|
166
166
|
let geojson_layer = L.geoJSON(geojson).addTo(map)
|
@@ -3,6 +3,8 @@ import importlib
|
|
3
3
|
import json
|
4
4
|
import pathlib
|
5
5
|
import secrets
|
6
|
+
import shutil
|
7
|
+
import urllib.parse
|
6
8
|
|
7
9
|
from flask import Flask
|
8
10
|
from flask import render_template
|
@@ -97,7 +99,8 @@ def web_ui_main(
|
|
97
99
|
url_prefix="/explorer",
|
98
100
|
)
|
99
101
|
app.register_blueprint(
|
100
|
-
make_heatmap_blueprint(repository, tile_visit_accessor),
|
102
|
+
make_heatmap_blueprint(repository, tile_visit_accessor, config_accessor()),
|
103
|
+
url_prefix="/heatmap",
|
101
104
|
)
|
102
105
|
app.register_blueprint(
|
103
106
|
make_settings_blueprint(config_accessor, authenticator),
|
@@ -115,7 +118,7 @@ def web_ui_main(
|
|
115
118
|
make_summary_blueprint(repository, config_accessor()),
|
116
119
|
url_prefix="/summary",
|
117
120
|
)
|
118
|
-
app.register_blueprint(make_tile_blueprint(), url_prefix="/tile")
|
121
|
+
app.register_blueprint(make_tile_blueprint(config_accessor()), url_prefix="/tile")
|
119
122
|
app.register_blueprint(
|
120
123
|
make_upload_blueprint(
|
121
124
|
repository, tile_visit_accessor, config_accessor(), authenticator
|
@@ -123,11 +126,20 @@ def web_ui_main(
|
|
123
126
|
url_prefix="/upload",
|
124
127
|
)
|
125
128
|
|
129
|
+
base_dir = pathlib.Path("Open Street Map Tiles")
|
130
|
+
dir_for_source = base_dir / urllib.parse.quote_plus(config_accessor().map_tile_url)
|
131
|
+
if base_dir.exists() and not dir_for_source.exists():
|
132
|
+
subdirs = base_dir.glob("*")
|
133
|
+
dir_for_source.mkdir()
|
134
|
+
for subdir in subdirs:
|
135
|
+
shutil.move(subdir, dir_for_source)
|
136
|
+
|
126
137
|
@app.context_processor
|
127
138
|
def inject_global_variables() -> dict:
|
128
139
|
return {
|
129
140
|
"version": _try_get_version(),
|
130
141
|
"num_activities": len(repository),
|
142
|
+
"map_tile_attribution": config_accessor().map_tile_attribution,
|
131
143
|
}
|
132
144
|
|
133
145
|
app.run(host=host, port=port)
|
@@ -73,7 +73,7 @@
|
|
73
73
|
})
|
74
74
|
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
75
75
|
maxZoom: 19,
|
76
|
-
attribution: '
|
76
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
77
77
|
}).addTo(map)
|
78
78
|
let explorer_layer_cluster_color = L.geoJSON(explorer_geojson, {
|
79
79
|
style: function (feature) {
|
@@ -87,58 +87,34 @@
|
|
87
87
|
let explorer_layer_first_age_color = L.geoJSON(explorer_geojson, {
|
88
88
|
style: function (feature) {
|
89
89
|
return {
|
90
|
-
color: "#440154", fillColor: feature.properties.first_age_color,
|
91
|
-
weight: 0.5
|
90
|
+
color: " #440154", fillColor: feature.properties.first_age_color, weight: 0.5
|
92
91
|
}
|
93
92
|
},
|
94
93
|
onEachFeature: onEachFeature
|
95
|
-
})
|
96
|
-
|
97
|
-
|
98
|
-
return {
|
99
|
-
color: "#440154", fillColor: feature.properties.last_age_color, weight:
|
100
|
-
0.5
|
101
|
-
}
|
102
|
-
},
|
103
|
-
onEachFeature: onEachFeature
|
104
|
-
})
|
105
|
-
|
106
|
-
let bbox = {{ center.bbox| safe }}
|
107
|
-
if (bbox) {
|
108
|
-
map.fitBounds(L.geoJSON(bbox).getBounds())
|
109
|
-
}
|
110
|
-
|
111
|
-
let explorer_square_layer = L.geoJSON(square_geojson,
|
112
|
-
{
|
113
|
-
style: function (feature) {
|
94
|
+
}) let explorer_layer_last_age_color = L.geoJSON(explorer_geojson, {
|
95
|
+
style:
|
96
|
+
function (feature) {
|
114
97
|
return {
|
115
|
-
color: "
|
98
|
+
color: "#440154", fillColor: feature.properties.last_age_color, weight:
|
99
|
+
0.5
|
116
100
|
}
|
117
|
-
}
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
function changeColor(method) {
|
101
|
+
}, onEachFeature: onEachFeature
|
102
|
+
}) let bbox = {{ center.bbox| safe }} if (bbox) {
|
103
|
+
map.fitBounds(L.geoJSON(bbox).getBounds())
|
104
|
+
} let explorer_square_layer = L.geoJSON(square_geojson, {
|
105
|
+
style: function (feature) { return { color: "blue", fill: false, weight: 2 } }
|
106
|
+
}).addTo(map)
|
107
|
+
active_layer = explorer_layer_cluster_color function changeColor(method) {
|
124
108
|
map.removeLayer(active_layer)
|
125
|
-
if (method == "cluster") {
|
126
|
-
active_layer = explorer_layer_cluster_color
|
127
|
-
} else if (method == "first") {
|
109
|
+
if (method == "cluster") { active_layer = explorer_layer_cluster_color } else if (method == "first") {
|
128
110
|
active_layer = explorer_layer_first_age_color
|
129
111
|
} else if (method == "last") {
|
130
112
|
active_layer = explorer_layer_last_age_color
|
131
|
-
}
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
bounds = map.getBounds()
|
137
|
-
zoom = "{{ zoom }}"
|
138
|
-
window.location.href =
|
139
|
-
`/explorer/${zoom}/${bounds.getNorth()}/${bounds.getEast()}/${bounds.getSouth()}/${bounds.getWest()}/${suffix}`
|
140
|
-
}
|
141
|
-
</script>
|
113
|
+
} map.addLayer(active_layer)
|
114
|
+
} function downloadAs(suffix) {
|
115
|
+
bounds = map.getBounds() zoom = "{{ zoom }}"
|
116
|
+
window.location.href = `/explorer/${zoom}/${bounds.getNorth()}/${bounds.getEast()}/${bounds.getSouth()}/${bounds.getWest()}/${suffix}`
|
117
|
+
} </script>
|
142
118
|
</div>
|
143
119
|
</div>
|
144
120
|
|
@@ -6,12 +6,15 @@ from flask import Response
|
|
6
6
|
from ...core.activities import ActivityRepository
|
7
7
|
from ...explorer.tile_visits import TileVisitAccessor
|
8
8
|
from .heatmap_controller import HeatmapController
|
9
|
+
from geo_activity_playground.core.config import Config
|
9
10
|
|
10
11
|
|
11
12
|
def make_heatmap_blueprint(
|
12
|
-
repository: ActivityRepository,
|
13
|
+
repository: ActivityRepository,
|
14
|
+
tile_visit_accessor: TileVisitAccessor,
|
15
|
+
config: Config,
|
13
16
|
) -> Blueprint:
|
14
|
-
heatmap_controller = HeatmapController(repository, tile_visit_accessor)
|
17
|
+
heatmap_controller = HeatmapController(repository, tile_visit_accessor, config)
|
15
18
|
blueprint = Blueprint("heatmap", __name__, template_folder="templates")
|
16
19
|
|
17
20
|
@blueprint.route("/")
|
@@ -8,6 +8,7 @@ from PIL import Image
|
|
8
8
|
from PIL import ImageDraw
|
9
9
|
|
10
10
|
from geo_activity_playground.core.activities import ActivityRepository
|
11
|
+
from geo_activity_playground.core.config import Config
|
11
12
|
from geo_activity_playground.core.heatmap import convert_to_grayscale
|
12
13
|
from geo_activity_playground.core.heatmap import GeoBounds
|
13
14
|
from geo_activity_playground.core.heatmap import get_sensible_zoom_level
|
@@ -29,10 +30,14 @@ OSM_TILE_SIZE = 256 # OSM tile size in pixel
|
|
29
30
|
|
30
31
|
class HeatmapController:
|
31
32
|
def __init__(
|
32
|
-
self,
|
33
|
+
self,
|
34
|
+
repository: ActivityRepository,
|
35
|
+
tile_visit_accessor: TileVisitAccessor,
|
36
|
+
config: Config,
|
33
37
|
) -> None:
|
34
38
|
self._repository = repository
|
35
39
|
self._tile_visit_accessor = tile_visit_accessor
|
40
|
+
self._config = config
|
36
41
|
|
37
42
|
self.tile_histories = self._tile_visit_accessor.tile_state["tile_history"]
|
38
43
|
self.tile_evolution_states = self._tile_visit_accessor.tile_state[
|
@@ -140,11 +145,11 @@ class HeatmapController:
|
|
140
145
|
tile_counts = np.sqrt(tile_counts) / 5
|
141
146
|
tile_counts[tile_counts > 1.0] = 1.0
|
142
147
|
|
143
|
-
cmap = pl.get_cmap(
|
148
|
+
cmap = pl.get_cmap(self._config.color_scheme_for_heatmap)
|
144
149
|
data_color = cmap(tile_counts)
|
145
150
|
data_color[data_color == cmap(0.0)] = 0.0 # remove background color
|
146
151
|
|
147
|
-
map_tile = np.array(get_tile(z, x, y)) / 255
|
152
|
+
map_tile = np.array(get_tile(z, x, y, self._config.map_tile_url)) / 255
|
148
153
|
map_tile = convert_to_grayscale(map_tile)
|
149
154
|
map_tile = 1.0 - map_tile # invert colors
|
150
155
|
for c in range(3):
|
@@ -168,7 +173,12 @@ class HeatmapController:
|
|
168
173
|
background = np.zeros((*pixel_bounds.shape, 3))
|
169
174
|
for x in range(tile_bounds.x_tile_min, tile_bounds.x_tile_max):
|
170
175
|
for y in range(tile_bounds.y_tile_min, tile_bounds.y_tile_max):
|
171
|
-
tile =
|
176
|
+
tile = (
|
177
|
+
np.array(
|
178
|
+
get_tile(tile_bounds.zoom, x, y, self._config.map_tile_url)
|
179
|
+
)
|
180
|
+
/ 255
|
181
|
+
)
|
172
182
|
|
173
183
|
i = y - tile_bounds.y_tile_min
|
174
184
|
j = x - tile_bounds.x_tile_min
|
@@ -35,7 +35,7 @@
|
|
35
35
|
});
|
36
36
|
L.tileLayer('/heatmap/tile/{z}/{x}/{y}/{{ kinds_str }}.png', {
|
37
37
|
maxZoom: 19,
|
38
|
-
attribution: '
|
38
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
39
39
|
}).addTo(map)
|
40
40
|
|
41
41
|
let bbox = {{ center.bbox| safe }}
|
@@ -15,6 +15,43 @@ from geo_activity_playground.webui.authenticator import needs_authentication
|
|
15
15
|
from geo_activity_playground.webui.settings.controller import SettingsController
|
16
16
|
|
17
17
|
|
18
|
+
VEGA_COLOR_SCHEMES_CONTINUOUS = [
|
19
|
+
"lightgreyred",
|
20
|
+
"lightgreyteal",
|
21
|
+
"lightmulti",
|
22
|
+
"lightorange",
|
23
|
+
"lighttealblue",
|
24
|
+
"blues",
|
25
|
+
"tealblues",
|
26
|
+
"teals",
|
27
|
+
"greens",
|
28
|
+
"browns",
|
29
|
+
"oranges",
|
30
|
+
"reds",
|
31
|
+
"purples",
|
32
|
+
"warmgreys",
|
33
|
+
"greys",
|
34
|
+
]
|
35
|
+
|
36
|
+
MATPLOTLIB_COLOR_SCHEMES_CONTINUOUS = [
|
37
|
+
"afmhot",
|
38
|
+
"bone",
|
39
|
+
"cividis",
|
40
|
+
"copper",
|
41
|
+
"gist_gray",
|
42
|
+
"gist_heat",
|
43
|
+
"gnuplot2",
|
44
|
+
"gray",
|
45
|
+
"Greys_r",
|
46
|
+
"hot",
|
47
|
+
"inferno",
|
48
|
+
"magma",
|
49
|
+
"pink",
|
50
|
+
"plasma",
|
51
|
+
"viridis",
|
52
|
+
]
|
53
|
+
|
54
|
+
|
18
55
|
def int_or_none(s: str) -> Optional[int]:
|
19
56
|
if s:
|
20
57
|
try:
|
@@ -56,44 +93,16 @@ def make_settings_blueprint(
|
|
56
93
|
config_accessor().color_scheme_for_kind = request.form[
|
57
94
|
"color_scheme_for_kind"
|
58
95
|
]
|
96
|
+
config_accessor().color_scheme_for_heatmap = request.form[
|
97
|
+
"color_scheme_for_heatmap"
|
98
|
+
]
|
59
99
|
config_accessor.save()
|
60
100
|
flash("Updated color schemes.", category="success")
|
101
|
+
|
61
102
|
return render_template(
|
62
103
|
"settings/color-schemes.html.j2",
|
63
104
|
color_scheme_for_counts=config_accessor().color_scheme_for_counts,
|
64
|
-
color_scheme_for_counts_avail=
|
65
|
-
"viridis",
|
66
|
-
"magma",
|
67
|
-
"inferno",
|
68
|
-
"plasma",
|
69
|
-
"cividis",
|
70
|
-
"turbo",
|
71
|
-
"bluegreen",
|
72
|
-
"bluepurple",
|
73
|
-
"goldgreen",
|
74
|
-
"goldorange",
|
75
|
-
"goldred",
|
76
|
-
"greenblue",
|
77
|
-
"orangered",
|
78
|
-
"purplebluegreen",
|
79
|
-
"purpleblue",
|
80
|
-
"purplered",
|
81
|
-
"redpurple",
|
82
|
-
"yellowgreenblue",
|
83
|
-
"yellowgreen",
|
84
|
-
"yelloworangebrown",
|
85
|
-
"yelloworangered",
|
86
|
-
"darkblue",
|
87
|
-
"darkgold",
|
88
|
-
"darkgreen",
|
89
|
-
"darkmulti",
|
90
|
-
"darkred",
|
91
|
-
"lightgreyred",
|
92
|
-
"lightgreyteal",
|
93
|
-
"lightmulti",
|
94
|
-
"lightorange",
|
95
|
-
"lighttealblue",
|
96
|
-
],
|
105
|
+
color_scheme_for_counts_avail=VEGA_COLOR_SCHEMES_CONTINUOUS,
|
97
106
|
color_scheme_for_kind=config_accessor().color_scheme_for_kind,
|
98
107
|
color_scheme_for_kind_avail=[
|
99
108
|
"accent",
|
@@ -111,6 +120,8 @@ def make_settings_blueprint(
|
|
111
120
|
"tableau10",
|
112
121
|
"tableau20",
|
113
122
|
],
|
123
|
+
color_scheme_for_heatmap=config_accessor().color_scheme_for_heatmap,
|
124
|
+
color_scheme_for_heatmap_avail=MATPLOTLIB_COLOR_SCHEMES_CONTINUOUS,
|
114
125
|
)
|
115
126
|
|
116
127
|
@blueprint.route("/equipment-offsets", methods=["GET", "POST"])
|
@@ -18,8 +18,17 @@
|
|
18
18
|
</div>
|
19
19
|
|
20
20
|
<div class="mb-3">
|
21
|
-
<label class="form-label">Color scheme for
|
22
|
-
<select class="form-select" aria-label="Color scheme for heatmaps" name="
|
21
|
+
<label class="form-label">Color scheme for interactive heatmap</label>
|
22
|
+
<select class="form-select" aria-label="Color scheme for heatmaps" name="color_scheme_for_heatmap">
|
23
|
+
{% for cs in color_scheme_for_heatmap_avail %}
|
24
|
+
<option {% if cs==color_scheme_for_heatmap %} selected {% endif %}>{{ cs }}</option>
|
25
|
+
{% endfor %}
|
26
|
+
</select>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="mb-3">
|
30
|
+
<label class="form-label">Color scheme for count plots</label>
|
31
|
+
<select class="form-select" aria-label="Color scheme for count plots" name="color_scheme_for_counts">
|
23
32
|
{% for cs in color_scheme_for_counts_avail %}
|
24
33
|
<option {% if cs==color_scheme_for_counts %} selected {% endif %}>{{ cs }}</option>
|
25
34
|
{% endfor %}
|
@@ -20,7 +20,7 @@
|
|
20
20
|
})
|
21
21
|
L.tileLayer('/tile/color/{z}/{x}/{y}.png', {
|
22
22
|
maxZoom: 19,
|
23
|
-
attribution: '
|
23
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
24
24
|
}).addTo(map)
|
25
25
|
|
26
26
|
let geojson_layer = L.geoJSON(geojson).addTo(map)
|
@@ -106,7 +106,7 @@
|
|
106
106
|
|
107
107
|
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
108
108
|
maxZoom: 19,
|
109
|
-
attribution: '
|
109
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
110
110
|
}).addTo(map)
|
111
111
|
|
112
112
|
let explorer_geojson = {{ explored_geojson| safe }}
|
@@ -21,7 +21,7 @@ class SummaryController:
|
|
21
21
|
def render(self) -> dict:
|
22
22
|
kind_scale = make_kind_scale(self._repository.meta, self._config)
|
23
23
|
df = embellished_activities(self._repository.meta)
|
24
|
-
df = df.loc[df["consider_for_achievements"]]
|
24
|
+
# df = df.loc[df["consider_for_achievements"]]
|
25
25
|
|
26
26
|
year_kind_total = (
|
27
27
|
df[["year", "kind", "distance_km", "hours"]]
|
@@ -31,7 +31,7 @@ class SummaryController:
|
|
31
31
|
)
|
32
32
|
|
33
33
|
return {
|
34
|
-
"
|
34
|
+
"plot_distance_heatmaps": plot_distance_heatmaps(df, self._config),
|
35
35
|
"plot_monthly_distance": plot_monthly_distance(df, kind_scale),
|
36
36
|
"plot_yearly_distance": plot_yearly_distance(year_kind_total, kind_scale),
|
37
37
|
"plot_year_cumulative": plot_year_cumulative(df),
|
@@ -112,17 +112,10 @@ def embellished_activities(meta: pd.DataFrame) -> pd.DataFrame:
|
|
112
112
|
return df
|
113
113
|
|
114
114
|
|
115
|
-
def
|
116
|
-
return
|
117
|
-
alt.Chart(
|
118
|
-
meta.loc[
|
119
|
-
(
|
120
|
-
meta["start"]
|
121
|
-
>= pd.to_datetime(
|
122
|
-
datetime.datetime.now() - datetime.timedelta(days=2 * 365)
|
123
|
-
)
|
124
|
-
)
|
125
|
-
],
|
115
|
+
def plot_distance_heatmaps(meta: pd.DataFrame, config: Config) -> dict[int, str]:
|
116
|
+
return {
|
117
|
+
year: alt.Chart(
|
118
|
+
meta.loc[(meta["year"] == year)],
|
126
119
|
title="Daily Distance Heatmap",
|
127
120
|
)
|
128
121
|
.mark_rect()
|
@@ -130,7 +123,7 @@ def plot_distance_heatmap(meta: pd.DataFrame, config: Config) -> str:
|
|
130
123
|
alt.X("date(start):O", title="Day of month"),
|
131
124
|
alt.Y(
|
132
125
|
"yearmonth(start):O",
|
133
|
-
scale=alt.Scale(reverse=True),
|
126
|
+
# scale=alt.Scale(reverse=True),
|
134
127
|
title="Year and month",
|
135
128
|
),
|
136
129
|
alt.Color(
|
@@ -146,7 +139,8 @@ def plot_distance_heatmap(meta: pd.DataFrame, config: Config) -> str:
|
|
146
139
|
],
|
147
140
|
)
|
148
141
|
.to_json(format="vega")
|
149
|
-
|
142
|
+
for year in sorted(meta["year"].unique())
|
143
|
+
}
|
150
144
|
|
151
145
|
|
152
146
|
def plot_monthly_distance(meta: pd.DataFrame, kind_scale: alt.Scale) -> str:
|
@@ -76,13 +76,26 @@
|
|
76
76
|
<p>Next we take one row per month, each column is a day-of-month. The brighter a box, the more distance you have covered
|
77
77
|
on that day. This makes it easy to spot those days where you really covered a lot of distance!</p>
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
|
80
|
+
<ul class="nav nav-pills mb-3" id="myTab" role="tablist">
|
81
|
+
{% for year, plot in plot_distance_heatmaps.items() %}
|
82
|
+
<li class="nav-item" role="presentation">
|
83
|
+
<button class="nav-link {% if loop.last %} active {% endif %}" id="heatmap-{{ year }}" data-bs-toggle="tab"
|
84
|
+
data-bs-target="#heatmap-{{ year }}-pane" type="button" role="tab" aria-controls="heatmap-{{ year }}-pane"
|
85
|
+
aria-selected="{{ loop.last }}">{{ year }}</button>
|
86
|
+
</li>
|
87
|
+
{% endfor %}
|
88
|
+
</ul>
|
89
|
+
|
90
|
+
<div class="tab-content mb-3" id="myTabContent">
|
91
|
+
{% for year, plot in plot_distance_heatmaps.items() %}
|
92
|
+
<div class="tab-pane fade {% if loop.last %} show active {% endif %}" id="heatmap-{{ year }}-pane" role="tabpanel"
|
93
|
+
aria-labelledby="heatmap-{{ year }}" tabindex="0">
|
94
|
+
{{ vega_direct("plot_distance_heatmap_%d"|format(year), plot) }}
|
82
95
|
</div>
|
96
|
+
{% endfor %}
|
83
97
|
</div>
|
84
98
|
|
85
|
-
|
86
99
|
<h2>Hall of Fame</h2>
|
87
100
|
|
88
101
|
<script>
|
@@ -92,7 +105,7 @@
|
|
92
105
|
})
|
93
106
|
L.tileLayer('/tile/color/{z}/{x}/{y}.png', {
|
94
107
|
maxZoom: 19,
|
95
|
-
attribution: '
|
108
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
96
109
|
}).addTo(map)
|
97
110
|
|
98
111
|
let geojson_layer = L.geoJSON(geojson).addTo(map)
|
@@ -26,7 +26,7 @@
|
|
26
26
|
})
|
27
27
|
L.tileLayer('/tile/color/{z}/{x}/{y}.png', {
|
28
28
|
maxZoom: 19,
|
29
|
-
attribution: '
|
29
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
30
30
|
}).addTo(map)
|
31
31
|
|
32
32
|
let geojson_layer = L.geoJSON(geojson).addTo(map)
|
@@ -2,12 +2,13 @@ from flask import Blueprint
|
|
2
2
|
from flask import Response
|
3
3
|
|
4
4
|
from .controller import TileController
|
5
|
+
from geo_activity_playground.core.config import Config
|
5
6
|
|
6
7
|
|
7
|
-
def make_tile_blueprint() -> Blueprint:
|
8
|
+
def make_tile_blueprint(config: Config) -> Blueprint:
|
8
9
|
blueprint = Blueprint("tiles", __name__, template_folder="templates")
|
9
10
|
|
10
|
-
tile_controller = TileController()
|
11
|
+
tile_controller = TileController(config)
|
11
12
|
|
12
13
|
@blueprint.route("/color/<z>/<x>/<y>.png")
|
13
14
|
def tile_color(x: str, y: str, z: str):
|
@@ -3,18 +3,22 @@ import io
|
|
3
3
|
import matplotlib.pyplot as pl
|
4
4
|
import numpy as np
|
5
5
|
|
6
|
+
from geo_activity_playground.core.config import Config
|
6
7
|
from geo_activity_playground.core.tiles import get_tile
|
7
8
|
|
8
9
|
|
9
10
|
class TileController:
|
11
|
+
def __init__(self, config: Config):
|
12
|
+
self._config = config
|
13
|
+
|
10
14
|
def render_color(self, x: int, y: int, z: int) -> bytes:
|
11
|
-
map_tile = np.array(get_tile(z, x, y)) / 255
|
15
|
+
map_tile = np.array(get_tile(z, x, y, self._config.map_tile_url)) / 255
|
12
16
|
f = io.BytesIO()
|
13
17
|
pl.imsave(f, map_tile, format="png")
|
14
18
|
return bytes(f.getbuffer())
|
15
19
|
|
16
20
|
def render_grayscale(self, x: int, y: int, z: int) -> bytes:
|
17
|
-
map_tile = np.array(get_tile(z, x, y)) / 255
|
21
|
+
map_tile = np.array(get_tile(z, x, y, self._config.map_tile_url)) / 255
|
18
22
|
map_tile = np.sum(map_tile * [0.2126, 0.7152, 0.0722], axis=2) # to grayscale
|
19
23
|
map_tile = np.dstack((map_tile, map_tile, map_tile)) # to rgb
|
20
24
|
f = io.BytesIO()
|
@@ -22,7 +26,7 @@ class TileController:
|
|
22
26
|
return bytes(f.getbuffer())
|
23
27
|
|
24
28
|
def render_pastel(self, x: int, y: int, z: int) -> bytes:
|
25
|
-
map_tile = np.array(get_tile(z, x, y)) / 255
|
29
|
+
map_tile = np.array(get_tile(z, x, y, self._config.map_tile_url)) / 255
|
26
30
|
averaged_tile = np.sum(map_tile * [0.2126, 0.7152, 0.0722], axis=2)
|
27
31
|
grayscale_tile = np.dstack((averaged_tile, averaged_tile, averaged_tile))
|
28
32
|
factor = 0.7
|
@@ -102,8 +102,7 @@ def scan_for_activities(
|
|
102
102
|
) -> None:
|
103
103
|
if pathlib.Path("Activities").exists():
|
104
104
|
import_from_directory(
|
105
|
-
config.metadata_extraction_regexes,
|
106
|
-
config.num_processes,
|
105
|
+
config.metadata_extraction_regexes, config.num_processes, config
|
107
106
|
)
|
108
107
|
if pathlib.Path("Strava Export").exists():
|
109
108
|
import_from_strava_checkout()
|
{geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/RECORD
RENAMED
@@ -2,18 +2,18 @@ geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
2
2
|
geo_activity_playground/__main__.py,sha256=MBZn9K1m3PofiPNTtpsSTVCyB_Gz95TjVP-nb9v_-JE,3989
|
3
3
|
geo_activity_playground/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
geo_activity_playground/core/activities.py,sha256=soxMtdijnkZ1bYZU0q6wuM8NPNFoUpLwYp3IvBOaKJY,6927
|
5
|
-
geo_activity_playground/core/config.py,sha256=
|
5
|
+
geo_activity_playground/core/config.py,sha256=uqiwk7CgcuGx8JemHSsRKjRwyNT1YTb7V0gX0OJhfaI,5109
|
6
6
|
geo_activity_playground/core/coordinates.py,sha256=tDfr9mlXhK6E_MMIJ0vYWVCoH0Lq8uyuaqUgaa8i0jg,966
|
7
7
|
geo_activity_playground/core/enrichment.py,sha256=fUmk6avy_rqePlHmJQFTQhAxjgIRaxxmq18N2OSXBBg,7771
|
8
8
|
geo_activity_playground/core/heart_rate.py,sha256=IwMt58TpjOYqpAxtsj07zP2ttpN_J3GZeiv-qGhYyJc,1598
|
9
|
-
geo_activity_playground/core/heatmap.py,sha256=
|
9
|
+
geo_activity_playground/core/heatmap.py,sha256=iTxefUTjTToPrKpVbauJHXkqxpNppXOEK6vvKuNkHkk,5906
|
10
10
|
geo_activity_playground/core/paths.py,sha256=RBeUi38riP_msTGPy1TsPRNiblzE-lFivaJSLULE8b0,2503
|
11
11
|
geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
|
12
12
|
geo_activity_playground/core/similarity.py,sha256=Jo8jRViuORCxdIGvyaflgsQhwu9S_jn10a450FRL18A,3159
|
13
13
|
geo_activity_playground/core/tasks.py,sha256=aMDBWJqp6ek2ao6G6Xa8GOSZbcQqXoWL74SGRowRPIk,2942
|
14
14
|
geo_activity_playground/core/test_tiles.py,sha256=zce1FxNfsSpOQt66jMehdQRVoNdl-oiFydx6iVBHZXM,764
|
15
15
|
geo_activity_playground/core/test_time_conversion.py,sha256=Sh6nZA3uCTOdZTZa3yOijtR0m74QtZu2mcWXsDNnyQI,984
|
16
|
-
geo_activity_playground/core/tiles.py,sha256=
|
16
|
+
geo_activity_playground/core/tiles.py,sha256=qUe4h3rzHJb8xThAgUKSLElt8S6zID_OuaWXDkWLwAU,3539
|
17
17
|
geo_activity_playground/core/time_conversion.py,sha256=x5mXG6Y4GtdX7CBmwucGNSWBp9JQJDbZ7u0JkdUY1Vs,379
|
18
18
|
geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
geo_activity_playground/explorer/grid_file.py,sha256=k6j6KBEk2a2BY-onE8SV5TJsERGGyOrlY4as__meWpA,3304
|
@@ -22,7 +22,7 @@ geo_activity_playground/explorer/video.py,sha256=ROAmV9shfJyqTgnXVD41KFORiwnRgVp
|
|
22
22
|
geo_activity_playground/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
geo_activity_playground/importers/activity_parsers.py,sha256=XNQs0ziqAcVqIoiLAX5Ndmhb6v__29XdjUPvNvc7oBI,11082
|
24
24
|
geo_activity_playground/importers/csv_parser.py,sha256=O1pP5GLhWhnWcy2Lsrr9g17Zspuibpt-GtZ3ZS5eZF4,2143
|
25
|
-
geo_activity_playground/importers/directory.py,sha256=
|
25
|
+
geo_activity_playground/importers/directory.py,sha256=CA-vFOMm8G4MSM_Q09OwQKduCApL2PWaxLTVxgw_xpw,5908
|
26
26
|
geo_activity_playground/importers/strava_api.py,sha256=cJCZsLemhOlxTtZh0z_npidgae9SD5HyEUry2uvem_A,7775
|
27
27
|
geo_activity_playground/importers/strava_checkout.py,sha256=N-uGTkhBJMC7cPYjRRXHOSLwpK3wc6aaSrY2RQfSitA,9419
|
28
28
|
geo_activity_playground/importers/test_csv_parser.py,sha256=LXqva7GuSAfXYE2zZQrg-69lCtfy5MxLSq6BRwL_VyI,1191
|
@@ -31,13 +31,13 @@ geo_activity_playground/importers/test_strava_api.py,sha256=4vX7wDr1a9aRh8myxNrI
|
|
31
31
|
geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
32
|
geo_activity_playground/webui/activity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
33
|
geo_activity_playground/webui/activity/blueprint.py,sha256=Ub2mC9S9TII7CJaaWahnbNtT76uOGKNDWE0-j2C56CA,3893
|
34
|
-
geo_activity_playground/webui/activity/controller.py,sha256=
|
35
|
-
geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=
|
34
|
+
geo_activity_playground/webui/activity/controller.py,sha256=t4-9-Gxchc959AaOOflSTYhNiGBuJT2nu1UZ0Vv_nxU,19343
|
35
|
+
geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=Gitp3IR913YTjzlz_OiWIe3VSN4OmFGZVGPuMVINCg4,2693
|
36
36
|
geo_activity_playground/webui/activity/templates/activity/edit.html.j2,sha256=ckcTTxcQOhmvvAGNTEOtWCUG4LhvO4HfQImlIa5qKs8,1530
|
37
|
-
geo_activity_playground/webui/activity/templates/activity/lines.html.j2,sha256=
|
38
|
-
geo_activity_playground/webui/activity/templates/activity/name.html.j2,sha256=
|
39
|
-
geo_activity_playground/webui/activity/templates/activity/show.html.j2,sha256=
|
40
|
-
geo_activity_playground/webui/app.py,sha256=
|
37
|
+
geo_activity_playground/webui/activity/templates/activity/lines.html.j2,sha256=_ZDg1ruW-9UMJfOudy1-uY_-IcSSaagq7tPCih5Bb8g,1079
|
38
|
+
geo_activity_playground/webui/activity/templates/activity/name.html.j2,sha256=tKviMqMouHEGv3xBQVIsJgwj_hjwAsmGVefM3UMqlYg,2437
|
39
|
+
geo_activity_playground/webui/activity/templates/activity/show.html.j2,sha256=bEx37UGSTeeJl7gN4fjyOpINFQwZ5Zm-HOKpLqcJGfs,6905
|
40
|
+
geo_activity_playground/webui/app.py,sha256=nWbgxRHg0DThi44VFb04OIVwxnqIVmkr3cnO9ucE1Oo,5118
|
41
41
|
geo_activity_playground/webui/auth/blueprint.py,sha256=Lx-ZvMnfHLC1CMre1xPQI3k_pCtQoZvgRhtmafULzoE,812
|
42
42
|
geo_activity_playground/webui/auth/templates/auth/index.html.j2,sha256=ILQ5HvTEYc3OrtOAIFt1VrqWorVD70V9DC342znmP70,579
|
43
43
|
geo_activity_playground/webui/authenticator.py,sha256=k278OEVuOfAmTGT4F2X4pqSTwwkK_FA87EIhAeysEqc,1416
|
@@ -58,32 +58,32 @@ geo_activity_playground/webui/equipment/templates/equipment/index.html.j2,sha256
|
|
58
58
|
geo_activity_playground/webui/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
geo_activity_playground/webui/explorer/blueprint.py,sha256=EKnBs8llqT6Wy1uac18dF2epp3TebF9p3iGlSbj6Vl0,2337
|
60
60
|
geo_activity_playground/webui/explorer/controller.py,sha256=pIzWh0TpLJgKQZlS325-QT7nA1q9ms7fRqQIp24PNfo,11705
|
61
|
-
geo_activity_playground/webui/explorer/templates/explorer/index.html.j2,sha256=
|
61
|
+
geo_activity_playground/webui/explorer/templates/explorer/index.html.j2,sha256=PnAL0uDNjnziwbkCOpIWPb7KmS4c1dP-VLxjyXppUYc,6701
|
62
62
|
geo_activity_playground/webui/heatmap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
-
geo_activity_playground/webui/heatmap/blueprint.py,sha256=
|
64
|
-
geo_activity_playground/webui/heatmap/heatmap_controller.py,sha256=
|
65
|
-
geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2,sha256=
|
63
|
+
geo_activity_playground/webui/heatmap/blueprint.py,sha256=ZEImDIwT3uiDIKapqCU49llvyqG79n7ZEu1GHgoLZqo,1558
|
64
|
+
geo_activity_playground/webui/heatmap/heatmap_controller.py,sha256=Qw9MGW3TFWlG2JkA_r9RHgYq4hvPiJaZeAg5D9lIFC0,7821
|
65
|
+
geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2,sha256=BMjqbZ-btSFawuNDxgZxOkF5JvhD_p9DOBJ-4-1IKnU,1833
|
66
66
|
geo_activity_playground/webui/plot_util.py,sha256=pTTQoqOCkLVjkgOit7mbry28kMruZIL8amZozSzEpxQ,283
|
67
67
|
geo_activity_playground/webui/search/blueprint.py,sha256=7TDsiqEowMyHNlFImk-hCGso69KOieG4rfJnLRHpRz8,3300
|
68
68
|
geo_activity_playground/webui/search/templates/search/index.html.j2,sha256=d39uhteoY6JOZePqhLIYLERqkckW3oghMnTUZa7X1J8,3798
|
69
|
-
geo_activity_playground/webui/settings/blueprint.py,sha256=
|
69
|
+
geo_activity_playground/webui/settings/blueprint.py,sha256=GIrCOBNw-uNWDmKj4uduYOiKpoTLEfFbGmUmXkoBqro,9405
|
70
70
|
geo_activity_playground/webui/settings/controller.py,sha256=MIZVBfoGNvmJnB_ECV_x5eH2i6gDZvkWSQ4PcSKyLKs,9170
|
71
71
|
geo_activity_playground/webui/settings/templates/settings/admin-password.html.j2,sha256=VYwddpObD1RpeTH5Dm4y7VtmT7kwURDCIjxyzJeq08c,495
|
72
|
-
geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2,sha256=
|
72
|
+
geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2,sha256=iR91Wxd2_TMuIo9dBDZBrWSUGHNwTwzC6O8oNH-XBt4,1653
|
73
73
|
geo_activity_playground/webui/settings/templates/settings/equipment-offsets.html.j2,sha256=ltaYwFe8S8Mi72ddmIp1vwqlu8MEXXjBGfbpN2WBTC4,1728
|
74
74
|
geo_activity_playground/webui/settings/templates/settings/heart-rate.html.j2,sha256=UPT3MegRgSeff36lhCo0l3ZwhqNSIg5gM6h2s32GkCY,4255
|
75
75
|
geo_activity_playground/webui/settings/templates/settings/index.html.j2,sha256=r7HBq91BKBrWT2_TNR7OcLokuOcUK-Czyi_zOjveIRk,5287
|
76
76
|
geo_activity_playground/webui/settings/templates/settings/kind-renames.html.j2,sha256=yWVIMJq0XOpeTLglEqLxlQAG0mVhaCVw9j0Iu4nIrgI,757
|
77
77
|
geo_activity_playground/webui/settings/templates/settings/kinds-without-achievements.html.j2,sha256=IdUfXon1Pu8zX3NirKb28ypshLHOvZRpz2T4bJrzrak,1067
|
78
78
|
geo_activity_playground/webui/settings/templates/settings/metadata-extraction.html.j2,sha256=EUHZ-MWnSPAkiad0CHSj9UUkRUQwItTbKYJcZz9TVi0,2314
|
79
|
-
geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2,sha256=
|
79
|
+
geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2,sha256=OBKHlOezJauaDerLpOAJYq7wL-KxsWMn-BpSKJ93I0c,3051
|
80
80
|
geo_activity_playground/webui/settings/templates/settings/segmentation.html.j2,sha256=QV72TZcIxqql-vEsq2lKHzo5UxoxeeXkRA9se46GWKU,1187
|
81
81
|
geo_activity_playground/webui/settings/templates/settings/sharepic.html.j2,sha256=qZkfEpd4CtKKMaSSVadqvNEgMRYLV-0X-pw5-nJvukk,678
|
82
82
|
geo_activity_playground/webui/settings/templates/settings/strava.html.j2,sha256=FrXgT-m1PgvsQWo9kMKpk8QenKeifSDBCZFqKgsHRxQ,1827
|
83
83
|
geo_activity_playground/webui/square_planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
84
84
|
geo_activity_playground/webui/square_planner/blueprint.py,sha256=r2VkSM547chX85g6c1BQ8NC-tkdqGdYp-2ZALBiiDTc,1320
|
85
85
|
geo_activity_playground/webui/square_planner/controller.py,sha256=ML6ftOyr3tTh7D4DBcRP76CvkyTxqI5QWgMeG9wC8FE,3561
|
86
|
-
geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2,sha256=
|
86
|
+
geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2,sha256=xFbYBQtkOl3U4WGkvIuU_5uazGHn8ObvQfNgPGq0Gqg,6469
|
87
87
|
geo_activity_playground/webui/static/android-chrome-192x192.png,sha256=yxZgo8Jw4hrgOgrn3tvi9G0AXWGFD29kjCuxC07WoT4,17610
|
88
88
|
geo_activity_playground/webui/static/android-chrome-512x512.png,sha256=Uiv62gQpUjMOdp9d6exzd6IyOi5zgQdgjIVVWYw5m98,38891
|
89
89
|
geo_activity_playground/webui/static/apple-touch-icon.png,sha256=TNLa0YIS1mbWajvIQthC2bGve6ET3DbJzrAbs6Pf3Ps,13046
|
@@ -100,20 +100,20 @@ geo_activity_playground/webui/static/web-app-manifest-192x192.png,sha256=eEImN6i
|
|
100
100
|
geo_activity_playground/webui/static/web-app-manifest-512x512.png,sha256=vU9oQ4HnQerFDZVzcAT9twj4_Doc6_9v9wVvoRI-f_E,48318
|
101
101
|
geo_activity_playground/webui/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
102
102
|
geo_activity_playground/webui/summary/blueprint.py,sha256=tfA2aPF19yKwkQOb5lPQBySoQYYhTn49Iuh0SYvsGP8,593
|
103
|
-
geo_activity_playground/webui/summary/controller.py,sha256=
|
104
|
-
geo_activity_playground/webui/summary/templates/summary/index.html.j2,sha256=
|
105
|
-
geo_activity_playground/webui/templates/home.html.j2,sha256=
|
103
|
+
geo_activity_playground/webui/summary/controller.py,sha256=V3zBLNL_Yc_Ft-ZBaWnlLXhr8VsvTpipPUZ-G9sgUXs,9312
|
104
|
+
geo_activity_playground/webui/summary/templates/summary/index.html.j2,sha256=ctOx3Qjx6nRDpUtFf1DlJhK_gtU77Vwx_S6euLz9-W4,5183
|
105
|
+
geo_activity_playground/webui/templates/home.html.j2,sha256=JNQxpjDfXhLAJbZ7RaNBNe9u48nbvlEBrnU_Zizt544,2131
|
106
106
|
geo_activity_playground/webui/templates/page.html.j2,sha256=znTbtD2NALrhmUN_Q-F1ElGlKtecoUv8vOCcUtojrdI,11134
|
107
107
|
geo_activity_playground/webui/tile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
108
|
-
geo_activity_playground/webui/tile/blueprint.py,sha256=
|
109
|
-
geo_activity_playground/webui/tile/controller.py,sha256=
|
108
|
+
geo_activity_playground/webui/tile/blueprint.py,sha256=q0sw_F8L367Df01yjZijikEIglFBgg9lN61sbTAEOKQ,1018
|
109
|
+
geo_activity_playground/webui/tile/controller.py,sha256=XjUTbyAMeQET1D3mFtT8r5-xMcMOaELPZWtQ1Xp7Cuw,1428
|
110
110
|
geo_activity_playground/webui/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
111
111
|
geo_activity_playground/webui/upload/blueprint.py,sha256=xX9scEmleN_dL03jfhWRh5yI1WsFyhxUFiS_Ul2HWy4,1428
|
112
|
-
geo_activity_playground/webui/upload/controller.py,sha256=
|
112
|
+
geo_activity_playground/webui/upload/controller.py,sha256=EvoUnmKBo3QS2TLak7-yVZ16sEDyEB6Nf2MN_scHuhQ,4080
|
113
113
|
geo_activity_playground/webui/upload/templates/upload/index.html.j2,sha256=I1Ix8tDS3YBdi-HdaNfjkzYXVVCjfUTe5PFTnap1ydc,775
|
114
114
|
geo_activity_playground/webui/upload/templates/upload/reload.html.j2,sha256=YZWX5eDeNyqKJdQAywDBcU8DZBm22rRBbZqFjrFrCvQ,556
|
115
|
-
geo_activity_playground-0.
|
116
|
-
geo_activity_playground-0.
|
117
|
-
geo_activity_playground-0.
|
118
|
-
geo_activity_playground-0.
|
119
|
-
geo_activity_playground-0.
|
115
|
+
geo_activity_playground-0.33.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
|
116
|
+
geo_activity_playground-0.33.0.dist-info/METADATA,sha256=b7mpW0SMMsW1p2C-wZ0ZxpWXssdYXkNOmZ-43dnNwLI,1612
|
117
|
+
geo_activity_playground-0.33.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
118
|
+
geo_activity_playground-0.33.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
|
119
|
+
geo_activity_playground-0.33.0.dist-info/RECORD,,
|
{geo_activity_playground-0.31.0.dist-info → geo_activity_playground-0.33.0.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|