Экспорт нарисованных объектов

В Mapbox GL JS все редактируемые геометрические данные обычно представлены в формате GeoJSON, что делает экспорт универсальным и независимым от визуального слоя отображения. При использовании расширения Mapbox Draw данные хранятся внутри внутреннего состояния draw и могут быть извлечены в любой момент без обращения к серверу.

Основной принцип экспорта заключается в извлечении всех или части объектов, преобразовании их в GeoJSON и последующей сериализации в файл или передачу в сторонние системы.


Источник данных при рисовании

При использовании Mapbox Draw все нарисованные элементы хранятся в коллекции объектов:

const allFeatures = draw.getAll();

Метод getAll() возвращает объект формата FeatureCollection:

{
  "type": "FeatureCollection",
  "features": [
    {
      "id": "abc123",
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [...]
      },
      "properties": {}
    }
  ]
}

Каждый объект содержит:

  • геометрию (Point, LineString, Polygon)
  • уникальный идентификатор
  • пользовательские свойства

Экспорт в формат GeoJSON

GeoJSON является основным форматом обмена пространственными данными в Mapbox GL JS. Он подходит для сохранения, передачи и последующего восстановления объектов.

Прямое получение данных:

const geojson = draw.getAll();

Дальнейшее преобразование в строку:

const jsonString = JSON.stringify(geojson, null, 2);

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

const jsonString = JSON.stringify(geojson);

Скачивание GeoJSON как файла

Для сохранения данных на стороне клиента применяется механизм создания Blob-объекта и временной ссылки.

function downloadGeoJSON(data) {
  const blob = new Blob([JSON.stringify(data)], {
    type: "application/json"
  });

  const url = URL.createObjectURL(blob);

  const link = document.createElement("a");
  link.href = url;
  link.download = "map-features.geojson";
  document.body.appendChild(link);
  link.click();

  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

Вызов:

const data = draw.getAll();
downloadGeoJSON(data);

Фильтрация экспортируемых объектов

В некоторых сценариях требуется экспорт только части объектов, например:

  • только полигоны
  • только линии
  • объекты с определённым свойством

Фильтрация выполняется через стандартный метод массива features.

Экспорт только полигонов

const all = draw.getAll();

const polygons = {
  type: "FeatureCollection",
  features: all.features.filter(
    f => f.geometry.type === "Polygon"
  )
};

Экспорт по пользовательским свойствам

Если объекты содержат свойства в properties, возможна фильтрация по ним:

const filtered = {
  type: "FeatureCollection",
  features: all.features.filter(
    f => f.properties && f.properties.category === "restricted"
  )
};

Экспорт отдельных объектов

Mapbox Draw позволяет извлекать конкретный объект по идентификатору:

const feature = draw.get("feature-id");

Полученный объект может быть сохранён отдельно:

const singleExport = {
  type: "FeatureCollection",
  features: [feature]
};

Преобразование в другие форматы

GeoJSON часто служит промежуточным форматом. Далее возможны преобразования:

WKT (Well-Known Text)

Используется в GIS-системах и базах данных:

import * as turf from "@turf/turf";

const geojson = draw.getAll();

geojson.features.forEach(f => {
  const wkt = turf.toWkt(f);
});

CSV (для точечных объектов)

Для точек возможен экспорт координат в табличный вид:

const points = draw.getAll().features
  .filter(f => f.geometry.type === "Point")
  .map(f => ({
    id: f.id,
    lng: f.geometry.coordinates[0],
    lat: f.geometry.coordinates[1]
  }));

Сохранение структуры свойств

В Mapbox GL JS свойства объектов не ограничены системой и могут включать:

  • категории объектов
  • временные метки
  • пользовательские метаданные
  • ссылки на внешние системы

Пример добавления свойств при создании:

draw.add({
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [30.5, 50.5]
  },
  properties: {
    name: "Объект A",
    category: "infrastructure",
    createdAt: Date.now()
  }
});

При экспорте эти данные сохраняются автоматически.


Экспорт с сохранением состояния карты

В некоторых случаях требуется сохранить не только объекты, но и состояние редактирования:

  • активный режим Draw
  • выбранный объект
  • текущий стиль отображения

Пример структуры сохранения:

const session = {
  features: draw.getAll(),
  mode: draw.getMode(),
  selected: draw.getSelectedIds()
};

Автоматическое обновление экспортируемых данных

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

События Mapbox Draw:

map.on("draw.create", updateExport);
map.on("draw.update", updateExport);
map.on("draw.delete", updateExport);

Функция обновления:

function updateExport() {
  const data = draw.getAll();
  currentExport = JSON.stringify(data);
}

Передача данных на сервер

Экспортированные данные часто отправляются через HTTP-запрос:

fetch("/api/save-map", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify(draw.getAll())
});

На серверной стороне GeoJSON обычно сохраняется в базе данных типа:

  • PostgreSQL + PostGIS
  • MongoDB
  • ElasticSearch (геоиндексы)

Разделение экспорта по слоям

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

const data = draw.getAll();

const lines = data.features.filter(f => f.geometry.type === "LineString");
const polygons = data.features.filter(f => f.geometry.type === "Polygon");
const points = data.features.filter(f => f.geometry.type === "Point");

Результаты могут сохраняться как отдельные файлы или объединяться в структуру:

const exportPackage = {
  points,
  lines,
  polygons
};

Сериализация и контроль точности координат

GeoJSON допускает высокую точность координат, однако при экспорте может потребоваться нормализация:

function roundCoordinates(feature, precision = 6) {
  const round = (coord) =>
    Number(coord.toFixed(precision));

  const process = (coords) =>
    Array.isArray(coords[0])
      ? coords.map(process)
      : coords.map(round);

  return {
    ...feature,
    geometry: {
      ...feature.geometry,
      coordinates: process(feature.geometry.coordinates)
    }
  };
}

Применение к коллекции:

const cleaned = {
  type: "FeatureCollection",
  features: draw.getAll().features.map(f =>
    roundCoordinates(f, 5)
  )
};

Экспорт в архивированные структуры

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

import JSZip from "jszip";

const zip = new JSZip();

zip.file(
  "features.geojson",
  JSON.stringify(draw.getAll())
);

zip.generateAsync({ type: "blob" }).then(content => {
  const url = URL.createObjectURL(content);
});

Работа с версионностью экспортируемых данных

При необходимости хранения истории изменений формируется серия экспортов:

const versions = [];

function saveVersion() {
  versions.push({
    timestamp: Date.now(),
    data: draw.getAll()
  });
}

Такой подход позволяет отслеживать эволюцию геометрии объектов и восстанавливать состояние на любой момент времени.


Экспорт с привязкой к стилям и слоям

Хотя GeoJSON не содержит визуальных стилей Mapbox GL JS, можно сохранять метаданные слоёв:

const styledExport = {
  type: "FeatureCollection",
  features: draw.getAll().features,
  metadata: {
    style: "streets-v12",
    source: "mapbox-draw"
  }
};

Такая структура используется для последующего восстановления визуального контекста в приложениях, где требуется единый дизайн карты и данных.