Форматы представления географических объектов
В предыдущей статье мы затронули такие понятия, как форматы представления географических объектов, а также немного посмотрели на то, какие виды геометрии бывают. Сегодня мы начнем погружаться в детали, будет много технической информации. Надеюсь, вы еще не поджали свои маленькие хвостики.
Если вы не читали прошлую статью, настоятельно рекомендую сначала ознакомиться с ней.
Координаты
Координаты – фундаментальная геометрическая концепция. Свойство «coordinates» объекта геометрия состоит из пары/триплета координат (в случае геометрии типа «Point»), массива координат (объекты типа «LineString» или «MultiPoint»), массива массивов координат (объекты типа «Polygon», «MultiLineString») или многомерного массива координат (объекты типа «MultiPolygon»).
Координаты определяются массивом чисел. Этот массив должен содержать минимум два элемента, но их может быть больше. Порядок элементов должен быть следующим: x, y, z (для данных, находящихся в прямоугольной системе координат - смещение на восток, смещение на север, высота, для данных, находящихся в географической системе координат – долгота, широта, высота).
[30.315830718468703, 59.95010599732575, 3]
Система координат (СК) или проекция - определяют, как двумерная спроецированная карта описывает реальные местоположения на Земле с помощью координат. Нам из этого будет важно понимать, что СК имеет четкую формулу пересчета координат. Основные СК, которым будем оперировать:
[30.315830718468703, 59.95010599732575]
[3374742.838555017, 8388637.907395536]
В дальнейшем в разных статьях примеры геометрий я буду приводить в разных системах координат. Будем развивать вашу насмотренность, чтобы вы могли с первого взгляда на описание геометрии определять, в какой системе координат она описана.
Геометрия
Прежде чем приступать к форматам представления, повторим, какие виды геометрий у нас есть, а также узнаете новые.
Есть три основных вида геометрий, с которыми чаще всего будем сталкиваться на практике.
Если вы внимательно читали предыдущую статью, особенно описание каждого вида геометрий, наверняка обратили внимание на повторяющуюся фразу "состоит из набора точек". О чем нам это должно говорить? Точка является основой любой геометрии. На основе точек, или как их иначе называют, узлов, составляются все остальные геометрии.
Помимо основных видов, существуют еще подвиды - мульти- геометрии, а также геометрия в виде коллекции (geometry collection).
Мульти- геометрия состоит из набора геометрий одного вида, например:
- MultiPoint - Геометрия, состоящая из нескольких точек
- MultiLineString - Геометрия, состоящая из нескольких линий
- MultiPolygon - Геометрия, состоящая из нескольких полигонов.
Получается, что, если запустить цикл по массиву координат, можно получить геометрию в ее минимальном представлении.
А что тогда с коллекцией геометрией? 🤔
Её особенность заключается в том, что, в отличие от мульти- геометрий, в нее можно положить разные виды геометрий.
Форматы представления географических объектов
Чтобы работать с геометрией на фронтенде, обмениваться картографическими данными между системами или просто передавая по API, люди придумали и зафиксировали определенные форматы. На текущий момент для быстрого старта в мир картографии нам хватит двух - GeoJSON и WKT.
На деле же форматов гораздо больше - shape, mid/mif, kml и т.п., но подробнее мы их будем разбирать позднее.
GeoJSON
Вспомним определение - формат на основе JSON, предназначенный для представления географических объектов с их непространственными атрибутами.
Так как данный формат сделан на основе формата JSON, то без понимания, как он устроен далеко не уедешь. Прежде чем продолжать изучение картографии, убедитесь, что вы его знаете. А для тех, кто не в курсе, советую полезное чтиво.
GeoJSON - четко зафиксированный формат данных. Любое отклонение в сторону грозит риском, что ваши карты его не смогут понять и отобразить. Официально зафиксированный стандарт, можно изучить тут.
Зачем объяснять, если он зафиксирован и можно почитать официальную информацию?
- Не все котята знают английский язык
- Стандарты иногда пишутся очень заумным языком
- Раз уж мы взялись собирать всю информацию в одном месте, давайте соберем и это, тем более rfc не описывает все примеры 🙃
Будем идти от простого к сложному - от описания геометрий до описания целого слоя с набором объектов.
Представление геометрий
Геометрия описывается в виде объекта, который обязательно должен содержать два поля:
Теперь давайте посмотрим, как выглядят различные типы геометрий в формате geoJSON.
{ "type": "Point", "coordinates": [ 30.315830718468703, 59.95010599732575 ] }
{ "type": "LineString", "coordinates": [ [30.303325070439058, 59.94495377359024], [30.303536318623713, 59.947704454457636], [30.31001459629374, 59.950948553926935], [30.31445080817707, 59.95271151830221], [30.32008409310859, 59.95316987367963], [30.323956976498494, 59.95281729318262] ] }
{ "type": "Polygon", "coordinates": [ [ [30.307057121705924, 59.94953811490868], [30.32367531225134, 59.953099357880376], [30.320436173416283, 59.95658970985352], [30.307761282321366, 59.955144255147474], [30.307057121705924, 59.94953811490868] ] ] }
{ "type": "Polygon", "coordinates": [ [ [30.307195, 59.949252], [30.32423, 59.952949], [30.321053, 59.956903], [30.307918, 59.955141], [30.307195, 59.949252] ], [ [30.311009, 59.954324], [30.318907, 59.955442], [30.320281, 59.95368], [30.311094, 59.952218], [30.311009, 59.954324] ] ] }
{ "type": "MultiPoint", "coordinates": [ [30.260433, 59.932574], [30.293057, 59.941431], [30.290653, 59.92337] ] }
Мульти-линия - MultiLineString
{ "type": "MultiLineString", "coordinates": [ [ [30.296181, 59.954495], [30.312751, 59.964721] ], [ [30.295062, 59.955225], [30.311982, 59.96558] ], [ [30.306481, 59.950025], [30.305966, 59.952819], [30.308885, 59.95559], [30.311761, 59.956708], [30.317084, 59.95703], [30.320776, 59.956256], [30.323609, 59.955075], [30.324897, 59.953807] ] ] }
{ "type": "MultiPolygon", "coordinates": [ [ [ [30.283822, 59.951443], [30.286397, 59.950283], [30.288973, 59.952389], [30.286483, 59.953248], [30.283822, 59.951443] ] ], [ [ [30.308976, 59.948607], [30.312239, 59.947704], [30.317905, 59.948091], [30.321253, 59.949896], [30.323056, 59.951099], [30.322283, 59.952217], [30.315072, 59.952217], [30.311724, 59.95097], [30.308976, 59.948607] ] ], [ [ [30.333787, 59.953764], [30.33808, 59.953764], [30.33808, 59.955698], [30.333787, 59.955698], [30.333787, 59.953764] ] ] ] }
Коллекция геометрий - GeometryCollection
{ "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [30.304941,59.95776] }, { "type": "Polygon", "coordinates": [ [ [30.330697, 59.954623], [30.335848, 59.954623], [30.335848, 59.95703], [30.330697, 59.95703], [30.330697, 59.954623] ] ] } ] }
"Вау! Мы нарисовали слой!!!", - могли подумать вы, и оказались не правы. Мы нарисовали всего лишь геометрию, состоящую из двух разных простых геометрий. Мы даже не нарисовали объект, так как у нас нет набора атрибутов.
Представление объектов
С геометрией все понятно, но ведь у объекта есть атрибуты. Куда же их девать?
Чтобы описать объект вместе с его атрибутами, в geoJSON сделали сущность Feature.
Feature обязательно должен иметь следующие поля:
- type - тип, значение Feature
- geometry - описание геометрии
- properties - атрибуты объекта в виде JSON объекта.
Если объект содержит идентификатор, он может быть указан в виде отдельного поля id.
Если объект не содержит атрибутов, то поле properties должно быть установлено в значение пустого объекта. Отсутствие данного поля будет означать, что описание объекта является не валидным.
{ "type": "Feature", "properties": { "shape": "Circle", "radius": 192.8759203950539, "name": "Просто круг в центре города" }, "geometry": { "type": "Point", "coordinates": [30.304941,59.95776] }, "id": "8c2c17eb-0c4a-4ed5-bab9-d00ae8289303" }
А что с GeometryCollection? Все ровно также, как и с обычной геометрий. Поле geometry получает в виде значения описание GeometryCollection. Однако с точки зрения карты получится, что все ваши геометрии будут иметь одинаковый набор атрибутов, соответственно. Если вы хотите иметь разный атрибутивный состав, то у вас есть два пути описания:
- Отказаться от GeometryCollection и поместить каждую геометрию в свой Feature, получив тем самым несколько разных объектов
- Создать атрибут объекту Feature, который содержал бы значение массива объектов JSON, описывающих каждую геометрию отдельно
Представление слоя
Feature - это прекрасно, но мы хотим получать слой!
И на этот случай вам приходит сущность FeatureCollection. Эта сущность является самой верхоуровневой. Выше уже ничего не нет.
FeatureCollection должен иметь следующие поля:
Если объектов в слое еще нет, features должен быть объявлен как пустой массив.
В дальнейшем мы будем работать с Geoserver и получать слой разными способами. Забегу немного вперед. Если мы посмотрим на представление FeatureCollection, которое будет отдавать Geoserver, то увидим, что, помимо двух обязательных полей, он отдает еще ряд дополнительных, например, totalFeatures, crs и другие. Стандарт geoJSON позволяет расширять описание, добавляя дополнительные поля, если они необходимы. Мы даже можем добавить дополнительную геометрию, например, основная геометрия - полигон, а нам нужна центральная точка полигона. Однако стандарт говорит о том, что поле crs с указанием системы координат больше не используется, соответственно, валидаторы считают такой geoJSON невалидным
Geoserver отдает дополнительные поля скорее для упрощения жизни разработчику. Например, он позволяет делать пагинацию, запрашивая слой с limit и offset, как это делали бы мы в SQL. За счет передачи дополнительных полей мы четко можем понять, сколько всего объектов слое, а сколько мы получаем.
{ "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": { "shape": "Circle", "radius": 192.8759203950539, "name": "Просто круг в центре города" }, "geometry": { "type": "Point", "coordinates": [30.304941, 59.95776] }, "id": "8c2c17eb-0c4a-4ed5-bab9-d00ae8289303" }, { "type": "Feature", "properties": { "shape": "Rectangle", "name": "Unnamed Layer" }, "geometry": { "type": "Polygon", "coordinates": [ [ [30.330697, 59.954623], [30.335848, 59.954623], [30.335848, 59.95703], [30.330697, 59.95703], [30.330697, 59.954623] ] ] }, "id": "a6ccfde2-6ab9-4254-a051-c7a5faa4d594" }, { "type": "Feature", "properties": { "shape": "Line", "name": "Unnamed Layer" }, "geometry": { "type": "LineString", "coordinates": [ [30.301336, 59.969962], [30.30434, 59.96906], [30.308891, 59.969489] ] }, "id": "925627f4-f015-419f-8cf5-b84f1ec7d408" }], "totalFeatures": 10, "numberMatched": 10, "numberReturned": 3, "timeStamp": "2023-05-01T19:42:10.965Z", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::4326" } } }
Инструменты
Быстрый просмотр и редактор геометрий
WKT - Well-known text
Еще один формат представления географических объектов. Не привязан ни к каким форматам, позволяет описывать геометрию в виде строки. Какого-то конкретного стандарта я найти не смог, однако на сайте IBM есть вполне четкое описание.
Да, не хватает. GeoJSON хоть и позволяет описать весь слой, однако итоговое описание получается очень громоздким, что нам не подходит под некоторые задачи.
В отличие от GeoJSON, WKT не позволяет описать весь слой, не позволяет описывать объекты слоя, т.е. никаких атрибутов положить в текстовое описание мы не можем.
Так для чего же нам тогда пригодится этот формат?
Поскольку в нашей практической части мы будем использовать открытое ПО, ГИС Geoserver, реализующий стандарт OGC, он будет накладывать на нас определенные ограничения.
В некоторый момент мы придем к тому, что заходим отфильтровать слой, получив определенный набор объектов. В Geoserver мы можем сделать это двумя путями:
Я предпочитаю использовать второй вариант, так как его понимают большинство.
Query параметр сам по себе уже накладывает на нас ограничение. Передавать JSON таким способом плохая идея, тут нам на помощь как раз и приходит WKT.
Закончили минутку забегания вперед, давайте смотреть, как же описывать геометрию в данном формате.
WKT позволяет нам описать геометрию в виде строки определенного формата.
Координаты (долгота и широта) разделяются пробелом. Пары координат разделяются запятой.
Посмотрим на примерах, как это выглядит.
Точка
POINT EMPTY
SRID=4326;POINT(30.330697 59.954623)
SRID=4326;POINT(30.330697 59.954623 3)
LINESTRING EMPTY
SRID=4326;LINESTRING (30.303325070439058 59.94495377359024, 30.303536318623713 59.947704454457636, 30.31001459629374 59.950948553926935, 30.31445080817707 59.95271151830221, 30.32008409310859 59.95316987367963, 30.323956976498494 59.95281729318262)
SRID=4326;POLYGON ((30.307057121705924 59.94953811490868, 30.32367531225134 59.953099357880376, 30.320436173416283 59.95658970985352, 30.307761282321366 59.955144255147474, 30.307057121705924 59.94953811490868))
SRID=4326;POLYGON ((30.307195 59.949252, 30.32423 59.952949, 30.321053 59.956903, 30.307918 59.955141, 30.307195 59.949252), (30.311009 59.954324, 30.318907 59.955442, 30.320281 59.95368, 30.311094 59.952218, 30.311009 59.954324))
GEOMETRYCOLLECTION (POINT (30.304941 59.95776), POLYGON ((30.330697 59.954623, 30.335848 59.954623, 30.335848 59.95703, 30.330697 59.95703, 30.330697 59.954623)))
Инструменты
Online конвертер geoJSON в wkt
Данных знаний о геометрии и ее форматах представления на текущий момент нам хватит, чтобы начать решать базовые картографические задачи. В будущем мы будем рассматривать и другие форматы представления.
В следующей статье мы начнем разбирать Geoserver и затронем стандарт OGC.