Символизация линий 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 line style</Title>
      <FeatureTypeStyle>
        <Rule>
          <Title>Здесь идёт заголовок легенды</Title>
            
          <!-- ТЕЛО СТИЛЯ ИДЁТ ЗДЕСЬ -->
            
        </Rule>
      </FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
</StyledLayerDescriptor>


LineSymbolizer

Начнём мы снова со стиля по умолчанию, который GeoServer использует для линий.

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

Стиль по сравнению с точками весьма простой — в нём задаётся только цвет линии:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke">#0000FF</CssParameter>
  </Stroke>
</LineSymbolizer>

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

  • Geometry — опциональный, определяет источник геометрии
  • Stroke — обязательный, непосредственная стилизация линии
  • PerpendicularOffset — опциональный, создаёт линию, которая параллельна исходной и отстоит от неё на заданное количество пикселей

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

PerpendicularOffset, как правило, используется для создания копии исходной линии параллельно основной линии. Используется не так часто, однако в некоторых случаях наиболее удобное решение.

Stroke единственный из тэгов внутри LineSymbolizer, который является обязательным. Вероятней всего, он покроет все ваши запросы. Он заслуживает детального рассмотрения.


Stroke

Мы рассмотрим два тэга внутри StrokeGraphicStroke и CssParameter

GraphicStroke может содержать всё то, что мы рассматривали в символизации точек.

Тэгов CssParameter может быть много. Они задают цвет, толщину и другие базовые параметры символизации линии в зависимости от значения атрибута name:

  • stroke — цвет заливки в формате #RRGGBB (по умолчанию #000000)
  • stroke-width — толщина в пикселях. Значение по умолчанию — 1
  • stroke-opacity — непрозрачность, значение от 0 (полностью прозрачный) до 1 (полностью непрозрачный). Значение по умолчанию — 1
  • stroke-linejoin — определяет, как линии будут отображаться в месте пересечения сегментов. Возможные значения — mitre (острые углы, значение по умолчанию), round (скруглённые углы) и bevel (диагональные углы)
  • stroke-linecap — как линии будут отображаться на концах. Допустимы butt (резкие квадратные грани, значение по умолчанию), round (округлые грани) и square (слегка вытянутые грани)
  • stroke-dasharray — Всевозможные варианты штрихпунктирных линий. Значением является серия чисел, разделённых пробелами. Нечётные числа (первое, третье, …) задаёт длину штриха в пикселях. Чётные числа (второе, четвёртое, …) задают длину разрыва между штрихами. Например, последовательность 4 2 2 2 означает, что сначала идёт 4 пикселя линии, затем 2 пикселя пустоты, затем 2 пикселя линии и ещё 2 пикселя пустоты. После этого узор повторится
  • stroke-dashoffset — смещение узора stroke-dasharray относительно начала линии в пикселях. По умолчанию 0

Примеры

Начнём с несложного примера, где зададим цвет, толщину и небольшую прозрачность:

Цвет, толщина и прозрачность

Этого можно добиться с помощью следующего стиля:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke">#e55e30</CssParameter>
    <CssParameter name="stroke-width">0.2</CssParameter>
    <CssParameter name="stroke-opacity">0.8</CssParameter>
  </Stroke>
</LineSymbolizer>

За счёт прозрачности и малой толщины линия получилась «лёгкой» — такая не будет брать на себя акцент в сложном слое, состоящим из множества объектов, но в то же время её хорошо видно. Теперь сделаем штрихпунктирную линию:

Штрихпунктирная линия

Соответствующий стиль:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke">#86523a</CssParameter>
    <CssParameter name="stroke-width">2</CssParameter>
    <CssParameter name="stroke-dasharray">16 2 2 2 2 2</CssParameter>
  </Stroke>
</LineSymbolizer>

Сначала идёт штрих 16 пикселей, затем 2 пикселя пробел, 2 пикселя линия, 2 пробел, 2 линия и в конце ещё 2 пикселя пробел, чтобы при повторении узора 16 пикселей не слились с последним штрихом в 2 пикселя. stroke-dasharray работает и с GraphicStroke, что позволяет задавать, сколько места отвести под фигуру, а сколько сделать отступ:

Звёзды

… и стиль, с помощью которого сделана эта символизация:


<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>star</WellKnownName>
          <Fill>
            <CssParameter name="fill">#fcff05</CssParameter>
          </Fill>
          <Stroke>
            <CssParameter name="stroke">#000000</CssParameter>
            <CssParameter name="stroke-width">0.3</CssParameter>
          </Stroke>
        </Mark>
      <Size>10</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">10 10</CssParameter>
  </Stroke>
</LineSymbolizer>

Как я уже говорил, GraphicStroke может содержать все те тэги, которые были рассмотрены в символизации точек: стандартные и расширенные символы, картинки, символы шрифта и WKT. В примере выше мы использовали стандартный символ — звезду, для которой задали границу и заливку. Проблема подобной символизации линий в том, что контур линии разглядеть довольно сложно, особенно в местах скопления нескольких линий. Было бы неплохо, чтобы все звёзды были «сцеплены» друг с другом. И это вполне реально сделать! Для этого нужно задать два элемента LineSymbolizer — один проведёт контур линии, второй — звёзды:

Звёзды с линией

Теперь линии прослеживаются гораздо лучше. Такой эффект достигается за счёт следующего стиля:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke">#000000</CssParameter>
    <CssParameter name="stroke-width">0.5</CssParameter>
  </Stroke>
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>star</WellKnownName>
          <Fill>
            <CssParameter name="fill">#fcff05</CssParameter>
          </Fill>
          <Stroke>
            <CssParameter name="stroke">#000000</CssParameter>
            <CssParameter name="stroke-width">0.3</CssParameter>
          </Stroke>
        </Mark>
      <Size>10</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">10 10</CssParameter>
  </Stroke>
</LineSymbolizer>

Порядок следования тэгов важен! Здесь мы сначала определили линию, поэтому она идёт ниже звёзд. Если поменять эти LineSymbolizer местами, чёрная линия получится сверху и мы получим перечёркнутые звёзды.


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

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

Строящиеся узкоколейные железные дороги

Для воспроизведения данного стиля сделаем две символизации — штрихпунктирная линия и символ shape://vertline для перпендикулярных засечек:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke-width">1.5</CssParameter>
    <CssParameter name="stroke-dasharray">14 4</CssParameter>
  </Stroke>
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>shape://vertline</WellKnownName>
          <Stroke />
        </Mark>
        <Size>6</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">4 14</CssParameter>
    <CssParameter name="stroke-dashoffset">14</CssParameter>
  </Stroke>
</LineSymbolizer>

Первое, на что стоит обратить внимание, — мы задали смещение перпендикулярных засечек через stroke-dashoffset, чтобы засечки появились именно на середине штриха линии (значение вычислено методом проб и ошибок). Второе — пустой тэг <Stroke />. Есть большая разница между пустым тегом и отсутствующим вовсе. Если тэга Stroke не будет — не будет и границы. Если будет пустой тэг — все значения возьмутся по умолчанию (чёрная линия толщиной в пиксель).

Идём дальше — границы полярных владений:

Границы полярных владений

Здесь можно обойтись тремя тэгами LineSymbolizer:

  • Толстая штрихпунктирная линия с небольшой прозрачностью, которая идёт по основному контуру и имеет равную длину штриха и разрыва
  • Перпендикулярные засечки, расстояние между которыми рассчитано так, чтобы попадать на начало и конец штриха
  • Кружочки без заливки с границей, которые идут в разрывах линии


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke-width">4</CssParameter>
    <CssParameter name="stroke-dasharray">8 8</CssParameter>
    <CssParameter name="stroke-opacity">0.6</CssParameter>
  </Stroke>
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>shape://vertline</WellKnownName>
          <Stroke />
        </Mark>
        <Size>10</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">8 0</CssParameter>
    <CssParameter name="stroke-dashoffset">5</CssParameter>
  </Stroke>
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>circle</WellKnownName>
          <Stroke />
        </Mark>
        <Size>4</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">4 12</CssParameter>
    <CssParameter name="stroke-dashoffset">6</CssParameter>
  </Stroke>
