Символизация полигонов SLD

Заключительная часть по SLD, которая посвящена символизации полигонов. Рекомендую прочитать символизацию точек и символизацию линий, прежде чем переходить к полигонам, так как данная статья будет опираться на предыдущий материал.


Предполагается, что все примеры кода, описанные в этой статье, находятся внутри следующей XML:


<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor version="1.0.0"
       xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
       xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
       xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <NamedLayer>
    <Name></Name>
    <UserStyle>
      <Title>Basic polygon style</Title>
      <FeatureTypeStyle>
        <Rule>
          <Title>Здесь идёт заголовок легенды</Title>
            
          <!-- ТЕЛО СТИЛЯ ИДЁТ ЗДЕСЬ -->
            
        </Rule>
      </FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
</StyledLayerDescriptor>


PolygonSymbolizer

Внутри PolygonSymbolizer допустимо использование следующих тэгов:

  • Geometry — опциональный, определяет источник геометрии
  • Fill — опциональный, отвечает за заливку полигона
  • Stroke — опциональный, стилизация границы (контура) полигона

Geometry через определённые трансформации изменяет исходную геометрию и применяет стилизацию к той геометрии, которая получилась на выходе.

Stroke определяет границу полигона. В нём можно указать всё то, что мы рассматривали в символизации линий, раздел Stroke.

Fill задаёт заливку полигона. Может содержать два необязательных тэга — GraphicFill и CssParameter. В GraphicFill может содержаться тэг Graphic со всем содержимым, рассмотренным в символизации точек. CssParameter может быть всего два со следующими значениями тэга name:

  • fill — цвет заливки в формате #RRGGBB (по умолчанию #808080)
  • fill-opacity — непрозрачность, значение от 0 (полностью прозрачный) до 1 (полностью непрозрачный). Значение по умолчанию — 1

Вот, в целом, и вся символизация полигонов, так как основная часть тэгов уже была описана в двух предыдущих статьях. Для закрепления перейдём к примерам.


Примеры

Мы снова начнём со стилей попроще и постепенно будем наращивать сложность символизации.

Базовый стиль

Стиль по умолчанию

Базовый стиль, который GeoServer применяет ко всем слоям с полигонами, выглядит следующим образом:


<PolygonSymbolizer>
  <Fill>
    <CssParameter name="fill">#AAAAAA</CssParameter>
    <CssParameter name="fill-opacity">1</CssParameter>
  </Fill>
  <Stroke>
    <CssParameter name="stroke">#000000</CssParameter>
    <CssParameter name="stroke-width">1</CssParameter>
  </Stroke>
</PolygonSymbolizer>

Для заливки используется серый цвет, для границы — чёрная линия толщиной в 1 пиксель, полностью непрозрачная. В целом, fill и fill-opacity — это всё, что появилось нового в символизации полигонов (ну почти всё, есть ещё несколько вещей, о которых чуть позже). Для границы используется уже знакомая символизация линий, для заливки — символизация точек. Поэтому рассмотрим разные варианты комбинации уже знакомых приёмов.


Примеры из топографии

Чтобы не придумывать самому себе задания, я снова обратился к топографическим знакам — здесь и стилизация попадается довольно сложная, да и кому-нибудь может оказаться полезным готовый стиль. Например, возьмём кустарник:

Кустарник

Это один из самых простых стилей — просто нужно выявить повторяющийся фрагмент. Этим фрагментом будет «x», так как если повторять этот символ и по вертикали, и по горизонтали, мы получим косую штриховку. Аналогично при использовании «+» мы получим прямую штриховку:


<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>shape://times</WellKnownName>
          <Stroke />
        </Mark>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke>
    <CssParameter name="stroke-dasharray">10 2 2 2</CssParameter>
  </Stroke>
</PolygonSymbolizer>

Обратите внимание, что в WellKnownName был использован shape://times вместо x. В символизации точек были значки с отступом и без отступа. Дело в том, что если использовать значки «x» без отступа — они сольются и получится штриховка. Если же использовать «x» с отступом, мы получим отдельностоящие символы «x». Далее в стиле идёт пустой тэг Stroke — снова повторюсь, что есть большая разница между пустым тэгом и отсутствием тэга. Пустой тэг говорит о том, что все параметры идут по умолчанию и символы отрисуются чёрным цветом толщиной в 1 пиксель. Если тэг будет отсутствовать — символы не прорисуются вовсе и мы не получим заливку, так как GeoServer посчитает, что для символов заливки не задана символизация. Помимо заливки мы указали штрихпунктирную границу — просто потому что мы так можем. Рассмотрим ещё один несложный пример — фруктовые сады.

