geo-activity-playground 0.35.0__py3-none-any.whl → 0.36.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.
Files changed (47) hide show
  1. geo_activity_playground/__main__.py +12 -0
  2. geo_activity_playground/core/activities.py +21 -13
  3. geo_activity_playground/core/raster_map.py +246 -0
  4. geo_activity_playground/core/tiles.py +6 -50
  5. geo_activity_playground/explorer/video.py +1 -1
  6. geo_activity_playground/heatmap_video.py +93 -0
  7. geo_activity_playground/importers/activity_parsers.py +1 -1
  8. geo_activity_playground/importers/directory.py +6 -15
  9. geo_activity_playground/webui/activity/blueprint.py +3 -10
  10. geo_activity_playground/webui/activity/controller.py +10 -71
  11. geo_activity_playground/webui/app.py +32 -22
  12. geo_activity_playground/webui/{auth/blueprint.py → auth_blueprint.py} +1 -1
  13. geo_activity_playground/webui/calendar/blueprint.py +2 -5
  14. geo_activity_playground/webui/{eddington/controller.py → eddington_blueprint.py} +17 -13
  15. geo_activity_playground/webui/equipment/blueprint.py +2 -8
  16. geo_activity_playground/webui/explorer/blueprint.py +2 -10
  17. geo_activity_playground/webui/heatmap/blueprint.py +36 -10
  18. geo_activity_playground/webui/heatmap/heatmap_controller.py +151 -71
  19. geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +30 -12
  20. geo_activity_playground/webui/{search/blueprint.py → search_blueprint.py} +1 -1
  21. geo_activity_playground/webui/settings/blueprint.py +1 -2
  22. geo_activity_playground/webui/square_planner_blueprint.py +118 -0
  23. geo_activity_playground/webui/{summary/controller.py → summary_blueprint.py} +23 -24
  24. geo_activity_playground/webui/templates/page.html.j2 +11 -0
  25. geo_activity_playground/webui/tile_blueprint.py +42 -0
  26. geo_activity_playground/webui/upload_blueprint.py +1 -3
  27. {geo_activity_playground-0.35.0.dist-info → geo_activity_playground-0.36.0.dist-info}/METADATA +1 -1
  28. {geo_activity_playground-0.35.0.dist-info → geo_activity_playground-0.36.0.dist-info}/RECORD +36 -43
  29. geo_activity_playground/core/heatmap.py +0 -194
  30. geo_activity_playground/webui/eddington/__init__.py +0 -0
  31. geo_activity_playground/webui/eddington/blueprint.py +0 -19
  32. geo_activity_playground/webui/square_planner/__init__.py +0 -0
  33. geo_activity_playground/webui/square_planner/blueprint.py +0 -38
  34. geo_activity_playground/webui/square_planner/controller.py +0 -101
  35. geo_activity_playground/webui/summary/__init__.py +0 -0
  36. geo_activity_playground/webui/summary/blueprint.py +0 -17
  37. geo_activity_playground/webui/tile/__init__.py +0 -0
  38. geo_activity_playground/webui/tile/blueprint.py +0 -32
  39. geo_activity_playground/webui/tile/controller.py +0 -36
  40. /geo_activity_playground/webui/{auth/templates → templates}/auth/index.html.j2 +0 -0
  41. /geo_activity_playground/webui/{eddington/templates → templates}/eddington/index.html.j2 +0 -0
  42. /geo_activity_playground/webui/{search/templates → templates}/search/index.html.j2 +0 -0
  43. /geo_activity_playground/webui/{square_planner/templates → templates}/square_planner/index.html.j2 +0 -0
  44. /geo_activity_playground/webui/{summary/templates → templates}/summary/index.html.j2 +0 -0
  45. {geo_activity_playground-0.35.0.dist-info → geo_activity_playground-0.36.0.dist-info}/LICENSE +0 -0
  46. {geo_activity_playground-0.35.0.dist-info → geo_activity_playground-0.36.0.dist-info}/WHEEL +0 -0
  47. {geo_activity_playground-0.35.0.dist-info → geo_activity_playground-0.36.0.dist-info}/entry_points.txt +0 -0
