geo-activity-playground 1.3.2__tar.gz → 1.4.0__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 (180) hide show
  1. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/PKG-INFO +1 -1
  2. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/__main__.py +8 -0
  3. geo_activity_playground-1.4.0/geo_activity_playground/core/photos.py +39 -0
  4. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/raster_map.py +6 -0
  5. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/explorer/tile_visits.py +64 -5
  6. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/strava_checkout.py +11 -2
  7. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/app.py +7 -0
  8. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/explorer_blueprint.py +34 -64
  9. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py +15 -4
  10. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/heatmap_blueprint.py +3 -10
  11. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/photo_blueprint.py +1 -26
  12. geo_activity_playground-1.4.0/geo_activity_playground/webui/static/leaflet/images/layers-2x.png +0 -0
  13. geo_activity_playground-1.4.0/geo_activity_playground/webui/static/leaflet/images/layers.png +0 -0
  14. geo_activity_playground-1.4.0/geo_activity_playground/webui/static/server-side-explorer.js +96 -0
  15. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/explorer/server-side.html.j2 +0 -8
  16. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/heatmap/index.html.j2 +6 -0
  17. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/page.html.j2 +4 -1
  18. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/pyproject.toml +1 -1
  19. geo_activity_playground-1.3.2/geo_activity_playground/webui/static/server-side-explorer.js +0 -60
  20. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/LICENSE +0 -0
  21. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/__init__.py +0 -0
  22. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/README +0 -0
  23. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/env.py +0 -0
  24. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/script.py.mako +0 -0
  25. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/0f02b92c4f94_add_tag_color.py +0 -0
  26. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/38882503dc7c_add_tags_to_activities.py +0 -0
  27. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/451e7836b53d_add_square_planner_bookmark.py +0 -0
  28. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/63d3b7f6f93c_initial_version.py +0 -0
  29. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/85fe0348e8a2_add_time_series_uuid_field.py +0 -0
  30. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/93cc82ad1b60_add_parametricplotspec.py +0 -0
  31. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/ab83b9d23127_add_upstream_id.py +0 -0
  32. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/b03491c593f6_add_crop_indices.py +0 -0
  33. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/da2cba03b71d_add_photos.py +0 -0
  34. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/dc8073871da7_add_plotspec_group_by.py +0 -0
  35. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/e02e27876deb_add_square_planner_bookmark_name.py +0 -0
  36. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/f2f50843be2d_make_all_fields_in_activity_nullable.py +0 -0
  37. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/alembic/versions/script.py.mako +0 -0
  38. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/__init__.py +0 -0
  39. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/activities.py +0 -0
  40. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/config.py +0 -0
  41. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/coordinates.py +0 -0
  42. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/copernicus_dem.py +0 -0
  43. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/datamodel.py +0 -0
  44. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/enrichment.py +0 -0
  45. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/export.py +0 -0
  46. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/heart_rate.py +0 -0
  47. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/meta_search.py +0 -0
  48. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/missing_values.py +0 -0
  49. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/parametric_plot.py +0 -0
  50. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/paths.py +0 -0
  51. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/privacy_zones.py +0 -0
  52. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/similarity.py +0 -0
  53. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/summary_stats.py +0 -0
  54. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/tasks.py +0 -0
  55. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_datamodel.py +0 -0
  56. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_meta_search.py +0 -0
  57. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_missing_values.py +0 -0
  58. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_pandas_timezone.py +0 -0
  59. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_summary_stats.py +0 -0
  60. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_tiles.py +0 -0
  61. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_time_zone_from_location.py +0 -0
  62. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_time_zone_import.py +0 -0
  63. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/test_timezone_sqlalchemy.py +0 -0
  64. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/tiles.py +0 -0
  65. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/core/time_conversion.py +0 -0
  66. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/explorer/__init__.py +0 -0
  67. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/explorer/grid_file.py +0 -0
  68. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/explorer/video.py +0 -0
  69. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/heatmap_video.py +0 -0
  70. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/__init__.py +0 -0
  71. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/activity_parsers.py +0 -0
  72. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/csv_parser.py +0 -0
  73. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/directory.py +0 -0
  74. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/strava_api.py +0 -0
  75. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/test_csv_parser.py +0 -0
  76. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/test_directory.py +0 -0
  77. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/importers/test_strava_api.py +0 -0
  78. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/__init__.py +0 -0
  79. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/authenticator.py +0 -0
  80. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/__init__.py +0 -0
  81. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/activity_blueprint.py +0 -0
  82. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/auth_blueprint.py +0 -0
  83. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +0 -0
  84. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/calendar_blueprint.py +0 -0
  85. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/eddington_blueprints.py +0 -0
  86. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/entry_views.py +0 -0
  87. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/equipment_blueprint.py +0 -0
  88. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/export_blueprint.py +0 -0
  89. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/plot_builder_blueprint.py +0 -0
  90. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/search_blueprint.py +0 -0
  91. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/settings_blueprint.py +0 -0
  92. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/square_planner_blueprint.py +0 -0
  93. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/summary_blueprint.py +0 -0
  94. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/tile_blueprint.py +0 -0
  95. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/time_zone_fixer_blueprint.py +0 -0
  96. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/blueprints/upload_blueprint.py +0 -0
  97. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/columns.py +0 -0
  98. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/flasher.py +0 -0
  99. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/plot_util.py +0 -0
  100. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/search_util.py +0 -0
  101. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/bootstrap/bootstrap-dark-mode.js +0 -0
  102. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/bootstrap/bootstrap.bundle.min.js +0 -0
  103. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/bootstrap/bootstrap.min.css +0 -0
  104. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/android-chrome-192x192.png +0 -0
  105. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/android-chrome-512x512.png +0 -0
  106. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/apple-touch-icon.png +0 -0
  107. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/browserconfig.xml +0 -0
  108. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/favicon-16x16.png +0 -0
  109. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/favicon-32x32.png +0 -0
  110. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/favicon-48x48.png +0 -0
  111. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/favicon.ico +0 -0
  112. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/favicon.svg +0 -0
  113. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/mstile-150x150.png +0 -0
  114. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/site.webmanifest +0 -0
  115. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/web-app-manifest-192x192.png +0 -0
  116. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/favicons/web-app-manifest-512x512.png +0 -0
  117. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/images/layers-2x.png +0 -0
  118. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/images/layers.png +0 -0
  119. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/images/marker-icon-2x.png +0 -0
  120. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/images/marker-icon.png +0 -0
  121. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/images/marker-shadow.png +0 -0
  122. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/Leaflet.fullscreen.min.js +0 -0
  123. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/MarkerCluster.Default.css +0 -0
  124. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/MarkerCluster.css +0 -0
  125. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/fullscreen.png +0 -0
  126. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/fullscreen@2x.png +0 -0
  127. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/leaflet.css +0 -0
  128. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/leaflet.fullscreen.css +0 -0
  129. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/leaflet.js +0 -0
  130. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/leaflet/leaflet.markercluster.js +0 -0
  131. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/table-sort.min.js +0 -0
  132. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/vega/vega-embed@6.js +0 -0
  133. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/vega/vega-lite@4.js +0 -0
  134. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/static/vega/vega@5.js +0 -0
  135. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/day.html.j2 +0 -0
  136. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/edit.html.j2 +0 -0
  137. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/lines.html.j2 +0 -0
  138. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/name.html.j2 +0 -0
  139. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/show.html.j2 +0 -0
  140. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/activity/trim.html.j2 +0 -0
  141. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/auth/index.html.j2 +0 -0
  142. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/bubble_chart/index.html.j2 +0 -0
  143. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/calendar/index.html.j2 +0 -0
  144. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/calendar/month.html.j2 +0 -0
  145. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/eddington/distance.html.j2 +0 -0
  146. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/eddington/elevation_gain.html.j2 +0 -0
  147. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/elevation_eddington/index.html.j2 +0 -0
  148. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/equipment/index.html.j2 +0 -0
  149. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/export/index.html.j2 +0 -0
  150. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/hall_of_fame/index.html.j2 +0 -0
  151. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/home.html.j2 +0 -0
  152. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/photo/map.html.j2 +0 -0
  153. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/photo/new.html.j2 +0 -0
  154. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/plot-macros.html.j2 +0 -0
  155. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/plot_builder/edit.html.j2 +0 -0
  156. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/plot_builder/import-spec.html.j2 +0 -0
  157. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/plot_builder/index.html.j2 +0 -0
  158. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/search/index.html.j2 +0 -0
  159. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/search_form.html.j2 +0 -0
  160. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/admin-password.html.j2 +0 -0
  161. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/color-schemes.html.j2 +0 -0
  162. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/heart-rate.html.j2 +0 -0
  163. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/index.html.j2 +0 -0
  164. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/manage-equipments.html.j2 +0 -0
  165. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/manage-kinds.html.j2 +0 -0
  166. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/metadata-extraction.html.j2 +0 -0
  167. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/privacy-zones.html.j2 +0 -0
  168. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/segmentation.html.j2 +0 -0
  169. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/sharepic.html.j2 +0 -0
  170. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/strava.html.j2 +0 -0
  171. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/tags-edit.html.j2 +0 -0
  172. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/tags-list.html.j2 +0 -0
  173. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/tags-new.html.j2 +0 -0
  174. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/settings/tile-source.html.j2 +0 -0
  175. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/square_planner/index.html.j2 +0 -0
  176. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/summary/index.html.j2 +0 -0
  177. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/summary/vega-chart.html.j2 +0 -0
  178. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/time_zone_fixer/index.html.j2 +0 -0
  179. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/upload/index.html.j2 +0 -0
  180. {geo_activity_playground-1.3.2 → geo_activity_playground-1.4.0}/geo_activity_playground/webui/templates/upload/reload.html.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 1.3.2
