Обработка свойств объектов

В экосистеме Leaflet ключевую роль при работе с векторными слоями играет механизм хранения и обработки свойств объектов. Геометрия (точки, линии, полигоны) почти всегда сопровождается набором метаданных, описанных в формате GeoJSON через поле properties. Именно эти данные формируют основу для визуализации, фильтрации, интерактивного поведения и динамического стиля объектов на карте.

Структура properties в GeoJSON

GeoJSON-объект стандартно включает три основных компонента:

  • type — тип геометрии (Feature, FeatureCollection)
  • geometry — координаты объекта
  • properties — произвольный набор атрибутов

Пример базового объекта:

const feature = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [76.8897, 43.2389]
  },
  properties: {
    name: "Объект A",
    category: "station",
    capacity: 1200,
    active: true
  }
};

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

Доступ к свойствам внутри Leaflet

При использовании L.geoJSON каждый объект передаётся в обработчики, где доступ к properties осуществляется напрямую через feature.

L.geoJSON(data, {
  onEachFeature: function (feature, layer) {
    console.log(feature.properties.name);
  }
}).addTo(map);

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

Привязка всплывающих окон через свойства

Наиболее частое использование properties — формирование содержимого popup.

function onEachFeature(feature, layer) {
  const props = feature.properties;

  layer.bindPopup(
    `<strong>${props.name}</strong><br>
     Категория: ${props.category}<br>
     Вместимость: ${props.capacity}`
  );
}

L.geoJSON(data, {
  onEachFeature
}).addTo(map);

При этом структура данных может быть расширена без изменения логики отображения, что делает подход гибким и масштабируемым.

Безопасная обработка отсутствующих свойств

В реальных наборах данных свойства часто неполные. Отсутствие ключей требует дефолтной логики:

function getProp(props, key, fallback = "—") {
  return props && props[key] != null ? props[key] : fallback;
}

function onEachFeature(feature, layer) {
  const p = feature.properties;

  layer.bindPopup(`
    <strong>${getProp(p, "name")}</strong><br>
    Статус: ${getProp(p, "status", "неизвестен")}
  `);
}

Такая стратегия предотвращает ошибки при обращении к undefined и делает отображение стабильным даже при неполных данных.

Использование properties для стилизации

Свойства объектов часто используются для динамического определения стиля через функцию style.

function getColor(category) {
  switch (category) {
    case "station": return "blue";
    case "warehouse": return "orange";
    case "park": return "green";
    default: return "gray";
  }
}

L.geoJSON(data, {
  style: function (feature) {
    return {
      color: getColor(feature.properties.category),
      weight: 2,
      fillOpacity: 0.5
    };
  }
}).addTo(map);

Таким образом геометрия отделяется от визуальной логики, а свойства становятся источником конфигурации отображения.

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

Свойства позволяют ограничивать отображаемые элементы через filter.

L.geoJSON(data, {
  filter: function (feature) {
    return feature.properties.active === true;
  }
}).addTo(map);

Фильтрация выполняется до создания слоя, что влияет на производительность при больших наборах данных.

Расширенный вариант фильтрации:

function isValid(feature) {
  const p = feature.properties;
  return p && p.capacity > 500 && p.category !== "deprecated";
}

Привязка событий через свойства

Свойства могут использоваться для определения поведения объекта при взаимодействии.

function onEachFeature(feature, layer) {
  layer.on("click", function () {
    if (feature.properties.link) {
      window.open(feature.properties.link);
    }
  });
}

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

Преобразование properties перед отображением

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

function normalizeFeature(feature) {
  const p = feature.properties || {};

  return {
    ...feature,
    properties: {
      name: p.name || "Без названия",
      category: (p.category || "unknown").toLowerCase(),
      capacity: Number(p.capacity) || 0,
      active: Boolean(p.active)
    }
  };
}

const normalizedData = {
  type: "FeatureCollection",
  features: data.features.map(normalizeFeature)
};

Подобная трансформация снижает количество проверок в дальнейшем коде.

Группировка объектов по свойствам

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

function groupByCategory(features) {
  return features.reduce((acc, f) => {
    const key = f.properties.category || "unknown";
    if (!acc[key]) acc[key] = [];
    acc[key].push(f);
    return acc;
  }, {});
}

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

Динамическое обновление отображения на основе properties

Изменение свойств объекта может отражаться на карте без пересоздания всего слоя.

layer.feature.properties.active = false;

layer.setStyle({
  opacity: 0.3,
  fillOpacity: 0.1
});

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

Кастомные свойства и расширение модели данных

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

properties: {
  id: 1023,
  title: "Склад №5",
  metrics: {
    load: 0.78,
    temperature: 18.5
  },
  tags: ["logistics", "cold-storage"]
}

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

const load = feature.properties.metrics?.load ?? 0;

Интеграция properties с кластеризацией

При использовании кластеров свойства могут агрегироваться:

const markers = L.geoJSON(data, {
  onEachFeature: function (feature, layer) {
    layer.bindPopup(feature.properties.name);
  }
});

markerClusterGroup.addLayer(markers);

Дополнительно возможно вычисление агрегированных метрик по кластерам на основе исходных properties.

Связь properties с условным рендерингом

Логика отображения может зависеть от комбинации атрибутов:

function shouldHighlight(feature) {
  const p = feature.properties;
  return p.active && p.priority > 5;
}

L.geoJSON(data, {
  style: function (feature) {
    return shouldHighlight(feature)
      ? { weight: 3, color: "red" }
      : { weight: 1, color: "gray" };
  }
});

Сериализация и обратное восстановление данных

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

const serialized = JSON.stringify(geoJsonData);

const restored = JSON.parse(serialized);

При этом важно сохранять целостность структуры properties, поскольку именно она определяет поведение слоя после загрузки.

Работа с вычисляемыми свойствами

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

function enrichFeature(feature) {
  const p = feature.properties;

  p.label = `${p.name} (${p.category})`;
  p.isLarge = p.capacity > 1000;

  return feature;
}

Такая модель объединяет исходные данные и производные характеристики в одном объекте, упрощая дальнейшее использование в слоях Leaflet.