@@ -12,8 +12,6 @@ import pandas as pd
12
12
  from PIL import Image
13
13
  from PIL import ImageDraw
14
14
 
15
- from ...explorer.grid_file import make_grid_file_geojson
16
- from ...explorer.grid_file import make_grid_points
17
15
  from geo_activity_playground.core.activities import ActivityMeta
18
16
  from geo_activity_playground.core.activities import ActivityRepository
19
17
  from geo_activity_playground.core.activities import make_geojson_color_line
@@ -21,14 +19,13 @@ from geo_activity_playground.core.activities import make_geojson_from_time_serie
21
19
  from geo_activity_playground.core.activities import make_speed_color_bar
22
20
  from geo_activity_playground.core.config import Config
23
21
  from geo_activity_playground.core.heart_rate import HeartRateZoneComputer
24
- from geo_activity_playground.core.heatmap import build_map_from_tiles_around_center
25
- from geo_activity_playground.core.heatmap import GeoBounds
26
- from geo_activity_playground.core.heatmap import OSM_MAX_ZOOM
27
- from geo_activity_playground.core.heatmap import OSM_TILE_SIZE
28
- from geo_activity_playground.core.heatmap import PixelBounds
29
- from geo_activity_playground.core.heatmap import TileBounds
30
22
  from geo_activity_playground.core.privacy_zones import PrivacyZone
31
- from geo_activity_playground.core.tiles import compute_tile_float
23
+ from geo_activity_playground.core.raster_map import map_image_from_tile_bounds
24
+ from geo_activity_playground.core.raster_map import OSM_MAX_ZOOM
25
+ from geo_activity_playground.core.raster_map import OSM_TILE_SIZE
26
+ from geo_activity_playground.core.raster_map import tile_bounds_around_center
27
+ from geo_activity_playground.explorer.grid_file import make_grid_file_geojson
28
+ from geo_activity_playground.explorer.grid_file import make_grid_points
32
29
  from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
33
30
 
34
31
  logger = logging.getLogger(__name__)
@@ -412,62 +409,6 @@ def name_minutes_plot(meta: pd.DataFrame) -> str:
412
409
  )
413
410
 
414
411
 
415
- def make_pixel_bounds_square(bounds: PixelBounds) -> PixelBounds:
416
- x_radius = (bounds.x_max - bounds.x_min) // 2
417
- y_radius = (bounds.y_max - bounds.y_min) // 2
418
- x_center = (bounds.x_max + bounds.x_min) // 2
419
- y_center = (bounds.y_max + bounds.y_min) // 2
420
-
421
- radius = max(x_radius, y_radius)
422
-
423
- return PixelBounds(
424
- x_min=x_center - radius,
425
- y_min=y_center - radius,
426
- x_max=x_center + radius,
427
- y_max=y_center + radius,
428
- )
429
-
430
-
431
- def make_tile_bounds_square(bounds: TileBounds) -> TileBounds:
432
- x_radius = (bounds.x_tile_max - bounds.x_tile_min) / 2
433
- y_radius = (bounds.y_tile_max - bounds.y_tile_min) / 2
434
- x_center = (bounds.x_tile_max + bounds.x_tile_min) / 2
435
- y_center = (bounds.y_tile_max + bounds.y_tile_min) / 2
436
-
437
- radius = max(x_radius, y_radius)
438
-
439
- return TileBounds(
440
- zoom=bounds.zoom,
441
- x_tile_min=int(x_center - radius),
442
- y_tile_min=int(y_center - radius),
443
- x_tile_max=int(np.ceil(x_center + radius)),
444
- y_tile_max=int(np.ceil(y_center + radius)),
445
- )
446
-
447
-
448
- def get_crop_mask(geo_bounds: GeoBounds, tile_bounds: TileBounds) -> PixelBounds:
449
- min_x, min_y = compute_tile_float(
450
- geo_bounds.lat_max, geo_bounds.lon_min, tile_bounds.zoom
451
- )
452
- max_x, max_y = compute_tile_float(
453
- geo_bounds.lat_min, geo_bounds.lon_max, tile_bounds.zoom
454
- )
455
-
456
- crop_mask = PixelBounds(
457
- int((min_x - tile_bounds.x_tile_min) * OSM_TILE_SIZE),
458
- int((max_x - tile_bounds.x_tile_min) * OSM_TILE_SIZE),
459
- int((min_y - tile_bounds.y_tile_min) * OSM_TILE_SIZE),
460
- int((max_y - tile_bounds.y_tile_min) * OSM_TILE_SIZE),
461
- )
462
- crop_mask = make_pixel_bounds_square(crop_mask)
463
-
464
- return crop_mask
465
-
466
-
467
- def pixels_in_bounds(bounds: PixelBounds) -> int:
468
- return (bounds.x_max - bounds.x_min) * (bounds.y_max - bounds.y_min)
469
-
470
-
471
412
  def make_sharepic_base(time_series_list: list[pd.DataFrame], config: Config):
472
413
  all_time_series = pd.concat(time_series_list)
473
414
  tile_x = all_time_series["x"]
@@ -496,13 +437,11 @@ def make_sharepic_base(time_series_list: list[pd.DataFrame], config: Config):
496
437
  (tile_yz.max() + tile_yz.min()) / 2,
497
438
  )
498
439
 
499
- background = build_map_from_tiles_around_center(
500
- tile_xz_center,
501
- zoom,
502
- (target_width, target_height),
503
- (target_width, target_map_height),
504
- config,
440
+ tile_bounds = tile_bounds_around_center(
441
+ tile_xz_center, (target_width, target_height - footer_height), zoom
505
442
  )
443
+ tile_bounds.y2 += footer_height / OSM_TILE_SIZE
444
+ background = map_image_from_tile_bounds(tile_bounds, config)
506
445
 
507
446
  img = Image.fromarray((background * 255).astype("uint8"), "RGB")
508
447
  draw = ImageDraw.Draw(img, mode="RGBA")
@@ -10,24 +10,28 @@ from flask import Flask
10
10
  from flask import render_template
11
11
 
12
12
  from ..core.activities import ActivityRepository
13
+ from ..core.config import Config
14
+ from ..core.config import ConfigAccessor
13
15
  from ..explorer.tile_visits import TileVisitAccessor
14
16
  from .activity.blueprint import make_activity_blueprint
17
+ from .activity.controller import ActivityController
18
+ from .auth_blueprint import make_auth_blueprint
19
+ from .authenticator import Authenticator
15
20
  from .calendar.blueprint import make_calendar_blueprint
16
- from .eddington.blueprint import make_eddington_blueprint
21
+ from .calendar.controller import CalendarController
22
+ from .eddington_blueprint import make_eddington_blueprint
17
23
  from .entry_controller import EntryController
18
24
  from .equipment.blueprint import make_equipment_blueprint
25
+ from .equipment.controller import EquipmentController
19
26
  from .explorer.blueprint import make_explorer_blueprint
27
+ from .explorer.controller import ExplorerController
20
28
  from .heatmap.blueprint import make_heatmap_blueprint
21
- from .search.blueprint import make_search_blueprint
22
- from .square_planner.blueprint import make_square_planner_blueprint
23
- from .summary.blueprint import make_summary_blueprint
24
- from .tile.blueprint import make_tile_blueprint
29
+ from .search_blueprint import make_search_blueprint
30
+ from .settings.blueprint import make_settings_blueprint
31
+ from .square_planner_blueprint import make_square_planner_blueprint
32
+ from .summary_blueprint import make_summary_blueprint
33
+ from .tile_blueprint import make_tile_blueprint
25
34
  from .upload_blueprint import make_upload_blueprint
26
- from geo_activity_playground.core.config import Config
27
- from geo_activity_playground.core.config import ConfigAccessor
28
- from geo_activity_playground.webui.auth.blueprint import make_auth_blueprint
29
- from geo_activity_playground.webui.authenticator import Authenticator
30
- from geo_activity_playground.webui.settings.blueprint import make_settings_blueprint
31
35
 
32
36
 
33
37
  def route_start(app: Flask, repository: ActivityRepository, config: Config) -> None:
@@ -77,26 +81,32 @@ def web_ui_main(
77
81
 
78
82
  authenticator = Authenticator(config_accessor())
79
83
 
84
+ config = config_accessor()
85
+ activity_controller = ActivityController(repository, tile_visit_accessor, config)
86
+ calendar_controller = CalendarController(repository)
87
+ equipment_controller = EquipmentController(repository, config)
88
+ explorer_controller = ExplorerController(
89
+ repository, tile_visit_accessor, config_accessor
90
+ )
91
+
80
92
  route_start(app, repository, config_accessor())
81
93
 
82
94
  app.register_blueprint(make_auth_blueprint(authenticator), url_prefix="/auth")
83
-
84
95
  app.register_blueprint(
85
- make_activity_blueprint(
86
- repository, tile_visit_accessor, config_accessor(), authenticator
87
- ),
96
+ make_activity_blueprint(activity_controller, repository, authenticator),
88
97
  url_prefix="/activity",
89
98
  )
90
- app.register_blueprint(make_calendar_blueprint(repository), url_prefix="/calendar")
99
+ app.register_blueprint(
100
+ make_calendar_blueprint(calendar_controller), url_prefix="/calendar"
101
+ )
91
102
  app.register_blueprint(
92
103
  make_eddington_blueprint(repository), url_prefix="/eddington"
93
104
  )
94
105
  app.register_blueprint(
95
- make_equipment_blueprint(repository, config_accessor()), url_prefix="/equipment"
106
+ make_equipment_blueprint(equipment_controller), url_prefix="/equipment"
96
107
  )
97
108
  app.register_blueprint(
98
- make_explorer_blueprint(repository, tile_visit_accessor, config_accessor),
99
- url_prefix="/explorer",
109
+ make_explorer_blueprint(explorer_controller), url_prefix="/explorer"
100
110
  )
101
111
  app.register_blueprint(
102
112
  make_heatmap_blueprint(repository, tile_visit_accessor, config_accessor()),
@@ -107,7 +117,7 @@ def web_ui_main(
107
117
  url_prefix="/settings",
108
118
  )
109
119
  app.register_blueprint(
110
- make_square_planner_blueprint(repository, tile_visit_accessor),
120
+ make_square_planner_blueprint(tile_visit_accessor),
111
121
  url_prefix="/square-planner",
112
122
  )
113
123
  app.register_blueprint(
@@ -115,10 +125,10 @@ def web_ui_main(
115
125
  url_prefix="/search",
116
126
  )
117
127
  app.register_blueprint(
118
- make_summary_blueprint(repository, config_accessor()),
119
- url_prefix="/summary",
128
+ make_summary_blueprint(repository, config), url_prefix="/summary"
120
129
  )
121
- app.register_blueprint(make_tile_blueprint(config_accessor()), url_prefix="/tile")
130
+
131
+ app.register_blueprint(make_tile_blueprint(config), url_prefix="/tile")
122
132
  app.register_blueprint(
123
133
  make_upload_blueprint(
124
134
  repository, tile_visit_accessor, config_accessor(), authenticator
@@ -4,7 +4,7 @@ from flask import render_template
4
4
  from flask import request
5
5
  from flask import url_for
6
6
 
7
- from geo_activity_playground.webui.authenticator import Authenticator
7
+ from .authenticator import Authenticator
8
8
 
9
9
 
10
10
  def make_auth_blueprint(authenticator: Authenticator) -> Blueprint:
@@ -1,15 +1,12 @@
1
1
  from flask import Blueprint
2
2
  from flask import render_template
3
3
 
4
- from ...core.activities import ActivityRepository
5
- from .controller import CalendarController
4
+ from geo_activity_playground.webui.calendar.controller import CalendarController
6
5
 
7
6
 
8
- def make_calendar_blueprint(repository: ActivityRepository) -> Blueprint:
7
+ def make_calendar_blueprint(calendar_controller: CalendarController) -> Blueprint:
9
8
  blueprint = Blueprint("calendar", __name__, template_folder="templates")
10
9
 
11
- calendar_controller = CalendarController(repository)
12
-
13
10
  @blueprint.route("/")
14
11
  def index():
15
12
  return render_template(
@@ -1,17 +1,19 @@
1
1
  import altair as alt
2
2
  import numpy as np
3
3
  import pandas as pd
4
+ from flask import Blueprint
5
+ from flask import render_template
4
6
 
5
- from ...core.activities import ActivityRepository
7
+ from geo_activity_playground.core.activities import ActivityRepository
6
8
 
7
9
 
8
- class EddingtonController:
9
- def __init__(self, repository: ActivityRepository) -> None:
10
- self._repository = repository
10
+ def make_eddington_blueprint(repository: ActivityRepository) -> Blueprint:
11
+ blueprint = Blueprint("eddington", __name__, template_folder="templates")
11
12
 
12
- def render(self) -> dict:
13
- activities = self._repository.meta.loc[
14
- self._repository.meta["consider_for_achievements"]
13
+ @blueprint.route("/")
14
+ def index():
15
+ activities = repository.meta.loc[
16
+ repository.meta["consider_for_achievements"]
15
17
  ].copy()
16
18
  activities["day"] = [start.date() for start in activities["start"]]
17
19
 
@@ -67,11 +69,13 @@ class EddingtonController:
67
69
  .interactive()
68
70
  .to_json(format="vega")
69
71
  )
70
-
71
- return {
72
- "eddington_number": en,
73
- "logarithmic_plot": logarithmic_plot,
74
- "eddington_table": eddington.loc[
72
+ return render_template(
73
+ "eddington/index.html.j2",
74
+ eddington_number=en,
75
+ logarithmic_plot=logarithmic_plot,
76
+ eddington_table=eddington.loc[
75
77
  (eddington["distance_km"] > en) & (eddington["distance_km"] <= en + 10)
76
78
  ].to_dict(orient="records"),
77
- }
79
+ )
80
+
81
+ return blueprint
@@ -1,18 +1,12 @@
1
1
  from flask import Blueprint
2
2
  from flask import render_template
3
3
 
4
- from ...core.activities import ActivityRepository
5
- from .controller import EquipmentController
6
- from geo_activity_playground.core.config import Config
4
+ from geo_activity_playground.webui.equipment.controller import EquipmentController
7
5
 
8
6
 
9
- def make_equipment_blueprint(
10
- repository: ActivityRepository, config: Config
11
- ) -> Blueprint:
7
+ def make_equipment_blueprint(equipment_controller: EquipmentController) -> Blueprint:
12
8
  blueprint = Blueprint("equipment", __name__, template_folder="templates")
13
9
 
14
- equipment_controller = EquipmentController(repository, config)
15
-
16
10
  @blueprint.route("/")
17
11
  def index():
18
12
  return render_template(
@@ -4,20 +4,12 @@ from flask import render_template
4
4
  from flask import Response
5
5
  from flask import url_for
6
6
 
7
- from ...core.activities import ActivityRepository
8
- from ...explorer.tile_visits import TileVisitAccessor
9
- from .controller import ExplorerController
10
- from geo_activity_playground.core.config import ConfigAccessor
7
+ from geo_activity_playground.webui.explorer.controller import ExplorerController
11
8
 
12
9
 
13
10
  def make_explorer_blueprint(
14
- repository: ActivityRepository,
15
- tile_visit_accessor: TileVisitAccessor,
16
- config_accessor: ConfigAccessor,
11
+ explorer_controller: ExplorerController,
17
12
  ) -> Blueprint:
18
- explorer_controller = ExplorerController(
19
- repository, tile_visit_accessor, config_accessor
20
- )
21
13
  blueprint = Blueprint("explorer", __name__, template_folder="templates")
22
14
 
23
15
  @blueprint.route("/<zoom>")
@@ -1,12 +1,13 @@
1
+ import dateutil.parser
1
2
  from flask import Blueprint
2
3
  from flask import render_template
3
4
  from flask import request
4
5
  from flask import Response
5
6
 
6
- from ...core.activities import ActivityRepository
7
- from ...explorer.tile_visits import TileVisitAccessor
8
- from .heatmap_controller import HeatmapController
7
+ from geo_activity_playground.core.activities import ActivityRepository
9
8
  from geo_activity_playground.core.config import Config
9
+ from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
10
+ from geo_activity_playground.webui.heatmap.heatmap_controller import HeatmapController
10
11
 
11
12
 
12
13
  def make_heatmap_blueprint(
@@ -21,21 +22,46 @@ def make_heatmap_blueprint(
21
22
  def index():
22
23
  return render_template(
23
24
  "heatmap/index.html.j2",
24
- **heatmap_controller.render(request.args.getlist("kind"))
25
+ **heatmap_controller.render(
26
+ [int(k) for k in request.args.getlist("kind")],
27
+ request.args.get(
28
+ "date-start", type=dateutil.parser.parse, default=None
29
+ ),
30
+ request.args.get("date-end", type=dateutil.parser.parse, default=None),
31
+ )
25
32
  )
26
33
 
27
- @blueprint.route("/tile/<z>/<x>/<y>/<kinds>.png")
28
- def tile(x: str, y: str, z: str, kinds: str):
34
+ @blueprint.route("/tile/<int:z>/<int:x>/<int:y>.png")
35
+ def tile(x: int, y: int, z: int):
29
36
  return Response(
30
- heatmap_controller.render_tile(int(x), int(y), int(z), kinds.split(";")),
37
+ heatmap_controller.render_tile(
38
+ x,
39
+ y,
40
+ z,
41
+ [int(k) for k in request.args.getlist("kind")],
42
+ request.args.get(
43
+ "date-start", type=dateutil.parser.parse, default=None
44
+ ),
45
+ request.args.get("date-end", type=dateutil.parser.parse, default=None),
46
+ ),
31
47
  mimetype="image/png",
32
48
  )
33
49
 
34
- @blueprint.route("/download/<north>/<east>/<south>/<west>/<kinds>")
35
- def download(north: str, east: str, south: str, west: str, kinds: str):
50
+ @blueprint.route(
51
+ "/download/<float:north>/<float:east>/<float:south>/<float:west>/heatmap.png"
52
+ )
53
+ def download(north: float, east: float, south: float, west: float):
36
54
  return Response(
37
55
  heatmap_controller.download_heatmap(
38
- float(north), float(east), float(south), float(west), kinds.split(";")
56
+ north,
57
+ east,
58
+ south,
59
+ west,
60
+ [int(k) for k in request.args.getlist("kind")],
61
+ request.args.get(
62
+ "date-start", type=dateutil.parser.parse, default=None
63
+ ),
64
+ request.args.get("date-end", type=dateutil.parser.parse, default=None),
39
65
  ),
40
66
  mimetype="image/png",
41
67
  headers={"Content-disposition": 'attachment; filename="heatmap.png"'},