Инструменты пользователя

Инструменты сайта


notes:sld:polygon

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

2016-09-18 Колосов Сергей

Заключительная часть по 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 на этом не ограничиваются — есть ещё масса неосвещённых возможностей. Есть ещё подписи, различная символизация на разным масштабах, символизация в зависимости от атрибута объекта и многое другое. Но всё сразу охватить крайне сложно, возможно вернусь к стилям спустя некоторое время.

См. так же
notes/sld/polygon.txt · Последнее изменение: 2022/08/23 15:24 — Гуреев Евгений

Яндекс.Метрика ГеоС Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki