geo-activity-playground 1.3.2__py3-none-any.whl → 1.4.1__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/__main__.py +8 -0
- geo_activity_playground/core/photos.py +39 -0
- geo_activity_playground/core/raster_map.py +6 -0
- geo_activity_playground/explorer/tile_visits.py +64 -5
- geo_activity_playground/importers/strava_checkout.py +11 -2
- geo_activity_playground/webui/app.py +7 -0
- geo_activity_playground/webui/blueprints/explorer_blueprint.py +67 -97
- geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py +15 -4
- geo_activity_playground/webui/blueprints/heatmap_blueprint.py +3 -10
- geo_activity_playground/webui/blueprints/photo_blueprint.py +1 -26
- geo_activity_playground/webui/static/leaflet/images/layers-2x.png +0 -0
- geo_activity_playground/webui/static/leaflet/images/layers.png +0 -0
- geo_activity_playground/webui/static/server-side-explorer.js +49 -13
- geo_activity_playground/webui/templates/explorer/server-side.html.j2 +0 -8
- geo_activity_playground/webui/templates/heatmap/index.html.j2 +6 -0
- geo_activity_playground/webui/templates/page.html.j2 +4 -1
- {geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/METADATA +1 -1
- {geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/RECORD +21 -18
- {geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/LICENSE +0 -0
- {geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/WHEEL +0 -0
- {geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/entry_points.txt +0 -0
@@ -4,6 +4,7 @@ import pathlib
|
|
4
4
|
|
5
5
|
import coloredlogs
|
6
6
|
|
7
|
+
from .core.photos import main_inspect_photo
|
7
8
|
from .explorer.video import explorer_video_main
|
8
9
|
from .heatmap_video import main_heatmap_video
|
9
10
|
from .importers.strava_checkout import convert_strava_checkout
|
@@ -73,6 +74,13 @@ def main() -> None:
|
|
73
74
|
subparser.add_argument("--video-height", type=int, default=1080)
|
74
75
|
subparser.set_defaults(func=main_heatmap_video)
|
75
76
|
|
77
|
+
subparser = subparsers.add_parser(
|
78
|
+
"inspect-photo",
|
79
|
+
help="Extract EXIF data from the image to see how it would be imported",
|
80
|
+
)
|
81
|
+
subparser.add_argument("path", type=pathlib.Path)
|
82
|
+
subparser.set_defaults(func=main_inspect_photo)
|
83
|
+
|
76
84
|
options = parser.parse_args()
|
77
85
|
coloredlogs.install(
|
78
86
|
fmt="%(asctime)s %(name)s %(levelname)s %(message)s",
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import argparse
|
2
|
+
import datetime
|
3
|
+
import pathlib
|
4
|
+
import pprint
|
5
|
+
import zoneinfo
|
6
|
+
|
7
|
+
import dateutil.parser
|
8
|
+
import exifread
|
9
|
+
|
10
|
+
|
11
|
+
def ratio_to_decimal(numbers: list[exifread.utils.Ratio]) -> float:
|
12
|
+
deg, min, sec = numbers.values
|
13
|
+
return deg.decimal() + min.decimal() / 60 + sec.decimal() / 3600
|
14
|
+
|
15
|
+
|
16
|
+
def get_metadata_from_image(path: pathlib.Path) -> dict:
|
17
|
+
with open(path, "rb") as f:
|
18
|
+
tags = exifread.process_file(f)
|
19
|
+
metadata = {}
|
20
|
+
try:
|
21
|
+
metadata["latitude"] = ratio_to_decimal(tags["GPS GPSLatitude"])
|
22
|
+
metadata["longitude"] = ratio_to_decimal(tags["GPS GPSLongitude"])
|
23
|
+
except KeyError:
|
24
|
+
pass
|
25
|
+
try:
|
26
|
+
metadata["time"] = dateutil.parser.parse(
|
27
|
+
str(tags["EXIF DateTimeOriginal"])
|
28
|
+
+ str(tags.get("EXIF OffsetTime", "+00:00"))
|
29
|
+
).astimezone(zoneinfo.ZoneInfo("UTC"))
|
30
|
+
except KeyError:
|
31
|
+
pass
|
32
|
+
|
33
|
+
return metadata
|
34
|
+
|
35
|
+
|
36
|
+
def main_inspect_photo(options: argparse.Namespace) -> None:
|
37
|
+
path: pathlib.Path = options.path
|
38
|
+
metadata = get_metadata_from_image(path)
|
39
|
+
pprint.pprint(metadata)
|
@@ -283,3 +283,9 @@ class PastelImageTransform(ImageTransform):
|
|
283
283
|
averaged_tile = np.sum(image * [0.2126, 0.7152, 0.0722], axis=2)
|
284
284
|
grayscale_tile = np.dstack((averaged_tile, averaged_tile, averaged_tile))
|
285
285
|
return self._factor * grayscale_tile + (1 - self._factor) * image
|
286
|
+
|
287
|
+
|
288
|
+
class InverseGrayscaleImageTransform(ImageTransform):
|
289
|
+
def transform_image(self, image: np.ndarray) -> np.ndarray:
|
290
|
+
image = np.sum(image * [0.2126, 0.7152, 0.0722], axis=2) # to grayscale
|
291
|
+
return 1 - np.dstack((image, image, image)) # to rgb
|
@@ -13,6 +13,8 @@ from tqdm import tqdm
|
|
13
13
|
|
14
14
|
from ..core.activities import ActivityRepository
|
15
15
|
from ..core.config import Config
|
16
|
+
from ..core.datamodel import Activity
|
17
|
+
from ..core.datamodel import DB
|
16
18
|
from ..core.paths import atomic_open
|
17
19
|
from ..core.tasks import try_load_pickle
|
18
20
|
from ..core.tasks import work_tracker_path
|
@@ -20,10 +22,41 @@ from ..core.tasks import WorkTracker
|
|
20
22
|
from ..core.tiles import adjacent_to
|
21
23
|
from ..core.tiles import interpolate_missing_tile
|
22
24
|
|
25
|
+
# import sqlalchemy as sa
|
26
|
+
|
27
|
+
# from sqlalchemy import Column
|
28
|
+
# from sqlalchemy import ForeignKey
|
29
|
+
# from sqlalchemy import String
|
30
|
+
# from sqlalchemy import Table
|
31
|
+
# from sqlalchemy.orm import DeclarativeBase
|
32
|
+
# from sqlalchemy.orm import Mapped
|
33
|
+
# from sqlalchemy.orm import mapped_column
|
34
|
+
# from sqlalchemy.orm import relationship
|
23
35
|
|
24
36
|
logger = logging.getLogger(__name__)
|
25
37
|
|
26
38
|
|
39
|
+
# class VisitedTile(DB.Model):
|
40
|
+
# __tablename__ = "visited_tiles"
|
41
|
+
|
42
|
+
# zoom: Mapped[int] = mapped_column(primary_key=True)
|
43
|
+
# x: Mapped[int] = mapped_column(primary_key=True)
|
44
|
+
# y: Mapped[int] = mapped_column(primary_key=True)
|
45
|
+
|
46
|
+
# first_activity_id: Mapped[int] = mapped_column(
|
47
|
+
# ForeignKey("Activity.id", name="first_activity_id"), nullable=True
|
48
|
+
# )
|
49
|
+
# first_activity: Mapped[Activity] = relationship(back_populates="activities")
|
50
|
+
# first_visit: Mapped[Optional[datetime.datetime]] = mapped_column(
|
51
|
+
# sa.DateTime, nullable=True
|
52
|
+
# )
|
53
|
+
|
54
|
+
# __table_args__ = (
|
55
|
+
# sa.PrimaryKeyConstraint(zoom, x, y),
|
56
|
+
# {},
|
57
|
+
# )
|
58
|
+
|
59
|
+
|
27
60
|
class TileInfo(TypedDict):
|
28
61
|
activity_ids: set[int]
|
29
62
|
first_time: datetime.datetime
|
@@ -58,7 +91,6 @@ class TileState(TypedDict):
|
|
58
91
|
tile_visits: dict[int, dict[tuple[int, int], TileInfo]]
|
59
92
|
tile_history: dict[int, pd.DataFrame]
|
60
93
|
activities_per_tile: dict[int, dict[tuple[int, int], set[int]]]
|
61
|
-
processed_activities: set[int]
|
62
94
|
evolution_state: dict[int, TileEvolutionState]
|
63
95
|
version: int
|
64
96
|
|
@@ -99,7 +131,6 @@ def make_tile_state() -> TileState:
|
|
99
131
|
"tile_visits": collections.defaultdict(make_defaultdict_dict),
|
100
132
|
"tile_history": collections.defaultdict(pd.DataFrame),
|
101
133
|
"activities_per_tile": collections.defaultdict(make_defaultdict_set),
|
102
|
-
"processed_activities": set(),
|
103
134
|
"evolution_state": collections.defaultdict(TileEvolutionState),
|
104
135
|
"version": TILE_STATE_VERSION,
|
105
136
|
}
|
@@ -122,6 +153,9 @@ def _consistency_check(
|
|
122
153
|
|
123
154
|
for zoom, tile_visits in tile_visit_accessor.tile_state["tile_visits"].items():
|
124
155
|
for tile, meta in tile_visits.items():
|
156
|
+
if not pd.isna(meta["first_time"]) and meta["first_time"].tzinfo is None:
|
157
|
+
logger.info("Tile visits are stored without time zone.")
|
158
|
+
return False
|
125
159
|
if meta["first_id"] not in present_activity_ids:
|
126
160
|
logger.info(f"Activity {meta['first_id']} have been deleted.")
|
127
161
|
return False
|
@@ -143,10 +177,35 @@ def compute_tile_visits_new(
|
|
143
177
|
work_tracker.reset()
|
144
178
|
|
145
179
|
for activity_id in tqdm(
|
146
|
-
work_tracker.filter(repository.get_activity_ids()), desc="Tile visits", delay=
|
180
|
+
work_tracker.filter(repository.get_activity_ids()), desc="Tile visits", delay=1
|
147
181
|
):
|
148
182
|
_process_activity(repository, tile_visit_accessor.tile_state, activity_id)
|
149
183
|
work_tracker.mark_done(activity_id)
|
184
|
+
|
185
|
+
for zoom in reversed(range(20)):
|
186
|
+
tile_state = tile_visit_accessor.tile_state
|
187
|
+
if not (
|
188
|
+
tile_state["tile_history"][zoom]["time"].diff().dropna()
|
189
|
+
>= datetime.timedelta(seconds=0)
|
190
|
+
).all():
|
191
|
+
logger.warning(
|
192
|
+
f"The order of the tile history at {zoom=} is not chronological, resetting."
|
193
|
+
)
|
194
|
+
new_tile_history_soa: dict[str, list] = {
|
195
|
+
"activity_id": [],
|
196
|
+
"time": [],
|
197
|
+
"tile_x": [],
|
198
|
+
"tile_y": [],
|
199
|
+
}
|
200
|
+
for tile, visit in tile_state["tile_visits"][zoom].items():
|
201
|
+
new_tile_history_soa["activity_id"].append(visit["first_id"])
|
202
|
+
new_tile_history_soa["time"].append(visit["first_time"])
|
203
|
+
new_tile_history_soa["tile_x"].append(tile[0])
|
204
|
+
new_tile_history_soa["tile_y"].append(tile[1])
|
205
|
+
tile_state["tile_history"][zoom] = pd.DataFrame(new_tile_history_soa)
|
206
|
+
tile_state["tile_history"][zoom].sort_values("time", inplace=True)
|
207
|
+
# Reset the evolution state.
|
208
|
+
tile_state["evolution_state"] = collections.defaultdict(TileEvolutionState)
|
150
209
|
tile_visit_accessor.save()
|
151
210
|
work_tracker.close()
|
152
211
|
|
@@ -258,7 +317,7 @@ def _compute_cluster_evolution(
|
|
258
317
|
for index, row in tqdm(
|
259
318
|
tiles.iloc[s.cluster_start :].iterrows(),
|
260
319
|
desc=f"Cluster evolution for {zoom=}",
|
261
|
-
delay=
|
320
|
+
delay=1,
|
262
321
|
):
|
263
322
|
new_clusters = False
|
264
323
|
# Current tile.
|
@@ -337,7 +396,7 @@ def _compute_square_history(
|
|
337
396
|
for index, row in tqdm(
|
338
397
|
tiles.iloc[s.square_start :].iterrows(),
|
339
398
|
desc=f"Square evolution for {zoom=}",
|
340
|
-
delay=
|
399
|
+
delay=1,
|
341
400
|
):
|
342
401
|
tile = (row["tile_x"], row["tile_y"])
|
343
402
|
x, y = tile
|
@@ -3,6 +3,7 @@ import logging
|
|
3
3
|
import pathlib
|
4
4
|
import shutil
|
5
5
|
import sys
|
6
|
+
import traceback
|
6
7
|
import urllib.parse
|
7
8
|
import zoneinfo
|
8
9
|
from typing import Optional
|
@@ -22,6 +23,7 @@ from ..core.tasks import get_state
|
|
22
23
|
from ..core.tasks import set_state
|
23
24
|
from ..core.tasks import work_tracker_path
|
24
25
|
from ..core.tasks import WorkTracker
|
26
|
+
from .activity_parsers import ActivityParseError
|
25
27
|
from .activity_parsers import read_activity
|
26
28
|
from .csv_parser import parse_csv
|
27
29
|
|
@@ -182,12 +184,12 @@ def import_from_strava_checkout(config: Config) -> None:
|
|
182
184
|
]
|
183
185
|
|
184
186
|
for activity_id in tqdm(activities_ids_to_parse, desc="Import from Strava export"):
|
185
|
-
work_tracker.mark_done(activity_id)
|
186
187
|
index = all_activity_ids.index(activity_id)
|
187
188
|
row = {column: table[column][index] for column in header}
|
188
189
|
|
189
190
|
# Some manually recorded activities have no file name. Pandas reads that as a float. We skip those.
|
190
191
|
if not row["Filename"]:
|
192
|
+
work_tracker.mark_done(activity_id)
|
191
193
|
continue
|
192
194
|
|
193
195
|
start_datetime = dateutil.parser.parse(
|
@@ -196,7 +198,14 @@ def import_from_strava_checkout(config: Config) -> None:
|
|
196
198
|
|
197
199
|
activity_file = checkout_path / row["Filename"]
|
198
200
|
|
199
|
-
|
201
|
+
try:
|
202
|
+
activity, time_series = read_activity(activity_file)
|
203
|
+
except ActivityParseError as e:
|
204
|
+
logger.error(f"Error while parsing `{activity_file}`:")
|
205
|
+
traceback.print_exc()
|
206
|
+
continue
|
207
|
+
|
208
|
+
work_tracker.mark_done(activity_id)
|
200
209
|
|
201
210
|
if not len(time_series):
|
202
211
|
continue
|
@@ -6,6 +6,7 @@ import os
|
|
6
6
|
import pathlib
|
7
7
|
import secrets
|
8
8
|
import shutil
|
9
|
+
import sys
|
9
10
|
import threading
|
10
11
|
import urllib.parse
|
11
12
|
import uuid
|
@@ -32,6 +33,7 @@ from ..core.heart_rate import HeartRateZoneComputer
|
|
32
33
|
from ..core.paths import TIME_SERIES_DIR
|
33
34
|
from ..core.raster_map import GrayscaleImageTransform
|
34
35
|
from ..core.raster_map import IdentityImageTransform
|
36
|
+
from ..core.raster_map import InverseGrayscaleImageTransform
|
35
37
|
from ..core.raster_map import PastelImageTransform
|
36
38
|
from ..core.raster_map import TileGetter
|
37
39
|
from ..explorer.tile_visits import TileVisitAccessor
|
@@ -85,6 +87,7 @@ def importer_thread(
|
|
85
87
|
) -> None:
|
86
88
|
with app.app_context():
|
87
89
|
scan_for_activities(repository, tile_visit_accessor, config)
|
90
|
+
logger.info("Importer thread is done.")
|
88
91
|
|
89
92
|
|
90
93
|
def web_ui_main(
|
@@ -182,6 +185,7 @@ def web_ui_main(
|
|
182
185
|
"color": IdentityImageTransform(),
|
183
186
|
"grayscale": GrayscaleImageTransform(),
|
184
187
|
"pastel": PastelImageTransform(),
|
188
|
+
"inverse_grayscale": InverseGrayscaleImageTransform(),
|
185
189
|
}
|
186
190
|
flasher = FlaskFlasher()
|
187
191
|
heart_rate_zone_computer = HeartRateZoneComputer(config)
|
@@ -265,6 +269,9 @@ def web_ui_main(
|
|
265
269
|
variables["photo_count"] = DB.session.scalar(
|
266
270
|
sqlalchemy.select(sqlalchemy.func.count()).select_from(Photo)
|
267
271
|
)
|
272
|
+
variables["python_version"] = (
|
273
|
+
f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
274
|
+
)
|
268
275
|
return variables
|
269
276
|
|
270
277
|
app.run(host=host, port=port)
|
@@ -26,6 +26,7 @@ from ...core.coordinates import Bounds
|
|
26
26
|
from ...core.datamodel import Activity
|
27
27
|
from ...core.datamodel import DB
|
28
28
|
from ...core.raster_map import ImageTransform
|
29
|
+
from ...core.raster_map import OSM_TILE_SIZE
|
29
30
|
from ...core.raster_map import TileGetter
|
30
31
|
from ...core.tiles import compute_tile
|
31
32
|
from ...core.tiles import get_tile_upper_left_lat_lon
|
@@ -71,13 +72,14 @@ class MaxClusterColorStrategy(ColorStrategy):
|
|
71
72
|
self, tile_xy: tuple[int, int], grayscale: np.ndarray
|
72
73
|
) -> np.ndarray:
|
73
74
|
if tile_xy in self.max_cluster_members:
|
74
|
-
|
75
|
+
color = np.array([[[55, 126, 184, 70]]]) / 256
|
75
76
|
elif tile_xy in self.evolution_state.memberships:
|
76
|
-
|
77
|
+
color = np.array([[[77, 175, 74, 70]]]) / 256
|
77
78
|
elif tile_xy in self.tile_visits:
|
78
|
-
|
79
|
+
color = np.array([[[0, 0, 0, 70]]]) / 256
|
79
80
|
else:
|
80
|
-
|
81
|
+
color = np.array([[[0, 0, 0, 0]]]) / 256
|
82
|
+
return np.broadcast_to(color, grayscale.shape)
|
81
83
|
|
82
84
|
|
83
85
|
class ColorfulClusterColorStrategy(ColorStrategy):
|
@@ -98,11 +100,12 @@ class ColorfulClusterColorStrategy(ColorStrategy):
|
|
98
100
|
m = hashlib.sha256()
|
99
101
|
m.update(str(cluster_id).encode())
|
100
102
|
d = int(m.hexdigest(), base=16) / (256.0**m.digest_size)
|
101
|
-
|
103
|
+
color = np.array([[self._cmap(d)[:3] + (0.5,)]])
|
102
104
|
elif tile_xy in self.tile_visits:
|
103
|
-
|
105
|
+
color = np.array([[[0, 0, 0, 70]]]) / 256
|
104
106
|
else:
|
105
|
-
|
107
|
+
color = np.array([[[0, 0, 0, 0]]]) / 256
|
108
|
+
return np.broadcast_to(color, grayscale.shape)
|
106
109
|
|
107
110
|
|
108
111
|
class VisitTimeColorStrategy(ColorStrategy):
|
@@ -120,11 +123,15 @@ class VisitTimeColorStrategy(ColorStrategy):
|
|
120
123
|
relevant_time = (
|
121
124
|
tile_info["first_time"] if self.use_first else tile_info["last_time"]
|
122
125
|
)
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
+
if pd.isna(relevant_time):
|
127
|
+
color = np.array([[[0, 0, 0, 70]]]) / 256
|
128
|
+
else:
|
129
|
+
last_age_days = (today - relevant_time.date()).days
|
130
|
+
color = cmap(max(1 - last_age_days / (2 * 365), 0.0))
|
131
|
+
color = np.array([[color[:3] + (0.5,)]])
|
126
132
|
else:
|
127
|
-
|
133
|
+
color = np.array([[[0, 0, 0, 0]]]) / 256
|
134
|
+
return np.broadcast_to(color, grayscale.shape)
|
128
135
|
|
129
136
|
|
130
137
|
class NumVisitsColorStrategy(ColorStrategy):
|
@@ -138,9 +145,10 @@ class NumVisitsColorStrategy(ColorStrategy):
|
|
138
145
|
cmap = matplotlib.colormaps["viridis"]
|
139
146
|
tile_info = self.tile_visits[tile_xy]
|
140
147
|
color = cmap(min(len(tile_info["activity_ids"]) / 50, 1.0))
|
141
|
-
|
148
|
+
color = np.array([[color[:3] + (0.5,)]])
|
142
149
|
else:
|
143
|
-
|
150
|
+
color = np.array([[[0, 0, 0, 0]]]) / 256
|
151
|
+
return np.broadcast_to(color, grayscale.shape)
|
144
152
|
|
145
153
|
|
146
154
|
def make_explorer_blueprint(
|
@@ -159,7 +167,7 @@ def make_explorer_blueprint(
|
|
159
167
|
config_accessor().explorer_zoom_levels.append(zoom)
|
160
168
|
config_accessor().explorer_zoom_levels.sort()
|
161
169
|
config_accessor.save()
|
162
|
-
compute_tile_evolution(tile_visit_accessor, config_accessor())
|
170
|
+
compute_tile_evolution(tile_visit_accessor.tile_state, config_accessor())
|
163
171
|
flash(f"Enabled {zoom=} for explorer tiles.", category="success")
|
164
172
|
else:
|
165
173
|
flash(f"{zoom=} is not valid, must be between 0 and 19.", category="danger")
|
@@ -264,10 +272,11 @@ def make_explorer_blueprint(
|
|
264
272
|
tile_visits = tile_visit_accessor.tile_state["tile_visits"][zoom]
|
265
273
|
evolution_state = tile_visit_accessor.tile_state["evolution_state"][zoom]
|
266
274
|
|
267
|
-
map_tile = np.array(tile_getter.get_tile(z, x, y)) / 255
|
268
|
-
grayscale = image_transforms["grayscale"].transform_image(map_tile)
|
275
|
+
# map_tile = np.array(tile_getter.get_tile(z, x, y)) / 255
|
276
|
+
# grayscale = image_transforms["grayscale"].transform_image(map_tile)
|
277
|
+
grayscale = np.zeros((OSM_TILE_SIZE, OSM_TILE_SIZE, 4), dtype=np.float32)
|
269
278
|
square_line_width = 3
|
270
|
-
square_color = np.array([[[228, 26, 28]]]) / 256
|
279
|
+
square_color = np.array([[[228, 26, 28, 255]]]) / 256
|
271
280
|
|
272
281
|
color_strategy_name = request.args.get("color_strategy", "colorful_cluster")
|
273
282
|
if color_strategy_name == "default":
|
@@ -293,60 +302,51 @@ def make_explorer_blueprint(
|
|
293
302
|
tile_x = x // factor
|
294
303
|
tile_y = y // factor
|
295
304
|
tile_xy = (tile_x, tile_y)
|
296
|
-
result = color_strategy.color_image(tile_xy, grayscale)
|
305
|
+
result = color_strategy.color_image(tile_xy, grayscale).copy()
|
297
306
|
|
298
307
|
if x % factor == 0:
|
299
308
|
result[:, 0, :] = 0.5
|
300
309
|
if y % factor == 0:
|
301
310
|
result[0, :, :] = 0.5
|
302
311
|
|
312
|
+
if (
|
313
|
+
evolution_state.square_x is not None
|
314
|
+
and evolution_state.square_y is not None
|
315
|
+
):
|
303
316
|
if (
|
304
|
-
|
305
|
-
and evolution_state.
|
317
|
+
x % factor == 0
|
318
|
+
and tile_x == evolution_state.square_x
|
319
|
+
and evolution_state.square_y
|
320
|
+
<= tile_y
|
321
|
+
< evolution_state.square_y + evolution_state.max_square_size
|
306
322
|
):
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
< evolution_state.square_y + evolution_state.max_square_size
|
335
|
-
):
|
336
|
-
result[:, -square_line_width:] = blend_color(
|
337
|
-
result[:, -square_line_width:], square_color, 0.5
|
338
|
-
)
|
339
|
-
if (
|
340
|
-
(y + 1) % factor == 0
|
341
|
-
and (y + 1) // factor
|
342
|
-
== evolution_state.square_y + evolution_state.max_square_size
|
343
|
-
and evolution_state.square_x
|
344
|
-
<= tile_x
|
345
|
-
< evolution_state.square_x + evolution_state.max_square_size
|
346
|
-
):
|
347
|
-
result[-square_line_width:, :] = blend_color(
|
348
|
-
result[-square_line_width:, :], square_color, 0.5
|
349
|
-
)
|
323
|
+
result[:, 0:square_line_width] = square_color
|
324
|
+
if (
|
325
|
+
y % factor == 0
|
326
|
+
and tile_y == evolution_state.square_y
|
327
|
+
and evolution_state.square_x
|
328
|
+
<= tile_x
|
329
|
+
< evolution_state.square_x + evolution_state.max_square_size
|
330
|
+
):
|
331
|
+
result[0:square_line_width, :] = square_color
|
332
|
+
if (
|
333
|
+
(x + 1) % factor == 0
|
334
|
+
and (x + 1) // factor
|
335
|
+
== evolution_state.square_x + evolution_state.max_square_size
|
336
|
+
and evolution_state.square_y
|
337
|
+
<= tile_y
|
338
|
+
< evolution_state.square_y + evolution_state.max_square_size
|
339
|
+
):
|
340
|
+
result[:, -square_line_width:] = square_color
|
341
|
+
if (
|
342
|
+
(y + 1) % factor == 0
|
343
|
+
and (y + 1) // factor
|
344
|
+
== evolution_state.square_y + evolution_state.max_square_size
|
345
|
+
and evolution_state.square_x
|
346
|
+
<= tile_x
|
347
|
+
< evolution_state.square_x + evolution_state.max_square_size
|
348
|
+
):
|
349
|
+
result[-square_line_width:, :] = square_color
|
350
350
|
else:
|
351
351
|
result = grayscale
|
352
352
|
factor = 2 ** (zoom - z)
|
@@ -381,14 +381,7 @@ def make_explorer_blueprint(
|
|
381
381
|
result[
|
382
382
|
yo * width : (yo + 1) * width,
|
383
383
|
xo * width : xo * width + square_line_width,
|
384
|
-
] =
|
385
|
-
result[
|
386
|
-
yo * width : (yo + 1) * width,
|
387
|
-
xo * width : xo * width + square_line_width,
|
388
|
-
],
|
389
|
-
square_color,
|
390
|
-
0.5,
|
391
|
-
)
|
384
|
+
] = square_color
|
392
385
|
if (
|
393
386
|
tile_y == evolution_state.square_y
|
394
387
|
and evolution_state.square_x
|
@@ -399,14 +392,7 @@ def make_explorer_blueprint(
|
|
399
392
|
result[
|
400
393
|
yo * width : yo * width + square_line_width,
|
401
394
|
xo * width : (xo + 1) * width,
|
402
|
-
] =
|
403
|
-
result[
|
404
|
-
yo * width : yo * width + square_line_width,
|
405
|
-
xo * width : (xo + 1) * width,
|
406
|
-
],
|
407
|
-
square_color,
|
408
|
-
0.5,
|
409
|
-
)
|
395
|
+
] = square_color
|
410
396
|
|
411
397
|
if (
|
412
398
|
tile_x + 1
|
@@ -421,15 +407,7 @@ def make_explorer_blueprint(
|
|
421
407
|
yo * width : (yo + 1) * width,
|
422
408
|
(xo + 1) * width
|
423
409
|
- square_line_width : (xo + 1) * width,
|
424
|
-
] =
|
425
|
-
result[
|
426
|
-
yo * width : (yo + 1) * width,
|
427
|
-
(xo + 1) * width
|
428
|
-
- square_line_width : (xo + 1) * width,
|
429
|
-
],
|
430
|
-
square_color,
|
431
|
-
0.5,
|
432
|
-
)
|
410
|
+
] = square_color
|
433
411
|
|
434
412
|
if (
|
435
413
|
tile_y + 1
|
@@ -444,15 +422,7 @@ def make_explorer_blueprint(
|
|
444
422
|
(yo + 1) * width
|
445
423
|
- square_line_width : (yo + 1) * width,
|
446
424
|
xo * width : (xo + 1) * width,
|
447
|
-
] =
|
448
|
-
result[
|
449
|
-
(yo + 1) * width
|
450
|
-
- square_line_width : (yo + 1) * width,
|
451
|
-
xo * width : (xo + 1) * width,
|
452
|
-
],
|
453
|
-
square_color,
|
454
|
-
0.5,
|
455
|
-
)
|
425
|
+
] = square_color
|
456
426
|
if width >= 64:
|
457
427
|
result[yo * width, :, :] = 0.5
|
458
428
|
result[:, xo * width, :] = 0.5
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import collections
|
2
|
+
import logging
|
2
3
|
|
3
4
|
import pandas as pd
|
4
5
|
from flask import Blueprint
|
@@ -12,6 +13,9 @@ from ..search_util import search_query_from_form
|
|
12
13
|
from ..search_util import SearchQueryHistory
|
13
14
|
|
14
15
|
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
15
19
|
def make_hall_of_fame_blueprint(
|
16
20
|
repository: ActivityRepository,
|
17
21
|
search_query_history: SearchQueryHistory,
|
@@ -73,7 +77,14 @@ def _nominate_activities_inner(
|
|
73
77
|
|
74
78
|
for variable, title, format_str in ratings:
|
75
79
|
if variable in meta.columns and not pd.isna(meta[variable]).all():
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
+
try:
|
81
|
+
i = meta[variable].idxmax()
|
82
|
+
except ValueError as e:
|
83
|
+
print(meta[variable].tolist())
|
84
|
+
print(f"{meta[variable].dtype=}")
|
85
|
+
logger.error(f"Trying to work with {variable=}.")
|
86
|
+
logger.error(f"We got a ValueError: {e}")
|
87
|
+
else:
|
88
|
+
value = meta.loc[i, variable]
|
89
|
+
format_applied = format_str.format(value)
|
90
|
+
nominations[i].append(f"{title}{title_suffix}: {format_applied}")
|
@@ -221,13 +221,6 @@ def _render_tile_image(
|
|
221
221
|
|
222
222
|
cmap = pl.get_cmap(config.color_scheme_for_heatmap)
|
223
223
|
data_color = cmap(tile_counts)
|
224
|
-
data_color[
|
225
|
-
|
226
|
-
|
227
|
-
map_tile = convert_to_grayscale(map_tile)
|
228
|
-
map_tile = 1.0 - map_tile # invert colors
|
229
|
-
for c in range(3):
|
230
|
-
map_tile[:, :, c] = (1.0 - data_color[:, :, c]) * map_tile[
|
231
|
-
:, :, c
|
232
|
-
] + data_color[:, :, c]
|
233
|
-
return map_tile
|
224
|
+
data_color[tile_counts > 0, 3] = 0.8
|
225
|
+
data_color[tile_counts == 0, 3] = 0.0
|
226
|
+
return data_color
|
@@ -2,8 +2,6 @@ import datetime
|
|
2
2
|
import pathlib
|
3
3
|
import uuid
|
4
4
|
|
5
|
-
import dateutil.parser
|
6
|
-
import exifread
|
7
5
|
import geojson
|
8
6
|
import sqlalchemy
|
9
7
|
from flask import Blueprint
|
@@ -21,36 +19,13 @@ from ...core.datamodel import Activity
|
|
21
19
|
from ...core.datamodel import DB
|
22
20
|
from ...core.datamodel import Photo
|
23
21
|
from ...core.paths import PHOTOS_DIR
|
22
|
+
from ...core.photos import get_metadata_from_image
|
24
23
|
from ..authenticator import Authenticator
|
25
24
|
from ..authenticator import needs_authentication
|
26
25
|
from ..flasher import Flasher
|
27
26
|
from ..flasher import FlashTypes
|
28
27
|
|
29
28
|
|
30
|
-
def ratio_to_decimal(numbers: list[exifread.utils.Ratio]) -> float:
|
31
|
-
deg, min, sec = numbers.values
|
32
|
-
return deg.decimal() + min.decimal() / 60 + sec.decimal() / 3600
|
33
|
-
|
34
|
-
|
35
|
-
def get_metadata_from_image(path: pathlib.Path) -> dict:
|
36
|
-
with open(path, "rb") as f:
|
37
|
-
tags = exifread.process_file(f)
|
38
|
-
metadata = {}
|
39
|
-
try:
|
40
|
-
metadata["latitude"] = ratio_to_decimal(tags["GPS GPSLatitude"])
|
41
|
-
metadata["longitude"] = ratio_to_decimal(tags["GPS GPSLongitude"])
|
42
|
-
except KeyError:
|
43
|
-
pass
|
44
|
-
try:
|
45
|
-
metadata["time"] = datetime.datetime.strptime(
|
46
|
-
str(tags["EXIF DateTimeOriginal"]), "%Y:%m:%d %H:%M:%S"
|
47
|
-
)
|
48
|
-
except KeyError:
|
49
|
-
pass
|
50
|
-
|
51
|
-
return metadata
|
52
|
-
|
53
|
-
|
54
29
|
def make_photo_blueprint(
|
55
30
|
config_accessor: ConfigAccessor, authenticator: Authenticator, flasher: Flasher
|
56
31
|
) -> Blueprint:
|
Binary file
|
Binary file
|
@@ -1,23 +1,59 @@
|
|
1
|
+
let map = L.map('explorer-map', {
|
2
|
+
fullscreenControl: true,
|
3
|
+
center: [center_latitude, center_longitude],
|
4
|
+
zoom: 12
|
5
|
+
});
|
1
6
|
|
2
|
-
let
|
7
|
+
let base_maps = {
|
8
|
+
"Grayscale": L.tileLayer("/tile/grayscale/{z}/{x}/{y}.png", {
|
9
|
+
maxZoom: 19,
|
10
|
+
attribution: map_tile_attribution
|
11
|
+
}),
|
12
|
+
"Pastel": L.tileLayer("/tile/pastel/{z}/{x}/{y}.png", {
|
13
|
+
maxZoom: 19,
|
14
|
+
attribution: map_tile_attribution
|
15
|
+
}),
|
16
|
+
"Color": L.tileLayer("/tile/color/{z}/{x}/{y}.png", {
|
17
|
+
maxZoom: 19,
|
18
|
+
attribution: map_tile_attribution
|
19
|
+
}),
|
20
|
+
"Inverse Grayscale": L.tileLayer("/tile/inverse_grayscale/{z}/{x}/{y}.png", {
|
21
|
+
maxZoom: 19,
|
22
|
+
attribution: map_tile_attribution
|
23
|
+
}),
|
24
|
+
}
|
3
25
|
|
4
|
-
|
5
|
-
|
6
|
-
map.removeLayer(tile_layer)
|
7
|
-
}
|
8
|
-
tile_layer = L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=${method}`, {
|
26
|
+
let overlay_maps = {
|
27
|
+
"Colorful Cluster": L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=colorful_cluster`, {
|
9
28
|
maxZoom: 19,
|
10
29
|
attribution: map_tile_attribution
|
11
|
-
})
|
30
|
+
}),
|
31
|
+
"Max Cluster": L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=max_cluster`, {
|
32
|
+
maxZoom: 19,
|
33
|
+
attribution: map_tile_attribution
|
34
|
+
}),
|
35
|
+
"First Visit": L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=first`, {
|
36
|
+
maxZoom: 19,
|
37
|
+
attribution: map_tile_attribution
|
38
|
+
}),
|
39
|
+
"Last Visit": L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=last`, {
|
40
|
+
maxZoom: 19,
|
41
|
+
attribution: map_tile_attribution
|
42
|
+
}),
|
43
|
+
"Number of Visits": L.tileLayer(`/explorer/${zoom}/tile/{z}/{x}/{y}.png?color_strategy=visits`, {
|
44
|
+
maxZoom: 19,
|
45
|
+
attribution: map_tile_attribution
|
46
|
+
}),
|
47
|
+
"Heatmap": L.tileLayer("/heatmap/tile/{z}/{x}/{y}.png", {
|
48
|
+
maxZoom: 19,
|
49
|
+
attribution: map_tile_attribution
|
50
|
+
}),
|
12
51
|
}
|
13
52
|
|
14
|
-
|
15
|
-
|
16
|
-
center: [center_latitude, center_longitude],
|
17
|
-
zoom: 12
|
18
|
-
});
|
53
|
+
base_maps['Grayscale'].addTo(map)
|
54
|
+
overlay_maps["Colorful Cluster"].addTo(map)
|
19
55
|
|
20
|
-
|
56
|
+
var layerControl = L.control.layers(base_maps, overlay_maps).addTo(map);
|
21
57
|
|
22
58
|
if (bbox) {
|
23
59
|
map.fitBounds(L.geoJSON(bbox).getBounds())
|
@@ -24,14 +24,6 @@
|
|
24
24
|
</div>
|
25
25
|
</div>
|
26
26
|
|
27
|
-
<div class="btn-group mb-3" role="group">
|
28
|
-
<button type="button" class="btn btn-primary" onclick="changeColor('colorful_cluster')">Colorful Cluster</button>
|
29
|
-
<button type="button" class="btn btn-primary" onclick="changeColor('max_cluster')">Max Cluster</button>
|
30
|
-
<button type="button" class="btn btn-primary" onclick="changeColor('first')">First Visit</button>
|
31
|
-
<button type="button" class="btn btn-primary" onclick="changeColor('last')">Last Visit</button>
|
32
|
-
<button type="button" class="btn btn-primary" onclick="changeColor('visits')">Number of Visits</button>
|
33
|
-
</div>
|
34
|
-
|
35
27
|
<div class="row mb-3">
|
36
28
|
<div class="col">
|
37
29
|
<div id="explorer-map" class="mb-1" style="height: 800px;"></div>
|
@@ -18,6 +18,12 @@
|
|
18
18
|
center: [{{ center.latitude }}, {{ center.longitude }}],
|
19
19
|
zoom: 12
|
20
20
|
});
|
21
|
+
|
22
|
+
L.tileLayer("/tile/inverse_grayscale/{z}/{x}/{y}.png", {
|
23
|
+
maxZoom: 19,
|
24
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
25
|
+
}).addTo(map)
|
26
|
+
|
21
27
|
L.tileLayer('/heatmap/tile/{z}/{x}/{y}.png?{{ extra_args|safe }}', {
|
22
28
|
maxZoom: 19,
|
23
29
|
attribution: '{{ map_tile_attribution|safe }}'
|
@@ -204,9 +204,12 @@
|
|
204
204
|
|
205
205
|
<div class="row border-top py-3 my-4">
|
206
206
|
<ul class="nav col-4">
|
207
|
-
<li class="nav-item
|
207
|
+
<li class="nav-item"><a
|
208
208
|
href="https://github.com/martin-ueding/geo-activity-playground/blob/main/docs/changelog.md"
|
209
209
|
class="nav-link px-2 text-muted" target="_blank">Version {{ version }}</a></li>
|
210
|
+
|
211
|
+
<li class="nav-item"><span class="nav-link px-2 text-muted">Python {{ python_version
|
212
|
+
}}</span></li>
|
210
213
|
</ul>
|
211
214
|
<ul class="nav col-8 justify-content-end">
|
212
215
|
<li class="nav-item"><a href="https://github.com/martin-ueding/geo-activity-playground"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
geo_activity_playground/__main__.py,sha256=
|
2
|
+
geo_activity_playground/__main__.py,sha256=y49XBk0PmAiunx2pZvxLjng1C-AMgAB-f_DljtTCb34,3209
|
3
3
|
geo_activity_playground/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
4
4
|
geo_activity_playground/alembic/env.py,sha256=46oMzwSROaAsYuYWTd46txFdRLD3adm_SCn01A_ex8Q,2081
|
5
5
|
geo_activity_playground/alembic/script.py.mako,sha256=g1k4U3D8y4PPYRzW3DH7GEu6yN4EiAr62GgCu6cRpBo,524
|
@@ -29,8 +29,9 @@ geo_activity_playground/core/meta_search.py,sha256=nyvCuR7v0pd6KjA8W5Kr71bBafRdE
|
|
29
29
|
geo_activity_playground/core/missing_values.py,sha256=HjonaLV0PFMICnuMrbdUNnK9uy_8PBh_RxI5GuEMQK0,250
|
30
30
|
geo_activity_playground/core/parametric_plot.py,sha256=8CKB8dey7EmZtQnl6IOgBhpxkw0UCpQPWeiBw5PqW8k,5737
|
31
31
|
geo_activity_playground/core/paths.py,sha256=qQ4ujaIHmsxTGEWzf-76XS8FclEI2RC5COTUeuLEbDI,2938
|
32
|
+
geo_activity_playground/core/photos.py,sha256=pUnEfVesEKESlew6_KkYAF582ejLupnWTqs_RcvnRsA,1070
|
32
33
|
geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
|
33
|
-
geo_activity_playground/core/raster_map.py,sha256=
|
34
|
+
geo_activity_playground/core/raster_map.py,sha256=y7maiC_bmUwXsULC_XCZ1m8nGgU2jFe47QFB7TpC_V4,8257
|
34
35
|
geo_activity_playground/core/similarity.py,sha256=L2de3DPRdDeDY5AxZwLDcH7FjHWRWklr41VNU06q9kQ,3117
|
35
36
|
geo_activity_playground/core/summary_stats.py,sha256=v5FtWnE1imDF5axI6asVN55wCrlD73oZ6lvqzxsTN2c,1006
|
36
37
|
geo_activity_playground/core/tasks.py,sha256=-_9cxekoHSWzCW4XblNeqrwi2tTqr5AE7_-p8fdqhwc,2886
|
@@ -47,7 +48,7 @@ geo_activity_playground/core/tiles.py,sha256=LBn2V6WAvMxZeXSIQ8ruZL71iyvOXoFZMz7
|
|
47
48
|
geo_activity_playground/core/time_conversion.py,sha256=F-vQ-MdbTOqPTAELplDjT5m7kdaf1RsqBXELfsR5eOU,1329
|
48
49
|
geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
50
|
geo_activity_playground/explorer/grid_file.py,sha256=YNL_c4O1-kxaajATJwj4ZLywCL5Hpj9qy2h-F7rk8Yg,3260
|
50
|
-
geo_activity_playground/explorer/tile_visits.py,sha256=
|
51
|
+
geo_activity_playground/explorer/tile_visits.py,sha256=NUzC4jNb_vQExAIALrO2H1MiNs5JJKsOQKGietAcATE,16271
|
51
52
|
geo_activity_playground/explorer/video.py,sha256=7j6Qv3HG6On7Tn7xh7Olwrx_fbQnfzS7CeRg3TEApHg,4397
|
52
53
|
geo_activity_playground/heatmap_video.py,sha256=I8i1uVvbbPUXVtvLAROaLy58nQoUPnuMCZkERWNkQjg,3318
|
53
54
|
geo_activity_playground/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -55,12 +56,12 @@ geo_activity_playground/importers/activity_parsers.py,sha256=zWgLkHHd8rjWnKs-COB
|
|
55
56
|
geo_activity_playground/importers/csv_parser.py,sha256=O1pP5GLhWhnWcy2Lsrr9g17Zspuibpt-GtZ3ZS5eZF4,2143
|
56
57
|
geo_activity_playground/importers/directory.py,sha256=ucnB5sPBvXzLdaza2v8GVU75ArfGG4E7d5OXrCgoFq4,3562
|
57
58
|
geo_activity_playground/importers/strava_api.py,sha256=Fiqlc-VeuzsvgDcWt71JoPMri221cMjkeL4SH80gC5s,8426
|
58
|
-
geo_activity_playground/importers/strava_checkout.py,sha256=
|
59
|
+
geo_activity_playground/importers/strava_checkout.py,sha256=Pugtv0nbgfuVzBDC5e5Tfv1jShUYmMcDbpCQp2ULXow,9232
|
59
60
|
geo_activity_playground/importers/test_csv_parser.py,sha256=nOTVTdlzIY0TDcbWp7xNyNaIO6Mkeu55hVziVl22QE4,1092
|
60
61
|
geo_activity_playground/importers/test_directory.py,sha256=_fn_-y98ZyElbG0BRxAmGFdtGobUShPU86SdEOpuv-A,691
|
61
62
|
geo_activity_playground/importers/test_strava_api.py,sha256=7b8bl5Rh2BctCmvTPEhCadxtUOq3mfzuadD6F5XxRio,398
|
62
63
|
geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
-
geo_activity_playground/webui/app.py,sha256=
|
64
|
+
geo_activity_playground/webui/app.py,sha256=qsnwE20S-tWBbD_MgkIS8HVsRh6TO6mrwW_gWYHk0Bo,10653
|
64
65
|
geo_activity_playground/webui/authenticator.py,sha256=dhREYOu_TCD_nzFNuSlHIbf5K6TmwKdXtr1wxD8fBcc,1491
|
65
66
|
geo_activity_playground/webui/blueprints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
66
67
|
geo_activity_playground/webui/blueprints/activity_blueprint.py,sha256=tFy0GpOBhIP8xlmYc9PF4kAng-0MosXMJudVupGz2Yw,26771
|
@@ -70,11 +71,11 @@ geo_activity_playground/webui/blueprints/calendar_blueprint.py,sha256=SmOu5AfNNo
|
|
70
71
|
geo_activity_playground/webui/blueprints/eddington_blueprints.py,sha256=Ya5GJxfVESwmRlgMTYe9g75g8JHHTAAvYFmSD-3Uz4Q,8987
|
71
72
|
geo_activity_playground/webui/blueprints/entry_views.py,sha256=SDCzpUSb1FAb84tM0SnmrZQvtaTlO-Rqdj94hyIMDSc,2936
|
72
73
|
geo_activity_playground/webui/blueprints/equipment_blueprint.py,sha256=8L_7NZGErvu4jyigi2gg7HN_gegZRdsSFahUH7Dz6Lw,5727
|
73
|
-
geo_activity_playground/webui/blueprints/explorer_blueprint.py,sha256=
|
74
|
+
geo_activity_playground/webui/blueprints/explorer_blueprint.py,sha256=bqRG11EHJ67ZVrDVu9KhriCFihw9910fC6PuPmhFZDc,21352
|
74
75
|
geo_activity_playground/webui/blueprints/export_blueprint.py,sha256=C9yFH5gEJs2YtWE-EhcGDEyGwwaLgC1umybgIRi6duE,1036
|
75
|
-
geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py,sha256=
|
76
|
-
geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=
|
77
|
-
geo_activity_playground/webui/blueprints/photo_blueprint.py,sha256=
|
76
|
+
geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py,sha256=bOJ6ejDS6rw8-GEGo1Lihn5DS6j0t9e8CbcbRi44Pts,3168
|
77
|
+
geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=5LlYKMeOMIE7c3xGRZ52ld4Jxtdc3GNcb6lvt3v7NVA,8435
|
78
|
+
geo_activity_playground/webui/blueprints/photo_blueprint.py,sha256=eK3JSvOAsiTVDy5wardtqTDGIZ79jmwuXpdIPBI-GjU,7186
|
78
79
|
geo_activity_playground/webui/blueprints/plot_builder_blueprint.py,sha256=nGtYblRTJ0rasJvl_L35cs1Iry4LONPy_9TY4ytXB-Q,3838
|
79
80
|
geo_activity_playground/webui/blueprints/search_blueprint.py,sha256=Sv_KL1Cdai26y51qVfI-5jZLhtElREsEar1dbR_VAC4,2275
|
80
81
|
geo_activity_playground/webui/blueprints/settings_blueprint.py,sha256=cwes3QmRrC_HMP1g-Yc-x2BJycF4jF3StJl75v9acWo,20377
|
@@ -113,11 +114,13 @@ geo_activity_playground/webui/static/leaflet/MarkerCluster.Default.css,sha256=LW
|
|
113
114
|
geo_activity_playground/webui/static/leaflet/MarkerCluster.css,sha256=-bdWuWOXMFkX0v9Cvr3OWClPiYefDQz9GGZP_7xZxdc,886
|
114
115
|
geo_activity_playground/webui/static/leaflet/fullscreen.png,sha256=yDtz-dhjuAoo6q9xc00-_XNTrGwEWrN80pOneFdol4g,299
|
115
116
|
geo_activity_playground/webui/static/leaflet/fullscreen@2x.png,sha256=HVi2guZO6sekf2NggilbzjUTvJDweXpSMBS81fhtnX0,420
|
117
|
+
geo_activity_playground/webui/static/leaflet/images/layers-2x.png,sha256=Bm2sqFDY_77wB68AsG6sABVyje4nnFHzy2xxbffELt8,1259
|
118
|
+
geo_activity_playground/webui/static/leaflet/images/layers.png,sha256=Hbvp0CjikvNvy6j4s6KNXokydU_CIVuaxp5M3s9RB8Y,696
|
116
119
|
geo_activity_playground/webui/static/leaflet/leaflet.css,sha256=p4NxAoJBhIIN-hmNHrzRCf9tD_miZyoHS5obTRR9BMY,14806
|
117
120
|
geo_activity_playground/webui/static/leaflet/leaflet.fullscreen.css,sha256=YTbhDGEH5amI_JfotPMN7IByFpsN9e4tCBnv5oNdvHU,994
|
118
121
|
geo_activity_playground/webui/static/leaflet/leaflet.js,sha256=20nQCchB9co0qIjJZRGuk2_Z9VM-kNiyxNV1lvTlZBo,147552
|
119
122
|
geo_activity_playground/webui/static/leaflet/leaflet.markercluster.js,sha256=WL6HHfYfbFEkZOFdsJQeY7lJG_E5airjvqbznghUzRw,33724
|
120
|
-
geo_activity_playground/webui/static/server-side-explorer.js,sha256=
|
123
|
+
geo_activity_playground/webui/static/server-side-explorer.js,sha256=dmGTTCZ9OchOc18SJM6qE_EgG-6LEZhckpVIsBvCkv8,3262
|
121
124
|
geo_activity_playground/webui/static/table-sort.min.js,sha256=sFeDrgkXTePr2ciJU9_mLh-Z8qtYhPIQMgOZtj0LwBY,8506
|
122
125
|
geo_activity_playground/webui/static/vega/vega-embed@6.js,sha256=EtAqz74-xZ75o33UgiouBOKWG1u7Zxu-Zh0iIXFbmdo,60630
|
123
126
|
geo_activity_playground/webui/static/vega/vega-lite@4.js,sha256=roXmcY9bUF91uB9V-eSEUHEgfwoXe6B1xoDPuIe5ou8,267999
|
@@ -136,12 +139,12 @@ geo_activity_playground/webui/templates/eddington/distance.html.j2,sha256=9cLlIr
|
|
136
139
|
geo_activity_playground/webui/templates/eddington/elevation_gain.html.j2,sha256=h2mI1Uc1-P7rN_SeCVP_uadpQqX09ZpBG3Z6N8QWNLw,4723
|
137
140
|
geo_activity_playground/webui/templates/elevation_eddington/index.html.j2,sha256=WjquRFWaMzIZrvByhRIuhJbSCUW2HTfMck6THQHZI-I,4743
|
138
141
|
geo_activity_playground/webui/templates/equipment/index.html.j2,sha256=6pzSCJACMXA1fKgsO_KrCTvpumAKlelzj5f9dReey14,1742
|
139
|
-
geo_activity_playground/webui/templates/explorer/server-side.html.j2,sha256=
|
142
|
+
geo_activity_playground/webui/templates/explorer/server-side.html.j2,sha256=CrTFVghloFJcN25H18FZ6KQY7QgIqvyCauFzgYG_yCo,2547
|
140
143
|
geo_activity_playground/webui/templates/export/index.html.j2,sha256=vxqpAm9KnT405Qz7q0_td-HZ4mCjcPR4Lp6EnIEWisg,1652
|
141
144
|
geo_activity_playground/webui/templates/hall_of_fame/index.html.j2,sha256=P15fVPjXf0Wf6K_hd_lCMuw6-Q8_qfNqsBOWNpMfoXw,1804
|
142
|
-
geo_activity_playground/webui/templates/heatmap/index.html.j2,sha256=
|
145
|
+
geo_activity_playground/webui/templates/heatmap/index.html.j2,sha256=Q99v4LP5EnuvDYKayL52qujWaIroLsD89ly2cM2YvTI,1420
|
143
146
|
geo_activity_playground/webui/templates/home.html.j2,sha256=L6U44p05IagWERPxJ99cUMZzZCyhmA5IV7PL-QaO_mg,2258
|
144
|
-
geo_activity_playground/webui/templates/page.html.j2,sha256=
|
147
|
+
geo_activity_playground/webui/templates/page.html.j2,sha256=4vtvq7cV5YIXIUsjG44grHMqnjKWUlVy0eBwqW8UIzg,12315
|
145
148
|
geo_activity_playground/webui/templates/photo/map.html.j2,sha256=MWhqt5Q8ExiRhgxndcEnwngOj1qw0E0u4hKuiuY24Gg,1437
|
146
149
|
geo_activity_playground/webui/templates/photo/new.html.j2,sha256=0BO4ZJgJQM1Hlp9SHylEOfthpQlywDc-xFs8K_Spptc,392
|
147
150
|
geo_activity_playground/webui/templates/plot-macros.html.j2,sha256=lzsu8c8fcsVjgpdcmpwCa1e6EPALZtCS9RbvQ-DAtAs,2861
|
@@ -171,8 +174,8 @@ geo_activity_playground/webui/templates/summary/vega-chart.html.j2,sha256=mw8Hti
|
|
171
174
|
geo_activity_playground/webui/templates/time_zone_fixer/index.html.j2,sha256=s9r6BJMXmd7kLSyjkvH4xLi6e01S5bpGRcMgMMJyCAE,1760
|
172
175
|
geo_activity_playground/webui/templates/upload/index.html.j2,sha256=I1Ix8tDS3YBdi-HdaNfjkzYXVVCjfUTe5PFTnap1ydc,775
|
173
176
|
geo_activity_playground/webui/templates/upload/reload.html.j2,sha256=YZWX5eDeNyqKJdQAywDBcU8DZBm22rRBbZqFjrFrCvQ,556
|
174
|
-
geo_activity_playground-1.
|
175
|
-
geo_activity_playground-1.
|
176
|
-
geo_activity_playground-1.
|
177
|
-
geo_activity_playground-1.
|
178
|
-
geo_activity_playground-1.
|
177
|
+
geo_activity_playground-1.4.1.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
|
178
|
+
geo_activity_playground-1.4.1.dist-info/METADATA,sha256=TlfR52Fp08hHZ5jDPHtHeQQ7dqcsqiUbI6aU1a94nv0,1890
|
179
|
+
geo_activity_playground-1.4.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
180
|
+
geo_activity_playground-1.4.1.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
|
181
|
+
geo_activity_playground-1.4.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{geo_activity_playground-1.3.2.dist-info → geo_activity_playground-1.4.1.dist-info}/entry_points.txt
RENAMED
File without changes
|