Анализ видимости

Анализ видимости в пространственных задачах опирается на проверку пересечения лучей, сегментов и полигональных препятствий. В контексте Turf.js такие операции реализуются через комбинацию базовых геометрических функций: построение геометрий, вычисление пересечений, проверка принадлежности и измерение расстояний.

Ключевые типы объектов:

  • Point — точка наблюдения или цель
  • LineString — линия взгляда или маршрут луча
  • Polygon / MultiPolygon — препятствия, здания, зоны
  • FeatureCollection — набор объектов сцены

Базовый набор функций:

  • turf.point()
  • turf.lineString()
  • turf.polygon()
  • turf.booleanPointInPolygon()
  • turf.booleanIntersects()
  • turf.lineIntersect()
  • turf.distance()
  • turf.bearing()
  • turf.destination()

Эти операции формируют основу вычислений видимости в плоской модели координат.


Проверка нахождения точки в зоне видимости

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

  • зона обзора камеры
  • радиус действия сенсора
  • сектор обзора

Радиальная зона видимости

Проверка расстояния:

import * as turf from "@turf/turf";

const observer = turf.point([30.5, 50.5]);
const target = turf.point([30.55, 50.52]);

const dist = turf.distance(observer, target, { units: "kilometers" });

const visible = dist <= 2;

Полигональная зона обзора

const viewPolygon = turf.polygon([[
  [30.4, 50.4],
  [30.6, 50.4],
  [30.6, 50.6],
  [30.4, 50.6],
  [30.4, 50.4]
]]);

const target = turf.point([30.55, 50.55]);

const inside = turf.booleanPointInPolygon(target, viewPolygon);

Функция booleanPointInPolygon реализует алгоритм ray casting и позволяет определять принадлежность точки области видимости.


Линия видимости и базовая модель препятствий

Линия видимости (line of sight) моделируется как отрезок между наблюдателем и целевой точкой:

const observer = turf.point([30.5, 50.5]);
const target = turf.point([30.8, 50.7]);

const sightLine = turf.lineString([
  observer.geometry.coordinates,
  target.geometry.coordinates
]);

Далее выполняется проверка пересечений с препятствиями:

const building = turf.polygon([[
  [30.6, 50.55],
  [30.65, 50.55],
  [30.65, 50.65],
  [30.6, 50.65],
  [30.6, 50.55]
]]);

const intersects = turf.booleanIntersects(sightLine, building);

Если intersects === true, прямая видимости нарушена.


Проверка блокировки видимости множеством препятствий

При наличии множества объектов используется перебор FeatureCollection:

const obstacles = turf.featureCollection([
  building1,
  building2,
  building3
]);

let blocked = false;

for (const feature of obstacles.features) {
  if (turf.booleanIntersects(sightLine, feature)) {
    blocked = true;
    break;
  }
}

Для больших наборов объектов применяется предварительная фильтрация по bounding box или расстоянию.


Геометрия луча и дискретизация пространства

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

const line = turf.lineString([
  observer.geometry.coordinates,
  target.geometry.coordinates
]);

const segmentLength = 0.01; // км
const lineLength = turf.distance(observer, target);

const steps = Math.ceil(lineLength / segmentLength);

Далее создаются промежуточные точки:

const sampledPoints = [];

for (let i = 0; i <= steps; i++) {
  const segment = i / steps;
  const point = turf.along(line, lineLength * segment);
  sampledPoints.push(point);
}

Каждая точка проверяется на попадание в препятствие:

let blocked = false;

for (const p of sampledPoints) {
  for (const obstacle of obstacles.features) {
    if (turf.booleanPointInPolygon(p, obstacle)) {
      blocked = true;
      break;
    }
  }
  if (blocked) break;
}

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


Углы обзора и направленность видимости

Для ограничения видимости по направлению используется азимут:

const observer = turf.point([30.5, 50.5]);
const target = turf.point([30.55, 50.55]);

const angle = turf.bearing(observer, target);

Далее вводится поле зрения:

const fovMin = 30;
const fovMax = 120;

const isInView = angle >= fovMin && angle <= fovMax;

Такой подход используется в моделях камер, радаров и игровых системах.


Построение сектора обзора

Сектор обзора формируется через генерацию дуги:

const center = turf.point([30.5, 50.5]);

const radius = 2;
const steps = 64;

const arcPoints = [];

for (let i = 0; i <= steps; i++) {
  const angle = 30 + (90 * i) / steps;
  const destination = turf.destination(center, radius, angle, { units: "kilometers" });
  arcPoints.push(destination.geometry.coordinates);
}

arcPoints.push(center.geometry.coordinates);

const viewSector = turf.polygon([arcPoints]);

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


Пересечение линий видимости с геометрией сцены

Функция lineIntersect позволяет определить точки пересечения:

const intersections = turf.lineIntersect(sightLine, obstacles);

Результат содержит все точки пересечения линии с препятствиями.

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

const observerToTarget = turf.distance(observer, target);

let blocked = false;

intersections.features.forEach(intersection => {
  const dist = turf.distance(observer, intersection);
  if (dist < observerToTarget) {
    blocked = true;
  }
});

Оптимизация анализа видимости

При работе с большим количеством объектов применяются следующие методы:

Предварительная фильтрация по расстоянию

const maxRange = 5;

const filtered = obstacles.features.filter(f =>
  turf.distance(observer, f) <= maxRange
);

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

const bbox = turf.bbox(viewSector);
const bboxPolygon = turf.bboxPolygon(bbox);

const candidates = obstacles.features.filter(f =>
  turf.booleanIntersects(f, bboxPolygon)
);

Индексация пространственных данных

Для высоконагруженных систем используется R-tree или аналоги через сторонние библиотеки, где Turf.js применяется только для финальной геометрической проверки.


Модель затенения и плотных препятствий

В задачах городской геометрии здания рассматриваются как вертикальные экструзии, но Turf.js работает в 2D, поэтому применяется упрощение:

  • полигон здания = проекция основания
  • высота учитывается отдельно
const buildingHeight = 30;
const observerHeight = 2;

const heightBlocked = buildingHeight > observerHeight;

Комбинация с пересечением линии даёт финальную модель видимости:

  • есть пересечение по XY
  • высота препятствия перекрывает линию взгляда

Множественные цели и карта видимости

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

const visibleTargets = [];

for (const target of targets.features) {
  const line = turf.lineString([
    observer.geometry.coordinates,
    target.geometry.coordinates
  ]);

  let blocked = false;

  for (const obs of obstacles.features) {
    if (turf.booleanIntersects(line, obs)) {
      blocked = true;
      break;
    }
  }

  if (!blocked) {
    visibleTargets.push(target);
  }
}

Комбинированная модель анализа видимости

Полная проверка включает последовательность условий:

  • расстояние до цели
  • угол относительно направления наблюдения
  • отсутствие пересечений с препятствиями
  • принадлежность к зоне обзора
const dist = turf.distance(observer, target);
const angle = turf.bearing(observer, target);

const inRange = dist < maxRange;
const inAngle = angle >= fovMin && angle <= fovMax;

let blocked = false;

for (const obs of obstacles.features) {
  if (turf.booleanIntersects(
    turf.lineString([observer.geometry.coordinates, target.geometry.coordinates]),
    obs
  )) {
    blocked = true;
    break;
  }
}

const visible = inRange && inAngle && !blocked;

Эта модель используется в системах GIS-аналитики, симуляциях и пространственном моделировании сцен.