TMS (Tile Map Service) представляет собой стандарт публикации тайловых карт, в котором изображение карты разбивается на квадратные тайлы фиксированного размера и организуется по иерархии уровней масштабирования. Каждый уровень содержит сетку тайлов, где координаты определяются тройкой значений:
Главная особенность TMS заключается в способе нумерации оси Y: отсчёт начинается снизу вверх, то есть нулевой индекс соответствует нижнему ряду тайлов.
Тайловая модель позволяет эффективно загружать только видимую часть карты, минимизируя объём передаваемых данных и ускоряя отрисовку.
Несмотря на схожесть структуры, TMS и XYZ используют различную систему координат по оси Y.
В большинстве веб-картографических сервисов используется схема XYZ, где:
Пример URL:
/tiles/{z}/{x}/{y}.png
В TMS система координат по вертикали перевёрнута:
Пример URL:
/tiles/{z}/{x}/{-y-1}.png
Именно это различие требует преобразования координаты при интеграции TMS в OpenLayers.
Для уровня zoom = z количество тайлов по каждой оси определяется как:
n = 2^z
Для XYZ:
y_xyz = y
Для TMS:
y_tms = (2^z - 1) - y_xyz
Эта формула используется при построении URL тайлов и является ключевым моментом при подключении TMS-источников.
В OpenLayers управление тайловой сеткой осуществляется через
TileGrid. Для корректной работы с TMS важно
синхронизировать:
Пример базовой конфигурации:
import TileGrid from 'ol/tilegrid/TileGrid';
const tileGrid = new TileGrid({
extent: [-20037508.342789244, -20037508.342789244,
20037508.342789244, 20037508.342789244],
resolutions: Array.from({ length: 19 }, (_, z) =>
156543.03392804097 / Math.pow(2, z)
),
tileSize: 256
});
В OpenLayers чаще всего TMS подключается через
ol/source/XYZ с переопределением функции формирования
URL.
import XYZ from 'ol/source/XYZ';
const tmsSource = new XYZ({
tileUrlFunction: (tileCoord) => {
const z = tileCoord[0];
const x = tileCoord[1];
const y = tileCoord[2];
const flippedY = Math.pow(2, z) - y - 1;
return `https://example.com/tiles/${z}/${x}/${flippedY}.png`;
}
});
Здесь ключевым элементом является вычисление flippedY,
обеспечивающее совместимость с TMS-структурой.
После определения источника создаётся слой:
import TileLayer from 'ol/layer/Tile';
import Map from 'ol/Map';
const tmsLayer = new TileLayer({
source: tmsSource
});
const map = new Map({
target: 'map',
layers: [tmsLayer]
});
OpenLayers автоматически запрашивает тайлы только для текущей области видимости и нужного масштаба.
В случаях, когда требуется более гибкая логика (например,
нестандартные проекции или серверные API), используется
TileImage.
import TileImage from 'ol/source/TileImage';
const customTmsSource = new TileImage({
tileGrid: tileGrid,
tileUrlFunction: (tileCoord) => {
const z = tileCoord[0];
const x = tileCoord[1];
const y = tileCoord[2];
const flippedY = (1 << z) - y - 1;
return `/tms/${z}/${x}/${flippedY}.png`;
}
});
Использование побитового сдвига (1 << z) является
оптимизированной альтернативой Math.pow(2, z).
Многие локальные картографические серверы (например, Mapnik, GeoServer, MapProxy) предоставляют TMS-эндпоинты с фиксированной структурой URL.
Типичная структура:
/tms/1.0.0/{layer}/{z}/{x}/{y}.png
Однако важно учитывать, что некоторые серверы уже возвращают TMS-координаты, и инверсия Y в клиенте не требуется. В таких случаях дополнительное преобразование приводит к ошибкам отображения (сдвиг тайлов по вертикали).
Корректная работа TMS зависит от точного соответствия разрешений карты и уровней сервера.
Распространённая ошибка — несоответствие resolution массива:
const resolutions = [
156543.03392804097,
78271.51696402048,
39135.75848201024
];
Если сервер использует нестандартные масштабы, визуальные артефакты проявляются в виде:
OpenLayers поддерживает встроенное кэширование через механизмы
tileCache, которые уменьшают количество повторных
запросов.
Важные аспекты:
При работе с TMS часто возникает необходимость контролировать заголовки HTTP:
Эти параметры определяют поведение браузерного кэша и CDN.
При работе с TMS возможны ситуации отсутствия тайла на сервере. OpenLayers обрабатывает это через события источника:
tmsSource.on('tileloaderror', (event) => {
const tile = event.tile;
tile.setState(3);
});
Статус 3 соответствует ошибке загрузки.
Часто используется подмена отсутствующих тайлов прозрачными изображениями, чтобы избежать “дыр” в карте.
Некоторые сервисы используют модифицированную TMS-схему:
Обобщённая формула преобразования:
y_final = origin_offset - y_xyz
или комбинация:
y_final = (2^z - 1) - y_xyz + offset
OpenLayers не предоставляет автоматической поддержки таких схем,
поэтому логика полностью реализуется в tileUrlFunction.
Оптимизация загрузки тайлов достигается за счёт:
Дополнительный эффект даёт использование preload:
const map = new Map({
preload: 4
});
Это позволяет заранее загружать тайлы соседних уровней.
Наиболее часто используется проекция EPSG:3857. При работе с TMS важно, чтобы серверные тайлы были заранее сгенерированы именно в этой проекции.
Если сервер использует EPSG:4326, возникают:
В таких случаях требуется явное задание проекции слоя и трансформации
координат через ol/proj.
Комбинирование нескольких TMS слоёв требует строгого согласования:
Пример наложения:
const baseLayer = new TileLayer({ source: baseTms });
const overlayLayer = new TileLayer({ source: overlayTms });
map.addLayer(baseLayer);
map.addLayer(overlayLayer);
Несовпадение параметров приводит к визуальному расслоению данных.
При сложных системах хранения тайлов используется динамическая генерация URL:
tileUrlFunction: (tileCoord) => {
const [z, x, y] = tileCoord;
const flippedY = (1 << z) - 1 - y;
const shard = (x + y) % 4;
return `https://tile${shard}.server.com/${z}/${x}/${flippedY}.png`;
}
Шардирование распределяет нагрузку между несколькими поддоменами, увеличивая параллелизм загрузки.
TMS-слои часто используются как базовая подложка для векторных данных:
OpenLayers обеспечивает независимую отрисовку слоёв, где TMS выступает фоном, а векторные данные накладываются поверх через Canvas или WebGL рендерер.
Конфликты обычно возникают только при несовпадении проекций или z-index слоёв.