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

Стилизация полигонов в OpenLayers реализуется через систему стилей, основанную на объектах ol/style/Style, которые применяются к геометрии слоя и позволяют управлять внешним видом заливки, обводки и дополнительной графики, связанной с полигональными объектами.

Основой стилизации выступает комбинация трёх ключевых классов:

  • Fill — отвечает за заливку области полигона
  • Stroke — управляет контуром
  • Style — объединяет параметры и применяется к объекту
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';

const polygonStyle = new Style({
  fill: new Fill({
    color: 'rgba(0, 150, 255, 0.4)'
  }),
  stroke: new Stroke({
    color: '#1f4e79',
    width: 2
  })
});

Такой стиль применяется ко всем полигонам слоя одинаково и является статическим вариантом оформления.

Применение стиля к векторному слою

Стилизация задаётся через свойство style у VectorLayer:

import VectorLayer from 'ol/layer/Vector';

const layer = new VectorLayer({
  source: vectorSource,
  style: polygonStyle
});

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

Динамическая стилизация через функцию

Для более гибкого управления используется функция стиля, принимающая feature и текущий масштабный уровень.

const styleFunction = function (feature, resolution) {
  return new Style({
    fill: new Fill({
      color: feature.get('color') || 'rgba(0, 150, 255, 0.3)'
    }),
    stroke: new Stroke({
      color: '#333',
      width: resolution < 10 ? 3 : 1
    })
  });
};

layer.setStyle(styleFunction);

Здесь важны два механизма:

  • feature.get() позволяет извлекать атрибуты геометрии
  • resolution отражает текущий масштаб карты

Это позволяет изменять внешний вид полигона в зависимости от данных или уровня приближения.

Управление заливкой: однотонная и полупрозрачная заливка

Заливка полигона задаётся через RGBA или HEX с прозрачностью.

fill: new Fill({
  color: 'rgba(255, 0, 0, 0.2)'
})

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

Градиентная заливка через Canvas

OpenLayers не предоставляет встроенных градиентов в Fill, но поддерживает использование canvas-объектов:

const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

const gradient = context.createLinearGradient(0, 0, 100, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(1, 'red');

const style = new Style({
  fill: new Fill({
    color: gradient
  })
});

Такой подход используется при визуализации интенсивностей или статистических показателей.

Обводка полигона и её поведение

Контур задаётся через Stroke, где важны:

  • цвет
  • толщина линии
  • тип соединения
  • пунктир
stroke: new Stroke({
  color: '#000',
  width: 2,
  lineDash: [4, 6],
  lineJoin: 'round'
})

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

Стиль на основе атрибутов объектов

Часто полигоны содержат свойства, определяющие их внешний вид:

const styleFunction = (feature) => {
  const type = feature.get('type');

  const colors = {
    forest: 'rgba(34, 139, 34, 0.4)',
    water: 'rgba(0, 0, 255, 0.3)',
    urban: 'rgba(128, 128, 128, 0.3)'
  };

  return new Style({
    fill: new Fill({
      color: colors[type] || 'rgba(200, 200, 200, 0.3)'
    }),
    stroke: new Stroke({
      color: '#222',
      width: 1
    })
  });
};

Такой подход формирует правило-ориентированную картографическую визуализацию.

Масштабозависимая стилизация

Разрешение карты влияет на детализацию отображения:

const styleFunction = (feature, resolution) => {
  const visible = resolution < 50;

  return new Style({
    fill: new Fill({
      color: 'rgba(0, 0, 255, 0.2)'
    }),
    stroke: new Stroke({
      color: '#003366',
      width: visible ? 2 : 0
    })
  });
};

При уменьшении масштаба можно скрывать обводку или упрощать визуализацию.

Кэширование стилей для повышения производительности

Создание новых объектов стиля на каждый рендер снижает производительность. Оптимизация достигается через кеширование:

const styleCache = {};

const styleFunction = (feature) => {
  const key = feature.get('type');

  if (!styleCache[key]) {
    styleCache[key] = new Style({
      fill: new Fill({
        color: key === 'water'
          ? 'rgba(0, 0, 255, 0.3)'
          : 'rgba(0, 255, 0, 0.3)'
      }),
      stroke: new Stroke({
        color: '#333',
        width: 1
      })
    });
  }

  return styleCache[key];
};

Такой подход особенно важен при работе с тысячами полигонов.

Комбинирование нескольких стилей

Один объект может иметь несколько визуальных слоёв оформления:

const style = [
  new Style({
    stroke: new Stroke({
      color: '#000',
      width: 3
    })
  }),
  new Style({
    fill: new Fill({
      color: 'rgba(255, 255, 0, 0.3)'
    })
  })
];

Первый стиль отвечает за контур, второй — за заливку. Такой подход используется для сложных эффектов.

Использование изображений в стиле полигона

Помимо заливки цветом возможно использование паттернов:

const pattern = new Fill({
  color: 'rgba(0,0,0,0)',
  // canvas pattern может быть задан через createPattern
});

Через canvas можно задать текстуру:

const img = new Image();
img.src = 'pattern.png';

img.onl oad = function () {
  const patternCanvas = document.createElement('canvas');
  const ctx = patternCanvas.getContext('2d');

  const pattern = ctx.createPattern(img, 'repeat');

  const style = new Style({
    fill: new Fill({
      color: pattern
    })
  });
};

Стилизация на основе геометрических характеристик

Площадь или форма полигона может влиять на стиль:

const styleFunction = (feature) => {
  const geometry = feature.getGeometry();
  const area = geometry.getArea();

  return new Style({
    fill: new Fill({
      color: area > 1000000
        ? 'rgba(255, 0, 0, 0.4)'
        : 'rgba(0, 255, 0, 0.4)'
    }),
    stroke: new Stroke({
      color: '#000',
      width: 1
    })
  });
};

Такой метод применяется в тематических картах.

Сложные сценарии стилизации

При комбинировании условий учитываются сразу несколько факторов:

  • атрибуты объекта
  • масштаб
  • геометрия
  • состояние интерфейса
const styleFunction = (feature, resolution) => {
  const status = feature.get('status');
  const area = feature.getGeometry().getArea();

  let fillColor = 'rgba(200,200,200,0.3)';

  if (status === 'active' && area > 500000) {
    fillColor = 'rgba(255,0,0,0.4)';
  } else if (status === 'active') {
    fillColor = 'rgba(255,165,0,0.3)';
  }

  return new Style({
    fill: new Fill({ color: fillColor }),
    stroke: new Stroke({
      color: '#111',
      width: resolution < 20 ? 2 : 1
    })
  });
};

Поведение при перекрытии полигонов

Когда полигоны накладываются друг на друга, визуальное восприятие зависит от:

  • прозрачности заливки
  • порядка слоёв
  • zIndex стиля
new Style({
  zIndex: 10,
  fill: new Fill({
    color: 'rgba(0,0,255,0.3)'
  })
});

Более высокий zIndex поднимает объект выше в отрисовке.

Разделение логики стиля

Для крупных проектов стили выносятся в отдельные модули:

export function getPolygonStyle(feature, resolution) {
  return new Style({
    fill: new Fill({
      color: feature.get('color')
    }),
    stroke: new Stroke({
      color: '#444',
      width: 1
    })
  });
}

Такой подход упрощает сопровождение и масштабирование кода.