3
+ Version: 1.4.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -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=2
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=2,
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=2,
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
- activity, time_series = read_activity(activity_file)
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
- return blend_color(grayscale, np.array([[[55, 126, 184]]]) / 256, 0.3)
75
+ color = np.array([[[55, 126, 184, 70]]]) / 256
75
76
  elif tile_xy in self.evolution_state.memberships:
76
- return blend_color(grayscale, np.array([[[77, 175, 74]]]) / 256, 0.3)
77
+ color = np.array([[[77, 175, 74, 70]]]) / 256
77
78
  elif tile_xy in self.tile_visits:
78
- return blend_color(grayscale, 0.0, 0.3)
79
+ color = np.array([[[0, 0, 0, 70]]]) / 256
79
80
  else:
80
- return grayscale
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
- return blend_color(grayscale, np.array([[self._cmap(d)[:3]]]), 0.3)
103
+ color = np.array([[self._cmap(d)[:3] + (0.5,)]])
102
104
  elif tile_xy in self.tile_visits:
103
- return blend_color(grayscale, 0.0, 0.3)
105
+ color = np.array([[[0, 0, 0, 70]]]) / 256
104
106
  else:
105
- return grayscale
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
- last_age_days = (today - relevant_time.date()).days
124
- color = cmap(max(1 - last_age_days / (2 * 365), 0.0))
125
- return blend_color(grayscale, np.array([[color[:3]]]), 0.3)
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
- return grayscale
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
- return blend_color(grayscale, np.array([[color[:3]]]), 0.3)
148
+ color = np.array([[color[:3] + (0.5,)]])
142
149
  else:
