@@ -12,9 +12,10 @@ def _project_latlon_to_wgs84(lat, lon):
12
12
Projects lat and lon to WGS84 to get regular hexagons on a mapbox map
13
13
"""
14
14
x = lon * np .pi / 180
15
- y = np .arctanh (np .sin (lat * np .pi / 180 ))
15
+ y = np .arctanh (np .sin (lat * np .pi / 180 ))
16
16
return x , y
17
17
18
+
18
19
def _project_wgs84_to_latlon (x , y ):
19
20
"""
20
21
Projects lat and lon to WGS84 to get regular hexagons on a mapbox map
@@ -23,6 +24,7 @@ def _project_wgs84_to_latlon(x, y):
23
24
lat = (2 * np .arctan (np .exp (y )) - np .pi / 2 ) * 180 / np .pi
24
25
return lat , lon
25
26
27
+
26
28
def _human_format (number ):
27
29
"""
28
30
Transforms high numbers to human readable numer string
@@ -32,14 +34,17 @@ def _human_format(number):
32
34
magnitude = int (np .floor (np .log (number , k )))
33
35
return "%.2f%s" % (number / k ** magnitude , units [magnitude ])
34
36
37
+
35
38
def _getBoundsZoomLevel (lon_min , lon_max , lat_min , lat_max , mapDim ):
36
39
"""
37
40
Get the mapbox zoom level given bounds and a figure dimension
38
41
Source: https://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds
39
42
"""
40
43
41
- scale = 2 # adjustment to reflect MapBox base tiles are 512x512 vs. Google's 256x256
42
- WORLD_DIM = {'height' : 256 * scale , 'width' : 256 * scale }
44
+ scale = (
45
+ 2 # adjustment to reflect MapBox base tiles are 512x512 vs. Google's 256x256
46
+ )
47
+ WORLD_DIM = {"height" : 256 * scale , "width" : 256 * scale }
43
48
ZOOM_MAX = 18
44
49
45
50
def latRad (lat ):
@@ -55,11 +60,12 @@ def zoom(mapPx, worldPx, fraction):
55
60
lngDiff = lon_max - lon_min
56
61
lngFraction = ((lngDiff + 360 ) if lngDiff < 0 else lngDiff ) / 360
57
62
58
- latZoom = zoom (mapDim [' height' ], WORLD_DIM [' height' ], latFraction )
59
- lngZoom = zoom (mapDim [' width' ], WORLD_DIM [' width' ], lngFraction )
63
+ latZoom = zoom (mapDim [" height" ], WORLD_DIM [" height" ], latFraction )
64
+ lngZoom = zoom (mapDim [" width" ], WORLD_DIM [" width" ], lngFraction )
60
65
61
66
return min (latZoom , lngZoom , ZOOM_MAX )
62
67
68
+
63
69
def _compute_hexbin (
64
70
lat = None ,
65
71
lon = None ,
@@ -68,7 +74,7 @@ def _compute_hexbin(
68
74
color = None ,
69
75
nx = None ,
70
76
agg_func = None ,
71
- min_count = None
77
+ min_count = None ,
72
78
):
73
79
"""
74
80
Computes the aggregation at hexagonal bin level.
@@ -135,7 +141,7 @@ def _compute_hexbin(
135
141
136
142
d1 = (x - ix1 ) ** 2 + 3.0 * (y - iy1 ) ** 2
137
143
d2 = (x - ix2 - 0.5 ) ** 2 + 3.0 * (y - iy2 - 0.5 ) ** 2
138
- bdist = ( d1 < d2 )
144
+ bdist = d1 < d2
139
145
140
146
if color is None :
141
147
lattice1 = np .zeros ((nx1 , ny1 ))
@@ -186,32 +192,33 @@ def _compute_hexbin(
186
192
else :
187
193
lattice2 [i , j ] = np .nan
188
194
189
- accum = np .hstack ((lattice1 .astype (float ).ravel (),
190
- lattice2 .astype (float ).ravel ()))
195
+ accum = np .hstack (
196
+ (lattice1 .astype (float ).ravel (), lattice2 .astype (float ).ravel ())
197
+ )
191
198
good_idxs = ~ np .isnan (accum )
192
-
199
+
193
200
agreggated_value = accum [good_idxs ]
194
201
195
202
centers = np .zeros ((n , 2 ), float )
196
- centers [:nx1 * ny1 , 0 ] = np .repeat (np .arange (nx1 ), ny1 )
197
- centers [:nx1 * ny1 , 1 ] = np .tile (np .arange (ny1 ), nx1 )
198
- centers [nx1 * ny1 :, 0 ] = np .repeat (np .arange (nx2 ) + 0.5 , ny2 )
199
- centers [nx1 * ny1 :, 1 ] = np .tile (np .arange (ny2 ), nx2 ) + 0.5
203
+ centers [: nx1 * ny1 , 0 ] = np .repeat (np .arange (nx1 ), ny1 )
204
+ centers [: nx1 * ny1 , 1 ] = np .tile (np .arange (ny1 ), nx1 )
205
+ centers [nx1 * ny1 :, 0 ] = np .repeat (np .arange (nx2 ) + 0.5 , ny2 )
206
+ centers [nx1 * ny1 :, 1 ] = np .tile (np .arange (ny2 ), nx2 ) + 0.5
200
207
centers [:, 0 ] *= dx
201
208
centers [:, 1 ] *= dy
202
209
centers [:, 0 ] += xmin
203
210
centers [:, 1 ] += ymin
204
211
centers = centers [good_idxs ]
205
212
206
213
# Define normalised regular hexagon coordinates
207
- hx = [0 , .5 , .5 , 0 , - .5 , - .5 ]
214
+ hx = [0 , 0 .5 , 0 .5 , 0 , - 0 .5 , - 0 .5 ]
208
215
hy = [
209
216
- 0.5 / np .cos (np .pi / 6 ),
210
217
- 0.5 * np .tan (np .pi / 6 ),
211
218
0.5 * np .tan (np .pi / 6 ),
212
219
0.5 / np .cos (np .pi / 6 ),
213
220
0.5 * np .tan (np .pi / 6 ),
214
- - 0.5 * np .tan (np .pi / 6 )
221
+ - 0.5 * np .tan (np .pi / 6 ),
215
222
]
216
223
217
224
# Number of hexagons needed
@@ -221,7 +228,7 @@ def _compute_hexbin(
221
228
dxh = sorted (list (set (np .diff (sorted (centers [:, 0 ])))))[1 ]
222
229
dyh = sorted (list (set (np .diff (sorted (centers [:, 1 ])))))[1 ]
223
230
nx = dxh * 2
224
- ny = 2 / 3 * dyh / (0.5 / np .cos (np .pi / 6 ))
231
+ ny = 2 / 3 * dyh / (0.5 / np .cos (np .pi / 6 ))
225
232
226
233
# Coordinates for all hexagonal patches
227
234
hxs = np .array ([hx ] * m ) * nx + np .vstack (centers [:, 0 ])
@@ -236,6 +243,7 @@ def _compute_hexbin(
236
243
237
244
return hexagons_lats , hexagons_lons , hexagons_ids , agreggated_value
238
245
246
+
239
247
def _hexagons_to_geojson (hexagons_lats , hexagons_lons , ids = None ):
240
248
"""
241
249
Creates a geojson of hexagonal features based on the outputs of
@@ -249,12 +257,13 @@ def _hexagons_to_geojson(hexagons_lats, hexagons_lons, ids=None):
249
257
points .append (points [0 ])
250
258
features .append (
251
259
dict (
252
- type = ' Feature' ,
260
+ type = " Feature" ,
253
261
id = idx ,
254
- geometry = dict (type = ' Polygon' , coordinates = [points ])
262
+ geometry = dict (type = " Polygon" , coordinates = [points ]),
255
263
)
256
264
)
257
- return dict (type = 'FeatureCollection' , features = features )
265
+ return dict (type = "FeatureCollection" , features = features )
266
+
258
267
259
268
def hexbin_mapbox (
260
269
data_frame = None ,
@@ -280,10 +289,10 @@ def hexbin_mapbox(
280
289
height = None ,
281
290
):
282
291
args = build_dataframe (args = locals (), constructor = None )
283
-
292
+
284
293
if agg_func is None :
285
294
agg_func = np .mean
286
-
295
+
287
296
lat_range = args ["data_frame" ][args ["lat" ]].agg (["min" , "max" ]).values
288
297
lon_range = args ["data_frame" ][args ["lon" ]].agg (["min" , "max" ]).values
289
298
@@ -310,9 +319,9 @@ def hexbin_mapbox(
310
319
else :
311
320
mapDim = dict (height = height , width = width )
312
321
zoom = _getBoundsZoomLevel (* lon_range , * lat_range , mapDim )
313
-
322
+
314
323
if center is None :
315
- center = dict (lat = lat_range .mean (), lon = lon_range .mean ())
324
+ center = dict (lat = lat_range .mean (), lon = lon_range .mean ())
316
325
317
326
if args ["animation_frame" ] is not None :
318
327
groups = args ["data_frame" ].groupby (args ["animation_frame" ]).groups
@@ -334,31 +343,27 @@ def hexbin_mapbox(
334
343
)
335
344
agg_data_frame_list .append (
336
345
pd .DataFrame (
337
- np .c_ [hexagons_ids , aggregated_value ],
338
- columns = ["locations" , "color" ]
346
+ np .c_ [hexagons_ids , aggregated_value ], columns = ["locations" , "color" ]
339
347
)
340
348
)
341
- agg_data_frame = pd .concat (
342
- agg_data_frame_list , axis = 0 , keys = groups .keys ()
343
- ).rename_axis (index = ("frame" , "index" )).reset_index ("frame" )
344
-
349
+ agg_data_frame = (
350
+ pd .concat (agg_data_frame_list , axis = 0 , keys = groups .keys ())
351
+ .rename_axis (index = ("frame" , "index" ))
352
+ .reset_index ("frame" )
353
+ )
354
+
345
355
agg_data_frame ["color" ] = pd .to_numeric (agg_data_frame ["color" ])
346
356
347
357
if range_color is None :
348
- range_color = [
349
- agg_data_frame ["color" ].min (),
350
- agg_data_frame ["color" ].max ()
351
- ]
358
+ range_color = [agg_data_frame ["color" ].min (), agg_data_frame ["color" ].max ()]
352
359
353
360
return choropleth_mapbox (
354
361
data_frame = agg_data_frame ,
355
362
geojson = geojson ,
356
363
locations = "locations" ,
357
364
color = "color" ,
358
365
hover_data = {"color" : True , "locations" : False , "frame" : False },
359
- animation_frame = (
360
- "frame" if args ["animation_frame" ] is not None else None
361
- ),
366
+ animation_frame = ("frame" if args ["animation_frame" ] is not None else None ),
362
367
color_discrete_sequence = color_discrete_sequence ,
363
368
color_discrete_map = color_discrete_map ,
364
369
labels = labels ,
@@ -375,4 +380,5 @@ def hexbin_mapbox(
375
380
height = height ,
376
381
)
377
382
383
+
378
384
hexbin_mapbox .__doc__ = make_docstring (hexbin_mapbox )
0 commit comments