В Google Maps JavaScript API отсутствует универсальный встроенный
реестр всех добавленных на карту графических объектов. Карта не
предоставляет метода наподобие getOverlays(), поэтому
управление созданными примитивами (маркер, линия, полигон, круг,
прямоугольник) осуществляется через собственное хранение ссылок на
объекты в коде приложения.
Каждый добавленный объект наследует поведение MVCObject,
что позволяет получать и изменять его состояние, но не даёт глобального
списка всех объектов на карте. Следовательно, ключевая концепция работы
с нарисованными объектами заключается в явном сохранении ссылок при
создании.
Для интерактивного рисования используется библиотека
drawing, подключаемая при загрузке API:
<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=drawing"></script>
Основной компонент — google.maps.drawing.DrawingManager,
который управляет режимами рисования:
const drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: null,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.MARKER,
google.maps.drawing.OverlayType.POLYGON,
google.maps.drawing.OverlayType.POLYLINE,
google.maps.drawing.OverlayType.CIRCLE,
google.maps.drawing.OverlayType.RECTANGLE
]
}
});
drawingManager.setMap(map);
Каждый раз после завершения рисования генерируется событие
overlaycomplete, содержащее ссылку на созданный объект.
Именно это событие является центральной точкой для получения всех нарисованных элементов.
const drawnObjects = [];
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
const overlay = event.overlay;
const type = event.type;
drawnObjects.push({
type,
overlay
});
});
Структура event:
event.overlay — созданный объект (Marker, Polygon,
Polyline, Circle, Rectangle)event.type — тип объектаПрактика хранения только overlay недостаточна при
сложных сценариях. Обычно сохраняется расширенная структура:
const drawnObjects = [];
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
const obj = {
id: crypto.randomUUID(),
type: event.type,
overlay: event.overlay,
createdAt: Date.now()
};
drawnObjects.push(obj);
});
Такой подход позволяет:
Каждый тип оверлея имеет собственный API для извлечения геометрии.
const position = marker.getPosition();
const lat = position.lat();
const lng = position.lng();
const path = polyline.getPath();
const coordinates = [];
path.forEach(function(latlng) {
coordinates.push({
lat: latlng.lat(),
lng: latlng.lng()
});
});
const paths = polygon.getPaths();
const polygons = [];
paths.forEach(function(path) {
const ring = [];
path.forEach(function(latlng) {
ring.push({
lat: latlng.lat(),
lng: latlng.lng()
});
});
polygons.push(ring);
});
const center = circle.getCenter();
const radius = circle.getRadius();
const data = {
center: {
lat: center.lat(),
lng: center.lng()
},
radius
};
const bounds = rectangle.getBounds();
const data = {
northEast: {
lat: bounds.getNorthEast().lat(),
lng: bounds.getNorthEast().lng()
},
southWest: {
lat: bounds.getSouthWest().lat(),
lng: bounds.getSouthWest().lng()
}
};
Так как API не предоставляет глобального списка, перебор выполняется через локальный массив:
drawnObjects.forEach(item => {
console.log(item.type);
console.log(item.overlay);
});
Для получения координат необходимо обрабатывать каждый тип отдельно.
Удаление выполняется через метод setMap(null):
function removeObject(id) {
const index = drawnObjects.findIndex(o => o.id === id);
if (index === -1) return;
drawnObjects[index].overlay.setMap(null);
drawnObjects.splice(index, 1);
}
function clearAll() {
drawnObjects.forEach(obj => {
obj.overlay.setMap(null);
});
drawnObjects.length = 0;
}
При включённой опции редактирования (editable: true)
геометрия может изменяться пользователем. В этом случае важно
отслеживать изменения через события объекта.
polygon.setEditable(true);
google.maps.event.addListener(polygon.getPath(), 'set_at', updateData);
google.maps.event.addListener(polygon.getPath(), 'insert_at', updateData);
google.maps.event.addListener(polygon.getPath(), 'remove_at', updateData);
function updateData() {
const path = polygon.getPath();
const coords = [];
path.forEach(latlng => {
coords.push({ lat: latlng.lat(), lng: latlng.lng() });
});
console.log(coords);
}
Для сценариев, где требуется системное хранение геометрии,
применяется google.maps.Data.
map.data.addGeoJson(geoJsonObject);
Получение всех объектов:
map.data.forEach(function(feature) {
console.log(feature.getGeometry());
});
Извлечение координат:
map.data.forEach(function(feature) {
const geometry = feature.getGeometry();
geometry.forEachLatLng(function(latlng) {
console.log(latlng.lat(), latlng.lng());
});
});
Преимущества Data Layer:
setStyleДля сохранения состояния карты необходимо преобразовать объекты в JSON-подобную структуру вручную.
function serialize() {
return drawnObjects.map(obj => {
const overlay = obj.overlay;
if (obj.type === 'marker') {
const pos = overlay.getPosition();
return {
type: 'marker',
lat: pos.lat(),
lng: pos.lng()
};
}
if (obj.type === 'circle') {
const center = overlay.getCenter();
return {
type: 'circle',
center: {
lat: center.lat(),
lng: center.lng()
},
radius: overlay.getRadius()
};
}
if (obj.type === 'polyline') {
const path = overlay.getPath();
const coords = [];
path.forEach(p => coords.push({ lat: p.lat(), lng: p.lng() }));
return {
type: 'polyline',
path: coords
};
}
return null;
});
}
function restore(data) {
data.forEach(item => {
if (item.type === 'marker') {
new google.maps.Marker({
position: item,
map
});
}
if (item.type === 'circle') {
new google.maps.Circle({
center: item.center,
radius: item.radius,
map
});
}
if (item.type === 'polyline') {
new google.maps.Polyline({
path: item.path,
map
});
}
});
}
При масштабировании проекта хранение нарисованных объектов обычно выносится в отдельный слой состояния:
Ключевой принцип: карта рассматривается как визуализация состояния, а не как источник истины.