Работа с REST API

Работа с REST API в контексте геоинформационных веб-приложений строится вокруг принципа потоковой загрузки пространственных данных и их преобразования в источники слоёв. В OpenLayers REST-запросы не являются отдельным уровнем абстракции — они интегрируются в источники (source), которые управляют жизненным циклом данных, их кешированием, повторными запросами и привязкой к текущему экстенту карты.

Основные типы REST-взаимодействий в OpenLayers:

  • загрузка векторных данных (GeoJSON, TopoJSON, custom JSON)
  • тайловые сервисы (XYZ, WMTS)
  • OGC-сервисы (WFS, WMS, WMTS)
  • кастомные HTTP API (фильтры, пагинация, поиск объектов)

Векторные источники и REST-загрузка данных

Базовая модель REST-загрузки векторных данных строится вокруг ol/source/Vector, который поддерживает динамический загрузчик через loader.

Типовой сценарий: сервер отдаёт GeoJSON по HTTP-запросу.

import VectorSource fr om 'ol/source/Vector.js';
import GeoJSON fr om 'ol/format/GeoJSON.js';

const vectorSource = new VectorSource({
  format: new GeoJSON(),
  loader: function (extent, resolution, projection) {
    const url = `/api/features?bbox=${extent.join(',')}`;

    fetch(url)
      .then(response => response.json())
      .then(data => {
        const features = new GeoJSON().readFeatures(data, {
          featureProjection: projection
        });

        vectorSource.addFeatures(features);
      });
  }
});

Ключевой момент: extent передаётся автоматически и позволяет реализовать серверную фильтрацию по bounding box, что критично для производительности.


REST API с фильтрацией по bbox

Практически все геосервисы используют фильтрацию по границам:

  • bbox=minx,miny,maxx,maxy
  • geometry=intersects
  • layer, type, category

Пример запроса:

const url = new URL('/api/objects', window.location.origin);

url.searchParams.set('bbox', extent.join(','));
url.searchParams.set('lim it', 500);
url.searchParams.set('layer', 'roads');

На стороне сервера это позволяет:

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

Асинхронный loader и контроль состояния загрузки

loader в OpenLayers может управлять сложной логикой запросов, включая отмену и повтор.

const controller = new AbortController();

const source = new VectorSource({
  loader: (extent) => {
    controller.abort();
    const localController = new AbortController();

    fetch(`/api/data?bbox=${extent.join(',')}`, {
      signal: localController.signal
    })
      .then(r => r.json())
      .then(json => {
        const features = new GeoJSON().readFeatures(json);
        source.addFeatures(features);
      });

    controller.signal = localController.signal;
  }
});

Такая схема важна при:

  • панорамировании карты
  • быстром изменении масштаба
  • интерактивных фильтрах

Работа с пагинацией REST API

REST-сервисы часто возвращают данные порциями:

  • page / lim it
  • offset / limit
  • cursor-based pagination

Пример с пагинацией:

let page = 0;

function loadNext(extent) {
  fetch(`/api/features?bbox=${extent.join(',')}&page=${page}`)
    .then(r => r.json())
    .then(data => {
      source.addFeatures(new GeoJSON().readFeatures(data));
      page += 1;
    });
}

При геозапросах пагинация часто комбинируется с bbox-фильтрацией, что требует серверной оптимизации индексов.


WFS REST и фильтрация по пространственным условиям

OGC WFS (Web Feature Service) представляет REST-подобный интерфейс для пространственных запросов.

Пример запроса:

/geoserver/wfs?
service=WFS&
version=2.0.0&
request=GetFeature&
typeName=roads&
outputFormat=application/json&
bbox=...

В OpenLayers это часто оборачивается через VectorSource:

const source = new VectorSource({
  format: new GeoJSON(),
  url: (extent) => {
    return `/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&` +
           `typeName=roads&outputFormat=application/json&bbox=${extent.join(',')}`;
  }
});

Тайловые REST API и XYZ источники

REST не ограничивается GeoJSON. Тайловые серверы используют шаблоны URL:

import TileLayer from 'ol/layer/Tile.js';
import XYZ from 'ol/source/XYZ.js';

const layer = new TileLayer({
  source: new XYZ({
    url: '/tiles/{z}/{x}/{y}.png'
  })
});

Здесь REST-структура выражается через path parameters:

  • {z} — zoom
  • {x}, {y} — координаты тайла

Кастомные REST API с геометрическими преобразованиями

REST-сервисы часто возвращают данные в разных CRS (Coordinate Reference System). OpenLayers требует согласованного преобразования.

import {transform} from 'ol/proj.js';

const wgs84 = 'EPSG:4326';
const webMercator = 'EPSG:3857';

const coord = transform([71.4304, 51.1282], wgs84, webMercator);

При загрузке REST-данных важно учитывать:

  • сервер может отдавать EPSG:4326
  • карта обычно использует EPSG:3857
  • требуется featureProjection

REST API с авторизацией

Многие геосервисы защищены токенами:

fetch('/api/features', {
  headers: {
    'Authorization': 'Bearer TOKEN_VALUE'
  }
});

В OpenLayers это интегрируется через loader:

const source = new VectorSource({
  loader: (extent) => {
    fetch(`/api/data?bbox=${extent.join(',')}`, {
      headers: {
        'Authorization': 'Bearer TOKEN'
      }
    })
      .then(r => r.json())
      .then(json => source.addFeatures(new GeoJSON().readFeatures(json)));
  }
});

Типовые схемы:

  • JWT
  • API key
  • session cookie
  • OAuth2 proxy

Обработка CORS в REST-геосервисах

При работе с браузерным клиентом REST API обязан поддерживать CORS:

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Authorization

Отсутствие корректных заголовков приводит к блокировке загрузки данных даже при корректном URL.


Кеширование REST-ответов

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

1. HTTP caching

Cache-Control: max-age=3600
ETag: "abc123"

2. клиентский кеш OpenLayers

const source = new VectorSource({
  cacheSize: 200
});

3. мемоизация bbox-запросов

const cache = new Map();

function getKey(extent) {
  return extent.join(',');
}

Обновление данных при изменении экстента

REST-запросы часто привязаны к событию изменения карты:

map.on('moveend', () => {
  const extent = map.getView().calculateExtent();
  vectorSource.clear();
  loadData(extent);
});

Оптимизации:

  • debounce запросов
  • ограничение частоты (throttle)
  • объединение bbox при панорамировании

Обработка ошибок REST-запросов

Типовые сценарии:

  • 400 — некорректный bbox
  • 401 — отсутствие авторизации
  • 404 — слой не найден
  • 500 — ошибка сервера
fetch(url)
  .then(r => {
    if (!r.ok) throw new Error(r.status);
    return r.json();
  })
  .catch(err => {
    console.error('REST error:', err);
  });

Производительность REST-загрузки в геоклиентах

Ключевые факторы:

  • ограничение числа объектов (limit)
  • серверная генерализация геометрии
  • кластеризация на сервере или клиенте
  • тайловая векторизация (vector tiles)

REST-запросы становятся узким местом при:

  • больших экстентах
  • высокой плотности объектов
  • частом pan/zoom

REST API и векторные тайлы

Современный подход — замена GeoJSON на vector tiles:

import VectorTileLayer from 'ol/layer/VectorTile.js';
import VectorTileSource from 'ol/source/VectorTile.js';

const layer = new VectorTileLayer({
  source: new VectorTileSource({
    url: '/tiles/{z}/{x}/{y}.pbf'
  })
});

Преимущества REST-архитектуры:

  • минимальный объём данных
  • кэшируемость на CDN
  • высокая скорость рендеринга

Интеграция REST API с фильтрами интерфейса

REST-запросы часто динамически формируются на основе UI-фильтров:

function buildUrl(extent, filters) {
  const url = new URL('/api/features', window.location.origin);

  url.searchParams.set('bbox', extent.join(','));
  url.searchParams.set('type', filters.type);
  url.searchParams.set('year', filters.year);

  return url;
}

Такая модель позволяет:

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

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

Один слой может агрегировать данные из нескольких API:

Promise.all([
  fetch('/api/roads'),
  fetch('/api/buildings')
])
  .then(([r1, r2]) => Promise.all([r1.json(), r2.json()]))
  .then(([roads, buildings]) => {
    source.addFeatures([...roads, ...buildings]);
  });

Такая архитектура используется при:

  • слоистых картах
  • аналитических панелях
  • объединении внешних сервисов

ГеоREST и сторонние платформы

REST API часто предоставляются крупными платформами:

  • OpenStreetMap Overpass API
  • Mapbox Vector Tiles API
  • собственные GeoServer/ArcGIS REST endpoints

Каждый источник требует:

  • адаптера формата
  • нормализации CRS
  • унификации структуры Feature

Динамическая сериализация GeoJSON

REST-сервер может отдавать упрощённые или расширенные модели:

{
  "type": "Feature",
  "geometry": { ... },
  "properties": {
    "id": 1,
    "name": "Road A",
    "speed_limit": 60
  }
}

На клиенте OpenLayers преобразует это через ol/format/GeoJSON, сохраняя свойства без изменений, но нормализуя геометрию под внутренний формат рендера.