Overlaying traces in Mapbox for Python - python

I am currently working on a Geography project, for which I have to do some research on migration flows.
I want to represent migration flows using Python and Mapbox, based on a worldwide GeoJSON I previously downloaded. However, I am having some issues regarding the quality of the work, and can't find a proper solution.
I first uploaded the world GeoJSON:
countries = json.load(open("countries_without_antartica.geojson"))
I then extracted the coordinates with a function and grouped them into a list named countries_coords, with countries_lons, countries_lats = zip(*countries_coords).
I then start creating the figure.
Firstly, I initiate it:
fig = go.Figure()
Then, I put the information I extracted before into a ScatterMapbox environment:
fig.add_trace(go.Scattermapbox(
mode='lines',
name='Countries',
fill='toself',
fillcolor='lightgray',
line=dict(color='black', width=1),
lat=countries_lats,
lon=countries_lons,
opacity=1,
showlegend=False,
hoverinfo='skip',
))
I then specify the Mapbox style with: fig.update_layout(mapbox=dict(style='white-bg'))
That leaves the map with the GeoJSON data alone, as seen in this image:
The problem, however, starts right here: I then try to add a line to the map, indicating the first migration flow (in this case, from Spain to Australia). I do this with the following code:
fig.add_trace(
go.Scattermapbox(
name='flow1',
lon = [134.340916, -3.704239],
lat = [-25.039402, 40.415887],
mode = 'lines',
line = dict(width = 8,color = 'green')
)
)
However, the resulted figure is this:
I have several problems with that, as the migration flow line should be a somewhat curved line and not a straight one.
I realized the solution to THAT (and only THAT) problem was to use go.Scattergeo instead of go.Scattermapbox to represent the line, and so I did:
fig.add_trace(
go.Scattergeo(
name='flow1',
lon = [134.340916, -3.704239],
lat = [-25.039402, 40.415887],
mode = 'lines',
line = dict(width = 8,color = 'green')
)
)
BUT the line is now "behind" the map itself, so it is not visible (resulting in IMAGE 1 again).
The line with go.Scattergeo IS curved, and it DOES represent what I wanted it to represent, but it is not visible because it is "layered" behind the go.ScatterMapbox figure with the map.
How can I change the order of the traces? Is there a way to prevent the first trace from being "above" the second trace? I tried changing the order of appearance, but nothing worked.
EDIT 1
Following the solutions provided by #NikolasStevenson-Molnar and #BasvanderLinden, I rendered both the world and the migration flow by using go.Scattergeo. Code here:
fig.add_trace(go.Scattergeo(
mode='lines',
name='Countries',
fill='toself',
fillcolor='lightgray',
line=dict(color='black', width=1),
lat=countries_lats,
lon=countries_lons,
opacity=1,
showlegend=False,
hoverinfo='skip',
))
fig.add_trace(
go.Scattergeo(
name='flow1',
lon = [134.340916, -3.704239],
lat = [-25.039402, 40.415887],
mode = 'lines',
line = dict(width = 8,color = 'green')
)
)
Here, the result:
As you can see, the map is not as "great" as it should be. Some issues regarding it's quality are:
The countries are filled with the same colors as the background (i.e. the oceans). I cannot find a way to fill only de countries. While using go.Scattermapbox this was easily done by specifying the desired style (fig.update_layout(mapbox=dict(style='white-bg'))). However, 'go.Scattergeo' does not have that functionality.
The map seems to be outstretched horizontally (see how all the countries are way more wide in IMAGE 3 compared to IMAGE 1). This is particularly visible in the northern hemisphere.
It then occurred to me that issue 1 should be solved by "turning off" the filling atributes, so I coded:
fig.add_trace(go.Scattergeo(
mode='lines',
name='Countries',
line=dict(color='black', width=1),
lat=countries_lats,
lon=countries_lons,
opacity=1,
showlegend=False,
hoverinfo='skip',
))
Result is, again, not desirable, because the GeoJSON is plot above the default map that 'go.Scattergeo` provides. For example, when I zoom in into Spain, I get: Clearly, the two traces (default and GeoJSON) are operating at the same time, making the final result not-so-tidy. On top of that, the default trace just shows "territory", but not "political division", so -for example- Portugal is not drawn in the default trace but it is in the GeoJSON.
Hope this extra information is valuable to reach a proper solution.
Thank you in advance, for any help, advice, or solution you might give me.

