1
- from ._core import make_figure , build_dataframe
2
- from ._doc import make_docstring , docs
3
- from ._chart_types import choropleth_mapbox
4
- import plotly .graph_objs as go
1
+ from plotly .express ._core import build_dataframe
2
+ from plotly .express ._doc import make_docstring
3
+ from plotly .express ._chart_types import choropleth_mapbox
5
4
import numpy as np
6
5
import pandas as pd
7
- import re
8
6
9
7
10
8
def _project_latlon_to_wgs84 (lat , lon ):
11
9
"""
12
- Projects lat and lon to WGS84 to get regular hexagons on a mapbox map
10
+ Projects lat and lon to WGS84, used to get regular hexagons on a mapbox map
13
11
"""
14
12
x = lon * np .pi / 180
15
13
y = np .arctanh (np .sin (lat * np .pi / 180 ))
@@ -18,7 +16,7 @@ def _project_latlon_to_wgs84(lat, lon):
18
16
19
17
def _project_wgs84_to_latlon (x , y ):
20
18
"""
21
- Projects lat and lon to WGS84 to get regular hexagons on a mapbox map
19
+ Projects WGS84 to lat and lon, used to get regular hexagons on a mapbox map
22
20
"""
23
21
lon = x * 180 / np .pi
24
22
lat = (2 * np .arctan (np .exp (y )) - np .pi / 2 ) * 180 / np .pi
@@ -55,16 +53,8 @@ def zoom(mapPx, worldPx, fraction):
55
53
56
54
return min (latZoom , lngZoom , ZOOM_MAX )
57
55
58
-
59
56
def _compute_hexbin (
60
- lat = None ,
61
- lon = None ,
62
- lat_range = None ,
63
- lon_range = None ,
64
- color = None ,
65
- nx = None ,
66
- agg_func = None ,
67
- min_count = None ,
57
+ x , y , x_range , y_range , color , nx , agg_func , min_count
68
58
):
69
59
"""
70
60
Computes the aggregation at hexagonal bin level.
@@ -73,38 +63,36 @@ def _compute_hexbin(
73
63
74
64
Parameters
75
65
----------
76
- lat : np.ndarray
77
- Array of latitudes
78
- lon : np.ndarray
79
- Array of longitudes
80
- lat_range : np.ndarray
81
- Min and max latitudes
82
- lon_range : np.ndarray
83
- Min and max longitudes
66
+ x : np.ndarray
67
+ Array of x values (shape N)
68
+ y : np.ndarray
69
+ Array of y values (shape N)
70
+ x_range : np.ndarray
71
+ Min and max x (shape 2)
72
+ y_range : np.ndarray
73
+ Min and max y (shape 2)
84
74
color : np.ndarray
85
- Metric to aggregate at hexagon level
75
+ Metric to aggregate at hexagon level (shape N)
86
76
nx : int
87
77
Number of hexagons horizontally
88
78
agg_func : function
89
79
Numpy compatible aggregator, this function must take a one-dimensional
90
80
np.ndarray as input and output a scalar
91
- min_count : float
92
- Minimum value for which to display the aggregate
81
+ min_count : int
82
+ Minimum number of points in the hexagon for the hexagon to be displayed
93
83
94
84
Returns
95
85
-------
86
+ np.ndarray
87
+ X coordinates of each hexagon (shape M x 6)
88
+ np.ndarray
89
+ Y coordinates of each hexagon (shape M x 6)
90
+ np.ndarray
91
+ Centers of the hexagons (shape M x 2)
92
+ np.ndarray
93
+ Aggregated value in each hexagon (shape M)
96
94
97
95
"""
98
- # Project to WGS 84
99
- x , y = _project_latlon_to_wgs84 (lat , lon )
100
-
101
- if lat_range is None :
102
- lat_range = np .array ([lat .min (), lat .max ()])
103
- if lon_range is None :
104
- lon_range = np .array ([lon .min (), lon .max ()])
105
-
106
- x_range , y_range = _project_latlon_to_wgs84 (lat_range , lon_range )
107
-
108
96
xmin = x_range .min ()
109
97
xmax = x_range .max ()
110
98
ymin = y_range .min ()
@@ -224,6 +212,69 @@ def _compute_hexbin(
224
212
hxs = np .array ([hx ] * m ) * nx + np .vstack (centers [:, 0 ])
225
213
hys = np .array ([hy ] * m ) * ny + np .vstack (centers [:, 1 ])
226
214
215
+ return hxs , hys , centers , agreggated_value
216
+
217
+ def _compute_wgs84_hexbin (
218
+ lat = None ,
219
+ lon = None ,
220
+ lat_range = None ,
221
+ lon_range = None ,
222
+ color = None ,
223
+ nx = None ,
224
+ agg_func = None ,
225
+ min_count = None ,
226
+ ):
227
+ """
228
+ Computes the lat-lon aggregation at hexagonal bin level.
229
+ Latitude and longitude need to be projected to WGS84 before aggregating
230
+ in order to display regular hexagons on the map.
231
+
232
+ Parameters
233
+ ----------
234
+ lat : np.ndarray
235
+ Array of latitudes (shape N)
236
+ lon : np.ndarray
237
+ Array of longitudes (shape N)
238
+ lat_range : np.ndarray
239
+ Min and max latitudes (shape 2)
240
+ lon_range : np.ndarray
241
+ Min and max longitudes (shape 2)
242
+ color : np.ndarray
243
+ Metric to aggregate at hexagon level (shape N)
244
+ nx : int
245
+ Number of hexagons horizontally
246
+ agg_func : function
247
+ Numpy compatible aggregator, this function must take a one-dimensional
248
+ np.ndarray as input and output a scalar
249
+ min_count : int
250
+ Minimum number of points in the hexagon for the hexagon to be displayed
251
+
252
+ Returns
253
+ -------
254
+ np.ndarray
255
+ Lat coordinates of each hexagon (shape M x 6)
256
+ np.ndarray
257
+ Lon coordinates of each hexagon (shape M x 6)
258
+ pd.Series
259
+ Unique id for each hexagon, to be used in the geojson data (shape M)
260
+ np.ndarray
261
+ Aggregated value in each hexagon (shape M)
262
+
263
+ """
264
+ # Project to WGS 84
265
+ x , y = _project_latlon_to_wgs84 (lat , lon )
266
+
267
+ if lat_range is None :
268
+ lat_range = np .array ([lat .min (), lat .max ()])
269
+ if lon_range is None :
270
+ lon_range = np .array ([lon .min (), lon .max ()])
271
+
272
+ x_range , y_range = _project_latlon_to_wgs84 (lat_range , lon_range )
273
+
274
+ hxs , hys , centers , agreggated_value = _compute_hexbin (
275
+ x , y , x_range , y_range , color , nx , agg_func , min_count
276
+ )
277
+
227
278
# Convert back to lat-lon
228
279
hexagons_lats , hexagons_lons = _project_wgs84_to_latlon (hxs , hys )
229
280
@@ -237,7 +288,7 @@ def _compute_hexbin(
237
288
def _hexagons_to_geojson (hexagons_lats , hexagons_lons , ids = None ):
238
289
"""
239
290
Creates a geojson of hexagonal features based on the outputs of
240
- _compute_hexbin
291
+ _compute_wgs84_hexbin
241
292
"""
242
293
features = []
243
294
if ids is None :
@@ -255,12 +306,12 @@ def _hexagons_to_geojson(hexagons_lats, hexagons_lons, ids=None):
255
306
return dict (type = "FeatureCollection" , features = features )
256
307
257
308
258
- def hexbin_mapbox (
309
+ def create_hexbin_mapbox (
259
310
data_frame = None ,
260
311
lat = None ,
261
312
lon = None ,
262
313
color = None ,
263
- gridsize = 5 ,
314
+ nx_hexagon = 5 ,
264
315
agg_func = None ,
265
316
animation_frame = None ,
266
317
color_discrete_sequence = None ,
@@ -278,6 +329,9 @@ def hexbin_mapbox(
278
329
width = None ,
279
330
height = None ,
280
331
):
332
+ """
333
+ Returns a figure aggregating scattered points into connected hexagons
334
+ """
281
335
args = build_dataframe (args = locals (), constructor = None )
282
336
283
337
if agg_func is None :
@@ -286,13 +340,13 @@ def hexbin_mapbox(
286
340
lat_range = args ["data_frame" ][args ["lat" ]].agg (["min" , "max" ]).values
287
341
lon_range = args ["data_frame" ][args ["lon" ]].agg (["min" , "max" ]).values
288
342
289
- hexagons_lats , hexagons_lons , hexagons_ids , count = _compute_hexbin (
343
+ hexagons_lats , hexagons_lons , hexagons_ids , count = _compute_wgs84_hexbin (
290
344
lat = args ["data_frame" ][args ["lat" ]].values ,
291
345
lon = args ["data_frame" ][args ["lon" ]].values ,
292
346
lat_range = lat_range ,
293
347
lon_range = lon_range ,
294
348
color = None ,
295
- nx = gridsize ,
349
+ nx = nx_hexagon ,
296
350
agg_func = agg_func ,
297
351
min_count = - np .inf ,
298
352
)
@@ -323,13 +377,13 @@ def hexbin_mapbox(
323
377
agg_data_frame_list = []
324
378
for frame , index in groups .items ():
325
379
df = args ["data_frame" ].loc [index ]
326
- _ , _ , hexagons_ids , aggregated_value = _compute_hexbin (
380
+ _ , _ , hexagons_ids , aggregated_value = _compute_wgs84_hexbin (
327
381
lat = df [args ["lat" ]].values ,
328
382
lon = df [args ["lon" ]].values ,
329
383
lat_range = lat_range ,
330
384
lon_range = lon_range ,
331
385
color = df [args ["color" ]].values if args ["color" ] else None ,
332
- nx = gridsize ,
386
+ nx = nx_hexagon ,
333
387
agg_func = agg_func ,
334
388
min_count = None ,
335
389
)
@@ -372,5 +426,14 @@ def hexbin_mapbox(
372
426
height = height ,
373
427
)
374
428
375
-
376
- hexbin_mapbox .__doc__ = make_docstring (hexbin_mapbox )
429
+ create_hexbin_mapbox .__doc__ = make_docstring (
430
+ create_hexbin_mapbox ,
431
+ override_dict = dict (
432
+ nx_hexagon = ["int" , "Number of hexagons (horizontally) to be created" ],
433
+ agg_func = [
434
+ "function" ,
435
+ "Numpy array aggregator, it must take as input a 1D array" ,
436
+ "and output a scalar value." ,
437
+ ],
438
+ )
439
+ )
0 commit comments