143
- return grayscale
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":
@@ -311,9 +320,7 @@ def make_explorer_blueprint(
311
320
  <= tile_y
312
321
  < evolution_state.square_y + evolution_state.max_square_size
313
322
  ):
314
- result[:, 0:square_line_width] = blend_color(
315
- result[:, 0:square_line_width], square_color, 0.5
316
- )
323
+ result[:, 0:square_line_width] = square_color
317
324
  if (
318
325
  y % factor == 0
319
326
  and tile_y == evolution_state.square_y
@@ -321,10 +328,7 @@ def make_explorer_blueprint(
321
328
  <= tile_x
322
329
  < evolution_state.square_x + evolution_state.max_square_size
323
330
  ):
324
- result[0:square_line_width, :] = blend_color(
325
- result[0:square_line_width, :], square_color, 0.5
326
- )
327
-
331
+ result[0:square_line_width, :] = square_color
328
332
  if (
329
333
  (x + 1) % factor == 0
330
334
  and (x + 1) // factor
@@ -333,9 +337,7 @@ def make_explorer_blueprint(
333
337
  <= tile_y
334
338
  < evolution_state.square_y + evolution_state.max_square_size
335
339
  ):
336
- result[:, -square_line_width:] = blend_color(
337
- result[:, -square_line_width:], square_color, 0.5
338
- )
340
+ result[:, -square_line_width:] = square_color
339
341
  if (
340
342
  (y + 1) % factor == 0
341
343
  and (y + 1) // factor
@@ -344,9 +346,7 @@ def make_explorer_blueprint(
344
346
  <= tile_x
345
347
  < evolution_state.square_x + evolution_state.max_square_size
346
348
  ):
347
- result[-square_line_width:, :] = blend_color(
348
- result[-square_line_width:, :], square_color, 0.5
349
- )
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
- ] = blend_color(
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
- ] = blend_color(
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
- ] = blend_color(
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
- ] = blend_color(
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
- i = meta[variable].idxmax()
77
- value = meta.loc[i, variable]
78
- format_applied = format_str.format(value)
79
- nominations[i].append(f"{title}{title_suffix}: {format_applied}")
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[data_color == cmap(0.0)] = 0.0 # remove background color
225
-
226
- map_tile = np.array(get_tile(z, x, y, config.map_tile_url)) / 255
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: