Работа с фильтрацией объектов в Leaflet строится вокруг управления слоями (layers), группами объектов и функций отображения данных на основе их свойств. Архитектура библиотеки Leaflet предполагает, что каждый визуальный элемент карты существует как слой, что позволяет гибко включать, исключать и модифицировать отображаемые данные без перерисовки всей карты.
Фильтрация в Leaflet не является отдельным встроенным механизмом уровня «query», как в базах данных. Вместо этого используется управление наборами слоёв:
addTo(map))removeLayer)Каждый объект карты (маркер, полилиния, полигон) может быть помещён в отдельную логическую группу, что позволяет управлять видимостью через бизнес-логику приложения.
Наиболее простой способ фильтрации — не добавлять объекты, не соответствующие условию.
const points = [
{ lat: 48.8566, lng: 2.3522, type: "museum" },
{ lat: 48.8606, lng: 2.3376, type: "park" },
{ lat: 48.8529, lng: 2.3499, type: "museum" }
];
const museumsLayer = L.layerGroup().addTo(map);
points.forEach(p => {
if (p.type === "museum") {
L.marker([p.lat, p.lng]).addTo(museumsLayer);
}
});
Такой подход формирует статическую фильтрацию на этапе построения слоя. Изменение фильтра требует пересоздания группы.
Для динамической фильтрации удобно хранить все объекты в одной группе и управлять их видимостью:
const allMarkers = L.layerGroup().addTo(map);
const markerA = L.marker([48.85, 2.35], { category: "museum" });
const markerB = L.marker([48.86, 2.33], { category: "park" });
allMarkers.addLayer(markerA);
allMarkers.addLayer(markerB);
Фильтрация выполняется обходом слоёв:
function filterByCategory(category) {
allMarkers.eachLayer(layer => {
const match = layer.options.category === category;
if (match) {
if (!map.hasLayer(layer)) layer.addTo(map);
} else {
if (map.hasLayer(layer)) map.removeLayer(layer);
}
});
}
Такой метод обеспечивает мгновенное переключение видимости без пересоздания объектов.
GeoJSON-слои поддерживают встроенный механизм фильтрации через
функцию filter, которая вызывается для каждого объекта.
const geojsonData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: { type: "museum" },
geometry: {
type: "Point",
coordinates: [2.3522, 48.8566]
}
},
{
type: "Feature",
properties: { type: "park" },
geometry: {
type: "Point",
coordinates: [2.3376, 48.8606]
}
}
]
};
Применение фильтра:
const layer = L.geoJSON(geojsonData, {
filter: function(feature) {
return feature.properties.type === "museum";
},
pointToLayer: function(feature, latlng) {
return L.marker(latlng);
}
}).addTo(map);
Функция filter возвращает true или
false, определяя включение объекта в слой.
Для динамического изменения фильтра GeoJSON слой обычно пересоздаётся:
let currentLayer;
function render(filterType) {
if (currentLayer) {
map.removeLayer(currentLayer);
}
currentLayer = L.geoJSON(geojsonData, {
filter: f => f.properties.type === filterType
}).addTo(map);
}
Этот подход проще логически, но менее эффективен при больших объёмах данных.
FeatureGroup позволяет работать с коллекцией объектов как с единым контейнером:
const group = L.featureGroup().addTo(map);
group.addLayer(L.marker([48.85, 2.35], { tags: ["culture"] }));
group.addLayer(L.marker([48.86, 2.33], { tags: ["nature"] }));
Фильтрация по массиву тегов:
function filterByTag(tag) {
group.eachLayer(layer => {
const tags = layer.options.tags || [];
const visible = tags.includes(tag);
if (visible) {
layer.addTo(map);
} else {
map.removeLayer(layer);
}
});
}
Реальные сценарии часто требуют фильтрации по нескольким условиям одновременно:
function complexFilter({ type, minYear }) {
allMarkers.eachLayer(layer => {
const data = layer.options;
const matchType = !type || data.type === type;
const matchYear = !minYear || data.year >= minYear;
const visible = matchType && matchYear;
if (visible) {
layer.addTo(map);
} else {
map.removeLayer(layer);
}
});
}
Такой подход позволяет реализовать логические комбинации AND/OR прямо на клиенте.
Для векторных слоёв часто используется не удаление, а изменение визуального состояния:
function filterPolygons(type) {
geojsonLayer.eachLayer(layer => {
const match = layer.feature.properties.type === type;
layer.setStyle({
opacity: match ? 1 : 0.1,
fillOpacity: match ? 0.5 : 0.05
});
});
}
Это позволяет сохранять структуру данных на карте, изменяя только визуальное восприятие.
Эффективный подход — заранее разделить данные на категории:
const museums = L.layerGroup();
const parks = L.layerGroup();
L.marker([48.85, 2.35]).addTo(museums);
L.marker([48.86, 2.33]).addTo(parks);
museums.addTo(map);
parks.addTo(map);
Переключение:
function toggle(category, enabled) {
const layer = category === "museum" ? museums : parks;
if (enabled) {
layer.addTo(map);
} else {
map.removeLayer(layer);
}
}
Встроенный компонент управления слоями часто применяется как визуальный фильтр:
const baseMaps = {};
const overlays = {
Museums: museums,
Parks: parks
};
L.control.layers(baseMaps, overlays).addTo(map);
Каждый overlay фактически представляет фильтр отображения категории.
При работе с тысячами объектов важно учитывать производительность:
LayerGroup вместо удаления/создания
маркеровsetStyle на больших GeoJSONmap.remove() / map.add() при
необходимости)Пример батчинга:
function batchFilter(predicate) {
map.eachLayer(layer => {
if (layer instanceof L.Marker) {
const visible = predicate(layer.options);
layer._icon.style.display = visible ? "block" : "none";
}
});
}
Для сложных сценариев используется предварительное индексирование данных:
const index = {
museum: [],
park: []
};
markers.forEach(m => {
index[m.options.type].push(m);
});
function show(type) {
Object.keys(index).forEach(key => {
index[key].forEach(marker => {
if (key === type) {
marker.addTo(map);
} else {
map.removeLayer(marker);
}
});
});
}
Такой подход снижает стоимость фильтрации при повторных операциях.
Фильтрация объектов в Leaflet формируется через несколько уровней управления:
GeoJSON filter для декларативной
логики