geo-activity-playground 0.16.0__tar.gz → 0.16.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/PKG-INFO +1 -1
  2. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/activity_parsers.py +8 -6
  3. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/heatmap.py +0 -49
  4. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/importers/directory.py +3 -0
  5. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/explorer_controller.py +13 -13
  6. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/heatmap_controller.py +18 -38
  7. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/pyproject.toml +1 -1
  8. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/LICENSE +0 -0
  9. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/__init__.py +0 -0
  10. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/__main__.py +0 -0
  11. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/__init__.py +0 -0
  12. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/activities.py +0 -0
  13. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/cache_migrations.py +0 -0
  14. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/config.py +0 -0
  15. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/coordinates.py +0 -0
  16. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/tasks.py +0 -0
  17. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/test_tiles.py +0 -0
  18. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/core/tiles.py +0 -0
  19. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/explorer/__init__.py +0 -0
  20. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/explorer/grid_file.py +0 -0
  21. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/explorer/tile_visits.py +0 -0
  22. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/explorer/video.py +0 -0
  23. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/importers/strava_api.py +0 -0
  24. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/importers/strava_checkout.py +0 -0
  25. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/importers/test_strava_api.py +0 -0
  26. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/activity_controller.py +0 -0
  27. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/app.py +0 -0
  28. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/calendar_controller.py +0 -0
  29. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/eddington_controller.py +0 -0
  30. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/entry_controller.py +0 -0
  31. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/equipment_controller.py +0 -0
  32. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/grayscale_tile_controller.py +0 -0
  33. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/search_controller.py +0 -0
  34. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
  35. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/android-chrome-384x384.png +0 -0
  36. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
  37. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
  38. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
  39. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
  40. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/favicon.ico +0 -0
  41. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
  42. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/safari-pinned-tab.svg +0 -0
  43. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/static/site.webmanifest +0 -0
  44. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/summary_controller.py +0 -0
  45. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/activity.html.j2 +0 -0
  46. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/calendar-month.html.j2 +0 -0
  47. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/calendar.html.j2 +0 -0
  48. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/eddington.html.j2 +0 -0
  49. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/equipment.html.j2 +0 -0
  50. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/explorer.html.j2 +0 -0
  51. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/heatmap.html.j2 +0 -0
  52. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/index.html.j2 +0 -0
  53. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/page.html.j2 +0 -0
  54. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/search.html.j2 +0 -0
  55. {geo_activity_playground-0.16.0 → geo_activity_playground-0.16.2}/geo_activity_playground/webui/templates/summary-statistics.html.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.16.0
3
+ Version: 0.16.2
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -1,8 +1,7 @@
1
1
  import datetime
2
2
  import gzip
3
- import os
3
+ import logging
4
4
  import pathlib
5
- import tempfile
6
5
  import xml
7
6
 
8
7
  import dateutil.parser
@@ -12,6 +11,8 @@ import pandas as pd
12
11
  import tcxreader.tcxreader
13
12
  import xmltodict
14
13
 
14
+ logger = logging.getLogger(__name__)
15
+
15
16
 
16
17
  class ActivityParseError(BaseException):
17
18
  pass
@@ -45,7 +46,7 @@ def read_activity(path: pathlib.Path) -> pd.DataFrame:
45
46
  elif file_type in [".kml", ".kmz"]:
46
47
  df = read_kml_activity(path, opener)
47
48
  else:
48
- raise ActivityParseError(f"Unsupported file format with {path=}: {file_type}")
49
+ raise ActivityParseError(f"Unsupported file format: {file_type}")
49
50
 
50
51
  if len(df):
51
52
  try:
@@ -161,10 +162,11 @@ def read_tcx_activity(path: pathlib.Path, open) -> pd.DataFrame:
161
162
  with open(path, "rb") as f:
162
163
  content = f.read().strip()
163
164
 
164
- with tempfile.NamedTemporaryFile("wb", dir=os.getcwd(), suffix=".tcx") as f:
165
+ stripped_file = pathlib.Path("Cache/temp.tcx")
166
+ with open(stripped_file, "wb") as f:
165
167
  f.write(content)
166
- f.flush()
167
- data = tcx_reader.read(f.name)
168
+ data = tcx_reader.read(str(stripped_file))
169
+ stripped_file.unlink()
168
170
 
169
171
  for trackpoint in data.trackpoints:
170
172
  if trackpoint.latitude and trackpoint.longitude:
@@ -176,52 +176,3 @@ def gaussian_filter(image, sigma):
176
176
  image = np.fft.irfft2(image_fft * gaussian_fft)
177
177
 
178
178
  return image
179
-
180
-
181
- def build_heatmap_image(
182
- xy_data: np.ndarray,
183
- mean_latitude: float,
184
- num_activities: int,
185
- tile_bounds: TileBounds,
186
- ) -> np.ndarray:
187
- assert xy_data.shape[1] == 2
188
-
189
- data = np.zeros(tile_bounds.shape)
190
-
191
- xy_data = np.array(xy_data)
192
- xy_data = np.round(
193
- (xy_data - [tile_bounds.x_tile_min, tile_bounds.y_tile_min]) * OSM_TILE_SIZE
194
- )
195
-
196
- sigma_pixel = 1
197
- for j, i in xy_data.astype(int):
198
- data[
199
- i - sigma_pixel : i + sigma_pixel, j - sigma_pixel : j + sigma_pixel
200
- ] += 1.0
201
-
202
- res_pixel = (
203
- 156543.03 * np.cos(np.radians(mean_latitude)) / (2.0**tile_bounds.zoom)
204
- ) # from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
205
-
206
- # trackpoint max accumulation per pixel = 1/5 (trackpoint/meter) * res_pixel (meter/pixel) * activities
207
- # (Strava records trackpoints every 5 meters in average for cycling activites)
208
- m = np.round((1.0 / 5.0) * res_pixel * num_activities)
209
-
210
- data[data > m] = m
211
-
212
- # equalize histogram and compute kernel density estimation
213
- data_hist, _ = np.histogram(data, bins=int(m + 1))
214
- data_hist = np.cumsum(data_hist) / data.size # normalized cumulated histogram
215
- for i in range(data.shape[0]):
216
- for j in range(data.shape[1]):
217
- data[i, j] = m * data_hist[int(data[i, j])]
218
-
219
- data = gaussian_filter(data, float(sigma_pixel))
220
-
221
- data = (data - data.min()) / (data.max() - data.min())
222
-
223
- cmap = pl.get_cmap("hot")
224
-
225
- data_color = cmap(data)
226
- data_color[data_color == cmap(0.0)] = 0.0 # remove background color
227
- return data_color
@@ -45,6 +45,9 @@ def import_from_directory() -> None:
45
45
  traceback.print_exc()
46
46
  paths_with_errors.append((path, str(e)))
47
47
  continue
48
+ except:
49
+ logger.error(f"Encountered a problem with {path=}, see details below.")
50
+ raise
48
51
 
49
52
  work_tracker.mark_done(activity_id)
50
53
 
@@ -122,20 +122,20 @@ def get_three_color_tiles(
122
122
  else:
123
123
  square_geojson = "{}"
124
124
 
125
+ try:
126
+ feature_collection = geojson.FeatureCollection(
127
+ features=[
128
+ make_explorer_tile(x, y, v, zoom) for (x, y), v in tile_dict.items()
129
+ ]
130
+ )
131
+ explored_geojson = geojson.dumps(feature_collection)
132
+ except TypeError as e:
133
+ logger.error(f"Encountered TypeError while building GeoJSON: {e=}")
134
+ logger.error(f"{tile_dict = }")
135
+ raise
136
+
125
137
  result = {
126
- "explored_geojson": geojson.dumps(
127
- geojson.FeatureCollection(
128
- features=[
129
- make_explorer_tile(
130
- x,
131
- y,
132
- tile_dict[(x, y)],
133
- zoom,
134
- )
135
- for (x, y), v in tile_dict.items()
136
- ]
137
- )
138
- ),
138
+ "explored_geojson": explored_geojson,
139
139
  "max_cluster_size": max_cluster_size,
140
140
  "num_cluster_tiles": num_cluster_tiles,
141
141
  "num_tiles": len(tile_dict),
@@ -13,10 +13,7 @@ from PIL import Image
13
13
  from PIL import ImageDraw
14
14
 
15
15
  from geo_activity_playground.core.activities import ActivityRepository
16
- from geo_activity_playground.core.heatmap import build_heatmap_image
17
- from geo_activity_playground.core.heatmap import build_map_from_tiles
18
16
  from geo_activity_playground.core.heatmap import convert_to_grayscale
19
- from geo_activity_playground.core.heatmap import crop_image_to_bounds
20
17
  from geo_activity_playground.core.heatmap import GeoBounds
21
18
  from geo_activity_playground.core.heatmap import get_sensible_zoom_level
22
19
  from geo_activity_playground.core.tasks import work_tracker
@@ -69,7 +66,7 @@ class HeatmapController:
69
66
  }
70
67
  }
