corrections

master
gman 6 months ago
parent b0ea1c5794
commit c94eea3cf1

@ -448,7 +448,9 @@ map.on('load', () => {
2. Выведите в попап один из атрибутов стран 2. Выведите в попап один из атрибутов стран
3. Добавьте слой с границами озёр, установите им толщину в 2 пикселя 3. Добавьте слой с границами озёр, установите им толщину в 2 пикселя
4. Замените курсор на перекрестие (`crosshair`) при расположении поверх стран 4. Замените курсор на перекрестие (`crosshair`) при расположении поверх стран
5. Офомление карты оставляет желать лучшего. Попробуйте оформить карту в соответствии с собственными представлениями о прекрасном. 5. Внешний вид карты оставляет желать лучшего, оформите карту в соответствии с собственными представлениями о прекрасном
> Это упражнение полезно делать для всех карт, которые мы создадим в рамках пособия. Самостоятельная работа над оформлением веб-карты даст ощутимый толчок в понимании процесса веб-картографирования.
## Контрольные вопросы ## Контрольные вопросы

@ -21,7 +21,7 @@ import Option from '../../components/Option.astro';
![alt text](../../assets/image-17.png) ![alt text](../../assets/image-17.png)
Когда происходит вызов метода API -- запрос определённого URL -- выполняется соответствующая серверная функция. Для программирования серверных функций могут использоваться различные языки программирования Python, Go, Rust и даже JavaScript (NodeJS). Когда происходит вызов метода API -- запрос определённого URL, -- выполняется соответствующая серверная функция. Для программирования серверных функций могут использоваться различные языки программирования Python, Go, Rust и даже JavaScript (NodeJS).
<Card title='В какой вкладке инструментов разработчика можно увидеть запросы, который выполняет браузер на веб-странице?'> <Card title='В какой вкладке инструментов разработчика можно увидеть запросы, который выполняет браузер на веб-странице?'>
<MultipleChoice> <MultipleChoice>
@ -57,7 +57,7 @@ import Option from '../../components/Option.astro';
<details> <details>
<summary>Что это за база данных такая -- SQLite</summary> <summary>Что это за база данных такая -- SQLite</summary>
SQLite -- встраиваемая база данных, которая хранит все свои данные в одном файле. В одном файле хранятся все таблицы, индексы и другая информация о базе данных, что упрощает управление и резервное копирование. Благодаря этому, она не требует отдельного сервера и легко интегрируется в различные приложения. SQLite -- встраиваемая база данных, которая хранит все свои данные в одном файле. В одном файле хранятся все таблицы, индексы и другая информация о базе данных, что упрощает управление и резервное копирование. Благодаря этому она не требует отдельного сервера и легко интегрируется в различные приложения.
</details> </details>
@ -87,7 +87,7 @@ DB_LOCATION = "cities_index.sqlite"
### Список городов по году ### Список городов по году
Добавим первый метод API. Он возвращает пользователю все города за выбранный год. При обращении к этому методу бэкенд выполняет запрос к базе данных и формирует на основе ответа GeoJSON файл, который мы сможем сразу отправить на карту. Добавим первый метод API. Он возвращает пользователю все города за выбранный год. При обращении к этому методу бэкенд выполняет запрос к базе данных и формирует на основе ответа GeoJSON-файл, который мы сможем сразу отправить на карту.
> Мы могли бы отправить на фронтенд и неподготовленный файл. Собрать GeoJSON на клиентской стороне, как в карте вакансий, когда Google возвращал нам CSV. Но то был чужой API. А этот наш. И в нашем мы можем сделать так, как будет удобнее нам! > Мы могли бы отправить на фронтенд и неподготовленный файл. Собрать GeoJSON на клиентской стороне, как в карте вакансий, когда Google возвращал нам CSV. Но то был чужой API. А этот наш. И в нашем мы можем сделать так, как будет удобнее нам!
@ -214,7 +214,7 @@ map.on("load", () => {
}) })
``` ```
Механизм CORS -- Cross-Origin Resource Sharing -- призван повысить безопасность веб-страницы. Нам, чтобы избежать ошибки CORS, надо указать, что API может отвечать на запросы любых веб-страниц. Механизм CORS -- Cross-Origin Resource Sharing -- призван повысить безопасность веб-страницы. Чтобы избежать ошибки CORS, укажем, что API может отвечать на запросы любых веб-страниц.
> CORS -- это история про *веб-страницы*, поэтому выполняя запросы к API напрямую мы с ней не сталкивались. > CORS -- это история про *веб-страницы*, поэтому выполняя запросы к API напрямую мы с ней не сталкивались.
@ -249,7 +249,7 @@ def city_by_id(id):
Дадим пользователю возможность выбирать год, за который он хочет видеть индекс городов. Дадим пользователю возможность выбирать год, за который он хочет видеть индекс городов.
Разметим элемент с выпадающем списком. Разметим элемент с выпадающим списком.
```diff lang=html title=index.html ```diff lang=html title=index.html
<body> <body>
@ -296,7 +296,7 @@ map.on("load", () => {
### Сведения о городе ### Сведения о городе
Выведем по клику на город подробные сведения о нём. Для отображения информации используем модальное окно, то есть диалоговое окно вспывающее поверх страницы. Выведем по клику на город подробные сведения о нём. Для отображения информации используем модальное окно, то есть диалоговое окно, всплывающее поверх страницы.
```diff lang=html title=index.html ```diff lang=html title=index.html
<body> <body>
@ -324,7 +324,7 @@ map.on("load", () => {
<details> <details>
<summary>Узнать ответ</summary> <summary>Узнать ответ</summary>
В первом блоке `then` нам доступен специальный объект ответа (Response), но мы ещё не имеем доступа к данным, поэтому мы должны их извлечь их, например, методом `response.json()` или `response.text()`. В первом блоке `then` нам доступен специальный объект ответа (Response), но мы ещё не имеем доступа к данным, поэтому мы должны извлечь их, например, методом `response.json()` или `response.text()`.
</details> </details>
</Card> </Card>
@ -374,28 +374,30 @@ map.on("load", () => {
Для этой карты мы сами разработали логику работы серверной части, другими словами, разработали бэкенд: Для этой карты мы сами разработали логику работы серверной части, другими словами, разработали бэкенд:
1. Фронтенд обращается к методу API, который предоставляет данные о городах (этот метод мы объявили самостоятельно) 1. Фронтенд обращается к методу API, который предоставляет данные о городах (этот метод мы объявили самостоятельно).
1. Обращение к методу API инициирует выполнение серверной функции (эту функцию мы написали самостоятельно) 1. Обращение к методу API инициирует выполнение серверной функции (эту функцию мы написали самостоятельно).
1. В рамках функции мы обращаемся к базе данных (здесь похвастаться нечем, базу данных мы загрузили готовую) и перерабатываем полученные данные в формат GeoJSON 1. В рамках функции мы обращаемся к базе данных (здесь похвастаться нечем, базу данных мы загрузили готовую) и перерабатываем полученные данные в формат GeoJSON.
1. Формируем ответ и отправляем в клиентскую часть, который, благодаря формату GeoJSON, на клиентской стороне может быть сразу использован в качестве источника данных для карты, в отличие от варианта с готовым API, где нам приходилось трансформировать данные на клиентской стороне 1. Формируем ответ и отправляем с сервера на клиентскую часть, в браузер. Так как полученный ответ имеет формат GeoJSON, он подаётся на вход карте в качестве источника данных сразу в противовес предыдущему упражнению, где данные от внешнего API приходится [трансформировать](/2-api/#преобразование-данных), прежде чем использовать как источник данных для карты.
Такая карта полезна, когда нужно организовать серверную обработку данных. Мы рассмотрели относительно простой, но довольно распространённый случай, когда нам нужно получить данные из базы, привести их к нужному формату и отправить в клиентскую часть. Однако серверная обработка может быть и более сложной в зависимости от решаемых задач. Такая карта полезна, когда нужно организовать серверную обработку данных. Мы рассмотрели относительно простой, но довольно распространённый случай, когда нам нужно получить данные из базы, привести их к нужному формату и отправить в клиентскую часть. Однако серверная обработка может быть и более сложной в зависимости от решаемых задач.
## Упражнения ## Упражнения
1. Создайте метод, который вернёт список доступных годов 1. Создайте метод, который вернёт список доступных годов
1. Выведите модальное окно слева и выполните подлёт к точке клика 2. Выведите модальное окно слева и выполните подлёт к точке клика
1. Вы могли заметить, что то, что мы получаем методом запроса деталей о городе уже содержится в полному списке городов: вам предлагается избавиться от этой избыточности 3. Вы могли заметить, что данные, которые мы получаем запросом подробных сведений о городе уже содержатся в полном списке городов: вам предлагается избавиться от этой избыточности
<details> <details>
<summary>Есть два варианта -- подумайте над ними. Это тест на то, что вам ближе, бэкенд или фронтенд. Когда подумаете, можно посмотреть разгадку 👀</summary> <summary>Есть два варианта -- подумайте над ними. Это тест на то, что вам ближе, бэкенд или фронтенд. Когда подумаете, можно посмотреть разгадку 👀</summary>
Бэкендер: можно убрать из метода для списка городов лишние атрибуты<br/>Фронтендер: на клик по объекту можно не обращаться к серверу, а использовать данные из атрибутов объекта Бэкендер: можно убрать из метода для списка городов лишние атрибуты<br/>Фронтендер: на клик по объекту можно не обращаться к серверу, а использовать данные из атрибутов объекта
</details> </details>
4. При клике по городу выведите информацию об Индексе качества городской среды за все доступные в базе данных года
## Контрольные вопросы ## Контрольные вопросы
1. Сформулируйте запрос для получения всех данных из таблицы `cities` базы данных `cities.sqlite` 1. Сформулируйте запрос для получения всех данных из таблицы `cities` базы данных `cities.sqlite`
1. На каком языке программирования сформулирован этот запрос? 1. На каком языке программирования сформулирован этот запрос?
1. В каком формате бэкенд возращает подробные данные о городе по идентификатору? 1. Почему `<year>` пишется в угловых скобках в конструкции `@app.route("/cities/<year>")`?
1. В каком формате бэкенд возвращает подробные сведения о городе по идентификатору?
1. Для чего к ответу добавляется заголовок `"Access-Control-Allow-Origin": "*"`? 1. Для чего к ответу добавляется заголовок `"Access-Control-Allow-Origin": "*"`?
## Чтение ## Чтение

@ -30,7 +30,7 @@ import { Card, LinkCard } from "@astrojs/starlight/components";
Тайл привязан к глобальной системе координат одной точкой. Геометрии внутри тайла хранятся во внутренней системе координат тайла. Тайлы бывают векторными и растровыми. В векторных тайлах содержание одного тайла составляют точки, линии и полигоны, [особым образом](https://docs.mapbox.com/data/tilesets/guides/vector-tiles-standards/#encoding-attributes) кодируются атрибуты. В растровых тайлах содержание одного тайла составляют пиксели. Тайл привязан к глобальной системе координат одной точкой. Геометрии внутри тайла хранятся во внутренней системе координат тайла. Тайлы бывают векторными и растровыми. В векторных тайлах содержание одного тайла составляют точки, линии и полигоны, [особым образом](https://docs.mapbox.com/data/tilesets/guides/vector-tiles-standards/#encoding-attributes) кодируются атрибуты. В растровых тайлах содержание одного тайла составляют пиксели.
Растровые тайлы можно использовать как для растровой модели данных, например, снимков, ЦМР, индексных изображений, так и для векторной, когда на тайлы будет нарезаться подготовленное изображение карты. Векторные тайлы оказываются удачным решением для векторных наборов данных. Растровые тайлы можно использовать как для растровой модели данных (снимков, ЦМР, индексных изображений), так и для векторной, когда на тайлы будет нарезаться подготовленное изображение карты. Векторные тайлы оказываются удачным решением для векторных наборов данных.
<LinkCard title="Тайлы растровые и векторные" href="/extra/tiles" description="Что лучше 🤨"/> <LinkCard title="Тайлы растровые и векторные" href="/extra/tiles" description="Что лучше 🤨"/>
@ -114,7 +114,11 @@ import { Card, LinkCard } from "@astrojs/starlight/components";
![alt text](../../assets/image-10.png) ![alt text](../../assets/image-10.png)
Для проверки можно перенести наборы данных, загруженные в базу, на карту и посмотреть, как они выглядят. Чтобы проверить, что данные успешно загружены в базу, выполним следующие действия:
1. Обновим подключение `oikonyms-database`, нажав на него правой кнопкой мыши и выбрав в контекстном меню опцию "Обновить"
1. В схеме данных `public` найдём загруженный набор пространственных данных
1. Перенесём его на карту и удостоверимся, что геометрия и атрибуты отображаются корректно
### Векторные тайлы ### Векторные тайлы
@ -130,7 +134,7 @@ import { Card, LinkCard } from "@astrojs/starlight/components";
martin postgresql://postgres:password@localhost:5432/oikonyms martin postgresql://postgres:password@localhost:5432/oikonyms
``` ```
А в Windows приобетёт такой вид. А в Windows приобретёт такой вид.
![alt text](../../assets/image-11.png) ![alt text](../../assets/image-11.png)
@ -144,7 +148,7 @@ martin postgresql://postgres:password@localhost:5432/oikonyms
По адресам `localhost:3000/grid` и `localhost:3000/oikonyms` доступны описания наборов векторных тайлов в формате [TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/3.0.0). Наиболее существенным в нём является указание адреса, по которому доступны векторные тайлы -- `localhost:3000/grid/{z}/{x}/{y}` По адресам `localhost:3000/grid` и `localhost:3000/oikonyms` доступны описания наборов векторных тайлов в формате [TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/3.0.0). Наиболее существенным в нём является указание адреса, по которому доступны векторные тайлы -- `localhost:3000/grid/{z}/{x}/{y}`
> `/catalog`, `/grid`, `/grid/{z}/{x}/{y}` -- это всё эндпоинты API, которое для нас автоматически формирует Martin. Он же выполняет нужные серверные функции, за счёт которых мы получаем ответы, обращаясь к этим эндпоинтам. И ничего не пришлось писать самим, как в прошлом упражнении! > `/catalog`, `/grid`, `/grid/{z}/{x}/{y}` -- это всё эндпоинты API, которые для нас автоматически формирует Martin. Он же выполняет нужные серверные функции, за счёт которых мы получаем ответы, обращаясь к этим эндпоинтам. И ничего не пришлось писать самим, как в прошлом упражнении!
При обращении `{z}/{x}/{y}` заменяются на индекс запрашиваемого тайла, например, `0/0/0` для запроса тайла на весь мир при уровне зума `0` или `1/1/0` для тайла в верхнем правом углу при уровне зума 1. Некоторые тайлы могут быть пустыми. При обращении `{z}/{x}/{y}` заменяются на индекс запрашиваемого тайла, например, `0/0/0` для запроса тайла на весь мир при уровне зума `0` или `1/1/0` для тайла в верхнем правом углу при уровне зума 1. Некоторые тайлы могут быть пустыми.
@ -158,7 +162,7 @@ martin postgresql://postgres:password@localhost:5432/oikonyms
Добавляем источник пространственных данных и картографический слой. При добавлении источника пространственных данных указываем тип `vector`, а не `geojson`, как в прошлых картах. Добавляем источник пространственных данных и картографический слой. При добавлении источника пространственных данных указываем тип `vector`, а не `geojson`, как в прошлых картах.
При добавлении слоя указываем `source-layer` -- векторный тайл может содержать несколько слоёв. В нашем случае слой только один, посмотреть мы на него можем в TileJSON описании `localhost:3000/grid`, где идентификаторы слоёв указываются в обязательном списке `vector_layers`. При добавлении слоя указываем `source-layer` -- векторный тайл может содержать несколько слоёв. В нашем случае слой только один, посмотреть на него можно в TileJSON-описании `localhost:3000/grid`, где идентификаторы слоёв указываются в обязательном списке `vector_layers`.
![alt text](../../assets/image-13.png) ![alt text](../../assets/image-13.png)
@ -176,7 +180,7 @@ map.addLayer({
}) })
``` ```
При добавлении источника вместо `url` TileJSON-файла мы можем указать `tiles` -- список адресов, по которому можно выполнять запросы к тайлам. Если мы указываем `url`, MapLibre самостоятельно находит этот список в TileJSON описании. При добавлении источника вместо `url` TileJSON-файла мы можем указать `tiles` -- список адресов, по которому можно выполнять запросы к тайлам. Если мы указываем `url`, MapLibre самостоятельно находит этот список в TileJSON.
![alt text](../../assets/image-14.png) ![alt text](../../assets/image-14.png)
@ -243,7 +247,7 @@ map.addLayer({
#### Мультимасштабное содержание #### Мультимасштабное содержание
На картах можно менять уровень приближения. Картограф должен озаботится тем, чтобы содержание на каждом уровне было визуально понятным и приятным. На картах можно менять уровень приближения. Картограф должен озаботиться тем, чтобы содержание на каждом уровне было визуально понятным и приятным.
Точки ойконимов в мелком масштабе накладываются друг на друга и закрывают шестиугольники. Не будем показывать их до 9-го уровня зума. Точки ойконимов в мелком масштабе накладываются друг на друга и закрывают шестиугольники. Не будем показывать их до 9-го уровня зума.
@ -468,7 +472,7 @@ document.getElementById("filter").addEventListener("input", (e) => {
1. Пользователь открывает веб-карту при определённом зуме в определённом охвате. 1. Пользователь открывает веб-карту при определённом зуме в определённом охвате.
1. Картографическая библиотека определяет, какие тайлы попадают в охват при заданном зуме. 1. Картографическая библиотека определяет, какие тайлы попадают в охват при заданном зуме.
1. Картографическая библиотека выполняет запрашивает векторные тайлы, выполняя запрос к API сервера векторных тайлов. 1. Картографическая библиотека запрашивает векторные тайлы, выполняя запрос к API сервера векторных тайлов.
1. Сервер векторных тайлов получает запрос и выполняет серверную функцию, отвечающую за формирование тайла. Входным параметром для этой функции является индекс тайла Z/X/Y. 1. Сервер векторных тайлов получает запрос и выполняет серверную функцию, отвечающую за формирование тайла. Входным параметром для этой функции является индекс тайла Z/X/Y.
1. Серверная функция выполняет запрос к базе данных. Этот запрос вырезает из слоя пространственных данных объекты, попадающие в тайл, кодирует их атрибуты и геометрию. 1. Серверная функция выполняет запрос к базе данных. Этот запрос вырезает из слоя пространственных данных объекты, попадающие в тайл, кодирует их атрибуты и геометрию.
1. Тайлы приходят в клиентскую часть картографического веб-приложения, где картографическая библиотека извлекает нужный слой из тайлов, сшивает объекты, попадающие в несколько тайлов, отрисовывает пространственные данные как картографический слой. 1. Тайлы приходят в клиентскую часть картографического веб-приложения, где картографическая библиотека извлекает нужный слой из тайлов, сшивает объекты, попадающие в несколько тайлов, отрисовывает пространственные данные как картографический слой.

@ -11,6 +11,6 @@ PostGIS популярен. Легко найти информацию в сет
PostGIS универсален. Для большинства случаев связка Postgres + PostGIS оказывается подходящим вариантом. Она проста в обращении, поэтому используется на небольших проектах. Она может работать под нагрузкой с большими объёмами данных, поэтому используется на крупных предприятиях. PostGIS универсален. Для большинства случаев связка Postgres + PostGIS оказывается подходящим вариантом. Она проста в обращении, поэтому используется на небольших проектах. Она может работать под нагрузкой с большими объёмами данных, поэтому используется на крупных предприятиях.
PostGIS избыточен. В случаях, когда к пространственным данным не нужно совершать пространственных запросов, такая связка избыточна, например, нам в первом упражнении было достаточно статических GeoJSON файлов. PostGIS избыточен. В случаях, когда к пространственным данным не нужно совершать пространственных запросов, такая связка избыточна, например, нам в первом упражнении было достаточно статических GeoJSON-файлов.
PostGIS не хватает. Не хватает связки Postgres + PostGIS в краевых случаях, например, при необходимости [агрегации на лету](https://github.com/ClickHouse/adsb.exposed/) особо больших объёмов слабо структурированных данных (десятки и сотни миллионов строк) или необходимости особо быстрого ответа (Redis) PostGIS не хватает. Редко. Например, при необходимости [агрегации на лету](https://github.com/ClickHouse/adsb.exposed/) десятков и сотен миллионов строк слабо структурированных данных (ClickHouse) или при необходимости [особо быстрого ответа](https://www.youtube.com/watch?v=cSFWlF96Sds) большому количеству пользователей (Redis).

@ -5,7 +5,7 @@ sidebar:
order: 5 order: 5
--- ---
Нельзя сказать, что векторные тайлы легче, быстрее, производительнее, тем более лучше, чем растровые тайлы. Хотя некоторые авторы этим и грешат. Например, векторные тайлы, отображающие множество объектов с богатой атрибутикой, могут весит значительно больше растровых тайлов, а оформление векторных тайлов, тем более динамическое, может привести к замедлению отрисовки карты в браузере. Нельзя сказать, что векторные тайлы легче, быстрее, производительнее, тем более лучше, чем растровые тайлы. Хотя некоторые авторы этим и грешат. Например, векторные тайлы, отображающие множество объектов с богатой атрибутикой, могут весить значительно больше растровых тайлов, а оформление векторных тайлов, тем более динамическое, может привести к замедлению отрисовки карты в браузере.
![vector-raster](../../../assets/vector-raster.gif) ![vector-raster](../../../assets/vector-raster.gif)
@ -15,7 +15,7 @@ sidebar:
Изначально картографические тайловые системы или пирамиды тайлов применялись к растровым изображениям. Растровые тайлы делятся на стороне сервера, передаются в браузер, в браузере выглядят как единое изображение той части карты, которую просматривает пользователь. Изначально картографические тайловые системы или пирамиды тайлов применялись к растровым изображениям. Растровые тайлы делятся на стороне сервера, передаются в браузер, в браузере выглядят как единое изображение той части карты, которую просматривает пользователь.
У растровых тайлов свои недостатки. Например, нельзя обеспечить непрерывное изменение масштаба. Для интерактивного взаимодействия с объектами придётся определять, соответствие пикселей и объектов. Серверная стилизация изображения может создать избыточную нагрузку на сервер при наплыве пользователей. У растровых тайлов свои недостатки. Например, нельзя обеспечить непрерывное изменение масштаба. Для интерактивного взаимодействия с объектами придётся определять соответствие пикселей и объектов. Серверная стилизация изображения может создать избыточную нагрузку на сервер при наплыве пользователей.
Векторные тайлы нагружают клиентскую часть. Растровые тайлы нагружают серверную часть. Векторные тайлы нагружают клиентскую часть. Растровые тайлы нагружают серверную часть.

@ -5,7 +5,7 @@ sidebar:
order: 8 order: 8
--- ---
Формирование тайла представляет собой запрос кусочка из полного набора пространственных данных, его перепроецирование и кодирование в векторных тайл. Формирование тайла представляет собой запрос кусочка из полного набора пространственных данных, его перепроецирование и кодирование в векторный тайл.
Такие популярные серверы векторных тайлов, как Martin, Tegola, pg_tileserv, формируют тайл средствами базы данных PostGIS. Увидеть запросы, которые они используют, можно в режиме отладки. Изучив запросы, мы увидим, что на производительность формирования тайлов влияет Такие популярные серверы векторных тайлов, как Martin, Tegola, pg_tileserv, формируют тайл средствами базы данных PostGIS. Увидеть запросы, которые они используют, можно в режиме отладки. Изучив запросы, мы увидим, что на производительность формирования тайлов влияет
1. наличие пространственного индекса, так как есть этап запроса кусочка набора данных по границам тайла 1. наличие пространственного индекса, так как есть этап запроса кусочка набора данных по границам тайла

Loading…
Cancel
Save