Plotly Хороплет – Как добавить контур к форме из geojson

Вопрос или проблема

Я хочу нарисовать контур, выделяющий форму на хоровой карте, построенной из GeoJSON с использованием Plotly (в моем случае через Dash).

У меня есть dataframe “df”, похожий на этот:

state (строка) value (число с плавающей точкой) 
STATE_1        50.0
STATE_2        30.5
STATE_3        66.2

У меня также есть GeoJSON, загруженный как объект с этой структурой:

{
    'type': 'FeatureCollection',
    'features': [
        {
            'type': 'Feature',
            'id': 1,
            'properties': {
                'id': 'STATE_1',
            },
            'geometry': {'type': 'Polygon', 'coordinates': [...]},
        },
        ...
    ]
}

Я нарисовал свою базовую карту вот так:

fig = px.choropleth(
    df,
    geojson=my_geojson,
    color="value",
    locations="state",
    featureidkey='properties.id',
    projection='Mercator',
)
fig.update_geos(
    fitbounds="locations",
    visible=False,
)
fig.update_layout(
    coloraxis=dict(
        colorbar=dict(
            title="",
            orientation='h',
            x=0.5,
            y=0.1,
            xanchor="center",
            yanchor="top",
        ),
        colorscale=[[0, '#04cc9a'], [1, '#5259ad']],
    ),
    margin={"r": 0, "t": 0, "l": 0, "b": 0},
    dragmode=False,
)

Я пытался добавить другой след безуспешно, чтобы показать контур одного из штатов. Я пробовал как с использованием plotly express, так и объектов графиков plotly без результата.

Как мне это сделать?

Стратегия, которая в конце концов сработала для меня, была следующей:

  1. Создать основную фигуру, как показано в вопросе
  2. Создать вспомогательную фигуру с помощью plotly express. Это было необходимо для того, чтобы задать новому следу его маркер и цвет заливки. В противном случае он был бы заменен конфигурацией основной фигуры.
  3. Добавить след из вспомогательной фигуры в основную

Это будет что-то вроде этого:

# Создать основную фигуру, как показано в вопросе
fig = ...

# Отфильтровать данные и geojson, чтобы создать новый след
df_aux = df[df['state'] == 'STATE_1']
my_filtered_geojson = {...}  # Отфильтровывать так, чтобы он содержал только один Feature, соответствующий STATE_1

# Заменить значение на фиктивное, чтобы мы могли залить форму прозрачным цветом
df_aux ['value'] = 1  

# Создать вспомогательную фигуру
fig_aux = go.Figure(go.Choropleth(
    geojson=my_filtered_geojson,
    featureidkey='properties.id',
    locationmode="geojson-id",
    locations=df_aux['state'],
    z=df_aux['value'],  # Фиктивное значение, которое мы только что установили выше
    zmax=1,
    zmin=0,
    colorscale=[[0, 'rgba(0,0,0,0)'], [1, 'rgba(0,0,0,0)']],  # Цвета с альфа-каналом, оба полностью прозрачные
    showscale=False,
    marker=dict(
        line=dict(
            color="red",
            width=4,
        )
    ),
    hoverinfo='skip',  # Скрыть информацию при наведении, чтобы получить информацию основной фигуры
))

# Добавить новый след в основную фигуру
fig_principal.add_trace(fig_aux.data[0])

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

Хоровая карта, сделанная с помощью plotly, выделяющая одну форму

Ответ или решение

Чтобы добавить контур к фигуре на хоровой карте (Choropleth-karte) с помощью Plotly и GeoJSON, вам потребуется осуществить несколько шагов, как показано в вашем вопросе. Ниже представлена пошаговая инструкция, основываясь на ваших исходных данных и форматах.

Шаг 1: Создание основной карты

Сначала создайте основную карту, используя ваш DataFrame и GeoJSON. Используйте библиотеку Plotly Express для упрощения этого процесса:

import plotly.express as px

fig = px.choropleth(
    df,
    geojson=my_geojson,
    color="value",
    locations="state",
    featureidkey='properties.id',
    projection='Mercator',
)

fig.update_geos(
    fitbounds="locations",
    visible=False,
)

