geo-activity-playground 0.7.0__py3-none-any.whl → 0.8.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.
@@ -1,16 +1,11 @@
1
1
  import argparse
2
2
  import os
3
3
  import pathlib
4
- import tomllib
5
4
 
6
5
  import coloredlogs
7
6
 
8
7
  from geo_activity_playground.core.activities import ActivityRepository
9
- from geo_activity_playground.core.sources import TimeSeriesSource
10
- from geo_activity_playground.explorer.grid_file import get_border_tiles
11
- from geo_activity_playground.explorer.grid_file import get_explored_tiles
12
- from geo_activity_playground.explorer.grid_file import make_grid_file_geojson
13
- from geo_activity_playground.explorer.grid_file import make_grid_file_gpx
8
+ from geo_activity_playground.core.config import get_config
14
9
  from geo_activity_playground.explorer.video import explorer_video_main
15
10
  from geo_activity_playground.heatmap import generate_heatmaps_per_cluster
16
11
  from geo_activity_playground.importers.directory import import_from_directory
@@ -78,11 +73,10 @@ def main() -> None:
78
73
 
79
74
  def make_activity_repository(basedir: pathlib.Path) -> ActivityRepository:
80
75
  os.chdir(basedir)
76
+ config = get_config()
81
77
  if pathlib.Path("Activities").exists():
82
78
  import_from_directory()
83
- elif pathlib.Path("config.toml").exists():
84
- with open("config.toml", "rb") as f:
85
- config = tomllib.load(f)
79
+ elif config:
86
80
  if "strava" in config:
87
81
  import_from_strava_api()
88
82
  return ActivityRepository()
@@ -2,16 +2,15 @@ import dataclasses
2
2
  import datetime
3
3
  import functools
4
4
  import logging
5
- import pathlib
6
- import tomllib
7
5
  from typing import Iterator
8
6
  from typing import Optional
9
7
 
10
8
  import geojson
11
9
  import matplotlib
12
- import numpy as np
13
10
  import pandas as pd
14
11
 
12
+ from geo_activity_playground.core.config import get_config
13
+
15
14
 
16
15
  logger = logging.getLogger(__name__)
17
16
 
@@ -105,25 +104,35 @@ def make_geojson_color_line(time_series: pd.DataFrame) -> str:
105
104
  def extract_heart_rate_zones(time_series: pd.DataFrame) -> Optional[pd.DataFrame]:
106
105
  if "heartrate" not in time_series:
107
106
  return None
108
- config_path = pathlib.Path("config.toml")
109
- if not config_path.exists():
110
- logger.warning("Missing a config, cannot extract heart rate zones.")
111
- return None
112
- with open(config_path, "rb") as f:
113
- config = tomllib.load(f)
114
-
107
+ config = get_config()
115
108
  try:
116
- birthyear = config["heart"]["birthyear"]
109
+ heart_config = config["heart"]
117
110
  except KeyError:
118
111
  logger.warning(
119
- "Missing config entry `heart.birthyear`, cannot determine heart rate zones."
112
+ "Missing config entry `heart`, cannot determine heart rate zones."
113
+ )
114
+ return None
115
+
116
+ birthyear = heart_config.get("birthyear", None)
117
+ maximum = heart_config.get("maximum", None)
118
+ resting = heart_config.get("resting", None)
119
+
120
+ if not maximum and birthyear:
121
+ age = time_series["time"].iloc[0].year - birthyear
122
+ maximum = 220 - age
123
+ if not resting:
124
+ resting = 0
125
+ if not maximum:
126
+ logger.warning(
127
+ "Missing config entry `heart.maximum` or `heart.birthyear`, cannot determine heart rate zones."
120
128
  )
121
129
  return None
122
130
 
123
- age = time_series["time"].iloc[0].year - birthyear
124
- max_rate = 220 - age
125
- zones: pd.Series = time_series["heartrate"] * 10 // max_rate - 4
131
+ zones: pd.Series = (time_series["heartrate"] - resting) * 10 // (
132
+ maximum - resting
133
+ ) - 4
126
134
  zones.loc[zones < 0] = 0
