В этом примере подробно объясняется, как использовать GeoPandas для визуализации структурированных данных.

Чтобы начать, перейдите на mecsimcalc.com/create, нажмите Карты и выберите Создать новое пустое приложение.

Шаг 1: Код

Получить данные

Сначала импортируйте или создайте геопространственные данные для визуализации. В этом примере мы создадим DataFrame Panda, а затем преобразуем его в GeoDataFrame.

Создайте DataFrame с координатами Easting и Northing.

df = pd.DataFrame({"Easting": [444246.35, 444247.044, 444247.3266, 444247.5912, 444247.8385],
                   "Northing": [5465340.18, 5465359.6118, 5465367.5256, 5465374.9338, 5465381.8589]})

df выглядит так:

Объедините столбцы Easting и Northing в один столбец. Это облегчит создание фигур позже. Новый столбец должен называться geometry, как будет объяснено позже.

df["geometry"] = df[["Easting", "Northing"]].values.tolist()

df выглядит так:

Теперь преобразуйте df DataFrame в GeoDataFrame, gdf.

gdf = geopandas.GeoDataFrame(df, crs="EPSG:26911")

Система отсчета координат (CRS)

Обратите внимание, что координаты указаны в восточном направлении и северном направлении, тогда как GeoPandas ожидает широту и долготу. Поэтому координаты должны быть проецированы на широту и долготу. К счастью, GeoDataFrame имеет встроенную crs (систему отсчета координат), которая применяет проекцию к столбцу geometry.

В этом примере crs равно "EPSG:26911", но для вашего кода оно, вероятно, будет другим, в зависимости от используемой системы отсчета координат.

СОВЕТ

Если ваши координаты уже указаны в широте и долготе, вы можете пропустить настройку атрибута crs.

ОПАСНОСТЬ

Ваши фигуры должны находиться в столбце geometry, чтобы проекция crs применялась правильно!

Манипулировать данными

Преобразуйте столбец geometry в точки, применив функцию Point из shapely.geometry ко всем строкам, используя .apply().

gdf["geometry"] = gdf["geometry"].apply(Point)

gdf выглядит так:

Далее мы создадим дополнительные геометрии в отдельном GeoDataFrame, а затем добавим его в конец существующих GeoDataFrame, gdf.

Используйте LineString и Polygon для создания новых фигур внутри нового GeoDataFrame под названием more_geometries.

СОВЕТ

Вы можете найти более стройные геометрии здесь.

Используйте pd.concat для присоединения more_geometries к концу gdf. Обратите внимание, что axis=0 означает объединение в новую строку, тогда как axis=1 означает объединение в новый столбец.

more_geometries = geopandas.GeoDataFrame(
    {
        "geometry": [
            LineString([[444248.0694, 5.465388e06], [444248.2847, 5.465394e06], [444248.4852, 5.465400e06]]),
            Polygon([[444243.6719, 5.465405e06], [444248.8454, 5.465410e06], [444249.0068, 5.465415e06], [444299.1569, 5.465419e06]]),
        ]
    }
)
gdf = pd.concat((gdf, more_geometries), axis=0)

gdf выглядит так:

Теперь, когда мы определили все геометрии, мы можем добавить наши вычисленные значения для каждой геометрии в виде нового столбца с именем value. Здесь мы используем некоторые случайные числа, но вам следует вычислять более значимые числа с помощью других библиотек, таких как Numpy, GeoPandas или Pandas.

gdf["value"] = [1,2,3,4,5,6,7] # computed values

gdf выглядит так:

Экспорт карты

Интерактивная карта

Экспортируйте gdf GeoDataFrame как интерактивную карту:

m1 = gdf.explore("value", cmap="Spectral", name="My shapes", tiles=None)
m1.options["preferCanvas"] = True

Где,

  • gdf.explore возвращает объект карты Folium.
  • "value" — это столбец gdf, который содержит числовые значения для визуализации.
  • cmap — цвет шкалы. Смотрите больше цветовых карт здесь.
  • name – это имя LayerControl для геометрических фигур.
  • tiles=None чтобы не устанавливать тайл карты. В большинстве случаев вам не нужно указывать атрибут tiles.
  • m1.options["preferCanvas"] = True следует установить на True, когда вы рисуете много геометрии или когда карта кажется медленной или тормозной. В противном случае вы можете пропустить эту строку.

Затем мы добавим на карту LayerControl, который позволит переключать фрагменты карты и включать и выключать геометрию.

Добавьте каждое TileLayer отдельно на карту. Обратите внимание, что OpenStreetMap и Stamen Terrain являются строками, тогда как Satellite должен указывать URL-адрес для tiles. Это связано с тем, что OpenStreetMap и Stamen Terrain — это тайлы карты, встроенные в Folium, тогда как Satellite — это пользовательский фрагмент карты.

