Создание тестовых данных

Тестовые данные в контексте Turf.js строятся вокруг стандарта GeoJSON, где любая геометрия представляется в виде объектов Feature, FeatureCollection и базовых геометрий (Point, LineString, Polygon). Структура данных определяет корректность работы большинства геопространственных функций.

Минимальные формы:

const point = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [30.5, 50.45]
  },
  properties: {}
};

Коллекция объектов:

const collection = {
  type: "FeatureCollection",
  features: [point]
};

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


Создание точечных данных

Точечные данные используются для тестирования расстояний, кластеризации, поиска ближайших объектов и пространственных фильтров.

Базовое создание через @turf/helpers:

import { point, featureCollection } from "@turf/helpers";

const p1 = point([30, 50], { id: 1 });
const p2 = point([31, 51], { id: 2 });

const points = featureCollection([p1, p2]);

Генерация массива точек вручную:

const points = {
  type: "FeatureCollection",
  features: Array.from({ length: 100 }).map((_, i) => ({
    type: "Feature",
    geometry: {
      type: "Point",
      coordinates: [
        30 + Math.random(),
        50 + Math.random()
      ]
    },
    properties: {
      index: i
    }
  }))
};

Ключевой аспект тестовых точек — контроль диапазонов координат. Часто используется ограничивающий прямоугольник (bbox):

const bbox = [29, 49, 32, 52];

Генерация случайных точек внутри области

Для создания более реалистичных данных применяется randomPoint:

import { randomPoint } from "@turf/random";

const points = randomPoint(200, {
  bbox: [29, 49, 32, 52]
});

Такая генерация позволяет проверять:

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

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


Формирование линейных данных

Линии используются для тестирования маршрутизации, измерения длины, пересечений и анализа геометрии.

Простейшая линия:

import { lineString } from "@turf/helpers";

const line = lineString([
  [30, 50],
  [31, 51],
  [32, 50.5]
]);

Генерация ломаной:

const line = {
  type: "Feature",
  geometry: {
    type: "LineString",
    coordinates: Array.from({ length: 50 }).map((_, i) => [
      30 + i * 0.1,
      50 + Math.sin(i * 0.2)
    ])
  },
  properties: {}
};

Для тестов важно варьировать:

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

Создание полигонов

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

Базовый полигон:

import { polygon } from "@turf/helpers";

const poly = polygon([[
  [30, 50],
  [31, 50],
  [31, 51],
  [30, 51],
  [30, 50]
]]);

Генерация прямоугольных тестовых областей:

function createGridPolygon(x, y, size) {
  return polygon([[
    [x, y],
    [x + size, y],
    [x + size, y + size],
    [x, y + size],
    [x, y]
  ]]);
}

Полигоны для тестирования часто включают:

  • вогнутые формы
  • самопересекающиеся контуры (для проверки устойчивости алгоритмов)
  • вложенные кольца (holes)

Пример полигона с отверстием:

const polyWithHole = polygon([
  [
    [30, 50],
    [32, 50],
    [32, 52],
    [30, 52],
    [30, 50]
  ],
  [
    [30.5, 50.5],
    [31.5, 50.5],
    [31.5, 51.5],
    [30.5, 51.5],
    [30.5, 50.5]
  ]
]);

Использование bounding box для генерации данных

Bounding box является основным инструментом ограничения тестовых сценариев.

const bbox = [29, 49, 32, 52];

С его помощью строятся:

  • случайные точки
  • сетки
  • полигоны фиксированного размера
  • контрольные зоны пересечений

Генерация равномерной сетки точек:

function createGridPoints(bbox, step) {
  const [minX, minY, maxX, maxY] = bbox;
  const points = [];

  for (let x = minX; x <= maxX; x += step) {
    for (let y = minY; y <= maxY; y += step) {
      points.push({
        type: "Feature",
        geometry: {
          type: "Point",
          coordinates: [x, y]
        },
        properties: {}
      });
    }
  }

  return {
    type: "FeatureCollection",
    features: points
  };
}

Детерминированные тестовые данные

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

Простейший линейный конгруэнтный генератор:

function createRNG(seed) {
  let state = seed;

  return function () {
    state = (state * 1664525 + 1013904223) % 4294967296;
    return state / 4294967296;
  };
}

const rand = createRNG(42);

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

const points = Array.from({ length: 50 }).map(() => ({
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [
      30 + rand(),
      50 + rand()
    ]
  },
  properties: {}
}));

Тестовые сценарии с пересечениями

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

Создание пересекающихся линий:

const lineA = lineString([[30, 50], [32, 52]]);
const lineB = lineString([[30, 52], [32, 50]]);

Создание перекрывающихся полигонов:

const polyA = polygon([[
  [30, 50],
  [32, 50],
  [32, 52],
  [30, 52],
  [30, 50]
]]);

const polyB = polygon([[
  [31, 51],
  [33, 51],
  [33, 53],
  [31, 53],
  [31, 51]
]]);

Такие структуры используются для проверки:

  • boolean-операций
  • вычисления площади пересечения
  • корректности топологических алгоритмов

Генерация окружностей и буферных зон

Для тестирования операций буферизации применяются окружности:

import { circle } from "@turf/turf";

const center = [30, 50];

const c = circle(center, 5, {
  steps: 64,
  units: "kilometers"
});

Окружности используются для проверки:

  • буферов вокруг точек
  • зон влияния
  • радиальных поисков

Создание сеток и тайловых структур

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

function createTileGrid(bbox, rows, cols) {
  const [minX, minY, maxX, maxY] = bbox;

  const dx = (maxX - minX) / cols;
  const dy = (maxY - minY) / rows;

  const features = [];

  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      const x = minX + j * dx;
      const y = minY + i * dy;

      features.push({
        type: "Feature",
        geometry: {
          type: "Polygon",
          coordinates: [[
            [x, y],
            [x + dx, y],
            [x + dx, y + dy],
            [x, y + dy],
            [x, y]
          ]]
        },
        properties: { row: i, col: j }
      });
    }
  }

  return {
    type: "FeatureCollection",
    features
  };
}

Комбинирование различных типов геометрий

Сложные тестовые наборы формируются из смешанных коллекций:

const mixed = {
  type: "FeatureCollection",
  features: [
    point([30, 50]),
    lineString([[30, 50], [31, 51]]),
    polygon([[
      [30, 50],
      [31, 50],
      [31, 51],
      [30, 51],
      [30, 50]
    ]])
  ]
};

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


Краевые и ошибочные случаи

Тестовые данные должны включать некорректные и пограничные структуры:

  • пустые FeatureCollection
  • null координаты
  • вырожденные линии (одна точка)
  • полигоны без замыкания
  • координаты вне допустимых диапазонов

Пример вырожденной линии:

const degenerateLine = lineString([
  [30, 50],
  [30, 50]
]);

Такие случаи выявляют слабые места геометрических алгоритмов и обработчиков ошибок.