135
+ zones.loc[zones > 5] = 5
127
136
  df = pd.DataFrame({"heartzone": zones, "step": time_series["time"].diff()}).dropna()
128
137
  duration_per_zone = df.groupby("heartzone").sum()["step"].dt.total_seconds() / 60
129
138
  duration_per_zone.name = "minutes"
@@ -0,0 +1,22 @@
1
+ import functools
2
+ import logging
3
+ import pathlib
4
+
5
+
6
+ try:
7
+ import tomllib
8
+ except ModuleNotFoundError:
9
+ import tomli as tomllib
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @functools.cache
16
+ def get_config() -> dict:
17
+ config_path = pathlib.Path("config.toml")
18
+ if not config_path.exists():
19
+ logger.warning("Missing a config, some features might be missing.")
20
+ return {}
21
+ with open(config_path, "rb") as f:
22
+ return tomllib.load(f)
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import pathlib
2
3
  from typing import Iterator
3
4
 
@@ -6,12 +7,16 @@ import gpxpy
6
7
  import numpy as np
7
8
  import pandas as pd
8
9
  import scipy.ndimage
10
+ import sklearn.cluster
9
11
 
10
12
  from geo_activity_playground.core.activities import ActivityRepository
11
13
  from geo_activity_playground.core.tiles import get_tile_upper_left_lat_lon
12
14
  from geo_activity_playground.explorer.converters import get_tile_history
13
15
 
14
16
 
17
+ logger = logging.getLogger(__name__)
18
+
19
+
15
20
  def get_three_color_tiles(
16
21
  tiles: pd.DataFrame, repository: ActivityRepository, zoom: int
17
22
  ) -> str:
@@ -19,7 +24,10 @@ def get_three_color_tiles(
19
24
  a = np.zeros((2**zoom, 2**zoom), dtype=np.int8)
20
25
  a[tiles["tile_x"], tiles["tile_y"]] = 1
21
26
 
22
- tile_dict = {elem: 1 for elem in zip(tiles["tile_x"], tiles["tile_y"])}
27
+ tile_dict = {
28
+ elem: {"cluster": False, "square": False}
29
+ for elem in zip(tiles["tile_x"], tiles["tile_y"])
30
+ }
23
31
 
24
32
  for x, y in tile_dict.keys():
25
33
  if (
@@ -28,7 +36,7 @@ def get_three_color_tiles(
28
36
  and (x, y + 1) in tile_dict
29
37
  and (x, y - 1) in tile_dict
30
38
  ):
31
- tile_dict[(x, y)] = 2
39
+ tile_dict[(x, y)]["cluster"] = True
32
40
 
33
41
  # Compute biggest square.
34
42
  square_size = 1
@@ -52,43 +60,75 @@ def get_three_color_tiles(
52
60
  square_x, square_y, square_size = biggest
53
61
  for x in range(square_x, square_x + square_size):
54
62
  for y in range(square_y, square_y + square_size):
55
- tile_dict[(x, y)] = 3
56
-
57
- tile_metadata = {
58
- (row["tile_x"], row["tile_y"]): {
59
- "first_visit": row["time"].date().isoformat(),
60
- "activity_id": row["activity_id"],
61
- "activity_name": repository.get_activity_by_id(row["activity_id"]).name,
62
- }
63
- for index, row in tiles.iterrows()
64
- }
65
-
66
- # Find non-zero tiles.
67
- return geojson.dumps(
68
- geojson.FeatureCollection(
69
- features=[
70
- make_explorer_tile(
71
- x,
72
- y,
73
- {
74
- "color": {1: "red", 2: "green", 3: "blue"}[v],
75
- **tile_metadata[(x, y)],
76
- },
77
- zoom,
78
- )
79
- for (x, y), v in tile_dict.items()
80
- ]
63
+ tile_dict[(x, y)]["square"] = True
64
+
65
+ for index, row in tiles.iterrows():
66
+ tile_dict[(row["tile_x"], row["tile_y"])].update(
67
+ {
68
+ "first_visit": row["time"].date().isoformat(),
69
+ "activity_id": row["activity_id"],
70
+ "activity_name": repository.get_activity_by_id(row["activity_id"]).name,
71
+ "color": map_color(tile_dict[(row["tile_x"], row["tile_y"])]),
72
+ }
81
73
  )
74
+
75
+ num_cluster_tiles = sum(value["cluster"] for value in tile_dict.values())
76
+
77
+ cluster_tiles = np.array(
78
+ [tile for tile, value in tile_dict.items() if value["cluster"]]
82
79
  )
83
80
 
81
+ logger.info("Run DBSCAN cluster finding algorithm …")
82
+ dbscan = sklearn.cluster.DBSCAN(eps=1.1, min_samples=1)
83
+ labels = dbscan.fit_predict(cluster_tiles)
84
+ label_counts = dict(zip(*np.unique(labels, return_counts=True)))
85
+ max_cluster_size = max(
86
+ count for label, count in label_counts.items() if label != -1
87
+ )
88
+ for xy, label in zip(cluster_tiles, labels):
89
+ tile_dict[tuple(xy)]["cluster_id"] = int(label)
90
+ tile_dict[tuple(xy)]["this_cluster_size"] = int(label_counts[label])
91
+
92
+ # Find non-zero tiles.
93
+ result = {
94
+ "explored_geojson": geojson.dumps(
95
+ geojson.FeatureCollection(
96
+ features=[
97
+ make_explorer_tile(
98
+ x,
99
+ y,
100
+ tile_dict[(x, y)],
101
+ zoom,
102
+ )
103
+ for (x, y), v in tile_dict.items()
104
+ ]
105
+ )
106
+ ),
107
+ "max_cluster_size": max_cluster_size,
108
+ "num_cluster_tiles": num_cluster_tiles,
109
+ "num_tiles": len(tile_dict),
110
+ "square_size": square_size,
111
+ }
112
+ return result
113
+
114
+
115
+ def map_color(tile_meta: dict) -> str:
116
+ if tile_meta["square"]:
117
+ return "blue"
118
+ elif tile_meta["cluster"]:
119
+ return "green"
120
+ else:
121
+ return "red"
122
+
84
123
 
85
124
  def get_border_tiles(tiles: pd.DataFrame, zoom: int) -> list[list[list[float]]]:
86
- a = np.zeros((2**zoom, 2**zoom), dtype=np.int8)
87
- a[tiles["tile_x"], tiles["tile_y"]] = 1
88
- dilated = scipy.ndimage.binary_dilation(a, iterations=2)
89
- border = dilated - a
90
- border_x, border_y = np.where(border)
91
- return make_grid_points(zip(border_x, border_y), zoom)
125
+ tile_set = set(zip(tiles["tile_x"], tiles["tile_y"]))
126
+ border_tiles = set()
127
+ for x, y in tile_set:
128
+ for neighbor in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
129
+ if neighbor not in tile_set:
130
+ border_tiles.add(neighbor)
131
+ return make_grid_points(border_tiles, zoom)
92
132
 
93
133
 
94
134
  def get_explored_tiles(tiles: pd.DataFrame, zoom: int) -> list[list[list[float]]]:
@@ -4,22 +4,16 @@ import json
4
4
  import logging
5
5
  import pathlib
6
6
  import pickle
7
- import tomllib
8
7
  from typing import Any
9
8
 
10
9
  import pandas as pd
11
10
  from stravalib import Client
12
11
  from stravalib.exc import RateLimitExceeded
13
12
 
14
-
15
- logger = logging.getLogger(__name__)
13
+ from geo_activity_playground.core.config import get_config
16
14
 
17
15
 
18
- @functools.cache
19
- def get_config() -> dict:
20
- config_path = pathlib.Path("config.toml")
21
- with open(config_path, "rb") as f:
22
- return tomllib.load(f)
16
+ logger = logging.getLogger(__name__)
23
17
 
24
18
 
25
19
  def get_state(path: pathlib.Path) -> Any:
@@ -19,6 +19,7 @@ class ActivityController:
19
19
  activity = self._repository.get_activity_by_id(id)
20
20
 
21
21
  time_series = self._repository.get_time_series(id)
22
+ time_series["distance/km"] = time_series["distance"] / 1000
22
23
  line_json = make_geojson_from_time_series(time_series)
23
24
 
24
25
  result = {
@@ -40,7 +41,9 @@ def distance_time_plot(time_series: pd.DataFrame) -> str:
40
41
  return (
41
42
  alt.Chart(time_series, title="Distance")
42
43
  .mark_line()
43
- .encode(alt.X("time", title="Time"), alt.Y("distance", title="Distance / km"))
44
+ .encode(
45
+ alt.X("time", title="Time"), alt.Y("distance/km", title="Distance / km")
46
+ )
44
47
  .interactive()
45
48
  .to_json(format="vega")
46
49
  )
@@ -1,6 +1,7 @@
1
1
  import functools
2
2
 
3
3
  from geo_activity_playground.core.activities import ActivityRepository
4
+ from geo_activity_playground.core.tiles import get_tile_upper_left_lat_lon
4
5
  from geo_activity_playground.explorer.converters import get_tile_history
5
6
  from geo_activity_playground.explorer.grid_file import get_border_tiles
6
7
  from geo_activity_playground.explorer.grid_file import get_explored_geojson
@@ -17,21 +18,26 @@ class ExplorerController:
17
18
  @functools.cache
18
19
  def render(self, zoom: int) -> dict:
19
20
  tiles = get_tile_history(self._repository, zoom)
21
+ medians = tiles.median()
22
+ median_lat, median_lon = get_tile_upper_left_lat_lon(
23
+ medians["tile_x"], medians["tile_y"], zoom
24
+ )
20
25
 
21
- explored_geojson = get_three_color_tiles(tiles, self._repository, zoom)
26
+ explored = get_three_color_tiles(tiles, self._repository, zoom)
22
27
 
23
- if zoom <= 14:
24
- points = get_border_tiles(tiles, zoom)
25
- missing_tiles_geojson = make_grid_file_geojson(points, "missing_tiles")
26
- make_grid_file_gpx(points, "missing_tiles")
28
+ points = get_border_tiles(tiles, zoom)
29
+ missing_tiles_geojson = make_grid_file_geojson(points, "missing_tiles")
30
+ make_grid_file_gpx(points, "missing_tiles")
27
31
 
28
- points = get_explored_tiles(tiles, zoom)
29
- explored_tiles_geojson = make_grid_file_geojson(points, "explored")
30
- make_grid_file_gpx(points, "explored")
31
- else:
32
- missing_tiles_geojson = {}
32
+ points = get_explored_tiles(tiles, zoom)
33
+ explored_tiles_geojson = make_grid_file_geojson(points, "explored")
34
+ make_grid_file_gpx(points, "explored")
33
35
 
34
36
  return {
35
- "explored_geojson": explored_geojson,
37
+ "center": {
38
+ "latitude": median_lat,
39
+ "longitude": median_lon,
40
+ },
41
+ "explored": explored,
36
42
  "missing_tiles_geojson": missing_tiles_geojson,
37
43
  }
@@ -4,6 +4,11 @@
4
4
  <div class="row mb-3">
5
5
  <div class="col">
6
6
  <h1>Explorer Tiles</h1>
7
+ <p>You have {{ explored.num_tiles }} explored tiles. There are {{ explored.num_cluster_tiles }} cluster tiles in
8
+ total. Your largest cluster consists of {{ explored.max_cluster_size }} tiles. Your largest square has size
9
+ {{
10
+ explored.square_size }}².
11
+ </p>
7
12
  </div>
8
13
  </div>
9
14
 
@@ -22,14 +27,21 @@
22
27
  <script>
23
28
  function onEachFeature(feature, layer) {
24
29
  if (feature.properties && feature.properties.first_visit) {
25
- layer.bindPopup(`First visited on ${feature.properties.first_visit} as part of <a href=/activity/${feature.properties.activity_id}>${feature.properties.activity_name}</a>.`)
30
+ let lines = [`First visit: ${feature.properties.first_visit}`,
31
+ `First activity: <a href=/activity/${feature.properties.activity_id}>${feature.properties.activity_name}</a>`]
32
+ if (feature.properties.this_cluster_size) {
33
+ lines.push(`This cluster size: ${feature.properties.this_cluster_size}`)
34
+ }
35
+ layer.bindPopup(lines.join('<br />'))
26
36
  }
27
37
  }
28
38
 
29
- let explorer_geojson = {{ explored_geojson| safe}}
39
+ let explorer_geojson = {{ explored.explored_geojson| safe}}
30
40
  let map = L.map('explorer-map', {
31
- fullscreenControl: true
32
- });
41
+ fullscreenControl: true,
42
+ center: [{{ center.latitude }}, {{ center.longitude }}],
43
+ zoom: 10
44
+ })
33
45
  L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
34
46
  maxZoom: 19,
35
47
  attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
@@ -38,7 +50,6 @@
38
50
  style: function (feature) { return { color: feature.properties.color, fillColor: feature.properties.color, weight: 1 } },
39
51
  onEachFeature: onEachFeature
40
52
  }).addTo(map)
41
- map.fitBounds(explorer_layer.getBounds())
42
53
  </script>
43
54
  </div>
44
55
  </div>
@@ -59,14 +70,15 @@
59
70
  <script>
60
71
  let missing_geojson = {{ missing_tiles_geojson| safe}}
61
72
  let missing_map = L.map('missing-map', {
62
- fullscreenControl: true
73
+ fullscreenControl: true,
74
+ center: [{{ center.latitude }}, {{ center.longitude }}],
75
+ zoom: 10
63
76
  });
64
77
  L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
65
78
  maxZoom: 19,
66
79
  attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
67
80
  }).addTo(missing_map)