folium.TileLayer("OpenStreetMap", name="Road").add_to(m1)
folium.TileLayer("Stamen Terrain", name="Terrain").add_to(m1)
folium.TileLayer(
    tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
    attr="Esri",
    name="Satellite",
).add_to(m1)
folium.LayerControl().add_to(m1)

Наконец, преобразуйте объект карты в HTML, который можно отобразить на веб-странице.

interactive_map = m1._repr_html_()

Статическая карта

Мы также экспортируем карту как статический график Matplotlib.

def plt_show(plt, width=500, dpi=100):
    # Converts matplotlib plt to image data string
    #   plt is the matplotlib pyplot or figure
    #   width is the width of the graph image in pixels
    #   dpi (dots per inch) is the resolution of the image
    bytes = io.BytesIO()
    plt.savefig(bytes, format="png", dpi=dpi)  # Save as png image
    if hasattr(plt, "close"):
        plt.close()
    bytes.seek(0)
    base64_string = "data:image/png;base64," + base64.b64encode(bytes.getvalue()).decode("utf-8")
    return "<img src='" + base64_string + "' width='" + str(width) + "'>"

m2 = gdf.plot("value", cmap="Spectral", legend=True)
static_map = plt_show(m2.figure)

Где,

  • gdf.plot возвращает фигуру Matplotlib.
  • "value" — это столбец gdf, который содержит числовые значения для визуализации.
  • cmap — цвет шкалы. Смотрите больше цветовых карт здесь.
  • legend=True отображает цветную полосу справа.
  • Передайте m2.figure в plt_show, чтобы вернуть изображение, которое можно отобразить на веб-странице.

Полный код

import io
import base64
import folium
import geopandas
import pandas as pd
from shapely.geometry import LineString, Point, Polygon

def plt_show(plt, width=500, dpi=100):
    # Converts matplotlib plt to image data string
    #   plt is the matplotlib pyplot or figure
    #   width is the width of the graph image in pixels
    #   dpi (dots per inch) is the resolution of the image
    bytes = io.BytesIO()
    plt.savefig(bytes, format="png", dpi=dpi)  # Save as png image
    if hasattr(plt, "close"):
        plt.close()
    bytes.seek(0)
    base64_string = "data:image/png;base64," + \
        base64.b64encode(bytes.getvalue()).decode("utf-8")
    return "<img src='" + base64_string + "' width='" + str(width) + "'>"

def main(inputs):
    # Create data as Panda's DataFrame
    df = pd.DataFrame({"Easting": [444246.35, 444247.044, 444247.3266, 444247.5912, 444247.8385],
                       "Northing": [5465340.18, 5465359.6118, 5465367.5256, 5465374.9338, 5465381.8589]})
    # Merge `Easting` and `Northing` columns into one column called `geometry`
    df["geometry"] = df[["Easting", "Northing"]].values.tolist()
    # Convert DataFrame to GeoDataFrame and project coordinates to "EPSG:26911"
    gdf = geopandas.GeoDataFrame(df, crs="EPSG:26911")
    # Convert all geometries into Point shapes
    gdf["geometry"] = gdf["geometry"].apply(Point)
    # Create a new GeoDataFrame with a LineString and a Polygon
    more_geometries = geopandas.GeoDataFrame(
        {
            "geometry": [
                LineString([[444248.0694, 5.465388e06], [444248.2847,
                                                         5.465394e06], [444248.4852, 5.465400e06]]),
                Polygon([[444243.6719, 5.465405e06], [444248.8454, 5.465410e06], [
                        444249.0068, 5.465415e06], [444299.1569, 5.465419e06]]),
            ]
        }
    )
    # Add the new geometries as new rows to the existing GeoDataFrame, gdf
    gdf = pd.concat((gdf, more_geometries), axis=0)
    # Add computed values as a new column to gdf
    gdf["value"] = [1, 2, 3, 4, 5, 6, 7]  # computed values
    # Export gdf as an interactive Folium map
    m1 = gdf.explore("value", cmap="Spectral", name="My shapes", tiles=None)
    # Set `preferCanvas` to optimize performance of map
    m1.options["preferCanvas"] = True
    # Add a LayerControl with different TileLayers
    folium.TileLayer("OpenStreetMap", name="Road").add_to(m1)
    folium.TileLayer("Stamen Terrain", name="Terrain").add_to(m1)
    folium.TileLayer(
        tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
        attr="Esri",
        name="Satellite",
    ).add_to(m1)
    folium.LayerControl().add_to(m1)
    # Get Folium map as HTML string
    interactive_map = m1._repr_html_()
    # Export gdf as Matplotlib plot image
    m2 = gdf.plot("value", cmap="Spectral", legend=True)
    static_map = plt_show(m2.figure)
    return {
        "interactive_map": interactive_map,
        "static_map": static_map
    }

Шаг 2: Вывод

{{ outputs.interactive_map }}
{{ outputs.static_map }}

Для получения дополнительных руководств посетите здесь.