Reputation: 2014
I am using plotlys scattermapbox to plot points on a map. I'd like to draw the polygon that cover 'x' mile
radius from a POI.
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
is a pandas dataframe that includes coordinates for locations within x miles
of POI. How do I update the map-graph
to draw a polygon that covers all the points?
Adding a layer to layout dictionary:
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
}
}
Upvotes: 0
Views: 5481
Reputation: 31146
poi_poly()
. NB radius is in meters as per UTM geometryimport 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()
has been updated. DataFrame is no longer mandatory for finding markers within POIimport 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},
)
Upvotes: 2
Reputation: 15722
See an example in the documentation 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()
Adjust the above based on your point and polygon coordinates.
If you want to use another 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/
Upvotes: 1