68
81
  let missing_layer = L.geoJSON(missing_geojson).addTo(missing_map)
69
- missing_map.fitBounds(missing_layer.getBounds())
70
82
  </script>
71
83
  </div>
72
84
  </div>
@@ -15,7 +15,7 @@
15
15
  let map = L.map('heatmap', {
16
16
  fullscreenControl: true,
17
17
  center: [{{ center.latitude }}, {{ center.longitude }}],
18
- zoom: 12
18
+ zoom: 12
19
19
  });
20
20
  L.tileLayer('/heatmap/tile/{z}/{x}/{y}.png', {
21
21
  maxZoom: 19,
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
7
7
  Author-email: mu@martin-ueding.de
8
- Requires-Python: >=3.10,<3.12
8
+ Requires-Python: >=3.9,<3.12
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
11
12
  Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: Programming Language :: Python :: 3.11
13
14
  Requires-Dist: Pillow (>=9.2.0,<10.0.0)
@@ -27,6 +28,7 @@ Requires-Dist: requests (>=2.28.1,<3.0.0)
27
28
  Requires-Dist: scikit-learn (>=1.3.0,<2.0.0)
28
29
  Requires-Dist: scipy (>=1.8.1,<2.0.0)
29
30
  Requires-Dist: stravalib (>=1.3.3,<2.0.0)
31
+ Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
30
32
  Requires-Dist: tqdm (>=4.64.0,<5.0.0)
31
33
  Requires-Dist: vegafusion (>=1.4.3,<2.0.0)
32
34
  Requires-Dist: vegafusion-python-embed (>=1.4.3,<2.0.0)
@@ -1,8 +1,9 @@
1
1
  geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- geo_activity_playground/__main__.py,sha256=0xUcQkoRcPhLJX3uluIFs1GtRRdaqqz6GVAR8UNDmmI,3151
2
+ geo_activity_playground/__main__.py,sha256=bGDI5TTWJakCBqlet-kqdDOmBgh9JbmV7v99g6GgT3A,2745
3
3
  geo_activity_playground/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- geo_activity_playground/core/activities.py,sha256=LnT9vZgt7lkim6T9pVHxtF8MjFIivmSqCqiR5YMuuBU,4237
4
+ geo_activity_playground/core/activities.py,sha256=6f-pnIJ58ZMrcwXJ19LJUafmip9vUVPlGyBeVUVnIxw,4484
5
5
  geo_activity_playground/core/activity_parsers.py,sha256=CaQB4jXm-4PjrXR7AcLkgZt82zGl4oCyLYlY0Zfx4H8,2135
6
+ geo_activity_playground/core/config.py,sha256=GNHEIeFI3dNRiFSMburn5URZHx0qkiitvePAx2toYUQ,456
6
7
  geo_activity_playground/core/coordinates.py,sha256=SxU2xDPZD-KxL2VBM94_wUFKkcG27-wTLkuu22ByVps,573
7
8
  geo_activity_playground/core/heatmap.py,sha256=BPhRNcieHgc5gSZdKRtYcv8Ljfwgi-teLr6VVBVlTkg,1289
8
9
  geo_activity_playground/core/plots.py,sha256=7FX1QRT_haBq9lQsehaZQ_rXLcJNHO2Fx_bWuBXwPnw,2420
@@ -12,18 +13,18 @@ geo_activity_playground/core/test_tiles.py,sha256=ATIcCqwAQMpkPAZD9VO__uinpuuOyz
12
13
  geo_activity_playground/core/tiles.py,sha256=aTC3roH8JMaEAMC_hUAwOz7_Yvvw7n4Vc-9y8pN05Pk,2399
13
14
  geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  geo_activity_playground/explorer/converters.py,sha256=UVU5qhnvAelY6rmqCnkqfDp1zYI_1LUatdJC0d16BWE,3051
15
- geo_activity_playground/explorer/grid_file.py,sha256=Pp1qgaL9WtfyZKa6uzgCrdXl6HD2YeOsU6RvhDeXibA,5499
16
+ geo_activity_playground/explorer/grid_file.py,sha256=lER2TAJjsG8BMjbdoXiwW9GXZLM8bH-9EeF7z7yckgg,6828
16
17
  geo_activity_playground/explorer/video.py,sha256=RGZik93ghfZuRILXf8pfUbPh5VV37_QRAR4FgOMfZqQ,4354
17
18
  geo_activity_playground/heatmap.py,sha256=jg0yuFvYoSa8K0uGL6MCMhWFclXWO3DVyrsdSJcH380,8552
18
19
  geo_activity_playground/importers/directory.py,sha256=Hk9YYJA2ssWaOyc6WFxKNwTo5DWZxXCW661spCyI7kQ,2622
19
- geo_activity_playground/importers/strava_api.py,sha256=vacbspe0vR8e3duav1KOBB-ODebSOV_hF5RWMzNojOo,5865
20
- geo_activity_playground/webui/activity_controller.py,sha256=JMLLsh26SdwBkvOUEvKK-w05ZulPXlFOfxRRFdEU4w4,2848
20
+ geo_activity_playground/importers/strava_api.py,sha256=3utIYnmw0nqY5oGBf-3VJPzGI1hTplYUfeWXoSuJ7gE,5749
21
+ geo_activity_playground/webui/activity_controller.py,sha256=sOYUYJGibYx6eGOxE17umSWubpybB8Xgcm07wB0v4LA,2941
21
22
  geo_activity_playground/webui/app.py,sha256=9-TgDI1LojplwPxeorMqtObGWIw1sB5qepqWVHUl3s4,3532
22
23
  geo_activity_playground/webui/calendar_controller.py,sha256=maQ1RlrD99pncOts3ue5ye4OHr6WB-E40eAzs8ZxwPI,2239
23
24
  geo_activity_playground/webui/eddington_controller.py,sha256=b5mYkciv7Wkd5zord-WsdrV_8c-qpVi-8DG3jIUEKhs,2616
24
25
  geo_activity_playground/webui/entry_controller.py,sha256=TixdwCDMKhHUVrbc6sAIjpvtmouHXtbQN0N641TB__s,1851
25
26
  geo_activity_playground/webui/equipment_controller.py,sha256=2asTMDEYdMz8a_l5fXL1ULXoUaDHywoDl9mZnfZSn5Q,2011
26
- geo_activity_playground/webui/explorer_controller.py,sha256=6d4Rp0VmclHyO3mhw21heub1XtSEAHgVPfuMxHahGs8,1535
27
+ geo_activity_playground/webui/explorer_controller.py,sha256=8OD9h7-UzlSU53tpA7LD0VsHbvRTka9U4CFjiuNQzGU,1766
27
28
  geo_activity_playground/webui/heatmap_controller.py,sha256=twfp9IQwUz9qQCjO0zMwhWjQYmHRpHM-A8BPnCj3ErY,3636
28
29
  geo_activity_playground/webui/static/android-chrome-192x192.png,sha256=30rNfBHxdLYC0Wx4cDkPZY-V17ZQZIc4PPLQBdz_w1U,20069
29
30
  geo_activity_playground/webui/static/android-chrome-384x384.png,sha256=bgeqAdyvDZBMch7rVi3qSawf0Zr4Go0EG8Ws_B8NApY,49297
@@ -40,12 +41,12 @@ geo_activity_playground/webui/templates/calendar-month.html.j2,sha256=LVokl95lPl
40
41
  geo_activity_playground/webui/templates/calendar.html.j2,sha256=b7a_YWhqyN2GYU7g4wIckU3UURTzNuL5fGe5SibVKM8,1099
41
42
  geo_activity_playground/webui/templates/eddington.html.j2,sha256=yl75IzWeIkFpwPj8FjTrzJsz_f-qdETPmNnAGLPJuL8,487
42
43
  geo_activity_playground/webui/templates/equipment.html.j2,sha256=BwZzbZ2AuFuiM_Fxu2KOqvhcgHd9yr1xL76ihb_6YKc,1317
43
- geo_activity_playground/webui/templates/explorer.html.j2,sha256=RR6Odp8dWHmOUzrXfFjVV3_BKvWnXDeOk4URpKwmSCk,2747
44
- geo_activity_playground/webui/templates/heatmap.html.j2,sha256=H4Jd1eSjDr9lu-ni9I1ErFoXQLbZiyP5NnKYpmHLizY,722
44
+ geo_activity_playground/webui/templates/explorer.html.j2,sha256=fVxPxicgDRvgOu1138E6bJs81CFvf1VkHuzGYAWW1ok,3399
45
+ geo_activity_playground/webui/templates/heatmap.html.j2,sha256=M56IKATu3TdlwXUgTK9w-vfoAubBv9oJrE3ot-wEf84,726
45
46
  geo_activity_playground/webui/templates/index.html.j2,sha256=6b0cdqiGnqrC_hjEg-z6-IZFqXoZa7V0JurQ4Xd6YJw,1968
46
47
  geo_activity_playground/webui/templates/page.html.j2,sha256=CWaDxYlXzJCWCuIsMma2-PCLrsEr38cSan01CGG00qU,5104
47
48
  geo_activity_playground/webui/templates/summary-statistics.html.j2,sha256=WylEkNplyXIt2bqYdZg93xupeP9wmaq5AWnpYaPkr-8,453
48
- geo_activity_playground-0.7.0.dist-info/METADATA,sha256=MXE_fiVKhfCk-jfiHuVTqhCNkkdxdu156uVao4tKG1I,1312
49
- geo_activity_playground-0.7.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
50
- geo_activity_playground-0.7.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
51
- geo_activity_playground-0.7.0.dist-info/RECORD,,
49
+ geo_activity_playground-0.8.0.dist-info/METADATA,sha256=lAYsRY6bF3_FhEX9MODLVrJvQH-SuP25fEppUVkqPH0,1425
50
+ geo_activity_playground-0.8.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
51
+ geo_activity_playground-0.8.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
52
+ geo_activity_playground-0.8.0.dist-info/RECORD,,