Другое

Визуализация сетки H3: Руководство по меткам и кривым индексов

Узнайте, как создавать визуализации шестигранных сеток H3 с помеченными шестиугольниками и плавными соединительными кривыми. Полное руководство на Python с использованием библиотек matplotlib и H3 для анализа геопространственных данных.

Как визуализировать индексы сетки H3 с метками и соединяющими кривыми?

Я хотел бы сгенерировать сетку H3 с метками внутри каждого шестиугольника, в соответствии с порядком индексов, и нарисовать кривую, соединяющую эти метки последовательно.

Примечание: Я ищу визуализацию, похожую на ту, что показана на эталонном изображении, где “индексная кривая” соединяет метки в порядке. Система сетки H3 - это система индексации шестиугольной сетки, разработанная компанией Uber.

Визуализация H3 сетки с метками и соединяющими кривыми

Визуализация H3 сетки с метками и соединяющими кривыми может быть реализована с помощью библиотек Python, таких как matplotlib, geopandas и Uber H3 библиотека. Процесс включает генерацию шестиугольных ячеек сетки, извлечение их границ и центроидов, размещение меток индексов H3 и создание плавных кривых, которые соединяют эти метки последовательно.

Содержание

Основы системы сетки H3

Система сетки H3 - это иерархическая шестиугольная система пространственной индексации от Uber, которая разделяет поверхность Земли на шестиугольные ячейки различного разрешения. Каждый шестиугольник имеет уникальный индекс H3, который можно использовать для пространственного анализа, агрегации и визуализации.

Ключевые характеристики H3, делающие ее подходящей для этой задачи визуализации:

  • Иерархическая структура: Разрешения варьируются от 0 (самое грубое) до 15 (самое детальное), при каждом разрешении примерно в 7 раз больше ячеек, чем на предыдущем уровне
  • Глобальное покрытие: Вся Земля покрыта шестиугольной сеткой
  • Компактная индексация: Каждый шестиугольник имеет уникальный 64-битный целый индекс
  • Отношения соседства: Соседние шестиугольники могут быть эффективно идентифицированы

Как объясняет Uber Engineering, “H3 позволяет нам анализировать географическую информацию для установки динамических цен и принятия других решений на уровне города. Мы используем H3 как систему сетки для анализа и оптимизации во всех наших рыночных платформах”.

Настройка окружения

Для создания визуализаций H3 сетки с метками и соединяющими кривыми вам понадобятся несколько библиотек Python:

python
# Установка необходимых пакетов
pip install h3 geopandas matplotlib shapely numpy scipy

Вот описание роли каждой библиотеки:

  • h3: Основная библиотека для операций индексации H3
  • geopandas: Для обработки пространственных данных и создания GeoDataFrames
  • matplotlib: Для создания визуализации
  • shapely: Для геометрических операций с границами шестиугольников
  • numpy: Для численных операций
  • scipy: Для создания плавных кривых через точки

Библиотека h3-py предоставляет привязки Python для H3 и предлагает несколько API с одинаковыми функциями, но разными форматами ввода/вывода.

Генерация H3 сетки с метками

Начнем с создания функции для генерации H3 сетки и размещения меток внутри каждого шестиугольника:

python
import h3
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import Polygon, MultiPolygon
import matplotlib.patches as patches
from scipy.interpolate import splprep, splev

def create_h3_grid_with_labels(center_lat, center_lng, resolution, k_ring=1):
    """
    Создать H3 сетку вокруг центральной точки с указанным разрешением и радиусом k-ring
    """
    # Получить индекс центрального шестиугольника
    center_hex = h3.geo_to_h3(center_lat, center_lng, resolution)
    
    # Получить все шестиугольники в k-ring
    hexagons = h3.k_ring(center_hex, k_ring)
    
    # Создать список геометрий и индексов шестиугольников
    hex_data = []
    for hex_idx in hexagons:
        # Получить границу шестиугольника
        boundary = h3.h3_to_geo_boundary(hex_idx, geo_json=False)
        
        # Создать полигон
        polygon = Polygon(boundary)
        
        # Получить центроид для размещения метки
        centroid = polygon.centroid
        
        hex_data.append({
            'h3_index': hex_idx,
            'geometry': polygon,
            'centroid': (centroid.x, centroid.y),
            'boundary': boundary
        })
    
    # Создать GeoDataFrame
    gdf = gpd.GeoDataFrame(hex_data, geometry='geometry', crs='EPSG:4326')
    
    return gdf

