tiles chapter

master
gman 1 year ago
parent c4f3941823
commit be2e07aa93

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

@ -25,7 +25,7 @@ import { Card, FileTree, LinkCard, TabItem, Tabs } from '@astrojs/starlight/comp
Создадим папку с заготовкой для нашей карты.
Добавим туда папку `backend`. Загрузим [отсюда](https://github.com/gtitov/flask-maplibre-map/raw/refs/heads/main/backend/cities_index.sqlite) и положим туда базу данных. И создадим в этой папке файл с нашим бэкендом `app.py`.
Добавим туда папку `backend`. Загрузим [отсюда](https://github.com/gtitov/flask-maplibre-map/raw/refs/heads/main/backend/cities_index.sqlite) базу данных и положим в созданную папку `backend`. Cоздадим в этой папке файл с нашим бэкендом `app.py`.
<FileTree>
- backend/
@ -145,7 +145,7 @@ def city_by_id(id):
json.dumps(dict(city), ensure_ascii=False),
mimetype="application/json",
)
# print("--- %s seconds ---" % (time.time() - start_time))
print("--- %s seconds ---" % (time.time() - start_time))
return r
```
@ -173,15 +173,11 @@ map.on("load", () => {
'circle-stroke-color': '#FFFFFF',
// SELECT MIN(total_points), MAX(total_points) FROM cities
'circle-color': [
'interpolate',
['linear'],
'interpolate', ['linear'],
['get', 'total_points'],
50,
'#d7191c',
150,
'#ffffbf',
250,
'#1a9641'
50, '#d7191c',
150, '#ffffbf',
250, '#1a9641'
],
'circle-opacity': 0.8,
// SELECT DISTINCT group_name FROM cities
@ -244,7 +240,7 @@ def city_by_id(id):
<div id='map'></div>
<dialog id="city-details-modal" onmousedown="this.close()"></dialog>
+ <div>
+ <p class="text-center">Год</p>
+ <p>Год</p>
+ <select id="year-selector">
+ <option value="2020" selected>2020</option>
+ <option value="2019">2019</option>

@ -1,6 +1,5 @@
---
title: Тайлы
draft: true
---
import { Card, LinkCard } from '@astrojs/starlight/components';
@ -18,7 +17,7 @@ import { Card, LinkCard } from '@astrojs/starlight/components';
Данные можно поделить на кусочки и передавать пользователю только *нужные кусочки* с *нужной детальностью*. Данные можно поделить на кусочки по-разному. Пространственные данные ожидаемо удобно делить на географические кусочки — тайлы. *Нужные кусочки* -- те, что попадают на экран. *Нужная детальность* -- та, что соответствует текущему масштабу карты.
Стандартная система тайлов делит планету на квадраты X/Y для каждого уровня зума Z. Каждый тайл имеет индекс Z/X/Y. По этому индексу и выполняются запросы тайлов, то есть формируется API сервиса векторных тайлов. Когда запрос выполняется, вызывается серверная функция, формирующая тайл, или возвращается заранее рассчитанный (кэшированный) тайл.
Стандартная система тайлов делит планету на квадраты X/Y для каждого уровня зума Z. Каждый тайл имеет индекс Z/X/Y. По этому индексу и выполняются запросы тайлов. Так формируется API сервиса векторных тайлов. Когда запрос выполняется, вызывается серверная функция, формирующая тайл, или возвращается заранее рассчитанный (кэшированный) тайл.
![zxy](../../../assets/zxy.png)
*[By AsPJT](https://commons.wikimedia.org/w/index.php?curid=149301346)*
@ -31,7 +30,7 @@ import { Card, LinkCard } from '@astrojs/starlight/components';
## Использование тайлов
Познакомимся к возможностями практического применения тайлов. Для этого загрузим наборы пространственных данных в базу пространственных данных. Подключим к ней сервер векторных тайлов. Получим векторные тайлы на клиентской стороне веб-приложения средствами картографической библиотеки.
Познакомимся c возможностями практического применения тайлов. Для этого загрузим наборы пространственных данных в базу пространственных данных. Подключим к ней сервер векторных тайлов. Получим векторные тайлы на клиентской стороне веб-приложения средствами картографической библиотеки.
### База пространственных данных
@ -39,23 +38,37 @@ import { Card, LinkCard } from '@astrojs/starlight/components';
#### Установка
Дистрибутив для сервера баз данных можно загрузим [здесь](https://www.postgresql.org/download/). При установке следует обратить внимание на порт, который будет занимать сервер баз данных, и пароль для пользователя `postgres`.
Дистрибутив для сервера баз данных загрузим [здесь](https://www.postgresql.org/download/). При установке следует обратить внимание на порт, который будет занимать сервер баз данных, и пароль для пользователя `postgres`.
> Сервер баз данных запускается локально. Доступ к локальному серверу баз данных осуществляется по заданному при установке порту -- обычно 5432. Если установщик предлагает другой порт, возможно, что у вас уже установлен сервер баз данных, который этот порт занимает.
После установки сервера баз данных Postgres можно установить расширение для работы с пространственными данными PostGIS. Дистрибутив доступен [тут](https://postgis.net/documentation/getting_started/#installing-postgis).
> Наблюдаются проблемы с установкой PostGIS на Windows через StackBuilder. Рабочим вариантом является самостоятельная загрузка дистрибутива PostGIS [отсюда](https://download.osgeo.org/postgis/windows/). Найдите папку с версией Postgres, которую установили. Например, для Postgres 17 нужна папка `pg17/`, в папке находится дистрибутив `pg17/postgis-bundle-pg17x64-setup-3.5.0-2.exe`.
> На Windows наблюдаются проблемы с установкой PostGIS через StackBuilder. Рабочим вариантом является самостоятельная загрузка дистрибутива PostGIS [отсюда](https://download.osgeo.org/postgis/windows/). Найдите папку с версией Postgres, которую установили. Например, для Postgres 17 нужна папка `pg17/`, в папке находится дистрибутив `pg17/postgis-bundle-pg17x64-setup-3.5.0-2.exe`.
#### Создание базы данных
Вместе с сервером базы данных устанавливается графический интерфейс для работы с базами данных PgAdmin 4.
![alt text](../../../assets/image.png)
> При необходимости, его можно установить [отдельно](https://www.pgadmin.org/download/).
Через этот интерфейс мы создадим базу данных на нашем локальном сервере.
И добавим к созданной базе расширение PostGIS.
![alt text](../../../assets/image-1.png)
Назовём базу данных `oikonyms`. Остальные параметры оставим по умолчанию.
![alt text](../../../assets/image-2.png)
И добавим к созданной базе расширение `PostGIS`.
![alt text](../../../assets/image-3.png)
![alt text](../../../assets/image-4.png)
База пространственных данных создана. Пора её наполнить!
#### Загрузка данных
@ -65,21 +78,53 @@ import { Card, LinkCard } from '@astrojs/starlight/components';
Сделаем это через [QGIS](https://www.qgis.org/download/).
![alt text](../../../assets/image-5.png)
Добавим слои на карту.
![alt text](../../../assets/image-6.png)
Выполним подключение к базе данных.
Перенесём слои с карты в базу данных.
![alt text](../../../assets/image-7.png)
Назовём подключение `oikonyms-database`. Наша база размещена на локальном сервере `localhost`, занимает порт `5432`, а называется `oikonyms`. Используем базовую аутентификацию (Authentication - Basic), где укажем логин и пароль.
![alt text](../../../assets/image-8.png)
Увидим нашу базу данных в списке подключений. Словом `public` обозначена схема базы данных, которую мы будем заполнять. Она создаётся для базы данных по умолчанию.
![alt text](../../../assets/image-9.png)
Перенесём слои с карты в базу данных. Это можно сделать прямо из списка слоёв.
![alt text](../../../assets/image-10.png)
Для проверки можно перенести наборы данных, загруженные в базу, на карту и посмотреть, как они выглядят.
### Векторные тайлы
Подготовим векторные тайлы на основе данных, загруженных в базу.
Мы не будем обращаться к базе данных напрямую. Мы добавим прослойку, которая будет формировать векторные тайлы на основе загруженных в базу данных.
#### Сервер векторных тайлов
Для подготовки векторных тайлов используем сервер векторных тайлов Martin. Загрузим это приложение [отсюда](https://github.com/maplibre/martin/releases/tag/v0.14.2).
Распакуем архив и запустим сервер векторных тайлов, указав подключение к локальной базе данных, куда мы загрузили ойконимы.
Распакуем архив и запустим сервер векторных тайлов, указав подключение к локальной базе данных, куда мы загрузили ойконимы. Чтобы это сделать, надо запустить терминал в папке, где распакован исполняемый файл Martin, например, `martin.exe`. И выполнить команду, которая в общем виде будет выглядеть так.
```sh
martin postgresql://postgres:password@localhost:5432/oikonyms
```
А в Windows приобетёт такой вид.
![alt text](../../../assets/image-11.png)
После успешного выполнения увидим это.
![alt text](../../../assets/image-12.png)
На последней строчке увидим, что каталог слоёв доступен по адресу `http://0.0.0.0:3000/catalog`, то есть по IP адресам компьютера. Мы воспользуемся нашим любимым `localhost` (другие обычно и недоступны).
Перейдём по адресу `localhost:3000/catalog`, чтобы увидеть доступные наборы векторных тайлов. Их должно быть два по количеству пространственных таблиц в базе данных.
@ -91,7 +136,11 @@ import { Card, LinkCard } from '@astrojs/starlight/components';
Остаётся принять эти векторные тайлы на карте. Заготовку для карты формируем как обычно.
Добавляем источник пространственных данных и картографический слой. При добавлении источника пространственных данных указываем тип `vector`, а не `geojson`, как в прошлых картах. При добавлении слоя указываем `source-layer` -- векторный тайл может содержать несколько слоёв. В нашем случае слой только один, посмотреть мы на него можем в TileJSON описании `localhost:3000/grid`, где идентификаторы слоёв указываются в обязательном списке `vector_layers`.
Добавляем источник пространственных данных и картографический слой. При добавлении источника пространственных данных указываем тип `vector`, а не `geojson`, как в прошлых картах.
При добавлении слоя указываем `source-layer` -- векторный тайл может содержать несколько слоёв. В нашем случае слой только один, посмотреть мы на него можем в TileJSON описании `localhost:3000/grid`, где идентификаторы слоёв указываются в обязательном списке `vector_layers`.
![alt text](../../../assets/image-13.png)
```js title=main.js {2, 8}
map.addSource("grid", {
@ -107,7 +156,9 @@ map.addLayer({
})
```
При добавлении источника мы можем указать не только `url` TileJSON описания, но и `tiles` -- список адресов, по которому можно выполнять запросы к тайлам. Если мы указываем `url`, MapLibre самостоятельно находит этот список в TileJSON описании.
При добавлении источника вместо `url` TileJSON-файла мы можем указать `tiles` -- список адресов, по которому можно выполнять запросы к тайлам. Если мы указываем `url`, MapLibre самостоятельно находит этот список в TileJSON описании.
![alt text](../../../assets/image-14.png)
```js title=main.js {3}
map.addSource("oikonyms", {
@ -125,8 +176,12 @@ map.addLayer({
### Веб-карта
Подключили источники данных. Теперь пора заняться визуализацией.
#### Оформление слоёв
Слой с сеткой шестиугольников раскрасим интерполяцией цвета по полю численности населения в ячейке.
```js title=main.js
map.addLayer({
id: "grid-layer",
@ -135,22 +190,18 @@ map.addLayer({
type: "fill",
paint: {
"fill-color": [
"interpolate",
["linear"],
"interpolate", ["linear"],
['to-number', ["get", "sum_pop"]],
0,
"#440154",
100,
"#39568c",
1000,
'#1f968b',
10000,
'#fde725'
0, "#440154",
100, "#39568c",
1000, '#1f968b',
10000, '#fde725'
]
}
})
```
Слой с точками населённых пунктов покажем пунсонами с обводкой.
```js title=main.js
map.addLayer({
@ -170,6 +221,10 @@ map.addLayer({
#### Подсветка при наведении
Мы уже видели изменение курсора при наведении на объект. Ещё одним вариантом является подсветка объекта при наведении курсора. Воспользуемся одним из способов реализации обводки.
Назначим поле `id` из свойств слоя идентификатором объектов слоя.
```diff title=main.js
map.addSource("grid", {
type: "vector",
@ -178,11 +233,15 @@ map.addSource("grid", {
})
```
Присвоим объектам слоя состояние `hover`, которое будет становиться для объекта`true`, когда курсор попадает на объект, и `false`, когда курсор переходит на другой объект или покидает слой.
```js title=main.js
let hoveredFeatureId = null;
map.on("mousemove", "grid-layer", (e) => {
if (hoveredFeatureId !== null) {
// последнему назначенному объекту
// присваиваем состояние `false`
map.setFeatureState(
{
source: "grid",
@ -192,7 +251,9 @@ map.on("mousemove", "grid-layer", (e) => {
{ hover: false }
)
}
// назначаем текущий объект
hoveredFeatureId = e.features[0].id
// текущему объекту присваиваем состояние `true`
map.setFeatureState(
{
source: "grid",
@ -203,7 +264,10 @@ map.on("mousemove", "grid-layer", (e) => {
)
})
// когда курсор покидает слой
map.on("mouseleave", "grid-layer", () => {
// последнему назначенному объекту
// присваиваем состояние `false`
map.setFeatureState(
{
source: "grid",
@ -214,7 +278,7 @@ map.on("mouseleave", "grid-layer", () => {
)
})
```
На основе состояния объекта делаем обводку.
```diff title=main.js
map.addLayer({
@ -248,6 +312,8 @@ map.addLayer({
#### Подлёт при клике
Действием при клике на ячейку сетки будет подлёт к точке клика на 10 уровень зума. Чтобы подчеркнуть интерактивность слоя, к обводке добавим и изменение курсора.
```js title=main.js
map.on("click", "grid-layer", (e) => {
map.flyTo({
@ -267,6 +333,8 @@ map.on('mouseleave', 'grid-layer', () => {
#### Попап при наведении
При наведении на пунсоны ойконимов выведем их названия в попап.
```js title=main.js
const popup = new maplibregl.Popup({
closeButton: false,
@ -287,6 +355,8 @@ map.on('mouseleave', 'oikonyms-layer', () => {
#### Фиксированный охват карты
Запретим перемещаться по карте за пределы нашей области интереса.
```diff title=main.js
const map = new maplibregl.Map({
container: "map",
@ -301,6 +371,10 @@ const map = new maplibregl.Map({
#### Интерактивная фильтрация
При использовании векторных тайлов можно организовать интерактивную фильтрацию.
Для этого добавим элемент, в который пользователь будет вводить граничное значение для численности населения.
```diff title=index.html
<body>
<div id="map"></div>
@ -321,6 +395,8 @@ const map = new maplibregl.Map({
}
```
И запрограммируем реакцию карты на ввод значений.
```js title=main.js
document.getElementById("filter").addEventListener("input", (e) => {
filterValue = parseInt(e.target.value)
@ -329,7 +405,7 @@ document.getElementById("filter").addEventListener("input", (e) => {
```
### Растровые тайлы
{/* ### Растровые тайлы
Загрузим файл с каналом Ландсата на Москву
@ -361,5 +437,10 @@ document.getElementById("filter").addEventListener("input", (e) => {
## Упраженения
1. Подсветка при клике
1. Подсветка при клике */}
## Чтение
1. TileJSON 3.0.0 / TileJSON [ссылка](https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/README.md)
1. Vector tiles standards / Mapbox [ссылка](https://docs.mapbox.com/data/tilesets/guides/vector-tiles-standards/)

Loading…
Cancel
Save