В основе работы картографического приложения на базе OpenLayers лежит слой источников данных (sources), отвечающий за получение пространственной информации, её загрузку, кеширование и передачу в слои отображения. Источник данных в OpenLayers — это абстракция, инкапсулирующая способ получения геометрии, тайлов или изображений из различных систем хранения и сервисов.
Кастомизация источников данных становится ключевым механизмом при интеграции нестандартных API, внутренних корпоративных сервисов, потоковых данных и динамически формируемых пространственных наборов.
OpenLayers предоставляет несколько классов источников:
ol/source/Vector — для векторных данныхol/source/TileXYZ — для тайлов по схеме XYZol/source/TileWMS — для WMS-сервисовol/source/VectorTile — для векторных тайловol/source/ImageStatic — для одиночных изображенийКаждый из них может быть расширен или заменён кастомной логикой загрузки.
Ключевые точки кастомизации:
loader (для VectorSource)tileLoadFunction (для tile sources)url как функция вместо строкиНаиболее гибкий способ создания источника — использование
ol/source/Vector с пользовательским загрузчиком.
import VectorSource from 'ol/source/Vector';
import GeoJSON from 'ol/format/GeoJSON';
const source = new VectorSource({
loader: function (extent, resolution, projection) {
const url = `/api/objects?bbox=${extent.join(',')}`;
fetch(url)
.then(response => response.json())
.then(data => {
const format = new GeoJSON();
const features = format.readFeatures(data, {
featureProjection: projection
});
source.addFeatures(features);
});
}
});
extent передаёт текущую видимую область картыresolution позволяет адаптировать уровень
детализацииprojection обеспечивает корректную трансформацию
координатТакой подход используется для серверов с поддержкой BBOX-фильтрации.
Для растровых и тайловых слоёв кастомизация выполняется через
tileLoadFunction.
import XYZ from 'ol/source/XYZ';
const source = new XYZ({
tileLoadFunction: function (tile, src) {
const img = tile.getImage();
fetch(src, {
headers: {
Authorization: 'Bearer TOKEN'
}
})
.then(response => response.blob())
.then(blob => {
img.src = URL.createObjectURL(blob);
});
},
url: 'https://tiles.example.com/{z}/{x}/{y}.png'
});
OpenLayers позволяет задавать url как функцию:
const source = new XYZ({
url: function (tileCoord) {
const z = tileCoord[0];
const x = tileCoord[1];
const y = tileCoord[2];
return `/dynamic-tiles/${z}/${x}/${y}?ts=${Date.now()}`;
}
});
Часто сервер возвращает данные не в GeoJSON, а в произвольной структуре.
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
const source = new VectorSource({
loader: function () {
fetch('/custom/api/points')
.then(res => res.json())
.then(data => {
const features = data.items.map(item => {
return new Feature({
geometry: new Point([item.lon, item.lat]),
name: item.title
});
});
source.addFeatures(features);
});
}
});
OpenLayers поддерживает расширение через ol/format.
import TextFormat from 'ol/format/Text';
class CustomFormat {
readFeatures(response) {
return response.records.map(r => {
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [r.x, r.y]
},
properties: r
};
});
}
}
Использование:
const format = new CustomFormat();
const features = format.readFeatures(data);
Кастомные источники часто требуют оптимизации сетевой нагрузки.
const cache = new Map();
function loadData(url) {
if (cache.has(url)) {
return Promise.resolve(cache.get(url));
}
return fetch(url)
.then(r => r.json())
.then(data => {
cache.set(url, data);
return data;
});
}
Кастомные источники могут работать в режиме стриминга.
const source = new VectorSource({
loader: function () {
const ws = new WebSocket('wss://stream.example.com');
ws.onmess age = function (event) {
const data = JSON.parse(event.data);
const feature = new Feature({
geometry: new Point(data.coords)
});
source.addFeature(feature);
};
}
});
При интеграции внешних API часто требуется ручная работа с проекциями.
import { transform } from 'ol/proj';
const coords = transform(
[30.5, 50.4],
'EPSG:4326',
'EPSG:3857'
);
Используется внутри loader для нормализации данных.
Иногда один слой объединяет несколько источников.
const sourceA = new VectorSource();
const sourceB = new VectorSource();
function mergeSources() {
return [...sourceA.getFeatures(), ...sourceB.getFeatures()];
}
При гибкости подхода возникают системные ограничения:
Загрузка данных только при попадании в viewport.
Привязка загрузки к событиям карты:
map.on('moveend', function () {
source.clear();
source.refresh();
});
Источник зависит от состояния слоя:
Кастомные источники часто работают в связке с сервером:
Практические техники:
import VectorSource from 'ol/source/Vector';
class CustomSource extends VectorSource {
refreshCustom() {
this.clear();
this.loader();
}
}
Такой подход применяется для: