Интерактивное перемещение объектов на карте в OpenLayers реализуется через взаимодействие Translate, предназначенное для перетаскивания геометрий по поверхности карты без изменения их формы. В отличие от редактирования вершин, Translate работает с целыми объектами, изменяя их координаты единым смещением, что делает его основным инструментом для drag-and-drop сценариев в картографических приложениях.
Translate взаимодействует с векторными объектами (features) и изменяет их геометрию на основе вычисленного смещения между начальной и текущей позицией курсора. При захвате объекта фиксируется исходная точка, далее каждое движение мыши преобразуется в вектор смещения в системе координат карты.
Ключевая особенность заключается в том, что Translate не модифицирует структуру геометрии (например, количество вершин), а применяет трансформацию ко всей геометрии целиком. Это делает его предсказуемым и эффективным для перемещения сложных объектов.
В OpenLayers Translate реализован как взаимодействие
ol/interaction/Translate. Подключение выполняется через
создание экземпляра и добавление его в карту.
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import Translate from 'ol/interaction/Translate.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
const source = new VectorSource();
const layer = new VectorLayer({
source: source
});
const translate = new Translate({
features: source.getFeaturesCollection()
});
const map = new Map({
target: 'map',
layers: [layer],
view: new View({
center: [0, 0],
zoom: 2
}),
interactions: [
translate
]
});
В данном случае используется коллекция объектов слоя, что позволяет перемещать любые добавленные features.
Translate принимает объект настроек, определяющий поведение взаимодействия.
Позволяет задать набор объектов, которые можно перемещать. Это может
быть Collection или массив features.
new Translate({
features: selectedFeatures
});
Использование коллекции удобно при реализации выделения объектов.
Альтернативный способ ограничения области перемещения — указание слоёв.
new Translate({
layers: [vectorLayer]
});
В этом случае перемещаться будут все объекты указанных слоёв.
Функция фильтрации позволяет более точно контролировать, какие объекты могут быть перемещены.
new Translate({
filter: function(feature, layer) {
return feature.get('draggable') === true;
}
});
Фильтр выполняется при каждом попытке захвата объекта.
Позволяет задать условие активации взаимодействия (например, только при нажатой клавише Shift).
import { shiftKeyOnly } from 'ol/events/condition.js';
new Translate({
condition: shiftKeyOnly
});
Это полезно при комбинировании нескольких типов взаимодействий.
Translate генерирует набор событий, позволяющих отслеживать процесс перемещения.
Срабатывает в момент начала перемещения.
translate.on('translatestart', function(event) {
const features = event.features;
console.log('Начато перемещение:', features.getLength());
});
На этом этапе можно сохранять исходные координаты или состояние объектов.
Событие вызывается во время перемещения, при каждом изменении позиции.
translate.on('translating', function(event) {
const delta = event.delta;
console.log('Смещение:', delta);
});
Используется для динамических эффектов, например, подсветки или отображения вспомогательной информации.
Срабатывает после завершения перемещения.
translate.on('translateend', function(event) {
const features = event.features;
console.log('Перемещение завершено');
});
На этом этапе обычно выполняется сохранение изменений в источник данных или отправка на сервер.
На практике Translate часто используется совместно с механизмом
выделения (Select). Это позволяет перемещать только
выбранные пользователем объекты.
import Sel ect from 'ol/interaction/Select.js';
const select = new Select();
const translate = new Translate({
features: select.getFeatures()
});
map.addInteraction(select);
map.addInteraction(translate);
Такой подход разделяет логику выбора и перемещения, делая интерфейс более гибким.
Translate работает с любыми типами геометрий:
Все координаты геометрии смещаются одинаково, что гарантирует сохранение формы.
При перемещении Polygon, например, изменяются все вершины контура одновременно, включая внутренние кольца (holes).
OpenLayers использует проекцию карты (обычно Web Mercator EPSG:3857). Translate выполняет преобразования в координатах карты, а не в экранных пикселях.
Это означает:
При использовании кастомных проекций важно учитывать корректность трансформаций координат источника.
В реальных приложениях часто требуется ограничить перемещение объектов определённой областью.
translate.on('translating', function(event) {
const geometry = event.features.item(0).getGeometry();
const extent = geometry.getExtent();
if (extent[0] < -1000000) {
event.preventDefault();
}
});
Более корректный подход — откатывать изменения после translateend, если объект вышел за допустимые границы.
При перемещении объектов часто требуется изменить их стиль для улучшения UX.
translate.on('translatestart', function(event) {
event.features.forEach(function(feature) {
feature.setStyle(highlightStyle);
});
});
translate.on('translateend', function(event) {
event.features.forEach(function(feature) {
feature.setStyle(defaultStyle);
});
});
Это помогает пользователю понять, какие объекты находятся в активном состоянии.
Translate и Modify взаимодействия решают разные задачи:
Их можно использовать одновременно:
import Modify fr om 'ol/interaction/Modify.js';
map.addInteraction(new Modify({ source: source }));
map.addInteraction(new Translate({ features: sel ect.getFeatures() }));
Важно учитывать порядок взаимодействий, чтобы избежать конфликтов при обработке событий мыши.
При работе с большим количеством features Translate может сталкиваться с нагрузкой из-за постоянного пересчёта геометрий.
Рекомендации:
Collection только для выбранных
объектовfiltertranslatingTranslate часто применяется для:
Пример добавления нового маркера и его перемещения:
const feature = new Feature({
geometry: new Point([0, 0])
});
source.addFeature(feature);
const select = new Select({
condition: singleClick
});
const translate = new Translate({
features: select.getFeatures()
});
map.addInteraction(select);
map.addInteraction(translate);
При необходимости можно «привязывать» перемещение к шагу сетки:
translate.on('translating', function(event) {
event.features.forEach(function(feature) {
const geom = feature.getGeometry();
const coord = geom.getCoordinates();
const snapped = [
Math.round(coord[0] / 1000) * 1000,
Math.round(coord[1] / 1000) * 1000
];
geom.setCoordinates(snapped);
});
});
Такой подход используется в задачах кадастровых систем и инженерного моделирования.
Translate поддерживает перемещение нескольких объектов одновременно. В этом случае смещение применяется ко всей группе, сохраняя взаимное расположение.
select.getFeatures().push(feature1);
select.getFeatures().push(feature2);
При захвате одного из объектов перемещается вся коллекция.
Translate может конфликтовать с базовыми событиями карты, такими как drag-pan. Обычно это решается настройкой условий активации:
import { platformModifierKeyOnly } fr om 'ol/events/condition.js';
new Translate({
condition: platformModifierKeyOnly
});
Это позволяет избежать случайного перемещения при навигации по карте.
После завершения перемещения данные обычно синхронизируются с источником:
translate.on('translateend', function(event) {
const feature = event.features.item(0);
const geometry = feature.getGeometry();
const coords = geometry.getCoordinates();
fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ coords })
});
});
Такой подход характерен для редактируемых картографических систем с серверной синхронизацией.