Использование кластеризации точечных объектов в MapServer

Решенные задачи, первая запись - описание решения.
Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Использование кластеризации точечных объектов в MapServer

Сообщение Denis Rykov » 15 ноя 2011, 22:15

Начиная с версии 6.0 в MapServer введена поддержка кластеризации точечных объектов. Решил попробовать данную фичу и представить некоторые рекомендации тем, кто захочет ее использовать.

1. Если кластеризация нужна только для визуализации данных, то можно взять за основу пример из документации - никаких проблем возникнуть не должно, пример того, что получилось - во вложении.

2. Если помимо отображения данных необходима поддержка запросов GetFeatureInfo - то тут нужны некоторые приседания. Посылаем запрос к кластеру, допустим, в формате "application/vnd.ogc.gml", при этом добавив в секцию METADATA map-файла тег gml_include_items "all" как описано здесь. В результате получаем довольно странный ответ от сервера:

Код: Выделить всё

<?xml version="1.0" encoding="ISO-8859-1"?>

<msGMLOutput
xmlns:gml="http://www.opengis.net/gml"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<herbary_layer>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount>7</Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
</herbary_layer>
</msGMLOutput>

то есть возвращается информация не о всех объектах кластера, а о самом кластере. Немножко погуглив, находим тикет #3873, который говорит о том, что для того, чтобы получить информацию обо всех объектах, необходимо указать ключ CLUSTER_GET_ALL_SHAPES. Но данная возможность доступна только в svn версии, а в последнем на текущий момент релизе (6.1) её нет. Ок, ставим MapServer из SVN и добавляем необходимый ключ внутрь объекта LAYER:

Код: Выделить всё

PROCESSING "CLUSTER_GET_ALL_SHAPES=True"
Пытаемся выполнить запрос к серверу, в ответ получаем ответ уже больше похожий на правду:

Код: Выделить всё

<?xml version="1.0" encoding="ISO-8859-1"?>

<msGMLOutput
xmlns:gml="http://www.opengis.net/gml"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<herbary_layer>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount></Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11695979.680968,11929262.599170 11695979.680968,11929262.599170</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Cluster:FeatureCount>7</Cluster:FeatureCount>
<Cluster:Group></Cluster:Group>
</herbary_feature>
</herbary_layer>
</msGMLOutput>


Но где же атрибутика? Опять же немного погуглив, высняется, что для того, чтобы определить список возвращаемых атрибутивных полей - их необходимо определить в директиве ITEMS слоя:

Код: Выделить всё

PROCESSING "ITEMS=Herbarium,NNPage_"
Проверяем запрос - ничего не изменилось, как не было атрибутики в ответе, так и нет. Методом тыка обнаружено, что для появления атрибутивных полей, необходимо помимо указания ITEMS еще внутри объекта METADATA cлоя определить ключ:

Код: Выделить всё

wms_include_items   "all"
Теперь ответ сервера стал выглядеть еще лучше:

Код: Выделить всё

<?xml version="1.0" encoding="ISO-8859-1"?>

<msGMLOutput
xmlns:gml="http://www.opengis.net/gml"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<herbary_layer>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11693488.739881,11930811.873711 11693488.739881,11930811.873711</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Herbarium>EP</Herbarium>
<NNPage_>10-243</NNPage_>
<Cluster:FeatureCount></Cluster:FeatureCount>
</herbary_feature>
<herbary_feature>
<gml:boundedBy>
<gml:Box srsName="EPSG:900913">
<gml:coordinates>11693488.739881,11930811.873711 11693488.739881,11930811.873711</gml:coordinates>
</gml:Box>
</gml:boundedBy>
<Herbarium>EP</Herbarium>
<NNPage_></NNPage_>
<Cluster:FeatureCount>2</Cluster:FeatureCount>
</herbary_feature>
</herbary_layer>
</msGMLOutput>

Однако видим, что внутри GML затесался левый тег <Cluster:FeatureCount>, который делает GML-документ невалидным и его становится сложно использовать в сторонних приложениях, например в OpenLayers. Чтобы убрать его отредактируем директиву wms_include_items, она должна выглядеть так:

Код: Выделить всё

wms_include_items   "Herbarium,NNPage_"
то есть содержать только необходимые поля, причем они обязательно должны быть предварительно указаны в PROCESSING ITEMS. В результате проделанных действий мы имеем на выходе валидный GML документ.

Теперь пара моментов, с которыми пока не понятно как бороться:
1. В ответе сервера на запрос GetFeatureInfo - последнее поле последнего объекта в кластере всегда пустое.
2. При использовании map-файла как в примере при добавлении PROCESSING "CLUSTER_GET_ALL_SHAPES=True" - непонятно куда исчезают подписи кластеров.
3*. Может кто-нибудь поможет разобраться. Открываем changeset и видим что в документацию помимо описания опции CLUSTER_GET_ALL_SHAPES добавлено еще описание некоторой опции CLUSTER_GET_ALL_SHAPES_ON_QUERY, последнюю я пытался найти в исходниках, но так и не нашёл никакого упоминания:)
Вложения
clustering.png
clustering.png (234.81 КБ) 19970 просмотров
Spatial is now, more than ever, just another column- The Geometry Column.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 16 ноя 2011, 20:39

Ответ и возможный вариант решения вопроса 2 из предыдущего пункта. Ключ CLUSTER_GET_ALL_SHAPES помимо того, что позволяет осуществить запрос и получить информацию о всех объектах входящих в кластер еще и заставляет MapServer столько раз отрисовывать символ точечного объекта, сколько раз он встречается в кластере, поэтому, предположительно, подписей становится не видно из-за того, что сверху они накрываются другими объектами кластера, этот факт косвенно подтверждает тот момент, что если в свойствах LABEL установить OFFSET, чтобы подписи выходили за пределы символики, то их становится видно. Поэтому можно либо устанавливать OFFSET, либо разделить вывод слоя на 2 отдельных слоя: первый конфигурируем с ключом CLUSTER_GET_ALL_SHAPES, но в CLASS задаем символику только для одиночных объектов, не попадающих в кластер, если в этом же слое попытаться определить CLASS и для второго класса - класса кластеров, то это сведется к первому подходу с использованием OFFSET. Однако даже если мы используем OFFSET - нам удаётся увидеть подпись с числом объекта в кластере, но при этом поскольку объекты несколько раз рисуются друг поверх друга, то это приводит к различным артефактам. Поэтому логично вынести символику (CLASS) кластеров в отдельный слой, который конфигурируется без использования CLUSTER_GET_ALL_SHAPES. Результат реализации данного подхода представлен во вложении.
Вложения
cluster02.png
cluster02.png (224.55 КБ) 19933 просмотра
Spatial is now, more than ever, just another column- The Geometry Column.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 25 ноя 2011, 19:56

Ссылка на треккер MapServer с описанием баги, касающейся "кривой" атрибутики последней фичи в кластере.
Spatial is now, more than ever, just another column- The Geometry Column.

Tokha
Интересующийся
Сообщения: 38
Зарегистрирован: 08 апр 2011, 09:53
Репутация: 2

GetFeature и кластеризация mapserver

Сообщение Tokha » 12 мар 2012, 16:09

Вопрос касается OpenLayers и GetFeature для кластеризованного слоя.

Получение атрибутов объекта из кластеризованного слоя по клику работает без проблем, с помощью OpenLayers.Control.WMSGetFeatureInfo.

Теперь возникла задача, по клику получить атрибуты этого объекта и занести его в векторный слой, для последующих действий. Но WMSGetFeatureInfo не отдает геометрию этого объекта (хотя попробовал поле с геометрией запрашивать дополнительно, как атрибут и затем преобразовывать. Но это неудобно.)

Для этих задач стал использовать OpenLayers.Control.GetFeature. Для обычных слоев все работает отлично, а вот на кластере не находит объектов. Как бы это дело разрешить?

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 12 мар 2012, 18:42

Я не понял проблему. Объект события getfeatureinfo содержит массив объектов в свойстве features.
Например:

Код: Выделить всё


map.addControl(
new OpenLayers.Control.WMSGetFeatureInfo({
autoActivate: true,
infoFormat: 'application/vnd.ogc.gml',
maxFeatures: 1000,
layers: [lay_herbary],
queryVisible: true,
eventListeners: {
'getfeatureinfo': function(e) {
console.log(e.features) <- массив объектов OpenLayers.Feature.Vector
...
Spatial is now, more than ever, just another column- The Geometry Column.

Tokha
Интересующийся
Сообщения: 38
Зарегистрирован: 08 апр 2011, 09:53
Репутация: 2

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Tokha » 13 мар 2012, 10:20

Да, все так, но при использовании OpenLayers.Control.WMSGetFeatureInfo у найденных объектов
e.features[0].geometry равен null, поэтому не получается применить такую конструкцию:

Код: Выделить всё

layer.addFeatures(e.features);
Через OpenLayers.Control.GetFeature, geometry содержит данные и найденные объекты легко добавляются в векторный слой. Но при этом GetFeature почему-то не находит объекты из кластеризованного слоя.

На самом деле, мне удобнее пользоваться OpenLayers.Control.WMSGetFeatureInfo, но как тогда сделать, чтобы e.features[0].geometry был не пустой?

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 13 мар 2012, 16:33

Сейчас проверил, да, вы правы. Попробую разобраться.
Spatial is now, more than ever, just another column- The Geometry Column.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 14 мар 2012, 05:53

Попробуйте так:

Код: Выделить всё


var format_xml = new OpenLayers.Format.XML();
...
eventListeners: {
'getfeatureinfo': function(e) {
elements = format_xml.read(e.text).getElementsByTagName('coordinates');
for (var i=0; i < elements.length; i+=1) {
coordinates = elements.textContent.split(' ')[0].split(',');
geometry = new OpenLayers.Geometry.Point(coordinates[0], coordinates[1]);
e.features.geometry = geometry;
}
}
}
Spatial is now, more than ever, just another column- The Geometry Column.

Tokha
Интересующийся
Сообщения: 38
Зарегистрирован: 08 апр 2011, 09:53
Репутация: 2

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Tokha » 14 мар 2012, 18:54

По-моему, в ответе getfeatureinfo не присутствуют координаты точки, а есть только координаты BBOX. Завтра проверю, но думаю, что это так.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 14 мар 2012, 20:43

Да, так, но как я понял BBOX для точки - это и есть координаты самой точки. Отпишитесь о результатах, я попробовал на своих данных, на первый взгляд всё ok.
Spatial is now, more than ever, just another column- The Geometry Column.

Tokha
Интересующийся
Сообщения: 38
Зарегистрирован: 08 апр 2011, 09:53
Репутация: 2

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Tokha » 15 мар 2012, 13:52

Да, оказывается я и сам использую BBOX, после getfeatureinfo.
Вот так:

Код: Выделить всё

e.features[0].bounds.getCenterLonLat()
Но!
Поскольку, OpenLayers.Control.WMSGetFeatureInfo не отдает geometry объекта, то я не могу получить координаты линии.

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

Таким образом, возвращаемся к началу:
1. Как научить OpenLayers.Control.WMSGetFeatureInfo отдавать geometry выбранных объектов (точки, линии, а потом и полигоны)?
или
2. Как заставить OpenLayers.Control.GetFeature "видеть" объекты кластеризованного слоя?

Можно, конечно, сделать еще один слой, не кластеризованный. На карте его не показывать, а только делать запросы к нему. Но это на крайний случай.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 15 мар 2012, 19:16

Понятно, просто я посмотрел в заголовок темы и подумал, что речь идёт именно о точечных геометриях. Ну ничего, попробуем что-нибудь придумать если получится.
Spatial is now, more than ever, just another column- The Geometry Column.

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 16 мар 2012, 05:03

В MapServer есть возможность отдавать значение геометрий. Для этого в блоке METADATA на уровне слоя (не внутри блока WEB на уровне объекта MAP) задайте значение двух параметров: wms_geometries и wms_[geometry name]_type. В случае точечного слоя этот блок будет выглядеть так:

Код: Выделить всё

METADATA
    gml_include_items   "all"
    wms_include_items   "all"
    wms_geometries "geom"
    wms_geom_type "point"
END
Где "geom" - имя поля геометрии в источнике данных, "point" - тип слоя. Доступные типы: point, multipoint, line, multiline, polygon, multipolygon. Протестировал на точечном слое - работает (MapServer version 6.1-dev, но вроде должно работать начиная с версии 5.6). Получается, что в этом случае никаких телодвижений со стороны OpenLayers предпринимать не нужно.
Spatial is now, more than ever, just another column- The Geometry Column.

Tokha
Интересующийся
Сообщения: 38
Зарегистрирован: 08 апр 2011, 09:53
Репутация: 2

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Tokha » 26 мар 2012, 11:58

Спасибо за наводку. Все заработало!
Сделал так:

Код: Выделить всё

METADATA
....
    	GML_GEOMETRY_TYPE "point"
    	GML_GEOMETRIES "geometry"
END		

Аватара пользователя
Denis Rykov
Гуру
Сообщения: 3376
Зарегистрирован: 11 апр 2008, 21:09
Репутация: 529
Ваше звание: Author
Контактная информация:

Re: Использование кластеризации точечных объектов в MapServe

Сообщение Denis Rykov » 26 мар 2012, 12:03

Хорошо, что отписались.
Spatial is now, more than ever, just another column- The Geometry Column.

Ответить

Вернуться в «Рецепты»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 3 гостя