you can go back to basics and calculate your own great circle line
https://geographiclib.sourceforge.io/html/python/examples.html#basic-geodesic-calculations has an example of how to achieve this
bringing it together
source countries GeoJSON and create a geopandas dataframe
use centroid capability to have data for the centre of a country
build utility function to calculate a great circle trace
finally show it in action with lines between three pairs of countries
import requests
import geopandas as gpd
import plotly.express as px
from pathlib import Path
from zipfile import ZipFile
import json, io
from geographiclib.geodesic import Geodesic
import math
# source geojson for country boundaries so we can calc centroids
geosrc = pd.json_normalize(
requests.get(
"https://pkgstore.datahub.io/core/geo-countries/7/datapackage.json"
).json()["resources"]
)
fn = Path(geosrc.loc[geosrc["name"].eq("geo-countries_zip"), "path"].values[0]).name
if not Path.cwd().joinpath(fn).exists():
r = requests.get(
geosrc.loc[geosrc["name"].eq("geo-countries_zip"), "path"].values[0],
stream=True,
)
with open(fn, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
zfile = ZipFile(fn)
with zfile.open(zfile.infolist()[0]) as f:
geojson = json.load(f)
gdf = gpd.GeoDataFrame.from_features(geojson).set_index("ISO_A3")
# centroids...
gdf["lon"] = gdf.apply(lambda r: r.geometry.centroid.x, axis=1)
gdf["lat"] = gdf.apply(lambda r: r.geometry.centroid.y, axis=1)
def worldcircleline(gdf, country1, country2, fig=None, color="blue"):
geod = Geodesic.WGS84 # define the WGS84 ellipsoid
l = geod.InverseLine(
gdf.loc[country1, "lat"],
gdf.loc[country1, "lon"],
gdf.loc[country2, "lat"],
gdf.loc[country2, "lon"],
Geodesic.LATITUDE | Geodesic.LONGITUDE,
)
da = 1
n = int(math.ceil(l.a13 / da))
da = l.a13 / n
lat = [
l.ArcPosition(
da * i, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL
)["lat2"]
for i in range(n + 1)
]
lon = [
l.ArcPosition(
da * i, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL
)["lon2"]
for i in range(n + 1)
]
tfig = px.line_mapbox(
lat=lat,
lon=lon,
mapbox_style="carto-positron",
zoom=1,
).update_traces(line={"color":color})
if fig is None:
return tfig.update_layout(margin={"l": 0, "r": 0, "b": 0, "t": 0})
else:
return fig.add_traces(tfig.data)
fig = worldcircleline(gdf, "ESP", "AUS")
worldcircleline(gdf, "GBR", "SGP", fig=fig, color="red")
worldcircleline(gdf, "IRL", "USA", fig=fig, color="green")

Related

Altair choropleth map visualization issue

I'm quite the beginner, so it might be something obvious.
I tried making a choropleth map, using this guide with Python's Altair package.
All code runs fine, except the very last one. Basically the Altair chart titled "choro" that should "fill" the administrative divisons on my map with colours, doesn't do anything. The resulting map if I overlay it on the background, is identical to the background itself, except for a bit different border strokes. I do not need labels, so I left out that one.
This is my code to be precise:
choro = alt.Chart(choro_data).mark_geoshape(
fill='lightgray',
stroke='black'
).encode(
alt.Color('properties.unemp',
type='quantitative',
scale=alt.Scale(scheme='bluegreen'),
title = "Unemployment by county")
).project(
type= 'mercator',
scale= 2000,
center= [20,47],
clipExtent= [[0, 0], [400, 300]]
)
The type of 'unemp' is float, if it is relevant.
What is the problem? Is the guide dated? Do I miss something obvious?
have used California geometry and unemployment data
two issues I found with altair
skip fill='lightgray'
projection generates errors whenever passing anything other than type
import geopandas as gpd
import pandas as pd
import requests
import altair as alt
# california counties
gdf = gpd.read_file(
"https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/california-counties.geojson"
)
# california un employment
df = pd.json_normalize(
requests.get("https://data.edd.ca.gov/resource/e6gw-gvii.json").json()
).loc[lambda d: d["area_type"].eq("County") & d["seasonally_adjusted_y_n"].eq("N")]
# make names consistent between geometry and unemployment data
df["name"] = df["area_name"].str.split(" ").apply(lambda l: " ".join(l[0:-1]))
df["unemp"] = pd.to_numeric(df["unemployment_rate"])
# join geometry and unemployment data
gdf = gdf.merge(df, on="name")
geojson = gdf.loc[:, ["name", "unemp", "geometry"]].__geo_interface__
choro_data = alt.Data(values=geojson["features"])
choro = (
alt.Chart(choro_data)
.mark_geoshape(stroke="black")
.encode(
alt.Color(
"properties.unemp",
type="quantitative",
scale=alt.Scale(scheme="bluegreen"),
title="Unemployment by county",
)
)
.project(
type="mercator",
# scale=2000, center=[20, 47], clipExtent=[[0, 0], [400, 300]]
)
)
choro

Multiple opacities in Mapbox - Plotly for Python

I am currently working on a Data Visualization project.
I want to plot multiple lines (about 200k) that represent travels from one Subway Station to all the others. This is, all the subway stations should be connected by a straight line.
The color of the line doesn't really matter (it could well be red, blue, etc.), but opacity is what matters the most. The bigger the number of travels between two random stations, the more opacity of that particular line; and vice versa.
I feel I am close to the desired output, but can't figure a way to do it properly.
The DataFrame I am using (df = pd.read_csv(...)) consists of a series of columns, namely: id_start_station, id_end_station, lat_start_station, long_start_station, lat_end_station, long_end_station, number_of_journeys.
I got to extract the coordinates by coding
lons = []
lons = np.empty(3 * len(df))
lons[::3] = df['long_start_station']
lons[1::3] = df['long_end_station']
lons[2::3] = None
lats = []
lats = np.empty(3 * len(df))
lats[::3] = df['lat_start_station']
lats[1::3] = df['lat_end_station']
lats[2::3] = None
I then started a figure by:
fig = go.Figure()
and then added a trace by:
fig.add_trace(go.Scattermapbox(
name='Journeys',
lat=lats,
lon=lons,
mode='lines',
line=dict(color='red', width=1),
opacity= ¿?, # PROBLEM IS HERE [1]
))
[1] So I tried a few different things to pass a opacity term:
I created a new tuple for the opacity of each trace, by:
opacity = []
opacity = np.empty(3 * len(df))
opacity [::3] = df['number_of_journeys'] / max(df['number_of_journeys'])
opacity [1::3] = df['number_of_journeys'] / max(df['number_of_journeys'])
opacity [2::3] = None
and passed it into [1], but this error came out:
ValueError:
Invalid value of type 'numpy.ndarray' received for the 'opacity' property of scattermapbox
The 'opacity' property is a number and may be specified as:
- An int or float in the interval [0, 1]
I then thought of passing the "opacity" term into the "color" term, by using rgba's property alpha, such as: rgba(255,0,0,0.5).
So I first created a "map" of all alpha parameters:
df['alpha'] = df['number_of_journeys'] / max(df['number_of_journeys'])
and then created a function to retrieve all the alpha parameters inside a specific color:
colors_with_opacity = []
def colors_with_opacity_func(df, empty_list):
for alpha in df['alpha']:
empty_list.extend(["rgba(255,0,0,"+str(alpha)+")"])
empty_list.extend(["rgba(255,0,0,"+str(alpha)+")"])
empty_list.append(None)
colors_with_opacity_func(df, colors_with_opacity)
and passed that into the color atribute of the Scattermapbox, but got the following error:
ValueError:
Invalid value of type 'builtins.list' received for the 'color' property of scattermapbox.line
The 'color' property is a color and may be specified as:
- A hex string (e.g. '#ff0000')
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
- A named CSS color:
aliceblue, antiquewhite, aqua, [...] , whitesmoke,
yellow, yellowgreen
Since it is a massive amount of lines, looping / iterating through traces will carry out performance issues.
Any help will be much appreciated. I can't figure a way to properly accomplish that.
Thank you, in advance.
EDIT 1 : NEW QUESTION ADDED
I add this question here below as I believe it can help others that are looking for this particular topic.
Following Rob's helpful answer, I managed to add multiple opacities, as specified previously.
However, some of my colleagues suggested a change that would improve the visualization of the map.
Now, instead of having multiple opacities (one for each trace, according to the value of the dataframe) I would also like to have multiple widths (according to the same value of the dataframe).
This is, following Rob's answer, I would need something like this:
BINS_FOR_OPACITY=10
opacity_a = np.geomspace(0.001,1, BINS_FOR_OPACITY)
BINS_FOR_WIDTH=10
width_a = np.geomspace(1,3, BINS_FOR_WIDTH)
fig = go.Figure()
# Note the double "for" statement that follows
for opacity, d in df.groupby(pd.cut(df["number_of_journeys"], bins=BINS_FOR_OPACITY, labels=opacity_a)):
for width, d in df.groupby(pd.cut(df["number_of_journeys"], bins=BINS_FOR_WIDTH, labels=width_a)):
fig.add_traces(
go.Scattermapbox(
name=f"{d['number_of_journeys'].mean():.2E}",
lat=np.ravel(d.loc[:,[c for c in df.columns if "lat" in c or c=="none"]].values),
lon=np.ravel(d.loc[:,[c for c in df.columns if "long" in c or c=="none"]].values),
line_width=width
line_color="blue",
opacity=opacity,
mode="lines+markers",
)
)
However, the above is clearly not working, as it is making much more traces than it should do (I really can't explain why, but I guess it might be because of the double loop forced by the two for statements).
It ocurred to me that some kind of solution could be hidding in the pd.cut part, as I would need something like a double cut, but couldn't find a way to properly doing it.
I also managed to create a Pandas series by:
widths = pd.cut(df.["size"], bins=BINS_FOR_WIDTH, labels=width_a)
and iterating over that series, but got the same result as before (an excess of traces).
To emphasize and clarify myself, I don't need to have only multiple opacities or multiple widths, but I need to have them both and at the same time, which is what's causing me some troubles.
Again, any help is deeply thanked.
opacity is per trace, for markers it can be done with color using rgba(a,b,c,d) but not for lines. (Same in straight scatter plots)
to demonstrate, I have used London Underground stations (filtered to reduce number of nodes). Plus gone to extra effort of formatting data as a CSV. JSON as source has nothing to do with solution
encoded to bin number_of_journeys for inclusion into a trace with a geometric progression used for calculating and opacity
this sample data set is generating 83k sample lines
import requests
import geopandas as gpd
import plotly.graph_objects as go
import itertools
import numpy as np
import pandas as pd
from pathlib import Path
# get geometry of london underground stations
gdf = gpd.GeoDataFrame.from_features(
requests.get(
"https://raw.githubusercontent.com/oobrien/vis/master/tube/data/tfl_stations.json"
).json()
)
# limit to zone 1 and stations that have larger number of lines going through them
gdf = gdf.loc[gdf["zone"].isin(["1","2","3","4","5","6"]) & gdf["lines"].apply(len).gt(0)].reset_index(
drop=True
).rename(columns={"id":"tfl_id", "name":"id"})
# wanna join all valid combinations of stations...
combis = np.array(list(itertools.combinations(gdf.index, 2)))
# generate dataframe of all combinations of stations
gdf_c = (
gdf.loc[combis[:, 0], ["geometry", "id"]]
.assign(right=combis[:, 1])
.merge(gdf.loc[:, ["geometry", "id"]], left_on="right", right_index=True, suffixes=("_start_station","_end_station"))
)
gdf_c["lat_start_station"] = gdf_c["geometry_start_station"].apply(lambda g: g.y)
gdf_c["long_start_station"] = gdf_c["geometry_start_station"].apply(lambda g: g.x)
gdf_c["lat_end_station"] = gdf_c["geometry_end_station"].apply(lambda g: g.y)
gdf_c["long_end_station"] = gdf_c["geometry_end_station"].apply(lambda g: g.x)
gdf_c = gdf_c.drop(
columns=[
"geometry_start_station",
"right",
"geometry_end_station",
]
).assign(number_of_journeys=np.random.randint(1,10**5,len(gdf_c)))
gdf_c
f = Path.cwd().joinpath("SO.csv")
gdf_c.to_csv(f, index=False)
# there's an requirement to start with a CSV even though no sample data has been provided, now we're starting with a CSV
df = pd.read_csv(f)
# makes use of ravel simpler...
df["none"] = None
# now it's simple to generate scattermapbox... a trace per required opacity
BINS=10
opacity_a = np.geomspace(0.001,1, BINS)
fig = go.Figure()
for opacity, d in df.groupby(pd.cut(df["number_of_journeys"], bins=BINS, labels=opacity_a)):
fig.add_traces(
go.Scattermapbox(
name=f"{d['number_of_journeys'].mean():.2E}",
lat=np.ravel(d.loc[:,[c for c in df.columns if "lat" in c or c=="none"]].values),
lon=np.ravel(d.loc[:,[c for c in df.columns if "long" in c or c=="none"]].values),
line_color="blue",
opacity=opacity,
mode="lines+markers",
)
)
fig.update_layout(
mapbox={
"style": "carto-positron",
"center": {'lat': 51.520214996769255, 'lon': -0.097792388774743},
"zoom": 9,
},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
)

How to get data about state which is currently hovered? Plotly Choropleth - USA map

I created an interactive map of individual US states. The map will contain information on electric vehicles in the US. Currently, it is colored depending on the range (average in kilometers) of a given vehicle.
Here is my code:
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('https://gist.githubusercontent.com/AlbertKozera/6396b4333d1a9222193e11401069ed9a/raw/ab8733a2135bcf61999bbcac4f92e0de5fd56794/Pojazdy%2520elektryczne%2520w%2520USA.csv')
for col in df.columns:
df[col] = df[col].astype(str)
df['range'] = pd.to_numeric(df['range'])
df_range = df.drop(columns = ['state', 'brand', 'model', 'year of production', 'type']).groupby('code', as_index=False)
df_range_mean = df_range.agg({'range':'mean'})
fig = go.Figure(data=go.Choropleth(
locations=df['code'].drop_duplicates(keep='first').reset_index(drop=True),
z = round(df_range_mean['range'], 2),
locationmode='USA-states',
colorscale='Reds',
autocolorscale=False,
marker_line_color='black',
))
fig.update_layout(
geo = dict(
scope='usa',
projection=go.layout.geo.Projection(type = 'albers usa'),
showlakes=True, # lakes
lakecolor='rgb(255, 255, 255)'),
)
fig.show()
It looks like this:
Here is my question:
I need to dynamically return information about the given state in which the mouse cursor is currently located. Unfortunately, I don't know how to do it and whether it is possible at all. I have to implement a method that will display a different image (chernoff face) depending on what state is currently highlighted by the user.
Can anyone tell me if there is any method that will return data about the currently highlighted state? Or maybe, unfortunately - I will have to write my own listener.
I was searching such a method in documentation but I couldn't find it.
The argument locations=df['state'] into the go.Choropleth function should return the abbreviation of the state you refer whenever you point the cursor.

Adding X-Y offsets to data points

I'm looking for a way to specify an X-Y offset to plotted data points. I'm just getting into Altair, so please bear with me.
The situation: I have a dataset recording daily measurements for 30 people. Every person can register several different types of measurements every day.
Example dataset & plot, with 2 people and 2 measurement types:
import pandas as pd
df = pd.DataFrame.from_dict({"date": pd.to_datetime(pd.date_range("2019-12-01", periods=5).repeat(4)),
"person": pd.np.tile(["Bob", "Amy"], 10),
"measurement_type": pd.np.tile(["score_a", "score_a", "score_b", "score_b"], 5),
"value": 20.0*np.random.random(size=20)})
import altair as alt
alt.Chart(df, width=600, height=100) \
.mark_circle(size=150) \
.encode(x = "date",
y = "person",
color = alt.Color("value"))
This gives me this graph:
In the example above, the 2 measurement types are plotted on top of each other. I would like to add an offset to the circles depending on the "measurement_type" column, so that they can all be made visible around the date-person location in the graph.
Here's a mockup of what I want to achieve:
I've been searching the docs but haven't figured out how to do this - been experimenting with the "stack" option, with the dx and dy options, ...
I have a feeling this should just be another encoding channel (offset or alike), but that doesn't exist.
Can anyone point me in the right direction?
There is currently no concept of an offset encoding in Altair, so the best approach to this will be to combine a column encoding with a y encoding, similar to the Grouped Bar Chart example in Altair's documentation:
alt.Chart(df,
width=600, height=100
).mark_circle(
size=150
).encode(
x = "date",
row='person',
y = "measurement_type",
color = alt.Color("value")
)
You can then fine-tune the look of the result using standard chart configuration settings:
alt.Chart(df,
width=600, height=alt.Step(25)
).mark_circle(
size=150
).encode(
x = "date",
row='person',
y = alt.Y("measurement_type", title=None),
color = alt.Color("value")
).configure_facet(
spacing=10
).configure_view(
strokeOpacity=0
)
Well I don't know what result you are getting up until know, but maybe write a function whith parameters likedef chart(DotsOnXAxis, FirstDotsOnYAxis, SecondDotsOnYAxis, OffsetAmount)
and then put those variables on the right place.
If you want an offset with the dots maybe put in a system like: SecondDotsOnYAxis = FirstDotsOnYAxis + OffsetAmount

Folium Choropleth map marker is not working

I'm trying to follow the blog post from Domino lab, Creating interactive crime maps with Folium. And I found that the code base is too old to run the Folium's Choropleth map marker. Although older version on Domino platform seems working (2015), the latest Ipython notebook doesn't work. So I'm guessing Folium changed something on markers? I tried to find the update but I can't find it. Are anyone familiar with this library? If so please give me advices.
My code below:
from IPython.display import HTML
def display(m, height=500):
"""Takes a folium instance and embed HTML."""
m._build_map()
srcdoc = m.HTML.replace('"', '"')
embed = HTML('<iframe srcdoc="{0}" '
'style="width: 100%; height: {1}px; '
'border: none"></iframe>'.format(srcdoc, height))
return embed
import folium
import pandas as pd
SF_COORDINATES = (37.76, -122.45)
crimedata = pd.read_csv('data/SFPD_Incidents_-_Current_Year__2015_.csv')
#for speed purposes
MAX_RECORDS = 1000
#create empty map zoomed in on San Francisco
map = folium.Map(location=SF_COORDINATES, zoom_start=12)
#add a marker for every record in the filtered data, use a clustered view
for each in crimedata[0:MAX_RECORDS].iterrows():
map.simple_marker(
location = [each[1]['Y'],each[1]['X']],
clustered_marker = True)
display(map)
#definition of the boundaries in the map
district_geo = r'data/sfpddistricts.json'
#calculating total number of incidents per district
crimedata2 = pd.DataFrame(crimedata['PdDistrict'].value_counts().astype(float))
crimedata2.to_json('data/crimeagg.json')
crimedata2 = crimedata2.reset_index()
crimedata2.columns = ['District', 'Number']
#creation of the choropleth
map1 = folium.Map(location=SF_COORDINATES, zoom_start=12)
map1.geo_json(geo_path = district_geo,
data_out = 'data/crimeagg.json',
data = crimedata2,
columns = ['District', 'Number'],
key_on = 'feature.properties.DISTRICT',
fill_color = 'YlOrRd',
fill_opacity = 0.7,
line_opacity = 0.2,
legend_name = 'Number of incidents per district')
display(map1)
Not sure if you mean markers (popups) or the choropleth method itself isn't working?
The map1.geo_json() method is deprecated (see here).
Instead, try map1.choropleth(geo_path = district_geo,
data_out = 'data/crimeagg.json',
data = crimedata2,
columns = ['District', 'Number'],
key_on = 'feature.properties.DISTRICT',
fill_color = 'YlOrRd',
fill_opacity = 0.7,
line_opacity = 0.2,
legend_name = 'Number of incidents per district')
The map.choropleth method worked for me, but don't know if they fixed the popup issue for choropleth maps. Hope this helps!
The mapObject.choropleth method is being depricated.
folium.GeoJson is the suggested method as per this github issue: https://github.com/python-visualization/folium/issues/589
A comment in that issue links to this example, which shows how to build the choropleth:
http://nbviewer.jupyter.org/github/python-visualization/folium/blob/master/examples/GeoJSON_and_choropleth.ipynb?flush_cache=true
TLDR
replace geo_json with GeoJson
and for the args like fill_color, use fillColor: <hex_color> in the style_function dictionary kwarg.

Categories