71
68
 
72
- def render_tile(self, x: int, y: int, z: int) -> bytes:
69
+ def _render_tile_image(self, x: int, y: int, z: int) -> np.ndarray:
73
70
  tile_pixels = (OSM_TILE_SIZE, OSM_TILE_SIZE)
74
71
  tile_count_cache_path = pathlib.Path(f"Cache/Heatmap/{z}/{x}/{y}.npy")
75
72
  if tile_count_cache_path.exists():
@@ -115,47 +112,30 @@ class HeatmapController:
115
112
  map_tile[:, :, c] = (1.0 - data_color[:, :, c]) * map_tile[
116
113
  :, :, c
117
114
  ] + data_color[:, :, c]
115
+ return map_tile
118
116
 
117
+ def render_tile(self, x: int, y: int, z: int) -> bytes:
119
118
  f = io.BytesIO()
120
- pl.imsave(f, map_tile, format="png")
119
+ pl.imsave(f, self._render_tile_image(x, y, z), format="png")
121
120
  return bytes(f.getbuffer())
122
121
 
123
122
  def download_heatmap(self, north, east, south, west) -> bytes:
124
123
  geo_bounds = GeoBounds(south, west, north, east)
125
- tile_bounds = get_sensible_zoom_level(geo_bounds, (2160, 3840))
126
- background = build_map_from_tiles(tile_bounds)
127
- background = convert_to_grayscale(background)
128
- background = 1.0 - background
129
-
130
- relevant_activities = set()
131
-
132
- for tile_x in range(tile_bounds.x_tile_min, tile_bounds.x_tile_max):
133
- for tile_y in range(tile_bounds.y_tile_min, tile_bounds.y_tile_max):
134
- tile = (tile_x, tile_y)
135
- if tile in self.tile_visits[tile_bounds.zoom]:
136
- relevant_activities |= self.tile_visits[tile_bounds.zoom][tile][
137
- "activity_ids"
138
- ]
139
-
140
- points = pd.concat(map(self._repository.get_time_series, relevant_activities))
141
- xy_data = np.array([points["x"], points["y"]]).T * 2**tile_bounds.zoom
142
-
143
- within = (
144
- (tile_bounds.x_tile_min <= xy_data[:, 0])
145
- & (xy_data[:, 0] <= tile_bounds.x_tile_max)
146
- & (tile_bounds.y_tile_min <= xy_data[:, 1])
147
- & (xy_data[:, 1] <= tile_bounds.y_tile_max)
148
- )
149
- xy_data = xy_data[within]
124
+ tile_bounds = get_sensible_zoom_level(geo_bounds, (4000, 4000))
150
125
 
151
- data_color = build_heatmap_image(
152
- xy_data, np.mean(points["latitude"]), len(relevant_activities), tile_bounds
153
- )
154
- for c in range(3):
155
- background[:, :, c] = (1.0 - data_color[:, :, c]) * background[
156
- :, :, c
157
- ] + data_color[:, :, c]
158
- background = crop_image_to_bounds(background, geo_bounds, tile_bounds)
126
+ background = np.zeros((*tile_bounds.shape, 3))
127
+ for x in range(tile_bounds.x_tile_min, tile_bounds.x_tile_max):
128
+ for y in range(tile_bounds.y_tile_min, tile_bounds.y_tile_max):
129
+ tile = np.array(get_tile(tile_bounds.zoom, x, y)) / 255
130
+
131
+ i = y - tile_bounds.y_tile_min
132
+ j = x - tile_bounds.x_tile_min
133
+
134
+ background[
135
+ i * OSM_TILE_SIZE : (i + 1) * OSM_TILE_SIZE,
136
+ j * OSM_TILE_SIZE : (j + 1) * OSM_TILE_SIZE,
137
+ :,
138
+ ] = self._render_tile_image(x, y, tile_bounds.zoom)
159
139
 
160
140
  f = io.BytesIO()
161
141
  pl.imsave(f, background, format="png")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "geo-activity-playground"
3
- version = "0.16.0"
3
+ version = "0.16.2"
4
4
  description = "Analysis of geo data activities like rides, runs or hikes."
5
5
  authors = ["Martin Ueding <mu@martin-ueding.de>"]
6
6
  license = "MIT"