Экспорт данных с карты

Архитектура данных карты HERE Maps

В основе работы с HERE Maps API в JavaScript лежит объектная модель, где все элементы карты представлены сущностями с геометрией и метаданными. Основные типы объектов:

  • маркеры (points)
  • линии (polylines)
  • полигоны (polygons)
  • группы объектов (H.map.Group)
  • пользовательские слои

Каждый объект карты хранит не только координаты, но и набор атрибутов, которые могут включать идентификаторы, описания, типы объектов и пользовательские поля. Именно эти данные становятся основой для экспорта.

Ключевая особенность заключается в том, что визуальное представление карты и её внутреннее состояние разделены: карта отображает объекты, но данные извлекаются из модели H.map.Object.


Сбор объектов карты

Перед экспортом данных необходимо получить доступ ко всем объектам, добавленным на карту. Обычно это делается через контейнер корневых объектов:

const objects = map.getObjects();

Если используется группировка:

const groupObjects = group.getObjects();

Рекурсивный обход требуется, если карта содержит вложенные группы:

function collectObjects(group) {
    let result = [];

    group.getObjects().forEach(obj => {
        if (obj instanceof H.map.Group) {
            result = result.concat(collectObjects(obj));
        } else {
            result.push(obj);
        }
    });

    return result;
}

На этом этапе формируется «плоский» массив объектов, пригодный для сериализации.


Преобразование объектов в универсальный формат

Для экспорта необходимо привести все объекты к единой структуре. Наиболее универсальным форматом является GeoJSON-подобная модель.

Базовое преобразование маркера:

function markerToFeature(marker) {
    const geo = marker.getGeometry();

    return {
        type: "Feature",
        geometry: {
            type: "Point",
            coordinates: [geo.lng, geo.lat]
        },
        properties: marker.getData ? marker.getData() : {}
    };
}

Преобразование линии:

function polylineToFeature(line) {
    const strip = line.getGeometry();

    return {
        type: "Feature",
        geometry: {
            type: "LineString",
            coordinates: strip.getLatLngAltArray().reduce((acc, val, i) => {
                if (i % 3 === 0) acc.push([strip.getLatLngAltArray()[i + 1], strip.getLatLngAltArray()[i]]);
                return acc;
            }, [])
        },
        properties: line.getData ? line.getData() : {}
    };
}

Экспорт в GeoJSON

GeoJSON является наиболее совместимым форматом для геопространственных данных.

Сбор всех объектов:

function exportToGeoJSON(objects) {
    const features = [];

    objects.forEach(obj => {
        if (obj instanceof H.map.Marker) {
            features.push(markerToFeature(obj));
        }

        if (obj instanceof H.map.Polyline) {
            features.push(polylineToFeature(obj));
        }
    });

    return {
        type: "FeatureCollection",
        features
    };
}

Полученный объект легко сериализуется:

const geojson = exportToGeoJSON(map.getObjects());
const jsonString = JSON.stringify(geojson);

Экспорт в JSON произвольной структуры

В ряде случаев требуется сохранить не только геометрию, но и бизнес-данные, связанные с объектами карты.

Универсальная модель:

function exportToJSON(objects) {
    return objects.map(obj => ({
        id: obj.id || null,
        type: obj instanceof H.map.Marker ? "marker"
            : obj instanceof H.map.Polyline ? "polyline"
            : obj instanceof H.map.Polygon ? "polygon"
            : "unknown",
        geometry: obj.getGeometry(),
        data: obj.getData ? obj.getData() : null
    }));
}

Такой формат удобен для восстановления состояния карты без потери контекста.


Экспорт в CSV

CSV применяется при интеграции с табличными системами. Подходит только для точечных объектов.

function exportMarkersToCSV(markers) {
    const header = "lat,lng,title\n";

    const rows = markers.map(m => {
        const g = m.getGeometry();
        const data = m.getData ? m.getData() : {};
        return `${g.lat},${g.lng},${data.title || ""}`;
    });

    return header + rows.join("\n");
}

CSV теряет сложные структуры, но остаётся удобным для аналитики.


Сохранение файла через Blob

Экспортированные данные необходимо преобразовать в файл для скачивания в браузере.

function downloadFile(content, filename, type) {
    const blob = new Blob([content], { type });
    const url = URL.createObjectURL(blob);

    const link = document.createElement("a");
    link.href = url;
    link.download = filename;
    link.click();

    URL.revokeObjectURL(url);
}

Пример использования:

const geojson = exportToGeoJSON(map.getObjects());
downloadFile(JSON.stringify(geojson), "map.geojson", "application/json");

Экспорт пользовательских слоёв

HERE Maps API поддерживает кастомные слои, содержащие визуальные и логические элементы. Для их экспорта требуется обход слоя:

function exportLayer(layer) {
    const objects = [];

    layer.getProvider().forEach(item => {
        objects.push(item);
    });

    return objects;
}

Если слой динамический, важно учитывать фильтры отображения и состояние видимости объектов.


Фильтрация и нормализация данных

Перед экспортом данные часто требуют очистки:

  • удаление временных объектов
  • исключение скрытых элементов
  • нормализация координат

Пример фильтрации:

function filterObjects(objects) {
    return objects.filter(obj => {
        return obj.getVisibility ? obj.getVisibility() : true;
    });
}

Нормализация координат:

function normalizeCoordinates(coord) {
    return {
        lat: Number(coord.lat.toFixed(6)),
        lng: Number(coord.lng.toFixed(6))
    };
}

Работа с маркерами и геометриями

Маркер в HERE Maps — это не просто точка, а объект с расширяемыми свойствами.

const marker = new H.map.Marker({ lat: 52.5, lng: 13.4 });
marker.setData({
    title: "Точка интереса",
    category: "restaurant"
});

При экспорте важно учитывать пользовательские данные:

const data = marker.getData();

Геометрия может быть извлечена независимо:

const geometry = marker.getGeometry();

Экспорт маршрутов

Маршруты обычно представлены серией точек или полилинией. Для их экспорта используется извлечение shape:

function exportRoute(route) {
    const shape = route.getGeometry().getLatLngAltArray();

    const coordinates = [];

    for (let i = 0; i < shape.length; i += 3) {
        coordinates.push([shape[i + 1], shape[i]]);
    }

    return {
        type: "Feature",
        geometry: {
            type: "LineString",
            coordinates
        }
    };
}

Маршруты часто содержат дополнительные данные: длительность, расстояние, манёвры.


Асинхронные сценарии экспорта

При больших объёмах данных экспорт может блокировать поток выполнения. Используются асинхронные подходы:

async function exportAsync(objects) {
    return new Promise(resolve => {
        setTimeout(() => {
            const data = exportToGeoJSON(objects);
            resolve(data);
        }, 0);
    });
}

Для сложных сцен с тысячами объектов применяется батчинг:

function batchProcess(objects, batchSize, callback) {
    let index = 0;

    function nextBatch() {
        const batch = objects.slice(index, index + batchSize);
        callback(batch);

        index += batchSize;

        if (index < objects.length) {
            setTimeout(nextBatch, 0);
        }
    }

    nextBatch();
}

Оптимизация объёма данных

При экспорте больших карт важно минимизировать размер выходных данных:

  • удаление избыточных атрибутов
  • сокращение координатной точности
  • группировка объектов

Пример сокращения:

function simplifyFeature(feature) {
    return {
        type: feature.type,
        geometry: feature.geometry,
        properties: {
            id: feature.properties.id
        }
    };
}

Безопасность и ограничения данных

Экспорт данных карты должен учитывать ограничения браузера и памяти:

  • Blob больших размеров может вызывать задержки
  • сериализация глубоких структур требует контроля циклических ссылок
  • пользовательские данные могут содержать чувствительную информацию

При обработке объектов важно избегать прямой сериализации экземпляров классов HERE Maps, так как они содержат внутренние ссылки и методы, не предназначенные для экспорта. Вместо этого используется явное извлечение данных через getGeometry() и getData().