pythonpandasplotlygpslatitude-longitude

Getting GPS boundaries for each hexbin in a python plotly 'hexbin_mapbox' heat map - Both centroid GPS point and GPS points for each corner of hexbin


I have created a hexbin "heat map" in Python using plotly by mapping a number of locations (using GPS latitude / longitude), along with the value of each location. See code below for sample df and hexbin figure plot.

Data Desired

When I mouse-over each hexbin, I can see the average value contained within that hexbin. But what I want is a way to download into a pandas df the following info for each hexbin:

My Question

How can I download the data described in the bullets above into a pandas df?

Code example

# Import dependencies
import pandas as pd
import numpy as np
import plotly.figure_factory as ff
import plotly.express as px

# Create a list of GPS coordinates
gps_coordinates = [[32.7792, -96.7959, 10000], 
                  [32.7842, -96.7920, 15000], 
                  [32.8021, -96.7819, 12000], 
                  [32.7916, -96.7833, 26000], 
                  [32.7842, -96.7920, 51000],
                  [32.7842, -96.7920, 17000], 
                  [32.7792, -96.7959, 25000], 
                  [32.7842, -96.7920, 19000], 
                  [32.7842, -96.7920, 31000], 
                  [32.7842, -96.7920, 40000]]

# Create a DataFrame with the GPS coordinates
df = pd.DataFrame(gps_coordinates, columns=['LATITUDE', 'LONGITUDE', 'Value'])

# Print the DataFrame
display(df)
# Create figure using 'df_redfin_std_by_year_and_acreage_bin' data
fig = ff.create_hexbin_mapbox(
      data_frame=df, lat='LATITUDE', lon='LONGITUDE',
      nx_hexagon=2, 
      opacity=0.2, 
      labels={"color": "Dollar Value"},
      color='Value',
      agg_func=np.mean, 
      color_continuous_scale="Jet",
      zoom=14,
      min_count=1, # This gets rid of boxes for which we have no data
      height=900,
      width=1600,
      show_original_data=True,
      original_data_marker=dict(size=5, opacity=0.6, color="deeppink"),
      )

# Create the map
fig.update_layout(mapbox_style="open-street-map")

fig.show()

Solution

  • You can extract the coordinates of the six corners of each hexbin as well as the values from fig.data[0]. However, I am not sure where the centroids information is stored in the figure object, but we can create a geopandas dataframe from this data, and get the directly get the centroids attribute of the geometry column:

    import geopandas as gpd from shapely.geometry import LineString

    coordinates = [feature['geometry']['coordinates'] for feature in fig.data[0].geojson['features']]
    values = fig.data[0]['z']
    hexbins_df = pd.DataFrame({'coordinates': coordinates, 'values': values}) 
    hexbins_df['geometry'] = hexbins_df['coordinates'].apply(lambda x: LineString(x[0]))
    
    hexbins_gdf = gpd.GeoDataFrame(hexbins_df, geometry='geometry')
    hexbins_gdf['centroid'] = hexbins_gdf['geometry'].centroid
    
    corners_df = hexbins_gdf['coordinates'].apply(lambda x: pd.Series(x[0])).rename(columns=lambda x: f'corner_{x+1}')
    hexbins_df = pd.concat([hexbins_df, corners_df], axis=1).drop(columns='corner_7') # we drop corner_7 since that is the same as the starting corner
    

    The resulting geopandas dataframe looks something like this:

    >>> hexbins_df
                                             coordinates        values  ...                                 corner_5                                 corner_6
    0  [[[-96.7889, 32.78215666477984], [-96.78539999...  28833.333333  ...     [-96.792400000007, 32.7872532054738]    [-96.792400000007, 32.78385554412095]
    1  [[[-96.792400000007, 32.777059832108314], [-96...  17500.000000  ...  [-96.79590000001399, 32.78215666477984]  [-96.79590000001399, 32.77875880877266]
    2  [[[-96.785399999993, 32.7872532054738], [-96.7...  26000.000000  ...            [-96.7889, 32.79234945416662]           [-96.7889, 32.788951987483806]
    3  [[[-96.785399999993, 32.79744541083471], [-96....  12000.000000  ...            [-96.7889, 32.80254107545448]            [-96.7889, 32.79914399815894]
    
    [4 rows x 21 columns]