Skip to content

Commit

Permalink
Add WSGI HTTP Server
Browse files Browse the repository at this point in the history
  • Loading branch information
denisbondar committed Oct 11, 2020
1 parent 147b9a3 commit 755a9fb
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 18 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ 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 --from=base /usr/local/bin/uwsgi /usr/local/bin/uwsgi
COPY . /app

VOLUME /app/cache
VOLUME /var/run/strava.sock
EXPOSE 9000

CMD ["python", "main.py"]
CMD ["uwsgi", "--ini", "wsgi.ini"]
121 changes: 109 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,106 @@

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

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

Функция построения (прогрева) кэша проверяет наличие тайлов в кэше в заданной
области географических координат и в заданном диапазоне масштабов. Если тайл
отсутствует - он загружается с CDN-серверов Strava и сохраняется в кэше.

Для запуска построителя кэша достаточно выполнить команду `python main.py`
в Docker-контейнере следующим образом:

```bash
docker run --rm \
-v /var/strava-cache:/app/cache \
--volume /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
denisbondar/strava-heatmap-cache \
python main.py
```

После того, как построитель кэша отработает - работа контейнера будет завершена.

**Стоит учитывать**, что одна итерация построителя кэша ограничена 4000 тайлами.
Это связано с лимитом на CDN CloudFront. Прежде чем запускать построитель для
следующей партии тайлов, следует подождать минут 10, иначе CDN заблокирует доступ
для вашей учетной записи на какое-то время.

#### Значения переменных в приведенных примерах

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

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

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

Для запуска wsgi-модуля, принимающего http-запросы вида `http://127.0.0.1:9000/z/x/y.png`, запустите
контейнер *без указания команды* следующим образом:

```bash
docker run --rm \
--volume /var/strava-cache:/app/cache \
--publish 127.0.0.1:9000:9000 \
--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
```

Контейнер с wsgi-модулем будет работать постоянно. В конфигурации указан порт 9000,
на котором принимаются http-подключения. Вы можете изменить его на более подходящий
для вас. Например: `--publish 127.0.0.1:80:9000` будет принимать подключение на 80 порту.

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

#### Добавление в конфигурацию стека GNINX сервера

Пример конфигурации nginx для wsgi (файл /etc/nginx/conf.d/strava.conf):
```
upstream strava_heatmap {
server 127.0.0.1:9000;
}
server {
listen 80;
server_name <доменное_имя_вашего_сервера>;
location / {
include uwsgi_params;
uwsgi_pass strava_heatmap;
}
}
```

## Использование кэша в различных приложениях

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

1. Создайте онлайн карту в OsmAnd. Назовите ее, например, **Strava Heatmap**.
Укажите диапазон масштаба 8..16. Можно указать URL бесплатных тайлов - он
будет использоваться, если OsmAnd не найдет тайлов в кэше.
`https://heatmap-external-a.strava.com/tiles/ride/bluered/{0}/{1}/{2}.png`.
[Подробнее на сайте OsmAnd](https://osmand.net/features/online-maps-plugin).

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

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

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

1. В JOSM в меню Слои - Настройки слоёв... в нижней части окна в списке
"Выбранные" нажмите кнопку **+TMS** и введите в п.4 готовый URL следующего вида:
Expand All @@ -52,7 +120,36 @@ https://strava.com (после аутентификации).
3. Рекомендуется немного размыть слой, т.к. границы тепловой карты могут быть
слишком резкими. Нажмите в списке слоев на слой **Strava Heatmap**, затем внизу
нажмите кнопку **Изменить видимость выбранного слоя** и настройте максимально
комфортную резкость.
комфортную резкость и другие параметры отобраения по необходимости.

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

[BRouter](http://brouter.de/) - довольно интересный проект для веломаршрутизации,
позволяющий тонко настраивать параметры и пожелания велосипедиста, которые
учитываются при построении маршрутов. Информация о дорогах, их покрытии и
качестве берется из OSM, так что актуализируя информацию о дорогах в своем
регионе OSM вы косвенно улучшается и работу маршрутизатора.

Еще одной интересной особенностью данного проекта является возможность добавления
своих собственных Слоёв (Layers) и Наложений (Overlays).

Для использования этой возможности, вам необходимо запустить wsgi-модуль.

1. Перейдите в [BRouter-Web](http://brouter.de/brouter-web/)

2. Справа на панели в списке слоёв (Layers) нажмите кнопку Custom layers.

3. В поле Custom layer name укажите, например, Strava Heatmap, а в поле
Custom layer URL укажите `http://127.0.0.1:9000/{z}/{x}/{y}.png`, если вы используете
настройки, указанные по умолчанию в этой инструкции. Иначе номер порта или адрес
могут быть другими.

4. Нажмите **Add overlay**.

В списке слоёв появится ваш новый слой наложения. Включите его при помощи чекбокса.
Вы также можете управлять прозрачностью этого слоя.
Существует проблема. Если масштаб карты более чем 16, то слой исчезает, т.к.
нет тайлов более крупного масштаба. Это особенность BRouter.

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

Expand Down
11 changes: 6 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
CloudFrontAuth = namedtuple("CloudFrontAuth", "key_pair_id signature policy")
GeoPoint = namedtuple("GeoPoint", "latitude longitude")
script_abs_dir = os.path.abspath(os.path.dirname(__file__))
cache_dir = os.path.join(script_abs_dir, 'cache')

auth_data = CloudFrontAuth(os.getenv('KEY_PAIR_ID'),
os.getenv('SIGNATURE'),
os.getenv('POLICY'))


class Tile:
Expand Down Expand Up @@ -208,11 +213,7 @@ def warm_up(self,
if __name__ == '__main__':
# 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, 'cache'))
cache = Cache(cache_dir)
strava_fetcher = StravaFetcher(auth_data, cache)
warmer = CacheWarmer(cache, strava_fetcher)

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
uwsgi
flask
aiohttp
aiofiles
14 changes: 14 additions & 0 deletions wsgi.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[uwsgi]
module = wsgi:app
processes = 4
master = true

;; HTTP socket
http-socket = :9000

;; For file socket
socket = /var/run/strava.sock
chmod-socket = 660
vacuum = true

die-on-term = true
30 changes: 30 additions & 0 deletions wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from flask import Flask, make_response, Response
from main import Cache, Tile, StravaFetcher, cache_dir, auth_data


app = Flask(__name__)
cache = Cache(cache_dir)
fetcher = StravaFetcher(auth_data, cache)


@app.route('/<int:z>/<int:x>/<int:y>.png')
def get_tile(x, y, z):
if z < 7 or z > 16:
return Response(status=404)

tile = Tile(x, y, z)
if not cache.tile_already_in_cache(tile):
fetcher.fetch([tile])
content = cache.read(tile)

response = make_response(content)
response.headers.set('Content-Type', 'image/png')
response.headers.set(
'Content-Disposition', 'inline', filename='%s.png' % y)
response.headers.set('Content-Length', len(content))
response.headers.set('Cache-Control', 'max-age=%d' % (60*60*24*30))
return response


if __name__ == '__main__':
app.run()

0 comments on commit 755a9fb

Please sign in to comment.