Другое

Исправление подсветки границ при наведении в геокартах Vega-Lite

Исправление проблем с подсветкой границ при наведении в геокартах Vega-Lite при использовании lookup transforms. Узнайте решения для сохранения интерактивности при наведении с данными TopoJSON в визуализациях Vega-Lite.

Vega-Lite: Подсветка границ геокарты при наведении не работает с преобразованием поиска

Я пытаюсь подсвечивать границы регионов (стран) на мировой карте Vega-Lite при наведении на них. Цель - сделать границу страны черной и более толстой при наведении.

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

Вот спецификация Vega-Lite, которую я использую:

json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {"country": "United States of America", "value": 18000},
      {"country": "Canada", "value": 7500},
      {"country": "Mexico", "value": 4200},
      {"country": "Brazil", "value": 6800},
      {"country": "Argentina", "value": 3100},
      {"country": "Chile", "value": 2400},
    ]
  },
  "layer": [
    {
      "name": "CHART_0",
      "data": {
        "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
        "format": {"type": "topojson", "feature": "countries"}
      },
      "mark": {"type": "geoshape", "fill": "#e9e6ef", "cursor": "pointer"},
      "encoding": {
        "stroke": {
          "condition": {"param": "geoHover", "empty": false, "value": "black"},
          "value": "white"
        },
        "strokeWidth": {
          "condition": {"param": "geoHover", "empty": false, "value": 2},
          "value": 0.5
        }
      }
    },
    {
      "name": "CHART_1",
      "encoding": {
        "color": {
          "field": "value",
          "type": "quantitative",
          "scale": {"scheme": "blues"}
        }
      },
      "layer": [
        {"mark": {"type": "geoshape", "tooltip": true, "cursor": "pointer"}}
      ],
      "transform": [
        {
          "lookup": "country",
          "from": {
            "data": {
              "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
              "format": {"type": "topojson", "feature": "countries"}
            },
            "key": "properties.name",
            "fields": ["properties", "geometry", "type"]
          }
        }
      ]
    }
  ],
  "params": [
    {
      "name": "geoHover",
      "select": {"type": "point", "on": "pointerover", "clear": "pointerout"},
      "views": ["CHART_0"]
    }
  ],
  "projection": {"type": "equalEarth"},
  "height": "container",
  "width": "container"
}

Как можно сделать подсветку границ при наведении корректной при использовании преобразования поиска с данными TopoJSON?

Содержание


Понимание основной проблемы

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

Как описано в вопросе на Stack Overflow, это распространенный паттерн, при котором “Vega-Lite теряет связь между наведенной фигурой и данными, используемыми для преобразования lookup.”

Ключевая задача — сохранение связи между географической геометрией и атрибутами данных при сохранении интерактивности при наведении.

Анализ вашего текущего спецификации

Анализируя вашу спецификацию, я могу определить несколько проблем:

  1. Ограничение области параметра: Параметр geoHover связан только с CHART_0, но преобразование lookup применяется в CHART_1. Это означает, что выбор при наведении не достигает слоя, где применяется фактическая географическая данные с преобразованием lookup.

  2. Несоответствие структуры данных: Преобразование lookup пытается выполнить объединение, используя "properties.name" в качестве ключа, но это может не правильно соответствовать названиям стран в ваших значениях данных.

  3. Разделение слоев: Наличие отдельных слоев для базовой географии и применения данных может мешать взаимодействиям при наведении, которые должны работать в обоих слоях.

Решения для работающего выделения при наведении

Решение 1: Подход с одним слоем

Наиболее прямое решение — объединить все в один слой и применить преобразование lookup перед кодированием наведения:

json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {"country": "United States of America", "value": 18000},
      {"country": "Canada", "value": 7500},
      {"country": "Mexico", "value": 4200},
      {"country": "Brazil", "value": 6800},
      {"country": "Argentina", "value": 3100},
      {"country": "Chile", "value": 2400},
    ]
  },
  "transform": [
    {
      "lookup": "country",
      "from": {
        "data": {
          "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
          "format": {"type": "topojson", "feature": "countries"}
        },
        "key": "properties.name",
        "fields": ["geometry", "type"]
      }
    }
  ],
  "mark": {"type": "geoshape", "cursor": "pointer"},
  "encoding": {
    "color": {
      "field": "value",
      "type": "quantitative",
      "scale": {"scheme": "blues"}
    },
    "stroke": {
      "condition": {"param": "geoHover", "empty": false, "value": "black"},
      "value": "white"
    },
    "strokeWidth": {
      "condition": {"param": "geoHover", "empty": false, "value": 2},
      "value": 0.5
    },
    "tooltip": {"field": "country"}
  },
  "params": [
    {
      "name": "geoHover",
      "select": {"type": "point", "on": "pointerover", "clear": "pointerout"}
    }
  ],
  "projection": {"type": "equalEarth"},
  "height": "container",
  "width": "container"
}

Решение 2: Исправление области параметра

Если вам нужно сохранить структуру слоев, исправьте область параметра для ссылки на оба слоя:

json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "layer": [
    {
      "name": "CHART_0",
      "data": {
        "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
        "format": {"type": "topojson", "feature": "countries"}
      },
      "mark": {"type": "geoshape", "fill": "#e9e6ef", "cursor": "pointer"},
      "encoding": {
        "stroke": {
          "condition": {"param": "geoHover", "empty": false, "value": "black"},
          "value": "white"
        },
        "strokeWidth": {
          "condition": {"param": "geoHover", "empty": false, "value": 2},
          "value": 0.5
        }
      }
    },
    {
      "name": "CHART_1",
      "data": {
        "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
        "format": {"type": "topojson", "feature": "countries"}
      },
      "mark": {"type": "geoshape", "cursor": "pointer"},
      "encoding": {
        "color": {
          "field": "value",
          "type": "quantitative",
          "scale": {"scheme": "blues"}
        },
        "tooltip": {"field": "country"}
      },
      "transform": [
        {
          "lookup": "properties.name",
          "from": {
            "data": {
              "values": [
                {"country": "United States of America", "value": 18000},
                {"country": "Canada", "value": 7500},
                {"country": "Mexico", "value": 4200},
                {"country": "Brazil", "value": 6800},
                {"country": "Argentina", "value": 3100},
                {"country": "Chile", "value": 2400},
              ]
            },
            "key": "country",
            "fields": ["value"]
          }
        }
      ]
    }
  ],
  "params": [
    {
      "name": "geoHover",
      "select": {"type": "point", "on": "pointerover", "clear": "pointerout"},
      "views": ["CHART_0", "CHART_1"]  // Включить оба слоя
    }
  ],
  "projection": {"type": "equalEarth"},
  "height": "container",
  "width": "container"
}

Альтернативные подходы

Использование меток Shape с преобразованием GeoShape

Для более сложных взаимодействий рассмотрите использование меток shape с преобразованием geoshape, как упоминается в документации Vega. Этот подход обеспечивает больший контроль над геометрическим рендерингом:

json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
    "format": {"type": "topojson", "feature": "countries"}
  },
  "transform": [
    {
      "lookup": "properties.name",
      "from": {
        "data": {
          "values": [
            {"country": "United States of America", "value": 18000},
            {"country": "Canada", "value": 7500},
            {"country": "Mexico", "value": 4200},
            {"country": "Brazil", "value": 6800},
            {"country": "Argentina", "value": 3100},
            {"country": "Chile", "value": 2400},
          ]
        },
        "key": "country",
        "fields": ["value"]
      }
    },
    {
      "geoshape": {"projection": "projection"}
    }
  ],
  "mark": {
    "type": "shape",
    "cursor": "pointer"
  },
  "encoding": {
    "color": {
      "field": "value",
      "type": "quantitative",
      "scale": {"scheme": "blues"}
    },
    "stroke": {
      "condition": {
        "param": "geoHover",
        "empty": false,
        "value": "black",
        "test": "datum.value !== null"
      },
      "value": "white"
    },
    "strokeWidth": {
      "condition": {
        "param": "geoHover",
        "empty": false,
        "value": 2,
        "test": "datum.value !== null"
      },
      "value": 0.5
    }
  },
  "params": [
    {
      "name": "geoHover",
      "select": {"type": "point", "on": "pointerover", "clear": "pointerout"}
    }
  ],
  "projection": {"type": "equalEarth"},
  "height": "container",
  "width": "container"
}

