Skip to content

Commit

Permalink
Add Docker image builder
Browse files Browse the repository at this point in the history
  • Loading branch information
denisbondar committed Oct 7, 2020
1 parent bb1ded2 commit 3805dad
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 5 deletions.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__pycache__/
.git/
.idea/
cache/
venv/
.gitignore
docker-build.sh
docker-run.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,4 @@ dmypy.json
cython_debug/

cache/
docker-run.sh
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM python:3.8-alpine as base

COPY requirements.txt /

RUN apk update \
&& apk add --no-cache --virtual \
.build-deps \
gcc \
musl-dev \
curl \
linux-headers

RUN pip install -r requirements.txt

################################################################################

FROM python:3.8-alpine

RUN mkdir -p /app/cache
WORKDIR /app
COPY --from=base /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages
COPY . /app

VOLUME /app/cache

CMD ["python", "main.py"]
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# STRAVA Heatmap cache

Скрипт предназначен для кэширования тайлов тепловой карты Strava с целью использования
их в приложении OsmAnd в качестве оффлайн карты или в приложении JOSM также в качестве
слоя, помогающего при картографировании.

## Запуск в Docker контейнере

Для запуска в Docker-контейнере достаточно выполнить:

```bash
docker run --rm \
-v /var/strava-cache:/app/cache \
--env KEY_PAIR_ID=CloudFront-Key-Pair-Id_from_cookies \
--env SIGNATURE=CloudFront-Signature_from_cookies \
--env POLICY=CloudFront-Policy_from_cookies \
--name strava-heatmap-cache \
denisbondar/strava-heatmap-cache
```

В качестве значений для переменных окружения `KEY_PAIR_ID`, `SIGNATURE`, `POLICY`
необходимо указать соответствующие значения, полученные из cookie на сайте
strava.com (после аутентификации).

Если у вас нет учётной записи на strava.com, то вы сможете загрузить только тайлы
масштаба не более 11.

## Использование в качествео оффлайн карты OsmAnd

1. Скопируйте каталог с кэшем на ваше андроид устройство в каталог
`/Android/data/net.osmand/files/tiles`. Желательно чтобы имя каталога было не
`cache`, а какое нибудь более понятное, например, `Strava heatmap`.
Так как файлов может быть очень много, то лучше создайте архив на компьютере,
скопируйте его на смартфон и там разархивируйте.

2. Откройте приложение OsmAnd. Меню - Настройки карты - Карта наложения.
Выберите Strava heatmap и укажите степень прозрачности. Или же включите опцию
"Показывать регулировку прозрачности", чтобы управлять ею прямо с карты.

## Использование в качестве слоя в JOSM

1. В JOSM в меню Слои - Настройки слоёв... в нижней части окна в списке
"Выбранные" нажмите кнопку **+TMS** и введите в п.4 готовый URL следующего вида:
`tms[17]:file:///var/strava-cache/{zoom}/{x}/{y}.png.tile`. Путь к кэшу указан
такой же, как и volume в примере запуска с использованием Docker, но у вас он
может быть каким угодно. В п.5 введите название слоя, например, `Strava Heatmap`.
Нажмите **ОК** в окне добавления подложки и **ОК** в окне настрек.

2. В меню Слои выберите слой **Strava Heatmap** - новый слой будет добавлен и
тайлы будут отображены на карте.

3. Рекомендуется немного размыть слой, т.к. границы тепловой карты могут быть
слишком резкими. Нажмите в списке слоев на слой **Strava Heatmap**, затем внизу
нажмите кнопку **Изменить видимость выбранного слоя** и настройте максимально
комфортную резкость.

## Известные проблема и дорожная карта

На данный момент скрипт находится в стадии разработки, поэтому многие моменты в нем
могут показаться неочевидными или непонятными. Например:

1. На данный момент в скрипт жестко зашиты координаты Одесского региона.
Позже это будет вынесено в переменные окружения и в параметры командной строки,
если скрипт запускается не через Docker.

2. Остальные жестко зашитые параметры также будут вынесены.

3. Проблема с ограничением загрузки со стороны CloudFront. Сейчас она частично
решается при помощи семафора для асинхронного запуска и частично как ограничение
количества загружаемых тайлов за один раз. Но это всё равно не позволяет одним
запуском загрузить все тайлы во всех масштабах.

4. Планируется реализовать http-endpoint для загрузки тайлов по http из кэша
с возможностью наполнения кэша, если запрашиваемых тайлов в нем нет.

5. Планируется при прогреве кэша реализовать возможность перезагрузки тайлов,
если в кэше тайл устарел (старше ?? дней?)
14 changes: 14 additions & 0 deletions docker-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

if [ -z "$1" ]; then
echo "Use: $0 <version_number> [push]"
exit
fi

docker build --pull --file=Dockerfile --tag denisbondar/strava-heatmap-cache:"$1" --tag denisbondar/strava-heatmap-cache:latest . \
&& if [ "$2" == 'push' ]; then
echo "Push to Docker HUB"
docker login \
&& docker push denisbondar/strava-heatmap-cache:"$1" \
&& docker push denisbondar/strava-heatmap-cache:latest;
fi
36 changes: 31 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from collections import namedtuple
from random import choice
from typing import List
from time import time

import aiofiles
import aiohttp
Expand Down Expand Up @@ -163,7 +164,27 @@ async def download_tile(self, tile: Tile):
await self.cache.write(tile, content)
# todo тут бы надо отдавать контент на обработку чтобы узнать что конкретно за ошибка
elif response.status == 403:
raise PermissionError("[403] STRAVA Access denied. " + await response.text())
"""
При неверных данных аутентификации:
Неверный ключ:
<?xml version="1.0" encoding="UTF-8"?><Error><Code>InvalidKey</Code><Message>Unknown Key</Message></Error>
Неверная подпись:
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MalformedSignature</Code><Message>Could not unencode Signature</Message></Error>
Неверная политика:
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MalformedPolicy</Code><Message>Malformed Policy</Message></Error>
При достижении лимита приходит HTML-страница:
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
...
"""
raise PermissionError("[403] STRAVA Access denied.")


class CacheWarmer:
Expand All @@ -183,18 +204,23 @@ def warm_up(self,
tiles.append(tile)
if max_tiles and len(tiles) >= max_tiles:
break
print("Count tiles to load:", len(tiles))
self.strava_fetcher.fetch(tiles)


if __name__ == '__main__':
auth_data = CloudFrontAuth(os.getenv('KEY-PAIR-ID'),
# point_1 = (GeoPoint(float(x), float(y)) for x, y in os.getenv('AREA_APEX').split(','))
# point_2 = (GeoPoint(float(x), float(y)) for x, y in os.getenv('AREA_VERTEX').split(','))

auth_data = CloudFrontAuth(os.getenv('KEY_PAIR_ID'),
os.getenv('SIGNATURE'),
os.getenv('POLICY'))
cache = Cache(os.path.join(script_abs_dir, os.getenv('CACHE_DIR')))
cache = Cache(os.path.join(script_abs_dir, 'cache'))
strava_fetcher = StravaFetcher(auth_data, cache)
warmer = CacheWarmer(cache, strava_fetcher)

start_time = time()
warmer.warm_up(GeoPoint(46.90946, 30.19284),
GeoPoint(46.10655, 31.39070),
7, 15, max_tiles=1000)
zoom_min=7, zoom_max=17,
max_tiles=2000)
print("Spent in", round((time() - start_time), 2), "seconds.")

0 comments on commit 3805dad

Please sign in to comment.