Загрузка GeoJSON из файла

Общая концепция работы с GeoJSON в MapLibre GL JS

GeoJSON является одним из ключевых форматов обмена геопространственными данными в веб-картографии. В контексте MapLibre GL JS он используется как источник векторных данных, который может быть отрисован на карте в виде точек, линий и полигонов.

В отличие от тайловых источников (vector tiles, raster tiles), GeoJSON-источник представляет собой целостный набор геометрий, загружаемый в память клиента и обрабатываемый непосредственно в браузере. Это делает его особенно удобным для:

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

Способы загрузки GeoJSON из файла

1. Загрузка через HTTP(S)-запрос

Наиболее распространённый сценарий — размещение .geojson файла на сервере и загрузка через URL.

import maplibregl from "maplibre-gl";

const map = new maplibregl.Map({
  container: "map",
  style: "https://demotiles.maplibre.org/style.json",
  center: [37.6173, 55.7558],
  zoom: 10
});

map.on("load", () => {
  map.addSource("cities", {
    type: "geojson",
    data: "https://example.com/data/cities.geojson"
  });

  map.addLayer({
    id: "cities-points",
    type: "circle",
    source: "cities",
    paint: {
      "circle-radius": 6,
      "circle-color": "#ff5500"
    }
  });
});

В данном случае MapLibre самостоятельно выполняет HTTP-запрос, загружает файл и парсит GeoJSON.

Ключевой момент: URL может быть как абсолютным, так и относительным, если сервер корректно настроен.


2. Загрузка локального файла через Fetch API

При необходимости явного контроля над загрузкой используется fetch, после чего данные передаются в MapLibre как объект.

map.on("load", async () => {
  const response = await fetch("/data/cities.geojson");
  const geojson = await response.json();

  map.addSource("cities", {
    type: "geojson",
    data: geojson
  });

  map.addLayer({
    id: "cities-fill",
    type: "circle",
    source: "cities",
    paint: {
      "circle-radius": 5,
      "circle-color": "#3b82f6"
    }
  });
});

Такой подход позволяет:

  • выполнять предварительную обработку данных
  • фильтровать или трансформировать GeoJSON
  • комбинировать несколько источников до добавления на карту

3. Загрузка локального файла через input (File API)

При работе с пользовательскими файлами используется механизм FileReader.

const input = document.getElementById("fileInput");

input.addEventListener("change", (event) => {
  const file = event.target.files[0];

  const reader = new FileReader();

  reader.onl oad = (e) => {
    const geojson = JSON.parse(e.target.result);

    map.addSource("uploaded-data", {
      type: "geojson",
      data: geojson
    });

    map.addLayer({
      id: "uploaded-layer",
      type: "fill",
      source: "uploaded-data",
      paint: {
        "fill-color": "#10b981",
        "fill-opacity": 0.5
      }
    });
  };

  reader.readAsText(file);
});

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


Структура GeoJSON и требования MapLibre

MapLibre GL JS поддерживает стандарт RFC 7946. Базовая структура выглядит следующим образом:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [37.6173, 55.7558]
      },
      "properties": {
        "name": "Москва"
      }
    }
  ]
}

Важные ограничения:

  • координаты всегда в порядке [долгота, широта]
  • используется WGS84 (EPSG:4326)
  • не допускаются проекции вроде Web Mercator внутри GeoJSON
  • геометрии должны быть валидными согласно спецификации

Обновление GeoJSON после загрузки

MapLibre GL JS позволяет динамически изменять данные источника.

Полная замена данных

map.getSource("cities").setData(newGeojson);

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


Частичное обновление данных

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

  • пересборка FeatureCollection целиком
  • локальное изменение объекта и повторная передача через setData
const source = map.getSource("cities");

fetch("/data/updated.geojson")
  .then(res => res.json())
  .then(data => {
    source.setData(data);
  });

Производительность при загрузке файлов

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

Основные ограничения:

  • десятки тысяч точек допустимы без проблем
  • сложные полигоны резко увеличивают нагрузку
  • большие файлы (>10–20 MB) могут вызывать задержки UI

Типичные оптимизации:

Фильтрация данных до загрузки

const filtered = geojson.features.filter(f => f.properties.active);

Упрощение геометрии

Перед загрузкой применяется алгоритм упрощения (например, Douglas-Peucker), чтобы уменьшить количество координат.

Разделение данных на источники

map.addSource("cities-small", {...});
map.addSource("cities-large", {...});

Использование GeoJSON с фильтрацией слоёв

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

map.addLayer({
  id: "cities-filtered",
  type: "circle",
  source: "cities",
  filter: ["==", ["get", "type"], "capital"],
  paint: {
    "circle-color": "#e11d48",
    "circle-radius": 7
  }
});

Фильтры MapLibre работают на стороне рендера и не требуют изменения GeoJSON.


Загрузка нескольких GeoJSON файлов

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

Promise.all([
  fetch("/data/cities.geojson").then(r => r.json()),
  fetch("/data/roads.geojson").then(r => r.json())
]).then(([cities, roads]) => {
  map.addSource("cities", { type: "geojson", data: cities });
  map.addSource("roads", { type: "geojson", data: roads });
});

Каждый источник отображается отдельным слоем, что позволяет разделять стили и поведение.


Кэширование GeoJSON

При загрузке через HTTP полезно учитывать кэширование браузера.

Заголовки сервера:

  • Cache-Control
  • ETag
  • Last-Modified

Это снижает нагрузку при повторных открытиях карты.


Обработка ошибок загрузки

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

fetch("/data/cities.geojson")
  .then(res => {
    if (!res.ok) throw new Error("Ошибка загрузки");
    return res.json();
  })
  .then(data => {
    map.getSource("cities").setData(data);
  })
  .catch(err => {
    console.error("GeoJSON не загружен", err);
  });

Динамическая генерация GeoJSON

GeoJSON часто формируется программно:

const points = [];

for (let i = 0; i < 100; i++) {
  points.push({
    type: "Feature",
    geometry: {
      type: "Point",
      coordinates: [
        37 + Math.random(),
        55 + Math.random()
      ]
    },
    properties: {
      id: i
    }
  });
}

map.addSource("random", {
  type: "geojson",
  data: {
    type: "FeatureCollection",
    features: points
  }
});

Такой подход используется в аналитических и симуляционных задачах.


Связь GeoJSON с визуальными слоями MapLibre

После загрузки GeoJSON он становится основой для различных типов слоёв:

  • circle — точки
  • line — линии маршрутов
  • fill — полигоны
  • symbol — подписи и иконки

Один источник может использоваться несколькими слоями одновременно:

map.addLayer({
  id: "cities-labels",
  type: "symbol",
  source: "cities",
  layout: {
    "text-field": ["get", "name"]
  }
});