fig.update_layout(
    coloraxis=dict(
        colorbar=dict(
            title="",
            orientation='h',
            x=0.5,
            y=0.1,
            xanchor="center",
            yanchor="top",
        ),
        colorscale=[[0, '#04cc9a'], [1, '#5259ad']],
    ),
    margin={"r": 0, "t": 0, "l": 0, "b": 0},
    dragmode=False,
)

Шаг 2: Создание вспомогательной карты для контура

Для добавления контура вам нужно создать вспомогательную карту. Эта карта будет использовать ту же структуру GeoJSON, но только для выделяемого региона, в данном случае STATE_1. Вот как это можно сделать:

  1. Отфильтруйте DataFrame и GeoJSON так, чтобы они содержали только нужный вами объект.
  2. Задайте непрозрачные цвета для контура.
# Фильтрация DataFrame для вашего конкретного состояния
df_aux = df[df['state'] == 'STATE_1']

# Отфильтруйте GeoJSON
my_filtered_geojson = {
    'type': 'FeatureCollection',
    'features': [feature for feature in my_geojson['features'] if feature['properties']['id'] == 'STATE_1']
}

# Установите фиктивное значение для корректного отображения
df_aux['value'] = 1

# Создайте вспомогательную фигуру
import plotly.graph_objects as go

fig_aux = go.Figure(go.Choropleth(
    geojson=my_filtered_geojson,
    featureidkey='properties.id',
    locationmode="geojson-id",
    locations=df_aux['state'],
    z=df_aux['value'],  # Используйте фиктивное значение
    zmax=1,
    zmin=0,
    colorscale=[[0, 'rgba(0,0,0,0)'], [1, 'rgba(0,0,0,0)']],  # Полностью прозрачные цвета
    showscale=False,
    marker=dict(
        line=dict(
            color="red",  # Цвет контура
            width=4,  # Ширина контура
        )
    ),
    hoverinfo='skip',  # Скрыть информацию о наведение
))

Шаг 3: Добавление контура в основную фигуру

После создания вспомогательной карты, добавьте её в основную фигуру:

# Добавьте новый след в основную фигуру
fig.add_trace(fig_aux.data[0])

Шаг 4: Тестирование и визуализация

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

Итоговая визуализация

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

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

Оцените материал
Добавить комментарий

Капча загружается...

  1. Гость

    Спасибо за подробное решение! Хотел бы предложить альтернативный подход, который может упростить ваш код и избежать создания дополнительной фигуры.

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

    Вот пример того, как это можно реализовать:

    import plotly.express as px
    import plotly.graph_objects as go

    # Создаем основную фигуру
    fig = px.choropleth(
    df,
    geojson=my_geojson,
    color="value",
    locations="state",
    featureidkey='properties.id',
    projection='mercator',
    )

    # Ищем координаты полигона для STATE_1
    for feature in my_geojson['features']:
    if feature['properties']['id'] == 'STATE_1':
    geometry = feature['geometry']
    break

    # Проверяем тип геометрии и извлекаем координаты
    if geometry['type'] == 'Polygon':
    coordinates = geometry['coordinates']
    elif geometry['type'] == 'MultiPolygon':
    # Если это MultiPolygon, объединяем все координаты
    coordinates = [coord for polygon in geometry['coordinates'] for coord in polygon]

    # Преобразуем координаты в списки долгот и широт
    lons = []
    lats = []
    for polygon in coordinates:
    lon_vals = [point[0] for point in polygon]
    lat_vals = [point[1] for point in polygon]
    # Добавляем None для разделения полигонов
    lons += lon_vals + [None]
    lats += lat_vals + [None]

    # Добавляем контур в основную фигуру
    fig.add_trace(go.Scattergeo(
    lon = lons,
    lat = lats,
    mode = 'lines',
    line = dict(color='red', width=4),
    showlegend=False,
    hoverinfo='skip',
    ))

    # Обновляем параметры географии
    fig.update_geos(fitbounds="locations", visible=False)

    # Обновляем макет фигуры
    fig.update_layout(
    margin={"r":0,"t":0,"l":0,"b":0},
    dragmode=False
    )

    # Отображаем фигуру
    fig.show()

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

    Кроме того, если вам нужно выделить несколько штатов, вы можете добавить их координаты в списки lons и lats аналогичным образом.

    Надеюсь, этот подход будет вам полезен!

    Ответить