Работа с геоданными является центральной частью Leaflet. Библиотека предоставляет развитый набор инструментов для загрузки, отображения, стилизации и обработки пространственных данных различных форматов. Наиболее важное место занимает поддержка стандарта GeoJSON, который фактически стал универсальным способом обмена географической информацией между веб-картами и серверными приложениями.
Leaflet позволяет работать как с простыми объектами — отдельными точками или линиями, так и со сложными наборами пространственных данных, содержащими тысячи объектов различных типов.
GeoJSON представляет собой текстовый формат хранения географических данных на основе JSON.
Каждый объект содержит описание геометрии и дополнительных атрибутов.
Пример точки:
{
"type": "Feature",
"properties": {
"name": "Москва"
},
"geometry": {
"type": "Point",
"coordinates": [37.6176, 55.7558]
}
}
Структура GeoJSON включает несколько основных сущностей:
| Тип | Назначение |
|---|---|
| Point | Точка |
| MultiPoint | Набор точек |
| LineString | Линия |
| MultiLineString | Набор линий |
| Polygon | Полигон |
| MultiPolygon | Набор полигонов |
| Feature | Объект с атрибутами |
| FeatureCollection | Коллекция объектов |
Leaflet умеет автоматически преобразовывать большинство типов геометрий в соответствующие картографические объекты.
Для отображения GeoJSON используется класс
L.geoJSON().
Простейший пример:
const geojsonLayer = L.geoJSON(data);
geojsonLayer.addTo(map);
Где data содержит объект GeoJSON.
Полный пример:
const city = {
type: "Feature",
properties: {
name: "Москва"
},
geometry: {
type: "Point",
coordinates: [37.6176, 55.7558]
}
};
L.geoJSON(city).addTo(map);
После добавления на карту точка автоматически отображается в виде стандартного маркера.
На практике чаще используются коллекции объектов.
Пример:
const cities = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
name: "Москва"
},
geometry: {
type: "Point",
coordinates: [37.6176, 55.7558]
}
},
{
type: "Feature",
properties: {
name: "Санкт-Петербург"
},
geometry: {
type: "Point",
coordinates: [30.3351, 59.9343]
}
}
]
};
L.geoJSON(cities).addTo(map);
Leaflet автоматически создаст отдельный слой для каждого объекта коллекции.
GeoJSON можно загружать поэтапно.
Создание пустого слоя:
const geojsonLayer = L.geoJSON().addTo(map);
Добавление объектов:
geojsonLayer.addData(data);
Добавление новой коллекции:
geojsonLayer.addData(newFeatures);
Такой подход часто применяется при динамической подгрузке информации с сервера.
Для линий и полигонов используется параметр style.
L.geoJSON(data, {
style: {
color: "#ff0000",
weight: 3,
opacity: 0.8
}
}).addTo(map);
Поддерживаются свойства:
| Свойство | Описание |
|---|---|
| color | Цвет границы |
| weight | Толщина линии |
| opacity | Прозрачность |
| fillColor | Цвет заливки |
| fillOpacity | Прозрачность заливки |
| dashArray | Пунктир |
Пример:
L.geoJSON(data, {
style: {
color: "blue",
fillColor: "lightblue",
fillOpacity: 0.5
}
}).addTo(map);
Стиль может определяться функцией.
L.geoJSON(data, {
style(feature) {
return {
color: feature.properties.color
};
}
}).addTo(map);
GeoJSON:
{
"properties": {
"color": "green"
}
}
В результате каждый объект получает собственное оформление.
По умолчанию точки отображаются как стандартные маркеры.
Для полного контроля используется параметр
pointToLayer.
L.geoJSON(data, {
pointToLayer(feature, latlng) {
return L.circleMarker(latlng);
}
}).addTo(map);
Создание кругового маркера:
L.geoJSON(data, {
pointToLayer(feature, latlng) {
return L.circleMarker(latlng, {
radius: 8,
color: "red",
fillColor: "orange",
fillOpacity: 0.7
});
}
}).addTo(map);
Внутри pointToLayer доступны свойства GeoJSON.
L.geoJSON(data, {
pointToLayer(feature, latlng) {
return L.circleMarker(latlng, {
radius: feature.properties.population / 1000000
});
}
}).addTo(map);
Размер маркера может зависеть от численности населения, рейтинга, объёма продаж или любых других данных.
Параметр filter позволяет исключать объекты до их
отображения.
L.geoJSON(data, {
filter(feature) {
return feature.properties.active;
}
}).addTo(map);
Отобразятся только объекты, у которых:
active === true
Более сложный пример:
L.geoJSON(data, {
filter(feature) {
return feature.properties.population > 1000000;
}
}).addTo(map);
Параметр onEachFeature вызывается для каждого элемента
коллекции.
L.geoJSON(data, {
onEachFeature(feature, layer) {
console.log(feature.properties.name);
}
}).addTo(map);
Аргументы:
| Аргумент | Описание |
|---|---|
| feature | Объект GeoJSON |
| layer | Соответствующий слой Leaflet |
Через этот обработчик обычно подключаются всплывающие окна и события.
Пример создания popup:
L.geoJSON(data, {
onEachFeature(feature, layer) {
layer.bindPopup(feature.properties.name);
}
}).addTo(map);
Более подробный вариант:
L.geoJSON(data, {
onEachFeature(feature, layer) {
const html = `
<h3>${feature.properties.name}</h3>
<p>Население: ${feature.properties.population}</p>
`;
layer.bindPopup(html);
}
}).addTo(map);
Каждый объект GeoJSON является полноценным слоем Leaflet.
Поэтому доступны все стандартные события.
L.geoJSON(data, {
onEachFeature(feature, layer) {
layer.on("click", () => {
console.log(feature.properties.name);
});
}
}).addTo(map);
Обработка наведения курсора:
layer.on("mouseover", () => {
console.log("Наведение");
});
Частый сценарий работы с полигонами.
function highlight(e) {
e.target.setStyle({
weight: 5,
color: "#ffff00"
});
}
Подключение:
L.geoJSON(data, {
onEachFeature(feature, layer) {
layer.on({
mouseover: highlight
});
}
}).addTo(map);
Восстановление исходного стиля:
function reset(e) {
geojsonLayer.resetStyle(e.target);
}
Каждый слой GeoJSON умеет вычислять свои границы.
const bounds = geojsonLayer.getBounds();
Автоматическое масштабирование карты:
map.fitBounds(
geojsonLayer.getBounds()
);
Метод особенно полезен при загрузке данных неизвестного размера.
Получение данных через Fetch API:
fetch("/data/cities.geojson")
.then(response => response.json())
.then(data => {
L.geoJSON(data).addTo(map);
});
Асинхронная версия:
async function loadData() {
const response = await fetch(
"/data/cities.geojson"
);
const data = await response.json();
L.geoJSON(data).addTo(map);
}
loadData();
Сервер может возвращать GeoJSON напрямую.
fetch("/api/regions")
.then(response => response.json())
.then(data => {
geojsonLayer.addData(data);
});
Такой подход широко используется в:
Большинство объектов Leaflet поддерживает экспорт.
Маркер:
const marker = L.marker([
55.7558,
37.6176
]);
const geojson = marker.toGeoJSON();
Линия:
const line = L.polyline([
[55.7, 37.5],
[55.8, 37.8]
]);
const geojson = line.toGeoJSON();
Полигон:
const polygon = L.polygon([
[55.7, 37.5],
[55.8, 37.8],
[55.6, 38.0]
]);
const geojson = polygon.toGeoJSON();
Группа объектов также может быть преобразована в GeoJSON.
const group = L.featureGroup([
marker1,
marker2,
polygon
]);
const geojson = group.toGeoJSON();
Результатом станет объект типа:
{
"type": "FeatureCollection"
}
Это позволяет сохранять данные пользователей на сервере или экспортировать их в файлы.
Важная особенность GeoJSON заключается в порядке координат.
GeoJSON использует:
[долгота, широта]
Например:
[37.6176, 55.7558]
Leaflet использует:
[широта, долгота]
Например:
[55.7558, 37.6176]
При создании GeoJSON Leaflet автоматически выполняет преобразование, однако при ручной обработке данных эту разницу необходимо учитывать.
При работе с тысячами объектов рекомендуется:
Включение Canvas:
const map = L.map("map", {
preferCanvas: true
});
Для больших наборов точек это может существенно повысить скорость отображения.
GeoJSON-слой наследует возможности FeatureGroup.
Поэтому доступны методы:
geojsonLayer.eachLayer(...)
geojsonLayer.clearLayers()
geojsonLayer.getLayers()
geojsonLayer.removeLayer(...)
geojsonLayer.addLayer(...)
Очистка слоя:
geojsonLayer.clearLayers();
Получение всех объектов:
const layers =
geojsonLayer.getLayers();
Перебор элементов:
geojsonLayer.eachLayer(layer => {
console.log(layer);
});
На одной карте могут одновременно отображаться разные наборы данных.
const roadsLayer =
L.geoJSON(roads);
const riversLayer =
L.geoJSON(rivers);
const citiesLayer =
L.geoJSON(cities);
Добавление на карту:
roadsLayer.addTo(map);
riversLayer.addTo(map);
citiesLayer.addTo(map);
Управление отображением через переключатель слоёв:
L.control.layers(null, {
"Дороги": roadsLayer,
"Реки": riversLayer,
"Города": citiesLayer
}).addTo(map);
Такой подход лежит в основе большинства профессиональных веб-ГИС, где карта формируется из множества независимых наборов геоданных, объединённых в единую интерактивную систему визуализации и анализа пространственной информации.