</LineSymbolizer>


Более сложные примеры

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

Древние исторические стены

Здесь отлично подойдёт WKT. Нам нужно лишь задать минимальную часть повторяющегося сегмента, остальное GeoServer сделает за нас:


<LineSymbolizer>
  <Stroke>
    <GraphicStroke>
      <Graphic>
        <Mark>
          <WellKnownName>
          	wkt://MULTILINESTRING(
                (0 1, 0 -1),
                (0 -1, 2 -1),
                (2 -1, 2 1),
                (2 1, 4 1)
              )
          </WellKnownName>
          <Stroke />
        </Mark>
        <Size>6</Size>
      </Graphic>
    </GraphicStroke>
    <CssParameter name="stroke-dasharray">6 6</CssParameter>
  </Stroke>
</LineSymbolizer>


WKT древние исторические стены В самом узоре нет ничего сложного. Он состоит из четырех линий, которые образуют непрерывную кривую. На картинке слева пунктиром показана исходная линия, синим — символизация WKT. GeoServer будет повторять этот узор, в результате чего точка (0, 1) замкнется с (4.1) и получится наша непрерывная каменная стена. stroke-dasharray понадобился, так как GeoServer слишком рано начинал повторять линию, что приводило к тому, что следующий узор налезал на предыдущий.

Ещё один интересный пример — зимние дороги:

Зимние дороги

Здесь идёт две линии со смещением, для чего нам понадобится тэг PerpendicularOffset. Одну линию мы разместим на 4 пикселя справа от исходной, вторую на 4 пикселя слева, обоим линиям зададим одинаковый пунктир:


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke-width">2</CssParameter>
    <CssParameter name="stroke-dasharray">2 4</CssParameter>
  </Stroke>
  <PerpendicularOffset>-4</PerpendicularOffset>
</LineSymbolizer>
<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke-width">2</CssParameter>
    <CssParameter name="stroke-dasharray">2 4</CssParameter>
  </Stroke>
  <PerpendicularOffset>4</PerpendicularOffset>
</LineSymbolizer>

Ну и последнее, что хотелось бы затронуть — предназначение тэга Geometry. С помощью этого тэга можно выделить отдельные точки линии и сделать символизацию для этих точек через PointSymbolizer. Как это работает — покажу на небольшом примере. Допустим, нам нужно, чтобы в конце линии была стрелка:

Линия со стрелкой на конце

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


<LineSymbolizer>
  <Stroke>
    <CssParameter name="stroke">#0000FF</CssParameter>
    <CssParameter name="stroke-width">2</CssParameter>
  </Stroke>
</LineSymbolizer>
<PointSymbolizer>
  <Geometry>
    <ogc:Function name="endPoint">
      <ogc:PropertyName>the_geom</ogc:PropertyName>
    </ogc:Function>
  </Geometry>
  <Graphic>
    <Mark>
      <WellKnownName>shape://oarrow</WellKnownName>
      <Fill>
        <CssParameter name="fill">#0000FF</CssParameter>
      </Fill>
      <Stroke>
        <CssParameter name="stroke">#0000FF</CssParameter>
        <CssParameter name="stroke-width">2</CssParameter>
      </Stroke>
    </Mark>
    <Size>30</Size>
    <Rotation>
      <ogc:Function name="endAngle">
        <ogc:PropertyName>the_geom</ogc:PropertyName>
      </ogc:Function>
    </Rotation>
  </Graphic>
</PointSymbolizer>

LineSymbolizer пропустим, его уже достаточно рассмотрели. В символизации точки мы определяем, что мы возьмём последнюю точку (функция endPoint) из геометрии линии (the_geom). Затем в Mark мы задаём стрелку и её заливку с границей. Но если стрелку указать таким образом, то все они будут смотреть в одну сторону, что нас не устраивает. Для того, чтобы угол стрелки совпадал с углом последнего сегмента, зададим для символизации точки поворот Rotation равным углу последнего сегмента endAngle. Всего функций очень много и порой они очень помогают.

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

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

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

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