О том, как мы храним высоту в XYZ Tiles

EasySat — сервис для доступа к космическим изображениям и пространственным данным территорий для решения задач бизнеса, экологии и управления.


О том, как мы храним высоту в XYZ Tiles


Наша публикация на Хабре: "О том, как мы храним высоту в XYZ Tiles".


Введение


Цифровые модели поверхности и рельефа (ЦМП/ЦМР) – являются важной и неотъемлемой составляющей геоинформационных систем (ГИС). Они являются источником высотной информации и решают задачу определения высоты Z в указанной точке с координатами XY на местности. ЦМП и ЦМР могут быть как регулярными, так и нерегулярными (чаще всего их делят по формату хранения высотных отметок (растровые и векторные). Растровые ЦМП/ЦМР чаще всего поставляются в виде геопривязанного растра с типом пиксельных значений позволяющих хранить дробные отрицательные числа (32-bit float). Повсеместная реализация и внедрение информационных сервисов (прежде всего онлайн), делает необходимым отображение пользователям высотной информации в интерактивном режиме (определение Z при перемещении курсора XY) и ставит перед разработчиком задачу донесения этой информации. Если в случае с векторными данными, мы можем отобразить высотный пикет в координатах XY и показать записанный ему атрибут Z, то с растровыми (регулярными) моделями высот такое не выйдет, иначе мы должны хранить атрибут на каждый пиксель нашего растра (так никто не делает) или быть готовыми его рассчитать и вернуть пользователю с сервера. В принципе, эта задача решена показом на растровых тайлах высотных отметок или горизонталей в виде рисунка, но такой способ отображения высот не интерактивен и несет лишнюю нагрузку на картографическую составляющую тайлов.


Проблема


Если перед пользователем стоит задача определения высоты Z в точке с конкретными координатами на местности XY, то скорее всего решением со стороны разработчика будет состоять в отправке запроса на сервер с координатами XY клика (или действия пользователя), на сервере выполнятся какие-то запросы (SQL) или скрипты и пользователю вернется ответ. Запросы или скрипты на сервере могут быть разной сложности, что добавляет времени и затрачивает вычислительные ресурсов сервера для формирования ответа.
Можно пользователю отдавать исходные 32 битные (float) ЦМП/ЦМР тайлы и считывать пиксельные значения Z из координат XY курсора (и растра), 32-бит тяжело для передачи и отображения на сервисе и, честно говоря, хочется простого и красивого решения.


Постановка задачи


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


1. Необходимо использовать только уже поддерживаемые форматы данных (у нас это png (xyz tiles) и geojson);
2. Исключить сервер для расчета высотной информации при работе пользователя с сервисом;
3. Максимально экономить на трафике при передаче высотной информации;
4. Реализовать интерактивную возможность (при перемещении курсора мыши XY показывать пользователю высоту Z) определения высот при работе с сервисом.


Поскольку основным для нашего сервиса растровым форматом является PNG, то мы будем использовать его. Мы работаем с 3 канальными 8 битными растровыми PNG. Пиксельные значения каждого из RGB каналов PNG, могут иметь значения в диапазоне [0…255], т.е. для каждого пикселя мы имеем 3 значения [[0…255], [0…255], [0…255]]. Мы должны записать высотную отметку (32 float) исходной ЦМП/ЦМР в соответствующий пиксель нашего PNG XYZ-tiles (8 integer x 3). При этом мы не должны потерять знак высоты (высоты бывают отрицательными и положительными) и дробную часть. Мы решили реализовать следующие технологию:


1. Берем исходную (32 float) ЦМП или ЦМР;
2. Готовим исходный PNG файл: проходим по каждому пикселю нашей исходной ЦМП или ЦМР и кодируем ее 3 значениями RGB (8 integer);
3. Нарезаем PNG растр на тайлы (XYZ) и отдаем пользователю как обычный XYZ tiles;
4. Frontend отображает PNG тайлы (без необходимости использования или написания дополнительных плагинов для специфических форматов);
5. При перемещении пользователем курсора мыши XY считываем RGB значения пикселя тайла XY и переводим их в отметку высоты Z;
6. Выводим пользователю значение высоты Z.


Решение


Для выполнения пункта 2 технологии нам необходимо рассчитать пиксельные значения PNG файла соответствующие значениями высоты исходной ЦМП/ЦМР. Исходные значение высоты Z (числитель), мы будем делить на 255 (знаменатель) и целое число от деления записывать в пиксельные значения канала R (т.е. если Z=60, то R=0, если Z=255, то R=1, если Z=546, то R = 2). Остаток от деления, мы будем записывать в канал G, т.е. для выше описанных случаев G= 60, G=0, G= 36. Таким образом формируя изображения из пиксельных значений высоты, мы можем кодировать любые целочисленные высоты. У нас остается еще один незаполненный канал B - сантиметры (см), она же, дробная часть в значениях высоты Z и знак отметки высот Z, которые мы пока с вами не рассматривали. Для себя мы решили, что для отрицательных высот мы будем назначать значения канала B=100, а для положительных значение канала B=0, а значения сантиметров высоты прибавлять к пиксельному значению канала B. То есть, для высот -0.5 м и 0.5 м, значения для канала B будут 150 и 50 соответственно.

Диапазон значений высот, которые мы можем записать таким способом [-65025.99, 65025.99]. Этого диапазона должно хватить для кодирования отметок любой высоты в пределах Солнечной системы. Мы используем описанную выше технологию для интерактивного отображения высот в нашем сервисе.