Рассматриваются вопросы динамической выборки из массива данных определенной части. Описывается решение, стандартизированное в спецификации OGC WMS, и частные реализации в программах GeoServer, UMN MapServer, QGIS MapServer. Не затрагивается раздел работы с временны́ми отрезками. Приводятся методы работы с фильтрами в javascript-библиотеке OpenLayers.
Обсудить в форуме Комментариев 11
Оглавление
Текущая редакция стандарта OGC Web Map Service Interface 1.3.0 (20.01.2004) предполагает настройку сервиса в реальном времени посредством отправки документов SLD. Фильтры можно задать в двух случаях: LayerFeatureConstraints для всего слоя (SLD 1.1.0, раздел 11.2 «Named layers») и для каждого правила (Rule) в стилях (SE 1.1.0, раздел 10 «Rules»).
Примечание: правила составления фильтров описываются в спецификации Filter Encoding Implementation.
Во втором случае данные из хранилища (например, БД или WFS) запрашиваются в полном объеме. А после применения стилей часть объектов может быть исключена, т.к. они не вошли ни в одно из правил. Накладные расходы на подготовку и транспортировку данных очевидны, но проект UMN MapServer пошел именно по этому пути (см. ниже).
Если же задать LayerFeatureConstraints для слоя, то запрос в хранилище будет производиться с учетом фильтра и лишние данные не будут включены в результат.
Обратимся к профилю Styled Layer Descriptor (версия 1.1.0 от 29.06.2007). Пример фильтра слоя выглядит так (описание namespace опущено):
<sld:StyledLayerDescriptor version="1.1.0"> <sld:NamedLayer> <se:Name>mylayer</se:Name> <!-- фильтр слоя --> <sld:LayerFeatureConstraints> <sld:FeatureTypeConstraint> <ogc:Filter> <ogc:PropertyIsEqualTo> <ogc:PropertyName>country</ogc:PropertyName> <ogc:Literal>UKRAINE</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter> </sld:FeatureTypeConstraint> </sld:LayerFeatureConstraints> <sld:UserStyle> <!-- описание стилей (rules) --> </sld:UserStyle> </sld:NamedLayer> </sld:StyledLayerDescriptor>
Полная реализация LayerFeatureConstraints есть в QGIS MapServer.
В программе UMN MapServer фильтр слоя напрямую не поддерживается, но если источником данных является БД, то выполняется довольно спорная оптимизация (ticket #2840): фильтры из всех rules объединяются в один общий, который используется при обращении к источникам данных. Если же вы хотите установить фильтр слоя другими методами (см. ниже), то такое поведение будет сильно мешать. Отключить эту оптимизацию можно только в исходном коде с перекомпиляцией (или применить бинарный патч).
GeoServer использует библиотеку GeoTools, в которой реализация LayerFeatureConstraints присутствует. (В реальной жизни не проверял.)
Подготовленный документ SLD сохраняется в файл, доступный для сервера WMS по протоколу HTTP. В строке запроса указывается полная ссылка в параметре SLD (одной строкой):
http://wms-server/wms?SERVICE=WMS& LAYERS=mylayer& SLD=http://other-server/layer_with_filter.sld
Заметьте, что имя слоя в параметре LAYERS и в документе SLD должно совпадать (в примере и там и там 'mylayer').
При работе в OpenLayers составляют условие фильтра и передают серверному скрипту (например, php или python) который оформляет документ SLD и сохраняет в виде файла. Как вариант, можно направить результат сразу на сервер WMS:
http://wms-server/wms?SERVICE=WMS& LAYERS=mylayer& SLD=http://other-server/style_filter.php?country#UKRAINE
Если отбросить решаемые трудности с кодированием строки, то нужно отметить только увеличение длины строки запроса и накладные расходы на постоянную работу скрипта (будет выполняться при рендеринге каждого тайла). Для несложных фильтров это метод может быть удобен.
Применение фильтра к слою:
lay_points.mergeNewParams({ sld: 'http://other-server/layer_with_filter.sld' });
При добавлении (обновлении) параметров слоя он автоматически перерисовывается.
Примечание: можно, также, передать все содержимое документа в параметре SLD_BODY. Но так как длина строки ограничена (зависит от браузера, 500-2000 знаков), то практического применения этот способ не нашел.
Использование SLD позволяет создавать веб-приложения, не зависящие от используемого на сервере WMS программного обеспечения. Но иногда бывает удобнее и проще воспользоваться дополнительными функциями (vendor feature) конкретного сервера.
Common Query LanguageВ GeoServer реализован собственный механизм фильтров слоя — CQL. В новой версии (см. пресс-релиз GeoServer 2.1 beta 3) обещают его расширенный вариант ECQL. Язык CQL введен в спецификации OGC Catalogue Services (CS 2.0.2, раздел 6.2.2). В тексте дано исключительно формальное описание, требующее для понимания определенной подготовки. Поэтому рекомендую обратиться к документации GeoServer и GeoTools.
OGC FE разрабатывается на основе CQL и по возможностям оба языка равны. В CQL присутствуют функции пространственных отношений (BBOX, пересечение и т.п.) и временные выборки.
Примеры фильтров:
city_name LIKE 'A%' AND population > 100000 country IN ('UKRAINE', 'BELARUS')
При использовании с сервисами WMS в строку запроса добавляется параметр CQL_FILTER:
http://wms-server/wms?SERVICE=WMS& LAYERS=mylayer& CQL_FILTER="country IN ('UKRAINE', 'BELARUS')"Аналогично для OpenLayers:
lay_points.mergeNewParams({ CQL_FILTER: "country IN ('UKRAINE', 'BELARUS')" });OGC Filter
GeoServer позволяет применять фильтры OGC напрямую. В параметре FILTER запроса передается описание фильтра в формате XML. Пример из документации:
http:/localhost:8080/geoserver/wms/kml_reflect? layers=topp:states& FILTER=%3CFilter%3E%3CPropertyIsBetween%3E%3CPropertyName%3Etopp:LAND_KM%3C/PropertyName%3E%3CLowerBoundary%3E%3CLiteral%3E100000%3C/Literal%3E%3C/LowerBoundary%3E%3CUpperBoundary%3E%3CLiteral%3E150000%3C/Literal%3E%3C/UpperBoundary%3E%3C/PropertyIsBetween%3E%3C/Filter%3E
Строка закодирована по методу Quoted-printable и эквивалентна:
<Filter> <PropertyIsBetween> <PropertyName>topp:LAND_KM</PropertyName> <LowerBoundary> <Literal>100000</Literal> </LowerBoundary> <UpperBoundary> <Literal>150000</Literal> </UpperBoundary> </PropertyIsBetween> </Filter>
Как правило, кодировать вручную не требуется, браузер выполняет эту работу самостоятельно. Но если понадобиться раскодировать, то рекомендую программу Штирлиц. (Последняя версия 4.01 вышла в 2001 году, но легко находится в интернете.)
featureidРедко используемый фильтр, предназнаен для выбора объекта по известному идентификатору:
http://localhost:8080/geoserver/wms/kml_reflect? layers=topp:states&featureid=states.5
Стационарный фильтр слоя задается в файле настроек ключевым словом FILTER:
LAYER NAME "mylayer" FILTER ([type]=’road’ and [size]<2) ... END # layer
Полный список возможных логических конструкций приведен в документации в разделе «MapServer expressions».
Run-time SubstitutionЧто бы изменять параметры динамически (см. главу «Run-time Substitution»), введем в фильтр переменные и опишем информацию, для проверки на корректность:
LAYER ... FILTER ( ("[region]" = %country%) AND ("[size]" < %size%) ) ... METADATA country_validation_pattern '.' size_validation_pattern '^[0-9]{1,}$' END END
Две переменные — %country% и %size%. Соответствующие им строки проверки задаются в форме Regular expression (RegExp): country — любое значение, size — только цифры. В документации строго не рекомендуют использовать RegExp '.', так как это создает опасность проведения атаки. Но в принципе, никто не запрещает использовать универсальный фильтр типа:
LAYER ... FILTER (%myfilter%) ... METADATA myfilter_validation_pattern '.' END END
Строка запроса к серверу WMS принимает вид:
http://wms-server/wms?SERVICE=WMS& LAYERS=mylayer& country='UKRAINE'& size='100000'
Есть и более общий механизм изменения любых параметров файла настроек. Он очень похож на описанный выше:
LAYER ... VALIDATION 'filter' '.' END END
Используется группа VALIDATION, в которой указывается имя параметра и строка проверки. Как я указал выше изменять можно любой параметр, имя которого внесено в группу. Это позволяет изменять, например, строку подключения к базе данных. На всякий случай приведу познавательную ссылку - SQL injection).
В строку запроса на сервер добавляются конструкция вида (пробелы можно заменять на символ '+', при необходимости):
&map.layer[0]=FILTER+("[country]" = "UKRAINE") &map.layer[0].class[0]=EXPRESSION+("[country]" = "BELARUS") &map.layer[0]=DATA+"the_geom from xxx using unique gid using srid=4326"MapScript
Пожалуй, наиболее удобный вариант изменения фильтра — это использование скрипта на одном из диалектов MapScript (PHP, Python, Perl и др.). Тем более, что при работе с этим сервером создание враппера желательно в любом случае.
Пример для PHP/MapScript:
<?php #dl('php_mapscript.so'); # создаем карту $map = ms_newMapobj('/usr/local/example.map'); # загружаем переданные параметры $request = ms_newowsrequestobj(); $request->loadparams(); # задаем фильтр $layer = $map->getLayerByName('mylayer'); $layer->setFilter($request->getValueByName('MY_FILTER')); # рисуем карту ms_ioinstallstdouttobuffer(); $map->owsdispatch($request); $contenttype = ms_iostripstdoutbuffercontenttype(); if ($contenttype == 'image/png') header('Content-type: image/png'); ms_iogetStdoutBufferBytes(); ms_ioresethandlers(); ?>
В библиотеке есть встроенная поддержка работы с фильтрами в формате OGC — набор классов OpenLayers.Filter.
Простое сравнение country = 'UKRAINE':
var filter = new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.EQUAL_TO, property: 'country', value: 'UKRAINE', });
Преобразование в XML:
var format = new OpenLayers.Format.Filter({version: "1.1.0"}); var xml = new OpenLayers.Format.XML(); var text = xml.write(format.write(filter));
В версии для разрабочиков (trunk) уже присутствует парсер OpenLayers.Format.CQL. Работа с ним аналогична рассмотреному выше.
Стили
Вот так выглядит точечный стиль с логическим условием (country = 'UKRAINE') OR (country = 'BELARUS'):
var style = new OpenLayers.Style(); var rule = new OpenLayers.Rule({ name: species_selected[i], filter: new OpenLayers.Filter.Logical({ type: OpenLayers.Filter.Logical.OR, filters: [ new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.EQUAL_TO, property: 'country', value: 'UKRAINE', }), new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.EQUAL_TO, property: 'country', value: 'BELARUS', }) ] }), symbolizer: { "Point": { graphicName: "circle", pointRadius: 3.5, strokeWidth: 1, strokeColor: "#000000", fillColor: palette[x], fillOpacity: 0.5 } } }); style.addRules([rule]);
Преобразование стиля в формат SLD (LayerFeatureConstraints не поддерживается):
var text = new OpenLayers.Format.SLD().write({ namedLayers: [{ name: "mylayer", userStyles: [style] }] });
И в заключение, небольшая справка по подстановочным символам (wild-card):
один любой символ | любое количество любых символов |
экранирование (escape) | Примечание | |
SLD (FE) | . (точка) | * | ! | Переопределяется в singleChar, wildCard, escapeChar |
UMN MapServer | . (точка) | * | \ | Про правилам RegExp |
CQL | _ | % | \ | |
SQL | _ | % | экранирование задается параметром escape |
Последнее обновление: September 09 2021
Дата создания: 29.12.2010
Автор(ы): Mavka
© GIS-Lab и авторы, 2002-2021. При использовании материалов сайта, ссылка на GIS-Lab и авторов обязательна. Содержание материалов - ответственность авторов. (подробнее).