使用 python 在散点图框中围绕点绘制多边形

Draw a polygon around point in scattermapbox using python

我正在使用 plotlys scattermapbox 在地图上绘制点。我想从 POI 绘制覆盖 'x' mile 半径的多边形。

dcc.Graph(id="map-graph"),

@application.callback([

                         Output("map-graph", "figure"),
                      
                      ],
                      [

                         Input("address", "value"),
                         Input("type", "value")
                      ]
                      
                     )
def update_graph(address, type):
    
    for i, row in df.iterrows():

        lat = row["Lat"]
        lng = row["Long"]

        data.append({

                     "type": "scattermapbox",
                     "lat": [lat],
                     "lon": [lng],
                     "name": "Location",
                     "showlegend": False,
                     "hoverinfo": "text",
                     "mode": "markers",
                     "marker": {
                                "symbol": "circle",
                                "size": 8,
                                "opacity": 0.8,
                                "color": "black"
                               }
                      }
        )

     # Plot POI 
     POI_Lat = 37.785908
     POI_Long = -122.400803

     data.append({
                    "type": "scattermapbox",
                    "lat": [POI_Lat],
                    "lon": [POI_Long],
                     "marker": {
                        "symbol": "circle,
                        "size": 28,
                        "opacity": 0.7,
                        "color": "rgb(128, 128, 128)"
                        }
                    }
        )

df 是一个 pandas 数据框,其中包含 POI x miles 内位置的坐标。如何更新 map-graph 以绘制覆盖所有点的多边形?

向布局字典添加图层:

gdf = circles(Lat, Long, radius=1609.34)

print(gdf['geometry'][0])

POLYGON ((385272.0167249573 3768678.19769511, 385264.2673129799 3768520.454790493,.......))


layout = {

                        "autosize": True,
                        "hovermode": "closest",
                        "mapbox": {

                            "accesstoken": MAPBOX_KEY,
                            "bearing": 0,
                            "center": {
                                "lat": layout_lat,
                                "lon": layout_lon
                            },
                            "layers": [
                                         {
                                             "source": json.loads(gdf.geometry.to_json()),
                                             "below": "traces",
                                             "type": "line",
                                             "color": "purple",
                                             "line": {"width": 1.5},
                                         }
                            ],
                            "pitch": 0,
                            "zoom": zoom,
                            "style": "outdoors",

                        },

                        "margin": {
                           "r": 0,
                           "t": 0,
                           "l": 0,
                           "b": 0,
                           "pad": 0
                       }

           }

查看文档中的示例 here

import plotly.graph_objects as go

fig = go.Figure(go.Scattermapbox(
    mode = "markers",
    lon = [-73.605], lat = [45.51],
    marker = {'size': 20, 'color': ["cyan"]}))

fig.update_layout(
    mapbox = {
        'style': "stamen-terrain",
        'center': { 'lon': -73.6, 'lat': 45.5},
        'zoom': 12, 'layers': [{
            'source': {
                'type': "FeatureCollection",
                'features': [{
                    'type': "Feature",
                    'geometry': {
                        'type': "MultiPolygon",
                        'coordinates': [[[
                            [-73.606352888, 45.507489991], [-73.606133883, 45.50687600],
                            [-73.605905904, 45.506773980], [-73.603533905, 45.505698946],
                            [-73.602475870, 45.506856969], [-73.600031904, 45.505696003],
                            [-73.599379992, 45.505389066], [-73.599119902, 45.505632008],
                            [-73.598896977, 45.505514039], [-73.598783894, 45.505617001],
                            [-73.591308727, 45.516246185], [-73.591380782, 45.516280145],
                            [-73.596778656, 45.518690062], [-73.602796770, 45.521348046],
                            [-73.612239983, 45.525564037], [-73.612422919, 45.525642061],
                            [-73.617229085, 45.527751983], [-73.617279234, 45.527774160],
                            [-73.617304713, 45.527741334], [-73.617492052, 45.527498362],
                            [-73.617533258, 45.527512253], [-73.618074188, 45.526759105],
                            [-73.618271651, 45.526500673], [-73.618446320, 45.526287943],
                            [-73.618968507, 45.525698560], [-73.619388002, 45.525216750],
                            [-73.619532966, 45.525064183], [-73.619686662, 45.524889290],
                            [-73.619787038, 45.524770086], [-73.619925742, 45.524584939],
                            [-73.619954486, 45.524557690], [-73.620122362, 45.524377961],
                            [-73.620201713, 45.524298907], [-73.620775593, 45.523650879]
                        ]]]
                    }
                }]
            },
            'type': "fill", 'below': "traces", 'color': "royalblue"}]},
    margin = {'l':0, 'r':0, 'b':0, 't':0})

fig.show()

根据您的点和多边形坐标调整以上内容。

如果你想使用另一个 mapbox style:

The accepted values for layout.mapbox.style are one of:
"white-bg" yields an empty white canvas which results in no external HTTP requests
"open-street-map", "carto-positron", "carto-darkmatter", "stamen-terrain", "stamen-toner" or "stamen-watercolor" yield maps composed of raster tiles from various public tile servers which do not require signups or access tokens
"basic", "streets", "outdoors", "light", "dark", "satellite", or "satellite-streets" yield maps composed of vector tiles from the Mapbox service, and do require a Mapbox Access Token or an on-premise Mapbox installation.
A Mapbox service style URL, which requires a Mapbox Access Token or an on-premise Mapbox installation. A Mapbox Style object as defined at https://docs.mapbox.com/mapbox-gl-js/style-spec/

https://plotly.com/python/mapbox-layers/

  • 基于对这个重复问题的回答Obtain coordinates of a Polygon / Multi-polygon around a point in python
  • 没有提供相关样本数据,所以我使用了英国医院数据
  • 已创建辅助函数 poi_poly()。根据 UTM 几何图形,NB 半径以米为单位
  • UTM 几何用于创建指定半径的多边形
  • 然后标记与此多边形相交。然后得到 convex hull
  • 也为 return 半径多边形提供了选项,在下面的示例中我已经 return 编辑它以证明凸包多边形在 POI[=34= 的半径内]
import shapely.geometry
import pandas as pd
import geopandas as gpd
import requests, io, json
import plotly.express as px
import random


def poi_poly(
    df,
    radius=10 ** 5,
    poi={"Longitude": 0.06665166467428207, "Latitude": 51.19034957885742},
    lon_col="Longitude",
    lat_col="Latitude",
    include_radius_poly=False,
):

    # generate a geopandas data frame of the POI
    gdfpoi = gpd.GeoDataFrame(
        geometry=[shapely.geometry.Point(poi["Longitude"], poi["Latitude"])],
        crs="EPSG:4326",
    )
    # extend point to radius defined (a polygon).  Use UTM so that distances work, then back to WSG84
    gdfpoi = (
        gdfpoi.to_crs(gdfpoi.estimate_utm_crs())
        .geometry.buffer(radius)
        .to_crs("EPSG:4326")
    )

    # create a geopandas data frame of all the points / markers
    if not df is None:
        gdf = gpd.GeoDataFrame(
            geometry=df.loc[:, ["Longitude", "Latitude"]]
            .dropna()
            .apply(
                lambda r: shapely.geometry.Point(r["Longitude"], r["Latitude"]), axis=1
            )
            .values,
            crs="EPSG:4326",
        )
    else:
        gdf = gpd.GeoDataFrame(geometry=gdfpoi)

    # create a polygon around the edges of the markers that are within POI polygon
    return pd.concat(
        [
            gpd.GeoDataFrame(
                geometry=[
                    gpd.sjoin(
                        gdf, gpd.GeoDataFrame(geometry=gdfpoi), how="inner"
                    ).unary_union.convex_hull
                ]
            ),
            gpd.GeoDataFrame(geometry=gdfpoi if include_radius_poly else None),
        ]
    )


# get some public addressess - hospitals.  data that can be scattered
dfhos = pd.read_csv(
    io.StringIO(
        requests.get("http://media.nhschoices.nhs.uk/data/foi/Hospital.csv").text
    ),
    sep="¬",
    engine="python",
)


# generate polygon of markers within 5 mile radius of Point of Interest
poi = dfhos.loc[random.randint(0, len(dfhos) - 1), ["Longitude", "Latitude"]].to_dict()
gdf = poi_poly(dfhos, poi=poi, radius=1609.34 * 5, include_radius_poly=True)

fig = (
    px.scatter_mapbox(
        dfhos,
        lat="Latitude",
        lon="Longitude",
        color="Sector",
        hover_data=["OrganisationName", "Postcode"],
    )
    .update_traces(marker={"size": 10})
    .update_layout(
        mapbox={
            "style": "open-street-map",
            "zoom": 9,
            "center": {"lat": poi["Latitude"], "lon": poi["Longitude"]},
            "layers": [
                {
                    "source": json.loads(gdf.geometry.to_json()),
                    "below": "traces",
                    "type": "line",
                    "color": "purple",
                    "line": {"width": 1.5},
                }
            ],
        },
        margin={"l": 0, "r": 0, "t": 0, "b": 0},
    )
)
fig.show()

只画一个圆形多边形

  • poi_poly() 已更新。 DataFrame 不再是在 POI
  • 中查找标记所必需的
  • 创建以一组 GPS 坐标为中心的圆(实际上是多边形)的简单示例
import plotly.graph_objects as go

poi = {"Latitude": 37.785908, "Longitude": -122.400803}

go.Figure(go.Scattermapbox()).update_layout(
    mapbox={
        "style": "open-street-map",
        "zoom": 9,
        "center": {"lat": poi["Latitude"], "lon": poi["Longitude"]},
        "layers": [
            {
                "source": json.loads(poi_poly(None, poi=poi, radius=1609).to_json()),
                "below": "traces",
                "type": "line",
                "color": "purple",
                "line": {"width": 1.5},
            }
        ],
    },
    margin={"l": 0, "r": 0, "t": 0, "b": 0},
)