AJAX запросы в OpenLayers

Общая модель загрузки данных через AJAX

В основе работы с удалёнными данными в OpenLayers лежит принцип асинхронной подгрузки геопространственных ресурсов: векторных объектов, тайлов, растровых слоёв и метаданных через HTTP-запросы. Библиотека не навязывает единственный способ выполнения запросов, а предоставляет уровень абстракции через загрузчики (loader), источники данных (source) и форматтеры (format).

Основные сценарии AJAX-взаимодействия:

  • загрузка GeoJSON/TopoJSON/XML данных
  • подгрузка векторных объектов по границам карты (BBOX)
  • работа с WFS/WMS/WMTS сервисами
  • динамическая подгрузка тайлов XYZ
  • отправка запросов к REST API

Векторные слои и механизм loader

Ключевой точкой интеграции AJAX в OpenLayers является ol/source/Vector. Источник поддерживает функцию loader, которая вызывается при необходимости загрузки данных.

Базовая схема загрузки GeoJSON

import VectorSource from 'ol/source/Vector.js';
import VectorLayer from 'ol/layer/Vector.js';
import GeoJSON from 'ol/format/GeoJSON.js';

const vectorSource = new VectorSource({
  loader: function () {
    fetch('https://example.com/data/points.geojson')
      .then(response => response.json())
      .then(data => {
        const features = new GeoJSON().readFeatures(data, {
          featureProjection: 'EPSG:3857'
        });

        vectorSource.addFeatures(features);
      })
      .catch(error => console.error(error));
  }
});

const vectorLayer = new VectorLayer({
  source: vectorSource
});

В этом сценарии AJAX-запрос выполняется вручную через fetch, а результат преобразуется в объекты Feature.


Загрузка данных по BBOX (ограничение области карты)

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

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

const vectorSource = new VectorSource({
  loader: function (extent) {
    const url = `https://example.com/api/features?bbox=${extent.join(',')}`;

    fetch(url)
      .then(res => res.json())
      .then(data => {
        const features = new GeoJSON().readFeatures(data, {
          featureProjection: 'EPSG:3857'
        });

        vectorSource.addFeatures(features);
      });
  },
  strategy: function (extent, resolution) {
    return [extent];
  }
});

Особенности BBOX-загрузки

  • extent передаётся автоматически
  • запросы зависят от текущего масштаба
  • стратегия (strategy) определяет частоту вызовов loader

Использование AbortController для отмены запросов

При частом панорамировании карты старые AJAX-запросы могут становиться неактуальными. Для управления этим используется AbortController.

let controller;

const vectorSource = new VectorSource({
  loader: function (extent) {
    if (controller) {
      controller.abort();
    }

    controller = new AbortController();

    const url = `https://example.com/api/data?bbox=${extent.join(',')}`;

    fetch(url, { signal: controller.signal })
      .then(res => res.json())
      .then(data => {
        vectorSource.clear();

        const features = new GeoJSON().readFeatures(data, {
          featureProjection: 'EPSG:3857'
        });

        vectorSource.addFeatures(features);
      })
      .catch(err => {
        if (err.name !== 'AbortError') {
          console.error(err);
        }
      });
  }
});

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


Тайловые AJAX-запросы (XYZ, WMS)

XYZ тайлы

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

const layer = new TileLayer({
  source: new XYZ({
    url: 'https://tile-server.com/{z}/{x}/{y}.png'
  })
});

Каждый тайл — это отдельный AJAX-запрос, формируемый по шаблону URL.


WMS запросы

import TileWMS from 'ol/source/TileWMS.js';

const layer = new TileLayer({
  source: new TileWMS({
    url: 'https://example.com/geoserver/wms',
    params: {
      LAYERS: 'workspace:layer',
      TILED: true
    }
  })
});

WMS использует параметры запроса:

  • BBOX
  • WIDTH / HEIGHT
  • CRS или SRS

Форматы данных и парсинг ответа

GeoJSON

import GeoJSON from 'ol/format/GeoJSON.js';

const format = new GeoJSON();

const features = format.readFeatures(response, {
  featureProjection: 'EPSG:3857'
});

GML (WFS)

import WFS from 'ol/format/WFS.js';

const format = new WFS();

const features = format.readFeatures(xmlResponse);

AJAX-ответы могут приходить как JSON, XML или текст, в зависимости от сервиса.


WFS запросы (GetFeature)

Пример построения запроса вручную:

const url = 'https://example.com/geoserver/wfs';

const params = new URLSearchParams({
  service: 'WFS',
  version: '2.0.0',
  request: 'GetFeature',
  typeName: 'workspace:layer',
  outputFormat: 'application/json'
});

fetch(`${url}?${params.toString()}`)
  .then(res => res.json())
  .then(data => {
    console.log(data);
  });

Кастомные AJAX-загрузчики

OpenLayers позволяет полностью контролировать процесс загрузки.

const vectorSource = new VectorSource({
  loader: function (extent, resolution, projection) {
    const url = 'https://api.example.com/search';

    const body = JSON.stringify({
      bbox: extent,
      srid: projection.getCode()
    });

    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: body
    })
      .then(res => res.json())
      .then(data => {
        const features = new GeoJSON().readFeatures(data, {
          featureProjection: projection
        });

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

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

  • сложными фильтрами
  • авторизацией через токены
  • аналитическими API

Авторизация в AJAX-запросах

Bearer Token

fetch('https://api.example.com/data', {
  headers: {
    'Authorization': 'Bearer TOKEN_VALUE'
  }
});

API Key

const url = `https://api.example.com/data?key=API_KEY`;

Обработка ошибок и повторные запросы

fetch(url)
  .then(res => {
    if (!res.ok) {
      throw new Error('HTTP error');
    }
    return res.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.error('Ошибка загрузки:', err);
  });

Расширенные схемы включают:

  • retry с экспоненциальной задержкой
  • fallback на кешированные данные
  • логирование состояния запроса

Кэширование AJAX-ответов

Простейший in-memory cache

const cache = new Map();

function load(url) {
  if (cache.has(url)) {
    return Promise.resolve(cache.get(url));
  }

  return fetch(url)
    .then(res => res.json())
    .then(data => {
      cache.set(url, data);
      return data;
    });
}

Оптимизация количества запросов

Ключевые подходы:

  • группировка BBOX-запросов
  • ограничение частоты (debounce)
  • использование тайлов вместо вектора при высоких масштабах
  • серверная агрегация данных

Синхронизация AJAX и состояния карты

При изменении:

  • центра карты
  • масштаба
  • проекции

выполняется пересчёт запросов через change-события:

map.getView().on('change:center', function () {
  vectorSource.clear();
  vectorSource.refresh();
});

Проекции и трансформация данных

AJAX-ответ часто приходит в EPSG:4326, тогда как карта работает в EPSG:3857.

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

const feature = new Feature({
  geometry: new Point(fromLonLat([longitude, latitude]))
});

При чтении GeoJSON:

new GeoJSON().readFeatures(data, {
  dataProjection: 'EPSG:4326',
  featureProjection: 'EPSG:3857'
});

Итоговая архитектура AJAX в OpenLayers

Типичная цепочка обработки данных:

  1. событие карты (move/end/zoom)
  2. вычисление extent
  3. формирование URL или тела запроса
  4. AJAX-запрос (fetch/XHR)
  5. парсинг ответа (GeoJSON/WFS/JSON/XML)
  6. преобразование координат
  7. добавление Feature в источник
  8. отрисовка слоя

Эта модель обеспечивает гибкую интеграцию OpenLayers с любыми гео-сервисами и REST API, сохраняя контроль над каждым этапом сетевого взаимодействия.