Полигоны с отверстиями

Полигон в API представляет собой замкнутую геометрическую фигуру, задаваемую набором координат. Внутренняя модель хранения основана на массиве контуров, где каждый контур — это последовательность точек типа LatLngLiteral или LatLng.

Ключевая особенность полигона — поддержка многоконтурной структуры, позволяющей описывать как внешнюю границу, так и внутренние вырезы (holes).


Базовая структура полигона

Полигон создаётся через конструктор google.maps.Polygon, которому передаётся объект конфигурации:

const polygon = new google.maps.Polygon({
  paths: [
    { lat: 55.75, lng: 37.61 },
    { lat: 55.76, lng: 37.62 },
    { lat: 55.75, lng: 37.63 }
  ],
  strokeColor: "#FF0000",
  strokeOpacity: 0.8,
  strokeWeight: 2,
  fillColor: "#FF0000",
  fillOpacity: 0.35
});

polygon.setMap(map);

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


Многоконтурная модель данных

Поддерживаются два уровня вложенности:

  • внешний контур (outer ring)
  • внутренние контуры (holes)

Структура paths принимает следующий вид:

paths: [
  [ /* внешний контур */ ],
  [ /* отверстие 1 */ ],
  [ /* отверстие 2 */ ]
]

Каждый элемент — массив координат.


Полигоны с отверстиями

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

const polygonWithHole = new google.maps.Polygon({
  paths: [
    // Внешний контур
    [
      { lat: 55.80, lng: 37.60 },
      { lat: 55.80, lng: 37.70 },
      { lat: 55.70, lng: 37.70 },
      { lat: 55.70, lng: 37.60 }
    ],
    // Внутренний контур (отверстие)
    [
      { lat: 55.78, lng: 37.63 },
      { lat: 55.78, lng: 37.67 },
      { lat: 55.74, lng: 37.67 },
      { lat: 55.74, lng: 37.63 }
    ]
  ],
  strokeColor: "#0000FF",
  strokeOpacity: 0.8,
  strokeWeight: 2,
  fillColor: "#0000FF",
  fillOpacity: 0.2
});

polygonWithHole.setMap(map);

Геометрическая логика отверстий

Механизм обработки внутренних контуров основан на правилах заполнения области:

  • внешний контур задаёт включаемую область
  • внутренние контуры вычитаются из неё
  • используется правило заполнения (fill rule), аналогичное even-odd

Это позволяет строить сложные геометрические формы без ручного вычисления разности множеств.


Важность направления обхода точек

Хотя API во многих случаях автоматически нормализует направление обхода, в сложных сценариях (особенно при ручной генерации геометрии) учитывается ориентация:

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

При некорректной ориентации возможны артефакты отображения или неверное вычитание областей.


Работа с динамическими полигонами

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

const paths = polygonWithHole.getPaths();

// изменение внешнего контура
paths.getAt(0).push(new google.maps.LatLng(55.79, 37.65));

// изменение отверстия
paths.getAt(1).push(new google.maps.LatLng(55.76, 37.66));

Метод getPaths() возвращает MVCArray, позволяющий реактивно обновлять геометрию.


Использование MVCArray

Внутренняя структура полигона основана на MVCArray, что обеспечивает реактивное обновление на карте:

  • добавление точки автоматически перерисовывает полигон
  • удаление точки мгновенно обновляет отображение
  • изменения не требуют повторного вызова setMap
const outerPath = new google.maps.MVCArray([
  new google.maps.LatLng(55.80, 37.60),
  new google.maps.LatLng(55.80, 37.70),
  new google.maps.LatLng(55.70, 37.70),
  new google.maps.LatLng(55.70, 37.60)
]);

const holePath = new google.maps.MVCArray([
  new google.maps.LatLng(55.78, 37.63),
  new google.maps.LatLng(55.78, 37.67),
  new google.maps.LatLng(55.74, 37.67),
  new google.maps.LatLng(55.74, 37.63)
]);

const polygon = new google.maps.Polygon({
  paths: new google.maps.MVCArray([outerPath, holePath])
});

polygon.setMap(map);

Вложенные отверстия и сложные структуры

API допускает несколько уровней внутренней геометрии:

  • один внешний контур
  • множество внутренних отверстий
  • вложенные отверстия не поддерживаются напрямую (hole-in-hole требует предварительной обработки)

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


Стилизация границ и заливки

Каждый полигон поддерживает независимую настройку визуальных параметров:

  • strokeColor — цвет границы
  • strokeOpacity — прозрачность границы
  • strokeWeight — толщина линии
  • fillColor — цвет заливки
  • fillOpacity — прозрачность заливки

Отверстия наследуют заливку внешнего полигона, но визуально вычитаются из области отображения.


Обработка кликов и событий

Полигон с отверстиями сохраняет стандартную модель событий:

polygon.addListener("click", (event) => {
  console.log(event.latLng);
});

Клики внутри отверстий не считаются попаданием в полигон, так как координаты исключаются из области покрытия.


Проверка принадлежности точки

Для вычисления попадания точки в полигон используется алгоритм ray casting с учётом внутренних контуров:

  • если точка внутри внешнего контура
  • и не внутри ни одного отверстия
  • тогда точка принадлежит полигону

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


Типичные ошибки при построении отверстий

Частые проблемы при работе с многоконтурными полигонами:

  • пересечение внешнего и внутреннего контуров
  • некорректное замыкание линий (несовпадение первой и последней точки)
  • самопересекающиеся отверстия
  • несогласованные координаты (разные системы или порядок lat/lng)

Такие ошибки приводят к некорректной отрисовке или исчезновению части геометрии.


Производительность при сложной геометрии

Рост числа контуров и вершин напрямую влияет на:

  • время отрисовки
  • частоту перерасчёта геометрии
  • нагрузку на браузер при интерактивных изменениях

Оптимизация обычно достигается:

  • упрощением контуров (simplification)
  • уменьшением количества точек
  • кэшированием неизменяемых геометрий