Лучшие практики для взаимодействий при наведении

  1. Сохраняйте согласованность структур данных: Убедитесь, что преобразование lookup не нарушает связь между геометрическими объектами и их идентификаторами.

  2. Используйте условное тестирование: Добавляйте тесты в ваши условия наведения, чтобы они применялись только к допустимым точкам данных, как показано в альтернативном подходе выше.

  3. Рассмотрите предварительную обработку данных: Для сложных наборов данных рассмотрите предварительную обработку данных для включения необходимых идентификаторов перед загрузкой в Vega-Lite.

  4. Тестируйте с минимальными примерами: Начните с простого рабочего примера и постепенно добавляйте сложность для изоляции проблем.

  5. Используйте подходящие типы выбора: Разные типы выбора лучше подходят для разных случаев использования. Точечные выборки хорошо работают для отдельных географических объектов.

Полный рабочий пример

Вот полная рабочая спецификация, демонстрирующая решение:

json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {"country": "United States of America", "value": 18000},
      {"country": "Canada", "value": 7500},
      {"country": "Mexico", "value": 4200},
      {"country": "Brazil", "value": 6800},
      {"country": "Argentina", "value": 3100},
      {"country": "Chile", "value": 2400},
    ]
  },
  "transform": [
    {
      "lookup": "country",
      "from": {
        "data": {
          "url": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",
          "format": {"type": "topojson", "feature": "countries"}
        },
        "key": "properties.name",
        "fields": ["geometry", "type"]
      }
    }
  ],
  "mark": {"type": "geoshape", "cursor": "pointer"},
  "encoding": {
    "color": {
      "field": "value",
      "type": "quantitative",
      "scale": {"scheme": "blues"}
    },
    "stroke": {
      "condition": {
        "param": "geoHover",
        "empty": false,
        "value": "black",
        "test": "datum.value !== null"
      },
      "value": "white"
    },
    "strokeWidth": {
      "condition": {
        "param": "geoHover",
        "empty": false,
        "value": 2,
        "test": "datum.value !== null"
      },
      "value": 0.5
    },
    "tooltip": [
      {"field": "country", "type": "nominal"},
      {"field": "value", "type": "quantitative"}
    ]
  },
  "params": [
    {
      "name": "geoHover",
      "select": {"type": "point", "on": "pointerover", "clear": "pointerout"}
    }
  ],
  "projection": {"type": "equalEarth"},
  "height": 500,
  "width": 800
}

Это решение сохраняет функциональность преобразования lookup, обеспечивая правильную работу выделения при наведении. Ключевые улучшения включают:

  • Применение преобразования lookup перед кодированием метки
  • Добавление тестов для обеспечения применения эффектов наведения только к допустимым точкам данных
  • Сохранение всей логики рендеринга в одном слое для лучшей области параметра

Источники

  1. Vega-Lite geo map hover border highlighting not working when using lookup transform - Stack Overflow
  2. GeoShape Transform - Vega Documentation
  3. Geoshape | Vega-Lite Documentation
  4. Selection Parameters | Vega-Lite Documentation
  5. Shape Mark - Vega-Lite Documentation

Заключение

Проблема выделения границы при наведении с преобразованиями lookup в Vega-Lite возникает из-за изменений в структуре данных, которые нарушают связь между наведенными фигурами и соответствующими данными. Основные решения:

  1. Используйте подход с одним слоем для сохранения согласованности данных
  2. Исправьте область параметра для ссылки на все соответствующие слои
  3. Добавляйте условные тесты в условия наведения для проверки данных
  4. Рассмотрите использование меток shape с преобразованиями geoshape для большего контроля

Наиболее надежное решение обычно — объединение всего в один слой с применением преобразования lookup перед кодированием метки. Этот подход сохраняет связь между географическими объектами и атрибутами данных, поддерживая правильную интерактивность при наведении.

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

Авторы
Проверено модерацией
Модерация