def plot_h3_grid_with_labels(gdf, ax=None):
    """
    Отобразить H3 сетку с метками внутри каждого шестиугольника
    """
    if ax is None:
        fig, ax = plt.subplots(figsize=(12, 10))
    
    # Отобразить границы шестиугольников
    gdf.plot(ax=ax, facecolor='lightblue', edgecolor='black', linewidth=1)
    
    # Добавить метки в центроидах шестиугольников
    for idx, row in gdf.iterrows():
        ax.text(row['centroid'][0], row['centroid'][1], 
                row['h3_index'], 
                ha='center', va='center', 
                fontsize=8, fontweight='bold')
    
    ax.set_aspect('equal')
    ax.set_title('H3 сетка с метками')
    ax.grid(True, alpha=0.3)
    
    return ax

Для использования этого кода:

python
# Создать H3 сетку вокруг Сан-Франциско
sf_lat, sf_lng = 37.7749, -122.4194
resolution = 8  # Разрешение от 0 до 15
gdf = create_h3_grid_with_labels(sf_lat, sf_lng, resolution, k_ring=2)

# Отобразить сетку
plot_h3_grid_with_labels(gdf)
plt.show()

Уровень разрешения H3 определяет детализацию вашей сетки, при этом разрешение 10 обычно используется для анализа на уровне города согласно документации Uber.

Создание соединяющих кривых

Теперь добавим соединяющие кривые, которые связывают метки последовательно. Мы будем использовать сплайн-интерполяцию из scipy для создания плавных кривых:

python
def create_index_curve(gdf, ax=None):
    """
    Создать плавную кривую, соединяющую метки шестиугольников в порядке индекса
    """
    if ax is None:
        fig, ax = plt.subplots(figsize=(12, 10))
    
    # Отсортировать шестиугольники по индексу H3 для получения последовательности
    sorted_gdf = gdf.sort_values('h3_index')
    
    # Извлечь координаты в порядке
    x_coords = [point[0] for point in sorted_gdf['centroid']]
    y_coords = [point[1] for point in sorted_gdf['centroid']]
    
    # Создать замкнутую кривую, добавив первую точку в конец
    x_coords.append(x_coords[0])
    y_coords.append(y_coords[0])
    
    # Использовать сплайн-интерполяцию для плавной кривой
    tck, u = splprep([x_coords, y_coords], s=0, per=True)
    u_new = np.linspace(0, 1, len(x_coords) * 3)
    curve_x, curve_y = splev(u_new, tck)
    
    # Отобразить соединяющую кривую
    ax.plot(curve_x, curve_y, 'r-', linewidth=2, alpha=0.7, 
            label='Кривая индекса')
    
    # Добавить стрелки для отображения направления
    for i in range(0, len(curve_x)-5, len(curve_x)//8):
        dx = curve_x[i+2] - curve_x[i]
        dy = curve_y[i+2] - curve_y[i]
        ax.arrow(curve_x[i], curve_y[i], dx*0.3, dy*0.3,
                head_width=0.001, head_length=0.002, 
                fc='red', ec='red', alpha=0.7)
    
    return ax

def plot_complete_visualization(gdf):
    """
    Создать полную визуализацию с сеткой, метками и соединяющей кривой
    """
    fig, ax = plt.subplots(figsize=(14, 12))
    
    # Отобразить сетку
    gdf.plot(ax=ax, facecolor='lightblue', edgecolor='black', linewidth=1)
    
    # Добавить метки
    for idx, row in gdf.iterrows():
        ax.text(row['centroid'][0], row['centroid'][1], 
                row['h3_index'], 
                ha='center', va='center', 
                fontsize=8, fontweight='bold')
    
    # Создать и отобразить соединяющую кривую
    ax = create_index_curve(gdf, ax)
    
    ax.set_aspect('equal')
    ax.set_title('H3 сетка с кривой индекса')
    ax.legend()
    ax.grid(True, alpha=0.3)
    
    return fig, ax

Сплайн-интерполяция с использованием scipy.interpolate.splprep и splev создает плавные кривые через центроиды шестиугольников, а мы добавляем направленные стрелки для отображения последовательности.

Полный пример реализации

Вот полный, рабочий пример, который объединяет все компоненты:

python
import h3
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
from scipy.interpolate import splprep, splev

def create_h3_grid_with_labels(center_lat, center_lng, resolution, k_ring=1):
    """Создать H3 сетку вокруг центральной точки"""
    center_hex = h3.geo_to_h3(center_lat, center_lng, resolution)
    hexagons = h3.k_ring(center_hex, k_ring)
    
    hex_data = []
    for hex_idx in hexagons:
        boundary = h3.h3_to_geo_boundary(hex_idx, geo_json=False)
        polygon = Polygon(boundary)
        centroid = polygon.centroid
        
        hex_data.append({
            'h3_index': hex_idx,
            'geometry': polygon,
            'centroid': (centroid.x, centroid.y),
            'boundary': boundary
        })
    
    return gpd.GeoDataFrame(hex_data, geometry='geometry', crs='EPSG:4326')

def create_index_curve(gdf, ax=None):
    """Создать плавную кривую, соединяющую метки шестиугольников в порядке индекса"""
    if ax is None:
        fig, ax = plt.subplots(figsize=(12, 10))
    
    # Отсортировать по индексу H3
    sorted_gdf = gdf.sort_values('h3_index')
    
    # Извлечь координаты
    x_coords = [point[0] for point in sorted_gdf['centroid']]
    y_coords = [point[1] for point in sorted_gdf['centroid']]
    
    # Замкнуть кривую
    x_coords.append(x_coords[0])
    y_coords.append(y_coords[0])
    
    # Сплайн-интерполяция
    tck, u = splprep([x_coords, y_coords], s=0, per=True)
    u_new = np.linspace(0, 1, len(x_coords) * 3)
    curve_x, curve_y = splev(u_new, tck)
    
    # Отобразить кривую со стрелками
    ax.plot(curve_x, curve_y, 'r-', linewidth=2, alpha=0.7, label='Кривая индекса')
    
    for i in range(0, len(curve_x)-5, len(curve_x)//8):
        dx = curve_x[i+2] - curve_x[i]
        dy = curve_y[i+2] - curve_y[i]
        ax.arrow(curve_x[i], curve_y[i], dx*0.3, dy*0.3,
                head_width=0.001, head_length=0.002, 
                fc='red', ec='red', alpha=0.7)
    
    return ax

def plot_h3_with_index_curve(center_lat, center_lng, resolution=8, k_ring=2):
    """Создать и отобразить полную визуализацию H3 с кривой индекса"""
    # Создать H3 сетку
    gdf = create_h3_grid_with_labels(center_lat, center_lng, resolution, k_ring)
    
    # Создать фигуру
    fig, ax = plt.subplots(figsize=(14, 12))
    
    # Отобразить сетку
    gdf.plot(ax=ax, facecolor='lightblue', edgecolor='black', linewidth=1)
    
    # Добавить метки
    for idx, row in gdf.iterrows():
        ax.text(row['centroid'][0], row['centroid'][1], 
                row['h3_index'], 
                ha='center', va='center', 
                fontsize=8, fontweight='bold')
    
    # Создать и отобразить кривую индекса
    ax = create_index_curve(gdf, ax)
    
    # Форматирование
    ax.set_aspect('equal')
    ax.set_title(f'H3 сетка (разрешение {resolution}) с кривой индекса')
    ax.legend()
    ax.grid(True, alpha=0.3)
    
    plt.tight_layout()
    return fig, ax

# Пример использования
if __name__ == "__main__":
    # Создать визуализацию для Сан-Франциско
    sf_lat, sf_lng = 37.7749, -122.4194
    fig, ax = plot_h3_with_index_curve(sf_lat, sf_lng, resolution=8, k_ring=2)
    plt.show()

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

Настройка и расширенные возможности

Вы можете настроить свою визуализацию несколькими способами:

Цветовые схемы и стилизация

python
def plot_customized_visualization(gdf):
    """Создать настроенную визуализацию с разными цветами и стилями"""
    fig, ax = plt.subplots(figsize=(14, 12))
    
    # Пользовательская цветовая схема на основе свойств шестиугольника
    gdf['hex_size'] = gdf.geometry.area
    gdf.plot(ax=ax, column='hex_size', cmap='viridis', 
             edgecolor='white', linewidth=0.5, alpha=0.8)
    
    # Добавить цветовую шкалу
    sm = plt.cm.ScalarMappable(cmap='viridis')
    sm.set_array(gdf['hex_size'])
    cbar = plt.colorbar(sm, ax=ax, shrink=0.8)
    cbar.set_label('Площадь шестиугольника')
    
    # Пользовательские метки
    for idx, row in gdf.iterrows():
        ax.text(row['centroid'][0], row['centroid'][1], 
                row['h3_index'][-4:],  # Показать последние 4 цифры для ясности
                ha='center', va='center', 
                fontsize=8, fontweight='bold',
                bbox=dict(boxstyle="round,pad=0.3", 
                         facecolor='white', alpha=0.7))
    
    # Улучшенная кривая
    ax = create_index_curve(gdf, ax)
    
    ax.set_aspect('equal')
    ax.set_title('Настроенная визуализация H3 сетки')
    plt.tight_layout()
    return fig, ax

Интерактивные визуализации с Plotly

Для лучшей интерактивности можно использовать Plotly вместо matplotlib:

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

def create_interactive_h3_map(gdf):
    """Создать интерактивную визуализацию H3 с Plotly"""
    # Преобразовать в WGS84 для веб-карт
    gdf_web = gdf.to_crs('EPSG:4326')
    
    # Создать фигуру
    fig = go.Figure()
    
    # Добавить патчи шестиугольников
    for idx, row in gdf_web.iterrows():
        # Преобразовать границу шестиугольника в полигон
        lons, lats = zip(*row['boundary'])
        
        # Замкнуть полигон
        lons = list(lons) + [lons[0]]
        lats = list(lats) + [lats[0]]
        
        fig.add_trace(go.Scattermapbox(
            mode='lines',
            lon=lons,
            lat=lats,
            fill='toself',
            fillcolor='rgba(173, 216, 230, 0.5)',
            line=dict(color='black', width=1),
            showlegend=False
        ))
    
    # Добавить метки
    for idx, row in gdf_web.iterrows():
        fig.add_trace(go.Scattermapbox(
            mode='text',
            lon=[row['centroid'][0]],
            lat=[row['centroid'][1]],
            text=[row['h3_index']],
            textfont=dict(size=10, color='black'),
            showlegend=False
        ))
    
    # Установить параметры отображения
    fig.update_layout(
        mapbox=dict(
            style='open-street-map',
            center=dict(lat=gdf_web.geometry.centroid.y.mean(),
                       lon=gdf_web.geometry.centroid.x.mean()),
            zoom=12
        ),
        height=600,
        title='Интерактивная H3 сетка'
    )
    
    return fig

Добавление значений данных к шестиугольникам

Вы можете улучшить свою визуализацию, добавляя значения данных к шестиугольникам:

python
def add_data_to_hexagons(gdf, data_dict):
    """Добавить значения данных к H3 шестиугольникам"""
    # Создать копию, чтобы не изменять оригинал
    gdf_with_data = gdf.copy()
    
    # Добавить столбец с данными
    gdf_with_data['data_value'] = gdf_with_data['h3_index'].map(data_dict)
    
    # Заполнить отсутствующие значения нулями
    gdf_with_data['data_value'] = gdf_with_data['data_value'].fillna(0)
    
    return gdf_with_data

# Пример использования с тестовыми данными
data_values = {
    '8f283470dffffff': 25,
    '8f283470d9fffff': 42,
    '8f283470dbfffff': 18,
    '8f283470ddfffff': 33,
    '8f283470d7fffff': 27,
    '8f283470dafffff': 15,
    '8f283470dfffff': 39
}

gdf_with_data = add_data_to_hexagons(gdf, data_values)

Рекомендации по производительности

При работе с визуализациями H3 учитывайте эти рекомендации по производительности:

Оптимизация памяти

python
def efficient_h3_visualization(gdf, max_hexagons=1000):
    """Создать оптимизированную визуализацию для больших наборов данных"""
    if len(gdf) > max_hexagons:
        # Выборка шестиугольников для лучшей производительности
        gdf_sampled = gdf.sample(n=max_hexagons, random_state=42)
        print(f"Выборка {max_hexagons} шестиугольников из {len(gdf)} всего")
    else:
        gdf_sampled = gdf
    
    # Использовать упрощенные геометрии для отображения
    gdf_simplified = gdf_sampled.copy()
    gdf_simplified['geometry'] = gdf_simplified['geometry'].simplify(tolerance=0.001)
    
    return gdf_simplified

Кэширование и предвычисление

python
import json
import os

def save_h3_grid_data(gdf, filename='h3_grid_data.json'):
    """Сохранить данные H3 сетки для повторного использования"""
    # Преобразовать в формат, подходящий для JSON
    data = {
        'hexagons': [
            {
                'h3_index': row['h3_index'],
                'centroid': list(row['centroid']),
                'boundary': row['boundary']
            }
            for idx, row in gdf.iterrows()
        ]
    }
    
    with open(filename, 'w') as f:
        json.dump(data, f)
    
    print(f"Сохранены данные H3 сетки в {filename}")

def load_h3_grid_data(filename='h3_grid_data.json'):
    """Загрузить ранее сохраненные данные H3 сетки"""
    if os.path.exists(filename):
        with open(filename, 'r') as f:
            data = json.load(f)
        
        # Преобразовать обратно в GeoDataFrame
        hex_data = []
        for hex_data_dict in data['hexagons']:
            boundary = hex_data_dict['boundary']
            polygon = Polygon(boundary)
            centroid = polygon.centroid
            
            hex_data.append({
                'h3_index': hex_data_dict['h3_index'],
                'geometry': polygon,
                'centroid': tuple(hex_data_dict['centroid']),
                'boundary': boundary
            })
        
        return gpd.GeoDataFrame(hex_data, geometry='geometry', crs='EPSG:4326')
    else:
        print(f"Файл {filename} не найден")
        return None

Пакетная обработка для больших областей

Для очень больших областей рассмотрите обработку пакетами:

python
def create_large_h3_grid(bbox, resolution, batch_size=50):
    """Создать H3 сетку для большой области пакетами"""
    min_lat, max_lat, min_lng, max_lng = bbox
    
    # Рассчитать размеры сетки
    lat_step = (max_lat - min_lat) / batch_size
    lng_step = (max_lng - min_lng) / batch_size
    
    all_hexagons = []
    
    for lat_idx in range(batch_size):
        for lng_idx in range(batch_size):
            # Рассчитать центр текущего пакета
            center_lat = min_lat + (lat_idx + 0.5) * lat_step
            center_lng = min_lng + (lng_idx + 0.5) * lng_step
            
            # Создать небольшую H3 сетку вокруг центральной точки
            batch_gdf = create_h3_grid_with_labels(
                center_lat, center_lng, resolution, k_ring=1
            )
            
            all_hexagons.append(batch_gdf)
    
    # Объединить все пакеты
    combined_gdf = pd.concat(all_hexagons, ignore_index=True)
    
    # Удалить дубликаты (шестиугольники на границах пакетов)
    combined_gdf = combined_gdf.drop_duplicates(subset=['h3_index'])
    
    return combined_gdf

Источники

  1. Репозиторий Uber H3 на GitHub - Иерархическая шестиугольная система пространственной индексации
  2. Блог Uber - H3: Иерархическая пространственная индексация Uber
  3. Официальный сайт H3 - Дискретная глобальная система сетки
  4. Analytics Vidhya - Руководство по H3 Uber для пространственной индексации
  5. Блог Uber - Визуализация городских ядер с H3
  6. Привязки H3 Python - GitHub
  7. Geographic Data Science with Python - Учебник по H3 сетке
  8. Библиотека Tobler - Генерация H3 шестиугольных сеток из GeoDataFrames
  9. Medium - Как преобразовать границы ячеек H3 в полигоны Shapely в Python
  10. Учебник по полигонам - документация h3-py

Заключение

Визуализация индексов H3 сетки с метками и соединяющими кривыми - это мощный способ понять пространственные отношения и поток данных в шестиугольных сетках. Вот основные выводы:

  1. Создание H3 сетки: Используйте функцию h3.geo_to_h3() для создания шестиугольных сеток вокруг конкретных координат, при этом уровни разрешения определяют детализацию.

  2. Размещение меток: Извлекайте центроиды шестиугольников с помощью геометрических операций и размещайте метки индексов H3 в этих точках для четкой идентификации.

  3. Генерация кривых: Реализуйте сплайн-интерполяцию с использованием scipy.interpolate.splprep() и splev() для создания плавных кривых, которые соединяют метки последовательно.

  4. Варианты настройки: Улучшайте визуализации с помощью цветовых схем, интерактивных функций с использованием Plotly и цветового кодирования на основе значений шестиугольников.

  5. Оптимизация производительности: Для больших наборов данных рассмотрите выборку, кэширование и пакетную обработку для поддержания производительности визуализации.

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

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

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