Фруктовые сады

Сразу видно, что нужно использовать знак circle для заливки кружочками. Но если просто залить кружочками, между ними не будет отступов и они все прилипнут друг к другу. Для решения этой проблемы воспользуемся тэгом VendorOption:


<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>circle</WellKnownName>
          <Stroke />
        </Mark>
        <Size>8</Size>
      </Graphic>
    </GraphicFill>
  </Fill>
  <VendorOption name="graphic-margin">6</VendorOption>
</PolygonSymbolizer>

Мы сделали заливку окружностями размером 8 пикселей с отступом в 6 пикселей. При этом мы не указали символизацию границ полигонов — из-за этого все штаты слились друг с другом в одну область. Что касается VendorOption — эти теги не являются частью спецификации SLD 1.0 и добавлены только в GeoServer’е. Ими можно пользоваться, когда не хватает возможностей SLD, но имейте ввиду, что при переносе стилей в другое окружение VendorOption будем вести себя непредсказуемо.

Рассмотрим ещё один пример — залежи:

Залежи

В качестве значка подойдёт открытая стрелка, повёрнутая на 90 градусов по часовой стрелке. Плюс сделаем небольшой отступ размером 5 пикселей, чтобы значки не сливались друг с другом:


<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>shape://oarrow</WellKnownName>
          <Stroke />
        </Mark>
        <Size>10</Size>
        <Rotation>90</Rotation>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
  <VendorOption name="graphic-margin">5</VendorOption>
</PolygonSymbolizer>

Таким несложным стилем мы получили наши залежи. Или нет?..


Более сложные примеры из топографии

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

Залежи

Как воспроизвести такое смещение? Ответ — всё тем же VendorOption и graphic-margin. Ранее мы задавали один отступ во все стороны. В случае «недозалежей», рассмотренных чуть выше, значок был размером 10 пикселей плюс с каждой стороны был отступ 5 пикселей. Выходило, что между значками был отступ 10 пикселей — сначала выделяется 10 пикселей на рисование самого значка, потом идёт отступ справа 5 пикселей, затем ещё отступ слева 5 пикселей уже у следующего значка, только затем рисовался следующий значок. Та же схема для вертикали — отступ 5 пикселей снизу одного значка суммируется с отступом 5 пикселей сверху следующего значка и выходит 10 пикселей расстояния между двумя соседними значками. Но для graphic-margin можно задать отступ для каждой стороны — последовательно для верхней, правой, нижней и левой сторон. В итоге нам понадобится три символизации — первая для заливки цветом (обязательно первой, иначе она зальёт и все знаки, идущие до неё), вторая для одного узора ложбин и третья для смещённого узора ложбин:


<PolygonSymbolizer>
  <Fill>
    <CssParameter name="fill">#3E8311</CssParameter>
  </Fill>
</PolygonSymbolizer>
<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>shape://oarrow</WellKnownName>
          <Stroke />
        </Mark>
        <Size>10</Size>
        <Rotation>90</Rotation>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
  <VendorOption name="graphic-margin">0 10 10 0</VendorOption>
</PolygonSymbolizer>
<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>shape://oarrow</WellKnownName>
          <Stroke />
        </Mark>
        <Size>10</Size>
        <Rotation>90</Rotation>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
  <VendorOption name="graphic-margin">10 0 0 10</VendorOption>
</PolygonSymbolizer>

Со сплошной заливкой всё понятно (первый PolygonSymbolizer). Дальше идёт основной узор. Он прижат к левому верхнему углу, так как у него отступ сверху и слева равен нулю. В то же время отступ справа и снизу равен 10 пикселям, что позволяет сохранить отступ в 10 пикселей между всеми значками (второй PolygonSymbolizer). Далее идёт смещённый узор, который отстоит от правого верхнего угла на 10 пикселей по вертикали и 10 пикселей по горизонтали, за счёт чего и достигается требуемый эффект (третий PolygonSymbolizer).

Возьмём ещё один пример — кустарники внутри земляного обрыва. Не уверен, что такое бывает, но для примера подойдёт отлично:

Кустарники

Земляной обрыв сделаем через символизацию линий. Заливка идёт по тому же принципу, что и в залежах — то есть следующий ряд со смещением, только значок другой. Причём, этот значок довольно трудно воспроизвести, используя стандартные фигуры. Гораздо проще использовать подготовленную картинку:


<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <ExternalGraphic>
          <OnlineResource xlink:type="simple" xlink:href="kust.png" />
          <Format>image/png</Format>
        </ExternalGraphic>
        <Size>32</Size>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
</PolygonSymbolizer>
<LineSymbolizer>
  <Stroke />
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>shape://vertline</WellKnownName>
          <Stroke />
        </Mark>
        <Size>6</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">6 8</CssParameter>
  </Stroke>
  <PerpendicularOffset>-3</PerpendicularOffset>
</LineSymbolizer>

Картинка кустарника Так как мы всё равно используем картинку, можно пойти на небольшую хитрость для оптимизации — а именно использовать сразу два кустарника на одной картинке, которые идут со смещением по диагонали. Таким образом мы сразу достигнем желаемого эффекта и не нужно будет делать две символизации для полигонов, как в залежах. Земляной обрыв можно сделать разными способами, один из них — сделать перпендикулярные засечки (те же засечки, что мы делали для железных дорог в символизации линий), но чтобы они шли не на исходной линии, а на смещённой внутрь. За счёт смещения засечки не будут пересекать исходную линию, а будут находиться слева от неё и смотреть внутрь полигона. Естественно, исходную линию тоже нужно прорисовать, для этого предназначен первый LineSymbolizer, который просто создаёт контур со всеми настройками по умолчанию.

Ну и последний пример, который хотелось бы рассмотреть — хвойный лес:

Хвойный лес

Итак, что мы здесь видим? Первая символизация — зелёная заливка. Далее идёт заливка кружочками — причём, со смещением, для чего нужно ещё две символизации по аналогии с залежами. И последняя символизация — ель в центре полигона. Ель одна на полигон — значит, это символизация точки, а не заливка полигона, где ели повторялись бы. Значит, нам нужно получить точку центра полигона через геометрическую функцию и в ней сделать символизацию точки:


<PolygonSymbolizer>
  <Fill>
    <CssParameter name="fill">#3E8311</CssParameter>
  </Fill>
</PolygonSymbolizer>
<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>circle</WellKnownName>
          <Stroke>
            <CssParameter name="stroke-width">0.4</CssParameter>
          </Stroke>
        </Mark>
        <Size>8</Size>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
  <VendorOption name="graphic-margin">0 16 16 0</VendorOption>
</PolygonSymbolizer>
<PolygonSymbolizer>
  <Fill>
    <GraphicFill>
      <Graphic>
        <Mark>
          <WellKnownName>circle</WellKnownName>
          <Stroke>
            <CssParameter name="stroke-width">0.4</CssParameter>
          </Stroke>
        </Mark>
        <Size>8</Size>
      </Graphic>
    </GraphicFill>
  </Fill>
  <Stroke />
  <VendorOption name="graphic-margin">12 4 4 12</VendorOption>
</PolygonSymbolizer>
<PointSymbolizer>
  <Geometry>
    <ogc:Function name="centroid">
      <ogc:PropertyName>the_geom</ogc:PropertyName>
    </ogc:Function>
  </Geometry>
  <Graphic>
    <Mark>
      <WellKnownName>ttf://Ume P Mincho S3#0x219E</WellKnownName>
      <Fill>
        <CssParameter name="fill">#000000</CssParameter>
      </Fill>
    </Mark>
    <Size>20</Size>
    <Rotation>90</Rotation>
  </Graphic>
</PointSymbolizer>

В PointSymbolizer есть тэг Geometry — он как раз и указывает, каким образом получить точку из полигона. Мы использовали функцию centroid — центр полигона. После того, как точка определена, можно ставить в неё ель. Хотя, признаться, это не ель — это двойная стрелка влево из шрифта Ume P Mincho S3, которая повернута на 90 градусов по часовой стрелке. Рисовать ель мне было лень, а WKT получился бы слишком сложным. Поэтому будем считать это елью за неимением лучшего.

И это всё, что я хотел рассмотреть в трилогии о SLD, хотя возможности SLD на этом не ограничиваются — есть ещё масса неосвещённых возможностей. Есть ещё подписи, различная символизация на разным масштабах, символизация в зависимости от атрибута объекта и многое другое. Но всё сразу охватить крайне сложно, возможно вернусь к стилям спустя некоторое время. А пока задавайте вопросы по изложенному материалу. Как минимум, постараюсь помочь в комментариях. А если я что-то упустил — дополню статью новым примером.

Символизация полигонов SLD
Метки:    

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *