Skip to content

Commit 7d1d1ea

Browse files
author
Florent Chehab
committed
fixed choropleth
1 parent 3777456 commit 7d1d1ea

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

folium/folium.py

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import os
1111
import time
1212

13+
import numpy as np
14+
1315
from branca.colormap import StepColormap
1416
from branca.element import CssLink, Element, Figure, JavascriptLink, MacroElement
1517
from branca.utilities import _parse_size, color_brewer
@@ -421,10 +423,11 @@ def fit_bounds(self, bounds, padding_top_left=None,
421423
)
422424

423425
def choropleth(self, geo_data, data=None, columns=None, key_on=None,
424-
threshold_scale=None, fill_color='blue', fill_opacity=0.6,
425-
line_color='black', line_weight=1, line_opacity=1, name=None,
426-
legend_name='', topojson=None, reset=False, smooth_factor=None,
427-
highlight=None):
426+
threshold_scale=None, fill_color='blue',
427+
nan_fill_color='black', fill_opacity=0.6,
428+
line_color='black', line_weight=1, line_opacity=1,
429+
name=None, legend_name='', topojson=None,
430+
reset=False, smooth_factor=None, highlight=None):
428431
"""
429432
Apply a GeoJSON overlay to the map.
430433
@@ -466,14 +469,17 @@ def choropleth(self, geo_data, data=None, columns=None, key_on=None,
466469
start with 'feature' and be in JavaScript objection notation.
467470
Ex: 'feature.id' or 'feature.properties.statename'.
468471
threshold_scale: list, default None
469-
Data range for D3 threshold scale. Defaults to the following range
470-
of quantiles: [0, 0.5, 0.75, 0.85, 0.9], rounded to the nearest
471-
order-of-magnitude integer. Ex: 270 rounds to 200, 5600 to 6000.
472+
Data range for D3 threshold scale. Defaults to a linear scale of
473+
6 bins going from min(values) to max(values).
474+
Values are expected to be sorted.
472475
fill_color: string, default 'blue'
473476
Area fill color. Can pass a hex code, color name, or if you are
474477
binding data, one of the following color brewer palettes:
475478
'BuGn', 'BuPu', 'GnBu', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'RdPu',
476479
'YlGn', 'YlGnBu', 'YlOrBr', and 'YlOrRd'.
480+
nan_fill_color: string, default 'black'
481+
Area fill color for nan or missing values.
482+
Can pass a hex code, color name.
477483
fill_opacity: float, default 0.6
478484
Area fill opacity, range 0-1.
479485
line_color: string, default 'black'
@@ -519,9 +525,6 @@ def choropleth(self, geo_data, data=None, columns=None, key_on=None,
519525
... highlight=True)
520526
521527
"""
522-
if threshold_scale is not None and len(threshold_scale) > 6:
523-
raise ValueError('The length of threshold_scale is {}, but it may '
524-
'not be longer than 6.'.format(len(threshold_scale))) # noqa
525528
if data is not None and not color_brewer(fill_color):
526529
raise ValueError('Please pass a valid color brewer code to '
527530
'fill_local. See docstring for valid codes.')
@@ -550,32 +553,43 @@ def choropleth(self, geo_data, data=None, columns=None, key_on=None,
550553
if data_min > 0 else -1)
551554
data_max = (data_max if data_max > 0 else 0
552555
if data_max < 0 else 1)
553-
data_min, data_max = (1.01*data_min-0.01*data_max,
554-
1.01*data_max-0.01*data_min)
555-
nb_class = 6
556-
color_domain = [data_min+i*(data_max-data_min)*1./nb_class
557-
for i in range(1+nb_class)]
556+
nb_bins = 6
557+
color_domain = list(np.linspace(data_min, data_max, nb_bins + 1))
558558
else:
559559
color_domain = None
560560

561561
if color_domain and key_on is not None:
562+
nb_bins = len(color_domain) - 1
562563
key_on = key_on[8:] if key_on.startswith('feature.') else key_on
563-
color_range = color_brewer(fill_color, n=len(color_domain))
564+
color_range = color_brewer(fill_color, n=nb_bins)
564565

565566
def get_by_key(obj, key):
566567
return (obj.get(key, None) if len(key.split('.')) <= 1 else
567568
get_by_key(obj.get(key.split('.')[0], None),
568569
'.'.join(key.split('.')[1:])))
569570

570571
def color_scale_fun(x):
571-
idx = len(
572-
[
573-
u for u in color_domain if
574-
get_by_key(x, key_on) in color_data and
575-
u <= color_data[get_by_key(x, key_on)]
576-
]
577-
)
578-
return color_range[idx-1]
572+
key_of_x = get_by_key(x, key_on)
573+
if key_of_x in color_data.keys():
574+
value_of_x = color_data[key_of_x]
575+
else:
576+
# The value is missing
577+
value_of_x = None
578+
579+
if value_of_x is None or np.isnan(value_of_x):
580+
# The value is missing or is deliberately nan
581+
return nan_fill_color
582+
else:
583+
color_idx = int(np.digitize(value_of_x, color_domain)) - 1
584+
# we consider that values outside the color domain
585+
# should be affected to the first (or last) color bin.
586+
<<<<<<< HEAD
587+
color_idx = max(0, color_idx)
588+
=======
589+
color_idx = max(0, color_idx)
590+
>>>>>>> c4a5bf24fb23ac8ffcf34f57cc3cfbc0a5da919f
591+
color_idx = min(nb_bins-1, color_idx)
592+
return color_range[color_idx]
579593
else:
580594
def color_scale_fun(x):
581595
return fill_color
@@ -614,9 +628,10 @@ def highlight_function(x):
614628

615629
# Create ColorMap.
616630
if color_domain:
617-
brewed = color_brewer(fill_color, n=len(color_domain))
631+
nb_bins = len(color_domain) - 1
632+
brewed = color_brewer(fill_color, n=nb_bins)
618633
color_scale = StepColormap(
619-
brewed[1:len(color_domain)],
634+
brewed,
620635
index=color_domain,
621636
vmin=color_domain[0],
622637
vmax=color_domain[-1],

0 commit comments

Comments
 (0)