Добавление контуров GeoJSON в виде слоев на плотностном картографическом поле

#python #matplotlib #plotly #geojson #contour

Вопрос:

Я хотел бы добавить контур погоды поверх plotly density_mapbox карты, но не уверен в необходимых шагах.

Сначала я создал matplotlib контурный график для визуализации данных.

Затем я использовал geojsoncontour для создания geojson файла из указанного matplotlib контурного графика контуров.

Что я хотел бы сделать сейчас, так это нанести контуры на ту же карту, что и на density_mapbox .

geojson и файлы .csv, содержащие данные, можно найти здесь.

Что касается файла .csv, «Rand_Data» — это данные, которые входят в density_mapbox график, «Rain_in» — это данные, используемые для создания контуров.

ссылка на данные: https://github.com/jkiefn1/Contours_and_plotly

Создание картографического поля:

 # Create the static figure
fig = px.density_mapbox(df
                        ,lat='lat'
                        ,lon='long'
                        ,z='Rand_Data'
                        ,hover_data={
                                     'lat':True # remove from hover data
                                     ,'long':True # remove from hover data
                                     ,col:True
                                    }
                        ,center=dict(lat=38.5, lon=-96)
                        ,zoom=3
                        ,radius=30
                        ,opacity=0.5
                        ,mapbox_style='open-street-map'
                        ,color_continuous_scale='inferno'
                       )

fig.show()
 

введите описание изображения здесь

Создание контурного графика matplotlib и создание файла geojson

 # Load in the DataFrame
path = r'/Users/joe_kiefner/Desktop/Sample_Data.csv'
df = pd.read_csv(path, index_col=[0])
data = []

# Define rain levels to be contours in geojson
levels = [0.25,0.5,1,2.5,5,10]

colors = ['royalblue',  'cyan',  'lime',  'yellow', 'red']
vmin   = 0
vmax   = 1
cm     = branca.colormap.LinearColormap(colors, vmin=vmin, vmax=vmax).to_step(len(levels))


x_orig = (df.long.values.tolist())
y_orig = (df.lat.values.tolist())
z_orig = np.asarray(df['Rain_in'].values.tolist())


x_arr          = np.linspace(np.min(x_orig), np.max(x_orig), 500)
y_arr          = np.linspace(np.min(y_orig), np.max(y_orig), 500)
x_mesh, y_mesh = np.meshgrid(x_arr, y_arr)

xscale = df.long.max() - df.long.min()
yscale = df.lat.max() - df.lat.min()

scale = np.array([xscale, yscale])


z_mesh = griddata((x_orig, y_orig), z_orig, (x_mesh, y_mesh), method='linear')


sigma = [5, 5]
z_mesh = sp.ndimage.filters.gaussian_filter(z_mesh, sigma, mode='nearest')

# Create the contour
contourf = plt.contourf(x_mesh, y_mesh, z_mesh, levels, alpha=0.9, colors=colors, 
                        linestyles='none', vmin=vmin, vmax=vmax)

# Convert matplotlib contourf to geojson
geojson = geojsoncontour.contourf_to_geojson(
    contourf=contourf,
    min_angle_deg=3,
    ndigits=2,
    unit='in',
    stroke_width=1,
    fill_opacity=0.3)
d = json.loads(geojson)
len_features=len(d['features'])
if not data:
    data.append(d)
else:
    for i in range(len(d['features'])):
         data[0]['features'].append(d['features'][i])
            
with open('/path/to/Sample.geojson', 'w') as f:
   dump(geojson, f)
 

введите описание изображения здесь

Ответ №1:

  • есть два основных варианта
    1. добавить в качестве слоев https://plotly.com/python/mapbox-layers/
    2. добавить как следы хороплета https://plotly.com/python/mapbox-county-choropleth/
    3. слои-легенда — опция аналогична слоям с добавлением возможности создания легенды путем добавления дополнительных трасс на рисунок
  • оба эти параметра указаны ниже. измените значение OPTION , чтобы переключаться между ними
  • слои означают, что нет легенды или текста при наведении курсора
  • если они присутствуют, переместите цветовую панель, чтобы она не перекрывала легенду. Требуется больше беатификации легенды и текста при наведении курсора…
 import json, requests
import pandas as pd
import geopandas as gpd
import plotly.express as px

txt = requests.get(
    "https://raw.githubusercontent.com/jkiefn1/Contours_and_plotly/main/Sample.geojson"
).text
js = json.loads(json.loads(txt))

df = pd.read_csv(
    "https://raw.githubusercontent.com/jkiefn1/Contours_and_plotly/main/Sample_Data.csv"
)

col = "Rand_Data"
fig = px.density_mapbox(
    df,
    lat="lat",
    lon="long",
    z="Rand_Data",
    hover_data={
        "lat": True,  # remove from hover data
        "long": True,  # remove from hover data
        col: True,
    },
    center=dict(lat=38.5, lon=-96),
    zoom=3,
    radius=30,
    opacity=0.5,
    mapbox_style="open-street-map",
    color_continuous_scale="inferno",
)

OPTION = "layers-legend"
if OPTION[0:6]=="layers":
    fig.update_traces(legendgroup="weather").update_layout(
        mapbox={
            "layers": [
                {
                    "source": f,
                    "type": "fill",
                    "color": f["properties"]["fill"],
                    "opacity": f["properties"]["fill-opacity"],
                }
                for f in js["features"]
            ],
        }
    )
    
    if OPTION=="layers-legend":
        # create a dummy figure to create a legend for the geojson
        dfl = pd.DataFrame(js["features"])
        dfl = pd.merge(
            dfl["properties"].apply(pd.Series),
            dfl["geometry"].apply(pd.Series)["coordinates"].apply(len).rename("len"),
            left_index=True,
            right_index=True,
        )
        figl = px.bar(
            dfl.loc[dfl["len"].gt(0)],
            color="title",
            x="fill",
            y="fill-opacity",
            color_discrete_map={cm[0]: cm[1] for cm in dfl.loc[:, ["title", "fill"]].values},
        ).update_traces(visible="legendonly")

        fig.add_traces(figl.data).update_layout(
            xaxis={"visible": False}, yaxis={"visible": False}, coloraxis={"colorbar":{"y":.25}}
        )
else:
    gdf = gpd.GeoDataFrame.from_features(js)
    gdf = gdf.loc[~gdf.geometry.is_empty]
    cmap = {
        list(d.values())[0]: list(d.values())[1]
        for d in gdf.loc[:, ["title", "fill"]].apply(dict, axis=1).tolist()
    }
    fig2 = px.choropleth_mapbox(
        gdf,
        geojson=gdf.geometry,
        locations=gdf.index,
        color="title",
        color_discrete_map=cmap,
        opacity=.3
    )
    fig.add_traces(fig2.data).update_layout(coloraxis={"colorbar":{"y":.25}})
    
    
fig
 

слои

введите описание изображения здесь

следы

введите описание изображения здесь

Комментарии:

1. Роб, можно ли в любом случае добавить какую-нибудь легенду с помощью метода слоев? Я обнаружил, что наложение данных делает их намного более чистыми, а именно, используя метод «Слои» сверху с параметром «тип», установленным на «линия».

2. обновлено — сюжетные легенды автоматически генерируются из следов. синтезировали дополнительные следы, чтобы элементы осадков были вставлены в легенду