How to make a circle shape radius plot on scatter mapbox (Plotly Python)

Question:

I want to retrieve the nearest coordinate point based on circle radius in kilometers and plot the result on scatter mapbox. But I dont know how to plot the circle shape radius. Anyone knows how to plot the circle shape radius?

So far my result just only data in circle radius without its circle shape. This is my code

import plotly.graph_objects as go
import plotly.express as px
from math import radians, cos, sin, asin, sqrt

def haversine(lon1, lat1, lon2, lat2):

    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371
    return c * r

center_point = pd.DataFrame({'Longitude':[long_input], 'Latitude':[lat_input]})
test_point = df_latlong[['Longitude','Latitude']].reset_index().drop('index',axis=1)

lat1 = center_point['Latitude'][0]
lon1 = center_point['Longitude'][0]

df_nearest = []
for i, (index, row) in enumerate(df_latlong.iterrows()):
    lat2 = test_point['Latitude'][i]
    lon2 = test_point['Longitude'][i]
    a = haversine(lon1, lat1, lon2, lat2)
    df_nearest.append(a)

df_latlong['Distance'] = df_nearest
df_radius = df_latlong[df_latlong['Distance'] <= 5] #5 kilometers for radius 

fig_map3 = px.scatter_mapbox(df_radius, lon=df_radius['Longitude'], lat=df_radius['Latitude'],
                             hover_name='#WELL', zoom=9, width=300, height=500)

fig_map3.update_layout(mapbox_style='open-street-map', margin={'r':0, 't':0, 'l':0, 'b':0})

fig_map3.show()

it has circle shape radius based on centroid that I have defined in center_point. How can I plot black circle like that? The radius has 5 kilometers. Any idea? Thanks!

Asked By: naranara

||

Answers:

First I created some sample data in the same geographic region as the data you showed in your screenshots.

Then, we can calculate the (latitude, longitude) points for a circle with radius r, center point, and number of sample points using the approximation given by @Stéphane (for your question, the 5 km radius you want around your center point is small enough that this approximation holds well).

Note: I tried to [unsuccessfully] calculate the exact coordinates of the circle using geog.propogate as outlined here, but it wasn’t generating the correct coordinates for a circle around the center coordinate.

Then you can pass the (latitude, longitude) points of the circle to go.Scattermapbox, set the mode to ‘lines’, and add the trace to your figure.

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from math import radians, cos, sin, asin, sqrt, pi

def haversine(lon1, lat1, lon2, lat2):

    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371
    return c * r

## create a sample dataframe to reproduce your data
lat_input, long_input = -0.622929, 117.575513,

lat_min, lat_max = -0.59, -0.68
long_min, long_max = 117.543, 117.614

np.random.seed(42)
df_latlong = pd.DataFrame({
    'Latitude': np.random.uniform(lat_min, lat_max, size=20),
    'Longitude': np.random.uniform(long_min, long_max, size=20),
    '#WELL': ["TN-test"]*20
})

center_point = pd.DataFrame({'Longitude':[long_input], 'Latitude':[lat_input]})
test_point = df_latlong[['Longitude','Latitude']].reset_index().drop('index',axis=1)

lat1 = center_point['Latitude'][0]
lon1 = center_point['Longitude'][0]

df_nearest = []
for i, (index, row) in enumerate(df_latlong.iterrows()):
    lat2 = test_point['Latitude'][i]
    lon2 = test_point['Longitude'][i]
    a = haversine(lon1, lat1, lon2, lat2)
    df_nearest.append(a)

df_latlong['Distance'] = df_nearest
df_radius = df_latlong[df_latlong['Distance'] <= 5] #5 kilometers for radius 

fig_map3 = px.scatter_mapbox(df_radius, lon=df_radius['Longitude'], lat=df_radius['Latitude'],
                             hover_name='#WELL', zoom=9, width=300, height=500)

radius = 5 * 1000 # m - the following code is an approximation that stays reasonably accurate for distances < 100km

# parameters
N = 360 # number of discrete sample points to be generated along the circle

# generate points
circle_lats, circle_lons = [], []
for k in range(N):
    # compute
    angle = pi*2*k/N
    dx = radius*cos(angle)
    dy = radius*sin(angle)
    circle_lats.append(lat1 + (180/pi)*(dy/6378137))
    circle_lons.append(lon1 + (180/pi)*(dx/6378137)/cos(lat1*pi/180))
circle_lats.append(circle_lats[0])
circle_lons.append(circle_lons[0])

fig_map3.add_trace(go.Scattermapbox(
    lat=circle_lats,
    lon=circle_lons,
    mode='lines',
    marker=go.scattermapbox.Marker(
        size=1, color="BlueViolet"
    ),
))

fig_map3.update_layout(mapbox_style='open-street-map', margin={'r':0, 't':0, 'l':0, 'b':0}, width=500)

fig_map3.show()
Answered By: Derek O
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.