【问题标题】:Draw a polygon around point in scattermapbox using python使用python在scattermapbox中的点周围绘制一个多边形
【发布时间】:2021-08-27 00:50:56
【问题描述】:

我正在使用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
                       }

           }

【问题讨论】:

    标签: python plotly mapbox plotly-dash mapbox-gl-js


    【解决方案1】:
    • 基于对这个重复问题的回答Obtain coordinates of a Polygon / Multi-polygon around a point in python
    • 没有提供有问题的样本数据,所以我使用了英国医院数据
    • 创建了一个辅助函数poi_poly()。根据 UTM 几何,NB 半径以米为单位
    • UTM 几何用于创建指定半径的多边形
    • 然后标记与该多边形相交。然后获取convex hull
    • 还提供了返回半径多边形的选项,在下面的示例中,我返回了这个以证明凸包多边形在 POI 的半径内
    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() 已更新。在 POI 中查找标记不再强制使用 DataFrame
    • 创建以一组 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},
    )
    

    【讨论】:

    • 我实现了你的答案并将layer 属性添加到layout 字典中,但是,多边形圆没有被渲染。问题已使用代码更新。
    • print(gdf['geometry'][0])的输出坐标不是WSG84,它们看起来像easting / northing。提供您的 df df.head(20).to_dict("list") 的样本或放在 GitHub 上我可以访问的地方
    • 我一次只有一个 poi,所以我正在通过(经纬度)。
    • 我最初的理解 - 你想要一个 POI 定义半径内的标记多边形。进一步增加了poi_poly() 的实用性,这样它就可以创建一个圆,而不必担心在定义的半径内构建标记多边形的额外逻辑
    【解决方案2】:

    查看文档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()
    

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

    如果你想使用另一个地图框style

    layout.mapbox.style 可接受的值为以下之一:
    "white-bg" 生成一个空白画布,导致没有外部 HTTP 请求
    “开放街道地图”,“carto-positron”,“carto-darkmatter”,“雄蕊地形”, “雄蕊调色剂”或“雄蕊水彩”产量地图由来自各种公共图块服务器的光栅图块组成,不需要注册或访问令牌
    “basic”、“streets”、“outdoors”、“light”、“dark”、“satellite”或“satellite-streets”产生由 Mapbox 服务的矢量图块组成的地图,并且确实需要 Mapbox 访问令牌或本地 Mapbox 安装。
    Mapbox 服务样式 URL,需要 Mapbox 访问令牌或本地 Mapbox 安装。 https://docs.mapbox.com/mapbox-gl-js/style-spec/

    中定义的 Mapbox 样式对象

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

    【讨论】:

    • 有没有办法使用mapbox本地获取中心x miles内的区域几何?我指的是MultiPolygon几何的坐标。
    • 几何坐标不是固定的,就我而言。我无法对它们进行硬编码。我需要获取代表 POI / 中心周围几何图形的坐标。
    • 我提供了一个硬编码数据的例子,因为我不知道你的数据是什么样的。你能提供一个最小的可重现的例子吗?没有更多细节很难提供帮助。
    猜你喜欢
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多