def map_face2color(face, colormap, scale, vmin, vmax): """ Normalize facecolor values by vmin/vmax and return rgb-color strings This function takes a tuple color along with a colormap and a minimum (vmin) and maximum (vmax) range of possible mean distances for the given parametrized surface. It returns an rgb color based on the mean distance between vmin and vmax """ if vmin >= vmax: raise exceptions.PlotlyError("Incorrect relation between vmin " "and vmax. The vmin value cannot be " "bigger than or equal to the value " "of vmax.") if len(colormap) == 1: # color each triangle face with the same color in colormap face_color = colormap[0] face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color if face == vmax: # pick last color in colormap face_color = colormap[-1] face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color else: if scale is None: # find the normalized distance t of a triangle face between # vmin and vmax where the distance is between 0 and 1 t = (face - vmin) / float((vmax - vmin)) low_color_index = int(t / (1. / (len(colormap) - 1))) face_color = colors.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], t * (len(colormap) - 1) - low_color_index) face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) else: # find the face color for a non-linearly interpolated scale t = (face - vmin) / float((vmax - vmin)) low_color_index = 0 for k in range(len(scale) - 1): if scale[k] <= t < scale[k + 1]: break low_color_index += 1 low_scale_val = scale[low_color_index] high_scale_val = scale[low_color_index + 1] face_color = colors.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], (t - low_scale_val) / (high_scale_val - low_scale_val)) face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color
def _any_to_rgb(color: Union[tuple, str]) -> str: c: str = label_rgb(color) if isinstance(color, tuple) else color if c.startswith("#"): c = label_rgb(hex_to_rgb(c)) if not c.startswith("rgb("): raise RuntimeError("Something went wrong with the logic above!") return c
def _colors(ncontours, colormap=None): """ Return a list of ``ncontours`` colors from the ``colormap`` colorscale. """ if colormap in clrs.PLOTLY_SCALES.keys(): cmap = clrs.PLOTLY_SCALES[colormap] else: raise exceptions.PlotlyError( "Colorscale must be a valid Plotly Colorscale." "The available colorscale names are {}".format( clrs.PLOTLY_SCALES.keys())) values = np.linspace(0, 1, ncontours) vals_cmap = np.array([pair[0] for pair in cmap]) cols = np.array([pair[1] for pair in cmap]) inds = np.searchsorted(vals_cmap, values) if "#" in cols[0]: # for Viridis cols = [clrs.label_rgb(clrs.hex_to_rgb(col)) for col in cols] colors = [cols[0]] for ind, val in zip(inds[1:], values[1:]): val1, val2 = vals_cmap[ind - 1], vals_cmap[ind] interm = (val - val1) / (val2 - val1) col = clrs.find_intermediate_color(cols[ind - 1], cols[ind], interm, colortype="rgb") colors.append(col) return colors
def to_RGB_255(cmap, as_str=True): from plotly import colors cmap = [colors.convert_to_RGB_255(c) for c in cmap] if as_str: return [colors.label_rgb(c) for c in cmap] else: return cmap
def _colors(ncontours, colormap=None): """ Return a list of ``ncontours`` colors from the ``colormap`` colorscale. """ if colormap in clrs.PLOTLY_SCALES.keys(): cmap = clrs.PLOTLY_SCALES[colormap] else: raise exceptions.PlotlyError( "Colorscale must be a valid Plotly Colorscale." "The available colorscale names are {}".format( clrs.PLOTLY_SCALES.keys())) values = np.linspace(0, 1, ncontours) vals_cmap = np.array([pair[0] for pair in cmap]) cols = np.array([pair[1] for pair in cmap]) inds = np.searchsorted(vals_cmap, values) if '#' in cols[0]: # for Viridis cols = [clrs.label_rgb(clrs.hex_to_rgb(col)) for col in cols] colors = [cols[0]] for ind, val in zip(inds[1:], values[1:]): val1, val2 = vals_cmap[ind - 1], vals_cmap[ind] interm = (val - val1) / (val2 - val1) col = clrs.find_intermediate_color(cols[ind - 1], cols[ind], interm, colortype='rgb') colors.append(col) return colors
def get_colors_from_name(colormapname, numbervalues, reverse=False): plotly_colors, plotly_scale = colors.convert_colors_to_same_type(colormapname) if reverse: plotly_colors.reverse() plotly_scale = np.array(plotly_scale) plotly_colors = np.array(list(map(literal_eval, [color[3:] for color in plotly_colors])))/255.0 vmin = 0 vmax = numbervalues values = np.arange(0, vmax) v = (values - vmin)/(vmax - vmin) closest_indices = [sorted(np.argsort(np.abs(plotly_scale - i))[0:2]) for i in v] newcolors = [colors.find_intermediate_color(plotly_colors[indices[0]], plotly_colors[indices[1]], value) for indices, value in zip(closest_indices, v)] newcolors = [colors.label_rgb(colors.convert_to_RGB_255(i)) for i in newcolors] return newcolors
) from plotly.express.colors import sequential # noinspection PyProtectedMember from ridgeplot._colors import _path_to_colors_dict # start off by getting all named color-scales defined in PLOTLY_SCALES all_colorscales_raw = PLOTLY_SCALES.copy() # turn plotly's default colors into the default color-scale all_colorscales_raw["default"] = make_colorscale(DEFAULT_PLOTLY_COLORS) # add all sequential color-scales for name, color_list in vars(sequential).items(): if name.startswith("_") or name.startswith("swatches"): continue all_colorscales_raw[name] = make_colorscale(color_list) all_colorscales_clean = {} for name, colorscale in all_colorscales_raw.items(): # convert all color-scales to 'rgb(r, g, b)' format if colorscale[0][1].startswith("#"): colorscale = [[s, label_rgb(hex_to_rgb(c))] for s, c in colorscale] # validate the color-scale as a sanity check and # use lower-case convention for all color-scales validate_colorscale(colorscale) all_colorscales_clean[name.lower()] = colorscale with _path_to_colors_dict.open(mode="w") as _colors_json: json.dump(all_colorscales_clean, _colors_json, indent=2)
def create_choropleth(fips, values, scope=["usa"], binning_endpoints=None, colorscale=None, order=None, simplify_county=0.02, simplify_state=0.02, asp=None, show_hover=True, show_state_data=True, state_outline=None, county_outline=None, centroid_marker=None, round_legend_values=False, exponent_format=False, legend_title="", **layout_options): """ Returns figure for county choropleth. Uses data from package_data. :param (list) fips: list of FIPS values which correspond to the con catination of state and county ids. An example is '01001'. :param (list) values: list of numbers/strings which correspond to the fips list. These are the values that will determine how the counties are colored. :param (list) scope: list of states and/or states abbreviations. Fits all states in the camera tightly. Selecting ['usa'] is the equivalent of appending all 50 states into your scope list. Selecting only 'usa' does not include 'Alaska', 'Puerto Rico', 'American Samoa', 'Commonwealth of the Northern Mariana Islands', 'Guam', 'United States Virgin Islands'. These must be added manually to the list. Default = ['usa'] :param (list) binning_endpoints: ascending numbers which implicitly define real number intervals which are used as bins. The colorscale used must have the same number of colors as the number of bins and this will result in a categorical colormap. :param (list) colorscale: a list of colors with length equal to the number of categories of colors. The length must match either all unique numbers in the 'values' list or if endpoints is being used, the number of categories created by the endpoints.\n For example, if binning_endpoints = [4, 6, 8], then there are 4 bins: [-inf, 4), [4, 6), [6, 8), [8, inf) :param (list) order: a list of the unique categories (numbers/bins) in any desired order. This is helpful if you want to order string values to a chosen colorscale. :param (float) simplify_county: determines the simplification factor for the counties. The larger the number, the fewer vertices and edges each polygon has. See http://toblerity.org/shapely/manual.html#object.simplify for more information. Default = 0.02 :param (float) simplify_state: simplifies the state outline polygon. See http://toblerity.org/shapely/manual.html#object.simplify for more information. Default = 0.02 :param (float) asp: the width-to-height aspect ratio for the camera. Default = 2.5 :param (bool) show_hover: show county hover and centroid info :param (bool) show_state_data: reveals state boundary lines :param (dict) state_outline: dict of attributes of the state outline including width and color. See https://plot.ly/python/reference/#scatter-marker-line for all valid params :param (dict) county_outline: dict of attributes of the county outline including width and color. See https://plot.ly/python/reference/#scatter-marker-line for all valid params :param (dict) centroid_marker: dict of attributes of the centroid marker. The centroid markers are invisible by default and appear visible on selection. See https://plot.ly/python/reference/#scatter-marker for all valid params :param (bool) round_legend_values: automatically round the numbers that appear in the legend to the nearest integer. Default = False :param (bool) exponent_format: if set to True, puts numbers in the K, M, B number format. For example 4000.0 becomes 4.0K Default = False :param (str) legend_title: title that appears above the legend :param **layout_options: a **kwargs argument for all layout parameters Example 1: Florida ``` import plotly.plotly as py import plotly.figure_factory as ff import numpy as np import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'] == 'Florida'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() binning_endpoints = list(np.mgrid[min(values):max(values):4j]) colorscale = ["#030512","#1d1d3b","#323268","#3d4b94","#3e6ab0", "#4989bc","#60a7c7","#85c5d3","#b7e0e4","#eafcfd"] fig = ff.create_choropleth( fips=fips, values=values, scope=['Florida'], show_state_data=True, colorscale=colorscale, binning_endpoints=binning_endpoints, round_legend_values=True, plot_bgcolor='rgb(229,229,229)', paper_bgcolor='rgb(229,229,229)', legend_title='Florida Population', county_outline={'color': 'rgb(255,255,255)', 'width': 0.5}, exponent_format=True, ) py.iplot(fig, filename='choropleth_florida') ``` Example 2: New England ``` import plotly.plotly as py import plotly.figure_factory as ff import pandas as pd NE_states = ['Connecticut', 'Maine', 'Massachusetts', 'New Hampshire', 'Rhode Island'] df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'].isin(NE_states)] colorscale = ['rgb(68.0, 1.0, 84.0)', 'rgb(66.0, 64.0, 134.0)', 'rgb(38.0, 130.0, 142.0)', 'rgb(63.0, 188.0, 115.0)', 'rgb(216.0, 226.0, 25.0)'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() fig = ff.create_choropleth( fips=fips, values=values, scope=NE_states, show_state_data=True ) py.iplot(fig, filename='choropleth_new_england') ``` Example 3: California and Surrounding States ``` import plotly.plotly as py import plotly.figure_factory as ff import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'] == 'California'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() colorscale = [ 'rgb(193, 193, 193)', 'rgb(239,239,239)', 'rgb(195, 196, 222)', 'rgb(144,148,194)', 'rgb(101,104,168)', 'rgb(65, 53, 132)' ] fig = ff.create_choropleth( fips=fips, values=values, colorscale=colorscale, scope=['CA', 'AZ', 'Nevada', 'Oregon', ' Idaho'], binning_endpoints=[14348, 63983, 134827, 426762, 2081313], county_outline={'color': 'rgb(255,255,255)', 'width': 0.5}, legend_title='California Counties', title='California and Nearby States' ) py.iplot(fig, filename='choropleth_california_and_surr_states_outlines') ``` Example 4: USA ``` import plotly.plotly as py import plotly.figure_factory as ff import numpy as np import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/laucnty16.csv' ) df_sample['State FIPS Code'] = df_sample['State FIPS Code'].apply( lambda x: str(x).zfill(2) ) df_sample['County FIPS Code'] = df_sample['County FIPS Code'].apply( lambda x: str(x).zfill(3) ) df_sample['FIPS'] = ( df_sample['State FIPS Code'] + df_sample['County FIPS Code'] ) binning_endpoints = list(np.linspace(1, 12, len(colorscale) - 1)) colorscale = ["#f7fbff", "#ebf3fb", "#deebf7", "#d2e3f3", "#c6dbef", "#b3d2e9", "#9ecae1", "#85bcdb", "#6baed6", "#57a0ce", "#4292c6", "#3082be", "#2171b5", "#1361a9", "#08519c", "#0b4083","#08306b"] fips = df_sample['FIPS'] values = df_sample['Unemployment Rate (%)'] fig = ff.create_choropleth( fips=fips, values=values, scope=['usa'], binning_endpoints=binning_endpoints, colorscale=colorscale, show_hover=True, centroid_marker={'opacity': 0}, asp=2.9, title='USA by Unemployment %', legend_title='Unemployment %' ) py.iplot(fig, filename='choropleth_full_usa') ``` """ # ensure optional modules imported if not _plotly_geo: raise ValueError(""" The create_choropleth figure factory requires the plotly-geo package. Install using pip with: $ pip install plotly-geo Or, install using conda with $ conda install -c plotly plotly-geo """) if not gp or not shapefile or not shapely: raise ImportError( "geopandas, pyshp and shapely must be installed for this figure " "factory.\n\nRun the following commands to install the correct " "versions of the following modules:\n\n" "```\n" "$ pip install geopandas==0.3.0\n" "$ pip install pyshp==1.2.10\n" "$ pip install shapely==1.6.3\n" "```\n" "If you are using Windows, follow this post to properly " "install geopandas and dependencies:" "http://geoffboeing.com/2014/09/using-geopandas-windows/\n\n" "If you are using Anaconda, do not use PIP to install the " "packages above. Instead use conda to install them:\n\n" "```\n" "$ conda install plotly\n" "$ conda install geopandas\n" "```") df, df_state = _create_us_counties_df(st_to_state_name_dict, state_to_st_dict) fips_polygon_map = dict(zip(df["FIPS"].tolist(), df["geometry"].tolist())) if not state_outline: state_outline = {"color": "rgb(240, 240, 240)", "width": 1} if not county_outline: county_outline = {"color": "rgb(0, 0, 0)", "width": 0} if not centroid_marker: centroid_marker = {"size": 3, "color": "white", "opacity": 1} # ensure centroid markers appear on selection if "opacity" not in centroid_marker: centroid_marker.update({"opacity": 1}) if len(fips) != len(values): raise PlotlyError("fips and values must be the same length") # make fips, values into lists if isinstance(fips, pd.core.series.Series): fips = fips.tolist() if isinstance(values, pd.core.series.Series): values = values.tolist() # make fips numeric fips = map(lambda x: int(x), fips) if binning_endpoints: intervals = utils.endpts_to_intervals(binning_endpoints) LEVELS = _intervals_as_labels(intervals, round_legend_values, exponent_format) else: if not order: LEVELS = sorted(list(set(values))) else: # check if order is permutation # of unique color col values same_sets = sorted(list(set(values))) == set(order) no_duplicates = not any(order.count(x) > 1 for x in order) if same_sets and no_duplicates: LEVELS = order else: raise PlotlyError( "if you are using a custom order of unique values from " "your color column, you must: have all the unique values " "in your order and have no duplicate items") if not colorscale: colorscale = [] viridis_colors = clrs.colorscale_to_colors( clrs.PLOTLY_SCALES["Viridis"]) viridis_colors = clrs.color_parser(viridis_colors, clrs.hex_to_rgb) viridis_colors = clrs.color_parser(viridis_colors, clrs.label_rgb) viri_len = len(viridis_colors) + 1 viri_intervals = utils.endpts_to_intervals( list(np.linspace(0, 1, viri_len)))[1:-1] for L in np.linspace(0, 1, len(LEVELS)): for idx, inter in enumerate(viri_intervals): if L == 0: break elif inter[0] < L <= inter[1]: break intermed = (L - viri_intervals[idx][0]) / (viri_intervals[idx][1] - viri_intervals[idx][0]) float_color = clrs.find_intermediate_color(viridis_colors[idx], viridis_colors[idx], intermed, colortype="rgb") # make R,G,B into int values float_color = clrs.unlabel_rgb(float_color) float_color = clrs.unconvert_from_RGB_255(float_color) int_rgb = clrs.convert_to_RGB_255(float_color) int_rgb = clrs.label_rgb(int_rgb) colorscale.append(int_rgb) if len(colorscale) < len(LEVELS): raise PlotlyError( "You have {} LEVELS. Your number of colors in 'colorscale' must " "be at least the number of LEVELS: {}. If you are " "using 'binning_endpoints' then 'colorscale' must have at " "least len(binning_endpoints) + 2 colors".format( len(LEVELS), min(LEVELS, LEVELS[:20]))) color_lookup = dict(zip(LEVELS, colorscale)) x_traces = dict(zip(LEVELS, [[] for i in range(len(LEVELS))])) y_traces = dict(zip(LEVELS, [[] for i in range(len(LEVELS))])) # scope if isinstance(scope, str): raise PlotlyError("'scope' must be a list/tuple/sequence") scope_names = [] extra_states = [ "Alaska", "Commonwealth of the Northern Mariana Islands", "Puerto Rico", "Guam", "United States Virgin Islands", "American Samoa", ] for state in scope: if state.lower() == "usa": scope_names = df["STATE_NAME"].unique() scope_names = list(scope_names) for ex_st in extra_states: try: scope_names.remove(ex_st) except ValueError: pass else: if state in st_to_state_name_dict.keys(): state = st_to_state_name_dict[state] scope_names.append(state) df_state = df_state[df_state["STATE_NAME"].isin(scope_names)] plot_data = [] x_centroids = [] y_centroids = [] centroid_text = [] fips_not_in_shapefile = [] if not binning_endpoints: for index, f in enumerate(fips): level = values[index] try: fips_polygon_map[f].type ( x_traces, y_traces, x_centroids, y_centroids, centroid_text, ) = _calculations( df, fips, values, index, f, simplify_county, level, x_centroids, y_centroids, centroid_text, x_traces, y_traces, fips_polygon_map, ) except KeyError: fips_not_in_shapefile.append(f) else: for index, f in enumerate(fips): for j, inter in enumerate(intervals): if inter[0] < values[index] <= inter[1]: break level = LEVELS[j] try: fips_polygon_map[f].type ( x_traces, y_traces, x_centroids, y_centroids, centroid_text, ) = _calculations( df, fips, values, index, f, simplify_county, level, x_centroids, y_centroids, centroid_text, x_traces, y_traces, fips_polygon_map, ) except KeyError: fips_not_in_shapefile.append(f) if len(fips_not_in_shapefile) > 0: msg = ("Unrecognized FIPS Values\n\nWhoops! It looks like you are " "trying to pass at least one FIPS value that is not in " "our shapefile of FIPS and data for the counties. Your " "choropleth will still show up but these counties cannot " "be shown.\nUnrecognized FIPS are: {}".format( fips_not_in_shapefile)) warnings.warn(msg) x_states = [] y_states = [] for index, row in df_state.iterrows(): if df_state["geometry"][index].type == "Polygon": x = row.geometry.simplify(simplify_state).exterior.xy[0].tolist() y = row.geometry.simplify(simplify_state).exterior.xy[1].tolist() x_states = x_states + x y_states = y_states + y elif df_state["geometry"][index].type == "MultiPolygon": x = [ poly.simplify(simplify_state).exterior.xy[0].tolist() for poly in df_state["geometry"][index] ] y = [ poly.simplify(simplify_state).exterior.xy[1].tolist() for poly in df_state["geometry"][index] ] for segment in range(len(x)): x_states = x_states + x[segment] y_states = y_states + y[segment] x_states.append(np.nan) y_states.append(np.nan) x_states.append(np.nan) y_states.append(np.nan) for lev in LEVELS: county_data = dict( type="scatter", mode="lines", x=x_traces[lev], y=y_traces[lev], line=county_outline, fill="toself", fillcolor=color_lookup[lev], name=lev, hoverinfo="none", ) plot_data.append(county_data) if show_hover: hover_points = dict( type="scatter", showlegend=False, legendgroup="centroids", x=x_centroids, y=y_centroids, text=centroid_text, name="US Counties", mode="markers", marker={ "color": "white", "opacity": 0 }, hoverinfo="text", ) centroids_on_select = dict( selected=dict(marker=centroid_marker), unselected=dict(marker=dict(opacity=0)), ) hover_points.update(centroids_on_select) plot_data.append(hover_points) if show_state_data: state_data = dict( type="scatter", legendgroup="States", line=state_outline, x=x_states, y=y_states, hoverinfo="text", showlegend=False, mode="lines", ) plot_data.append(state_data) DEFAULT_LAYOUT = dict( hovermode="closest", xaxis=dict( autorange=False, range=USA_XRANGE, showgrid=False, zeroline=False, fixedrange=True, showticklabels=False, ), yaxis=dict( autorange=False, range=USA_YRANGE, showgrid=False, zeroline=False, fixedrange=True, showticklabels=False, ), margin=dict(t=40, b=20, r=20, l=20), width=900, height=450, dragmode="select", legend=dict(traceorder="reversed", xanchor="right", yanchor="top", x=1, y=1), annotations=[], ) fig = dict(data=plot_data, layout=DEFAULT_LAYOUT) fig["layout"].update(layout_options) fig["layout"]["annotations"].append( dict( x=1, y=1.05, xref="paper", yref="paper", xanchor="right", showarrow=False, text="<b>" + legend_title + "</b>", )) if len(scope) == 1 and scope[0].lower() == "usa": xaxis_range_low = -125.0 xaxis_range_high = -55.0 yaxis_range_low = 25.0 yaxis_range_high = 49.0 else: xaxis_range_low = float("inf") xaxis_range_high = float("-inf") yaxis_range_low = float("inf") yaxis_range_high = float("-inf") for trace in fig["data"]: if all(isinstance(n, Number) for n in trace["x"]): calc_x_min = min(trace["x"] or [float("inf")]) calc_x_max = max(trace["x"] or [float("-inf")]) if calc_x_min < xaxis_range_low: xaxis_range_low = calc_x_min if calc_x_max > xaxis_range_high: xaxis_range_high = calc_x_max if all(isinstance(n, Number) for n in trace["y"]): calc_y_min = min(trace["y"] or [float("inf")]) calc_y_max = max(trace["y"] or [float("-inf")]) if calc_y_min < yaxis_range_low: yaxis_range_low = calc_y_min if calc_y_max > yaxis_range_high: yaxis_range_high = calc_y_max # camera zoom fig["layout"]["xaxis"]["range"] = [xaxis_range_low, xaxis_range_high] fig["layout"]["yaxis"]["range"] = [yaxis_range_low, yaxis_range_high] # aspect ratio if asp is None: usa_x_range = USA_XRANGE[1] - USA_XRANGE[0] usa_y_range = USA_YRANGE[1] - USA_YRANGE[0] asp = usa_x_range / usa_y_range # based on your figure width = float(fig["layout"]["xaxis"]["range"][1] - fig["layout"]["xaxis"]["range"][0]) height = float(fig["layout"]["yaxis"]["range"][1] - fig["layout"]["yaxis"]["range"][0]) center = ( sum(fig["layout"]["xaxis"]["range"]) / 2.0, sum(fig["layout"]["yaxis"]["range"]) / 2.0, ) if height / width > (1 / asp): new_width = asp * height fig["layout"]["xaxis"]["range"][0] = center[0] - new_width * 0.5 fig["layout"]["xaxis"]["range"][1] = center[0] + new_width * 0.5 else: new_height = (1 / asp) * width fig["layout"]["yaxis"]["range"][0] = center[1] - new_height * 0.5 fig["layout"]["yaxis"]["range"][1] = center[1] + new_height * 0.5 return go.Figure(fig)
def colormap(i): return label_rgb([ int(n) for n in find_intermediate_color([0, 255., 255.], [255., 0., 0], i) ])
def map_face2color(face, colormap, scale, vmin, vmax): """ Normalize facecolor values by vmin/vmax and return rgb-color strings This function takes a tuple color along with a colormap and a minimum (vmin) and maximum (vmax) range of possible mean distances for the given parametrized surface. It returns an rgb color based on the mean distance between vmin and vmax """ if vmin >= vmax: raise exceptions.PlotlyError("Incorrect relation between vmin " "and vmax. The vmin value cannot be " "bigger than or equal to the value " "of vmax.") if len(colormap) == 1: # color each triangle face with the same color in colormap face_color = colormap[0] face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color if face == vmax: # pick last color in colormap face_color = colormap[-1] face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color else: if scale is None: # find the normalized distance t of a triangle face between # vmin and vmax where the distance is between 0 and 1 t = (face - vmin) / float((vmax - vmin)) low_color_index = int(t / (1./(len(colormap) - 1))) face_color = colors.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], t * (len(colormap) - 1) - low_color_index ) face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) else: # find the face color for a non-linearly interpolated scale t = (face - vmin) / float((vmax - vmin)) low_color_index = 0 for k in range(len(scale) - 1): if scale[k] <= t < scale[k+1]: break low_color_index += 1 low_scale_val = scale[low_color_index] high_scale_val = scale[low_color_index + 1] face_color = colors.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], (t - low_scale_val)/(high_scale_val - low_scale_val) ) face_color = colors.convert_to_RGB_255(face_color) face_color = colors.label_rgb(face_color) return face_color
def trisurf(x, y, z, simplices, show_colorbar, edges_color, scale, colormap=None, color_func=None, plot_edges=False, x_edge=None, y_edge=None, z_edge=None, facecolor=None): """ Refer to FigureFactory.create_trisurf() for docstring """ # numpy import check if not np: raise ImportError("FigureFactory._trisurf() requires " "numpy imported.") points3D = np.vstack((x, y, z)).T simplices = np.atleast_2d(simplices) # vertices of the surface triangles tri_vertices = points3D[simplices] # Define colors for the triangle faces if color_func is None: # mean values of z-coordinates of triangle vertices mean_dists = tri_vertices[:, :, 2].mean(-1) elif isinstance(color_func, (list, np.ndarray)): # Pre-computed list / array of values to map onto color if len(color_func) != len(simplices): raise ValueError("If color_func is a list/array, it must " "be the same length as simplices.") # convert all colors in color_func to rgb for index in range(len(color_func)): if isinstance(color_func[index], str): if '#' in color_func[index]: foo = colors.hex_to_rgb(color_func[index]) color_func[index] = colors.label_rgb(foo) if isinstance(color_func[index], tuple): foo = colors.convert_to_RGB_255(color_func[index]) color_func[index] = colors.label_rgb(foo) mean_dists = np.asarray(color_func) else: # apply user inputted function to calculate # custom coloring for triangle vertices mean_dists = [] for triangle in tri_vertices: dists = [] for vertex in triangle: dist = color_func(vertex[0], vertex[1], vertex[2]) dists.append(dist) mean_dists.append(np.mean(dists)) mean_dists = np.asarray(mean_dists) # Check if facecolors are already strings and can be skipped if isinstance(mean_dists[0], str): facecolor = mean_dists else: min_mean_dists = np.min(mean_dists) max_mean_dists = np.max(mean_dists) if facecolor is None: facecolor = [] for index in range(len(mean_dists)): color = map_face2color(mean_dists[index], colormap, scale, min_mean_dists, max_mean_dists) facecolor.append(color) # Make sure facecolor is a list so output is consistent across Pythons facecolor = np.asarray(facecolor) ii, jj, kk = simplices.T triangles = graph_objs.Mesh3d(x=x, y=y, z=z, facecolor=facecolor, i=ii, j=jj, k=kk, name='') mean_dists_are_numbers = not isinstance(mean_dists[0], str) if mean_dists_are_numbers and show_colorbar is True: # make a colorscale from the colors colorscale = colors.make_colorscale(colormap, scale) colorscale = colors.convert_colorscale_to_rgb(colorscale) colorbar = graph_objs.Scatter3d( x=x[:1], y=y[:1], z=z[:1], mode='markers', marker=dict( size=0.1, color=[min_mean_dists, max_mean_dists], colorscale=colorscale, showscale=True), hoverinfo='none', showlegend=False ) # the triangle sides are not plotted if plot_edges is False: if mean_dists_are_numbers and show_colorbar is True: return [triangles, colorbar] else: return [triangles] # define the lists x_edge, y_edge and z_edge, of x, y, resp z # coordinates of edge end points for each triangle # None separates data corresponding to two consecutive triangles is_none = [ii is None for ii in [x_edge, y_edge, z_edge]] if any(is_none): if not all(is_none): raise ValueError("If any (x_edge, y_edge, z_edge) is None, " "all must be None") else: x_edge = [] y_edge = [] z_edge = [] # Pull indices we care about, then add a None column to separate tris ixs_triangles = [0, 1, 2, 0] pull_edges = tri_vertices[:, ixs_triangles, :] x_edge_pull = np.hstack([pull_edges[:, :, 0], np.tile(None, [pull_edges.shape[0], 1])]) y_edge_pull = np.hstack([pull_edges[:, :, 1], np.tile(None, [pull_edges.shape[0], 1])]) z_edge_pull = np.hstack([pull_edges[:, :, 2], np.tile(None, [pull_edges.shape[0], 1])]) # Now unravel the edges into a 1-d vector for plotting x_edge = np.hstack([x_edge, x_edge_pull.reshape([1, -1])[0]]) y_edge = np.hstack([y_edge, y_edge_pull.reshape([1, -1])[0]]) z_edge = np.hstack([z_edge, z_edge_pull.reshape([1, -1])[0]]) if not (len(x_edge) == len(y_edge) == len(z_edge)): raise exceptions.PlotlyError("The lengths of x_edge, y_edge and " "z_edge are not the same.") # define the lines for plotting lines = graph_objs.Scatter3d( x=x_edge, y=y_edge, z=z_edge, mode='lines', line=graph_objs.scatter3d.Line( color=edges_color, width=1.5 ), showlegend=False ) if mean_dists_are_numbers and show_colorbar is True: return [triangles, lines, colorbar] else: return [triangles, lines]
def trisurf(x, y, z, simplices, show_colorbar, edges_color, scale, colormap=None, color_func=None, plot_edges=False, x_edge=None, y_edge=None, z_edge=None, facecolor=None): """ Refer to FigureFactory.create_trisurf() for docstring """ # numpy import check if not np: raise ImportError("FigureFactory._trisurf() requires " "numpy imported.") points3D = np.vstack((x, y, z)).T simplices = np.atleast_2d(simplices) # vertices of the surface triangles tri_vertices = points3D[simplices] # Define colors for the triangle faces if color_func is None: # mean values of z-coordinates of triangle vertices mean_dists = tri_vertices[:, :, 2].mean(-1) elif isinstance(color_func, (list, np.ndarray)): # Pre-computed list / array of values to map onto color if len(color_func) != len(simplices): raise ValueError("If color_func is a list/array, it must " "be the same length as simplices.") # convert all colors in color_func to rgb for index in range(len(color_func)): if isinstance(color_func[index], str): if '#' in color_func[index]: foo = colors.hex_to_rgb(color_func[index]) color_func[index] = colors.label_rgb(foo) if isinstance(color_func[index], tuple): foo = colors.convert_to_RGB_255(color_func[index]) color_func[index] = colors.label_rgb(foo) mean_dists = np.asarray(color_func) else: # apply user inputted function to calculate # custom coloring for triangle vertices mean_dists = [] for triangle in tri_vertices: dists = [] for vertex in triangle: dist = color_func(vertex[0], vertex[1], vertex[2]) dists.append(dist) mean_dists.append(np.mean(dists)) mean_dists = np.asarray(mean_dists) # Check if facecolors are already strings and can be skipped if isinstance(mean_dists[0], str): facecolor = mean_dists else: min_mean_dists = np.min(mean_dists) max_mean_dists = np.max(mean_dists) if facecolor is None: facecolor = [] for index in range(len(mean_dists)): color = map_face2color(mean_dists[index], colormap, scale, min_mean_dists, max_mean_dists) facecolor.append(color) # Make sure facecolor is a list so output is consistent across Pythons facecolor = np.asarray(facecolor) ii, jj, kk = simplices.T triangles = graph_objs.Mesh3d(x=x, y=y, z=z, facecolor=facecolor, i=ii, j=jj, k=kk, name='') mean_dists_are_numbers = not isinstance(mean_dists[0], str) if mean_dists_are_numbers and show_colorbar is True: # make a colorscale from the colors colorscale = colors.make_colorscale(colormap, scale) colorscale = colors.convert_colorscale_to_rgb(colorscale) colorbar = graph_objs.Scatter3d( x=x[:1], y=y[:1], z=z[:1], mode='markers', marker=dict(size=0.1, color=[min_mean_dists, max_mean_dists], colorscale=colorscale, showscale=True), hoverinfo='None', showlegend=False) # the triangle sides are not plotted if plot_edges is False: if mean_dists_are_numbers and show_colorbar is True: return graph_objs.Data([triangles, colorbar]) else: return graph_objs.Data([triangles]) # define the lists x_edge, y_edge and z_edge, of x, y, resp z # coordinates of edge end points for each triangle # None separates data corresponding to two consecutive triangles is_none = [ii is None for ii in [x_edge, y_edge, z_edge]] if any(is_none): if not all(is_none): raise ValueError("If any (x_edge, y_edge, z_edge) is None, " "all must be None") else: x_edge = [] y_edge = [] z_edge = [] # Pull indices we care about, then add a None column to separate tris ixs_triangles = [0, 1, 2, 0] pull_edges = tri_vertices[:, ixs_triangles, :] x_edge_pull = np.hstack( [pull_edges[:, :, 0], np.tile(None, [pull_edges.shape[0], 1])]) y_edge_pull = np.hstack( [pull_edges[:, :, 1], np.tile(None, [pull_edges.shape[0], 1])]) z_edge_pull = np.hstack( [pull_edges[:, :, 2], np.tile(None, [pull_edges.shape[0], 1])]) # Now unravel the edges into a 1-d vector for plotting x_edge = np.hstack([x_edge, x_edge_pull.reshape([1, -1])[0]]) y_edge = np.hstack([y_edge, y_edge_pull.reshape([1, -1])[0]]) z_edge = np.hstack([z_edge, z_edge_pull.reshape([1, -1])[0]]) if not (len(x_edge) == len(y_edge) == len(z_edge)): raise exceptions.PlotlyError("The lengths of x_edge, y_edge and " "z_edge are not the same.") # define the lines for plotting lines = graph_objs.Scatter3d(x=x_edge, y=y_edge, z=z_edge, mode='lines', line=graph_objs.Line(color=edges_color, width=1.5), showlegend=False) if mean_dists_are_numbers and show_colorbar is True: return graph_objs.Data([triangles, lines, colorbar]) else: return graph_objs.Data([triangles, lines])
def create_choropleth(fips, values, scope=['usa'], binning_endpoints=None, colorscale=None, order=None, simplify_county=0.02, simplify_state=0.02, asp=None, show_hover=True, show_state_data=True, state_outline=None, county_outline=None, centroid_marker=None, round_legend_values=False, exponent_format=False, legend_title='', **layout_options): """ Returns figure for county choropleth. Uses data from package_data. :param (list) fips: list of FIPS values which correspond to the con catination of state and county ids. An example is '01001'. :param (list) values: list of numbers/strings which correspond to the fips list. These are the values that will determine how the counties are colored. :param (list) scope: list of states and/or states abbreviations. Fits all states in the camera tightly. Selecting ['usa'] is the equivalent of appending all 50 states into your scope list. Selecting only 'usa' does not include 'Alaska', 'Puerto Rico', 'American Samoa', 'Commonwealth of the Northern Mariana Islands', 'Guam', 'United States Virgin Islands'. These must be added manually to the list. Default = ['usa'] :param (list) binning_endpoints: ascending numbers which implicitly define real number intervals which are used as bins. The colorscale used must have the same number of colors as the number of bins and this will result in a categorical colormap. :param (list) colorscale: a list of colors with length equal to the number of categories of colors. The length must match either all unique numbers in the 'values' list or if endpoints is being used, the number of categories created by the endpoints.\n For example, if binning_endpoints = [4, 6, 8], then there are 4 bins: [-inf, 4), [4, 6), [6, 8), [8, inf) :param (list) order: a list of the unique categories (numbers/bins) in any desired order. This is helpful if you want to order string values to a chosen colorscale. :param (float) simplify_county: determines the simplification factor for the counties. The larger the number, the fewer vertices and edges each polygon has. See http://toblerity.org/shapely/manual.html#object.simplify for more information. Default = 0.02 :param (float) simplify_state: simplifies the state outline polygon. See http://toblerity.org/shapely/manual.html#object.simplify for more information. Default = 0.02 :param (float) asp: the width-to-height aspect ratio for the camera. Default = 2.5 :param (bool) show_hover: show county hover and centroid info :param (bool) show_state_data: reveals state boundary lines :param (dict) state_outline: dict of attributes of the state outline including width and color. See https://plot.ly/python/reference/#scatter-marker-line for all valid params :param (dict) county_outline: dict of attributes of the county outline including width and color. See https://plot.ly/python/reference/#scatter-marker-line for all valid params :param (dict) centroid_marker: dict of attributes of the centroid marker. The centroid markers are invisible by default and appear visible on selection. See https://plot.ly/python/reference/#scatter-marker for all valid params :param (bool) round_legend_values: automatically round the numbers that appear in the legend to the nearest integer. Default = False :param (bool) exponent_format: if set to True, puts numbers in the K, M, B number format. For example 4000.0 becomes 4.0K Default = False :param (str) legend_title: title that appears above the legend :param **layout_options: a **kwargs argument for all layout parameters Example 1: Florida ``` import plotly.plotly as py import plotly.figure_factory as ff import numpy as np import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'] == 'Florida'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() binning_endpoints = list(np.mgrid[min(values):max(values):4j]) colorscale = ["#030512","#1d1d3b","#323268","#3d4b94","#3e6ab0", "#4989bc","#60a7c7","#85c5d3","#b7e0e4","#eafcfd"] fig = ff.create_choropleth( fips=fips, values=values, scope=['Florida'], show_state_data=True, colorscale=colorscale, binning_endpoints=binning_endpoints, round_legend_values=True, plot_bgcolor='rgb(229,229,229)', paper_bgcolor='rgb(229,229,229)', legend_title='Florida Population', county_outline={'color': 'rgb(255,255,255)', 'width': 0.5}, exponent_format=True, ) py.iplot(fig, filename='choropleth_florida') ``` Example 2: New England ``` import plotly.plotly as py import plotly.figure_factory as ff import pandas as pd NE_states = ['Connecticut', 'Maine', 'Massachusetts', 'New Hampshire', 'Rhode Island'] df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'].isin(NE_states)] colorscale = ['rgb(68.0, 1.0, 84.0)', 'rgb(66.0, 64.0, 134.0)', 'rgb(38.0, 130.0, 142.0)', 'rgb(63.0, 188.0, 115.0)', 'rgb(216.0, 226.0, 25.0)'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() fig = ff.create_choropleth( fips=fips, values=values, scope=NE_states, show_state_data=True ) py.iplot(fig, filename='choropleth_new_england') ``` Example 3: California and Surrounding States ``` import plotly.plotly as py import plotly.figure_factory as ff import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/minoritymajority.csv' ) df_sample_r = df_sample[df_sample['STNAME'] == 'California'] values = df_sample_r['TOT_POP'].tolist() fips = df_sample_r['FIPS'].tolist() colorscale = [ 'rgb(193, 193, 193)', 'rgb(239,239,239)', 'rgb(195, 196, 222)', 'rgb(144,148,194)', 'rgb(101,104,168)', 'rgb(65, 53, 132)' ] fig = ff.create_choropleth( fips=fips, values=values, colorscale=colorscale, scope=['CA', 'AZ', 'Nevada', 'Oregon', ' Idaho'], binning_endpoints=[14348, 63983, 134827, 426762, 2081313], county_outline={'color': 'rgb(255,255,255)', 'width': 0.5}, legend_title='California Counties', title='California and Nearby States' ) py.iplot(fig, filename='choropleth_california_and_surr_states_outlines') ``` Example 4: USA ``` import plotly.plotly as py import plotly.figure_factory as ff import numpy as np import pandas as pd df_sample = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/laucnty16.csv' ) df_sample['State FIPS Code'] = df_sample['State FIPS Code'].apply( lambda x: str(x).zfill(2) ) df_sample['County FIPS Code'] = df_sample['County FIPS Code'].apply( lambda x: str(x).zfill(3) ) df_sample['FIPS'] = ( df_sample['State FIPS Code'] + df_sample['County FIPS Code'] ) binning_endpoints = list(np.linspace(1, 12, len(colorscale) - 1)) colorscale = ["#f7fbff", "#ebf3fb", "#deebf7", "#d2e3f3", "#c6dbef", "#b3d2e9", "#9ecae1", "#85bcdb", "#6baed6", "#57a0ce", "#4292c6", "#3082be", "#2171b5", "#1361a9", "#08519c", "#0b4083","#08306b"] fips = df_sample['FIPS'] values = df_sample['Unemployment Rate (%)'] fig = ff.create_choropleth( fips=fips, values=values, scope=['usa'], binning_endpoints=binning_endpoints, colorscale=colorscale, show_hover=True, centroid_marker={'opacity': 0}, asp=2.9, title='USA by Unemployment %', legend_title='Unemployment %' ) py.iplot(fig, filename='choropleth_full_usa') ``` """ # ensure optional modules imported if not gp or not shapefile or not shapely: raise ImportError( "geopandas, pyshp and shapely must be installed for this figure " "factory.\n\nRun the following commands to install the correct " "versions of the following modules:\n\n" "```\n" "pip install geopandas==0.3.0\n" "pip install pyshp==1.2.10\n" "pip install shapely==1.6.3\n" "```\n" "If you are using Windows, follow this post to properly " "install geopandas and dependencies:" "http://geoffboeing.com/2014/09/using-geopandas-windows/\n\n" "If you are using Anaconda, do not use PIP to install the " "packages above. Instead use conda to install them:\n\n" "```\n" "conda install plotly\n" "conda install geopandas\n" "```" ) df, df_state = _create_us_counties_df(st_to_state_name_dict, state_to_st_dict) fips_polygon_map = dict( zip( df['FIPS'].tolist(), df['geometry'].tolist() ) ) if not state_outline: state_outline = {'color': 'rgb(240, 240, 240)', 'width': 1} if not county_outline: county_outline = {'color': 'rgb(0, 0, 0)', 'width': 0} if not centroid_marker: centroid_marker = {'size': 3, 'color': 'white', 'opacity': 1} # ensure centroid markers appear on selection if 'opacity' not in centroid_marker: centroid_marker.update({'opacity': 1}) if len(fips) != len(values): raise PlotlyError( 'fips and values must be the same length' ) # make fips, values into lists if isinstance(fips, pd.core.series.Series): fips = fips.tolist() if isinstance(values, pd.core.series.Series): values = values.tolist() # make fips numeric fips = map(lambda x: int(x), fips) if binning_endpoints: intervals = utils.endpts_to_intervals(binning_endpoints) LEVELS = _intervals_as_labels(intervals, round_legend_values, exponent_format) else: if not order: LEVELS = sorted(list(set(values))) else: # check if order is permutation # of unique color col values same_sets = sorted(list(set(values))) == set(order) no_duplicates = not any(order.count(x) > 1 for x in order) if same_sets and no_duplicates: LEVELS = order else: raise PlotlyError( 'if you are using a custom order of unique values from ' 'your color column, you must: have all the unique values ' 'in your order and have no duplicate items' ) if not colorscale: colorscale = [] viridis_colors = clrs.colorscale_to_colors( clrs.PLOTLY_SCALES['Viridis'] ) viridis_colors = clrs.color_parser( viridis_colors, clrs.hex_to_rgb ) viridis_colors = clrs.color_parser( viridis_colors, clrs.label_rgb ) viri_len = len(viridis_colors) + 1 viri_intervals = utils.endpts_to_intervals( list(np.linspace(0, 1, viri_len)) )[1:-1] for L in np.linspace(0, 1, len(LEVELS)): for idx, inter in enumerate(viri_intervals): if L == 0: break elif inter[0] < L <= inter[1]: break intermed = ((L - viri_intervals[idx][0]) / (viri_intervals[idx][1] - viri_intervals[idx][0])) float_color = clrs.find_intermediate_color( viridis_colors[idx], viridis_colors[idx], intermed, colortype='rgb' ) # make R,G,B into int values float_color = clrs.unlabel_rgb(float_color) float_color = clrs.unconvert_from_RGB_255(float_color) int_rgb = clrs.convert_to_RGB_255(float_color) int_rgb = clrs.label_rgb(int_rgb) colorscale.append(int_rgb) if len(colorscale) < len(LEVELS): raise PlotlyError( "You have {} LEVELS. Your number of colors in 'colorscale' must " "be at least the number of LEVELS: {}. If you are " "using 'binning_endpoints' then 'colorscale' must have at " "least len(binning_endpoints) + 2 colors".format( len(LEVELS), min(LEVELS, LEVELS[:20]) ) ) color_lookup = dict(zip(LEVELS, colorscale)) x_traces = dict(zip(LEVELS, [[] for i in range(len(LEVELS))])) y_traces = dict(zip(LEVELS, [[] for i in range(len(LEVELS))])) # scope if isinstance(scope, str): raise PlotlyError( "'scope' must be a list/tuple/sequence" ) scope_names = [] extra_states = ['Alaska', 'Commonwealth of the Northern Mariana Islands', 'Puerto Rico', 'Guam', 'United States Virgin Islands', 'American Samoa'] for state in scope: if state.lower() == 'usa': scope_names = df['STATE_NAME'].unique() scope_names = list(scope_names) for ex_st in extra_states: try: scope_names.remove(ex_st) except ValueError: pass else: if state in st_to_state_name_dict.keys(): state = st_to_state_name_dict[state] scope_names.append(state) df_state = df_state[df_state['STATE_NAME'].isin(scope_names)] plot_data = [] x_centroids = [] y_centroids = [] centroid_text = [] fips_not_in_shapefile = [] if not binning_endpoints: for index, f in enumerate(fips): level = values[index] try: fips_polygon_map[f].type (x_traces, y_traces, x_centroids, y_centroids, centroid_text) = _calculations( df, fips, values, index, f, simplify_county, level, x_centroids, y_centroids, centroid_text, x_traces, y_traces, fips_polygon_map ) except KeyError: fips_not_in_shapefile.append(f) else: for index, f in enumerate(fips): for j, inter in enumerate(intervals): if inter[0] < values[index] <= inter[1]: break level = LEVELS[j] try: fips_polygon_map[f].type (x_traces, y_traces, x_centroids, y_centroids, centroid_text) = _calculations( df, fips, values, index, f, simplify_county, level, x_centroids, y_centroids, centroid_text, x_traces, y_traces, fips_polygon_map ) except KeyError: fips_not_in_shapefile.append(f) if len(fips_not_in_shapefile) > 0: msg = ( 'Unrecognized FIPS Values\n\nWhoops! It looks like you are ' 'trying to pass at least one FIPS value that is not in ' 'our shapefile of FIPS and data for the counties. Your ' 'choropleth will still show up but these counties cannot ' 'be shown.\nUnrecognized FIPS are: {}'.format( fips_not_in_shapefile ) ) warnings.warn(msg) x_states = [] y_states = [] for index, row in df_state.iterrows(): if df_state['geometry'][index].type == 'Polygon': x = row.geometry.simplify(simplify_state).exterior.xy[0].tolist() y = row.geometry.simplify(simplify_state).exterior.xy[1].tolist() x_states = x_states + x y_states = y_states + y elif df_state['geometry'][index].type == 'MultiPolygon': x = ([poly.simplify(simplify_state).exterior.xy[0].tolist() for poly in df_state['geometry'][index]]) y = ([poly.simplify(simplify_state).exterior.xy[1].tolist() for poly in df_state['geometry'][index]]) for segment in range(len(x)): x_states = x_states + x[segment] y_states = y_states + y[segment] x_states.append(np.nan) y_states.append(np.nan) x_states.append(np.nan) y_states.append(np.nan) for lev in LEVELS: county_data = dict( type='scatter', mode='lines', x=x_traces[lev], y=y_traces[lev], line=county_outline, fill='toself', fillcolor=color_lookup[lev], name=lev, hoverinfo='none', ) plot_data.append(county_data) if show_hover: hover_points = dict( type='scatter', showlegend=False, legendgroup='centroids', x=x_centroids, y=y_centroids, text=centroid_text, name='US Counties', mode='markers', marker={'color': 'white', 'opacity': 0}, hoverinfo='text' ) centroids_on_select = dict( selected=dict(marker=centroid_marker), unselected=dict(marker=dict(opacity=0)) ) hover_points.update(centroids_on_select) plot_data.append(hover_points) if show_state_data: state_data = dict( type='scatter', legendgroup='States', line=state_outline, x=x_states, y=y_states, hoverinfo='text', showlegend=False, mode='lines' ) plot_data.append(state_data) DEFAULT_LAYOUT = dict( hovermode='closest', xaxis=dict( autorange=False, range=USA_XRANGE, showgrid=False, zeroline=False, fixedrange=True, showticklabels=False ), yaxis=dict( autorange=False, range=USA_YRANGE, showgrid=False, zeroline=False, fixedrange=True, showticklabels=False ), margin=dict(t=40, b=20, r=20, l=20), width=900, height=450, dragmode='select', legend=dict( traceorder='reversed', xanchor='right', yanchor='top', x=1, y=1 ), annotations=[] ) fig = dict(data=plot_data, layout=DEFAULT_LAYOUT) fig['layout'].update(layout_options) fig['layout']['annotations'].append( dict( x=1, y=1.05, xref='paper', yref='paper', xanchor='right', showarrow=False, text='<b>' + legend_title + '</b>' ) ) if len(scope) == 1 and scope[0].lower() == 'usa': xaxis_range_low = -125.0 xaxis_range_high = -55.0 yaxis_range_low = 25.0 yaxis_range_high = 49.0 else: xaxis_range_low = float('inf') xaxis_range_high = float('-inf') yaxis_range_low = float('inf') yaxis_range_high = float('-inf') for trace in fig['data']: if all(isinstance(n, Number) for n in trace['x']): calc_x_min = min(trace['x'] or [float('inf')]) calc_x_max = max(trace['x'] or [float('-inf')]) if calc_x_min < xaxis_range_low: xaxis_range_low = calc_x_min if calc_x_max > xaxis_range_high: xaxis_range_high = calc_x_max if all(isinstance(n, Number) for n in trace['y']): calc_y_min = min(trace['y'] or [float('inf')]) calc_y_max = max(trace['y'] or [float('-inf')]) if calc_y_min < yaxis_range_low: yaxis_range_low = calc_y_min if calc_y_max > yaxis_range_high: yaxis_range_high = calc_y_max # camera zoom fig['layout']['xaxis']['range'] = [xaxis_range_low, xaxis_range_high] fig['layout']['yaxis']['range'] = [yaxis_range_low, yaxis_range_high] # aspect ratio if asp is None: usa_x_range = USA_XRANGE[1] - USA_XRANGE[0] usa_y_range = USA_YRANGE[1] - USA_YRANGE[0] asp = usa_x_range / usa_y_range # based on your figure width = float(fig['layout']['xaxis']['range'][1] - fig['layout']['xaxis']['range'][0]) height = float(fig['layout']['yaxis']['range'][1] - fig['layout']['yaxis']['range'][0]) center = (sum(fig['layout']['xaxis']['range']) / 2., sum(fig['layout']['yaxis']['range']) / 2.) if height / width > (1 / asp): new_width = asp * height fig['layout']['xaxis']['range'][0] = center[0] - new_width * 0.5 fig['layout']['xaxis']['range'][1] = center[0] + new_width * 0.5 else: new_height = (1 / asp) * width fig['layout']['yaxis']['range'][0] = center[1] - new_height * 0.5 fig['layout']['yaxis']['range'][1] = center[1] + new_height * 0.5 return fig