Home Machine Learning Visualizing Routes on Interactive Maps with Python: Half 1 | by Carlos J. Uribe | Jan, 2024

Visualizing Routes on Interactive Maps with Python: Half 1 | by Carlos J. Uribe | Jan, 2024

0
Visualizing Routes on Interactive Maps with Python: Half 1 | by Carlos J. Uribe | Jan, 2024

[ad_1]

In case you aren’t following this text collection, let me level out that we’re following an agile strategy within the creation of a call help system, so we construct it incrementally in so-called “sprints”. This entails we first need to arrive at a working prototype that solves the minimal worthwhile drawback for a visit planning system. This prototype can’t be thought of full till we’ve got a minimalist visualization performance, therefore this text. As soon as we’ve got the prototype prepared, i.e., as soon as the primary model of the system is usable in a method that provides marginal worth, we are able to begin perfecting its implementation to make it straightforward to increase, so we are able to add to the system extra practical options of the true, extra basic drawback. Thus, we’re now in a adequate method — not the definitive method — to show a route on a map in some significant method. The extra particular method will come partially 2 and half 3, the place we’ll refactor the code created right here so it follows an object-oriented design (customized sorts and courses) that may higher deal with the rising complexity of the routes that emerge from the addition of extra particulars and necessities to the journey planning drawback. However for now, this text (half 1) makes an attempt to reveal a practical and faster method of making interactive routes with Python. Let’s see how.

3.1 Displaying websites

We first have to create a map of Paris. We will middle it on the common location of our group of web sites:

avg_location = df_sites[['latitude', 'longitude']].imply()
map_paris = folium.Map(location=avg_location, zoom_start=13)

Then, to show the websites on high of it, we create one marker for every of the websites, and add it to the map. Markers solely want a location, however to shortly acknowledge which web site is at every marker, we go the positioning title to the tooltip attribute, so we see a pop-up with the title of the positioning any time we hover over the markers:

for web site in df_sites.itertuples():
marker = folium.Marker(location=(web site.latitude, web site.longitude),
tooltip=web site.web site)
marker.add_to(map_paris)

map_paris

Determine 6.1. Website markers on high of the map

After all, you’ll be able to zoom out and in, and transfer across the map if you must, with the + | - buttons within the upper-left nook and by click-and-drag.

The websites we simply displayed aren’t actually ordered, however let’s fake they’re. Let’s say the dataframe comprises a route within the metropolis, the place the numbers within the index point out the go to order of these websites:

df_route = df_sites.copy()
df_route.index.title = 'visit_order'

df_route

Now the websites are “related” in a particular order (the go to order), so we must also signify that truth within the map by including traces connecting consecutive stops within the route. These traces, or extra exactly, “segments”, are created with folium.PolyLine objects. So as to add them to the map in a single go we create some extra columns in df_route, holding the knowledge of the “subsequent stops”, so every cease is mapped to its following cease, constituting a route phase. That method, every row can retailer information about each the stops and the segments of the route.

df_route_segments = df_route.be part of(
df_route.shift(-1), # map every cease to its subsequent cease
rsuffix='_next'
).dropna() # final cease has no "subsequent one", so drop it

df_route_segments

For a set row, the primary three columns maintain info for the “present” web site, and the following three columns maintain info for the “subsequent web site” within the route. This enables us to create a marker and a phase in the identical for-loop iteration:

map_paris = folium.Map(location=avg_location, zoom_start=13)

for cease in df_route_segments.itertuples():
# marker for present cease
marker = folium.Marker(location=(cease.latitude, cease.longitude),
tooltip=cease.web site)
# line for the route phase connecting present to subsequent cease
line = folium.PolyLine(
places=[(stop.latitude, stop.longitude),
(stop.latitude_next, stop.longitude_next)],
tooltip=f"{cease.web site} to {cease.site_next}",
)
# add parts to the map
marker.add_to(map_paris)
line.add_to(map_paris)

# maker for final cease wasn't added in for loop, so including it now
folium.Marker(location=(cease.latitude_next, cease.longitude_next),
tooltip=cease.site_next).add_to(map_paris);

map_paris

Determine 6.2. Website markers related via traces

Now the order through which the stops are visited is evident, and while you hover over every line with the mouse you see which two stops the road is connecting. The identical is true for markers, the names of the websites they reference are displayed when hovering over them.

3.3 Enriching the map with interactive info

That is all effectively and good to get a tough thought of what the route appears like, however we are able to make the map a bit extra insightful with little further effort by including extra info that may be displayed interactively. For starters, the place does the route begin? With the present view, it’s not easy which of the websites is the “preliminary one”. There are two markers which are related to just one phase, so it should be one in every of these, however we’d have to take a look at the dataframe to differentiate the “begin” from the “end” web site. A greater method is to point out the preliminary web site with a particular icon in order that we are able to acknowledge it at a look. We try this by including a folium.Icon object to every marker, making the preliminary web site have a definite icon:

map_paris = folium.Map(location=avg_location, zoom_start=13)

for cease in df_route_segments.itertuples():
initial_stop = cease.Index == 0
# icon for present cease
icon = folium.Icon(icon='house' if initial_stop else 'info-sign',
shade='cadetblue' if initial_stop else 'crimson')
# marker for present cease
marker = folium.Marker(location=(cease.latitude, cease.longitude),
icon=icon, tooltip=cease.web site)
# line for the route phase connecting present to subsequent cease
line = folium.PolyLine(
places=[(stop.latitude, stop.longitude),
(stop.latitude_next, stop.longitude_next)],
tooltip=f"{cease.web site} to {cease.site_next}",
)
# add parts to the map
marker.add_to(map_paris)
line.add_to(map_paris)

# When for loop ends, the cease variable has the second-to-last
# cease within the route, so the marker for the final cease is lacking
# We add it now utilizing the "subsequent" columns of the final row
folium.Marker(
location=(cease.latitude_next, cease.longitude_next),
tooltip=cease.site_next,
icon = folium.Icon(icon='info-sign', shade='crimson')
).add_to(map_paris);

map_paris # present map

Determine 6.3. Route with cease markers coloured by sort

At a look, it’s clear now that the route begins on the marker with a “house” icon. Let’s exploit the interactivity just a little additional by exhibiting some extra info on the map that helps us perceive the route even higher. The essential information we’ll add is the distances between stops and the go to order variety of every cease. Since we’ve got in every row of df_route_segments each the beginning and ending factors of segments, we are able to simply add a column with the space between consecutive stops.

For readers within the present article solely (i.e., not prior to now articles), I’ve declared the perform ellipsoidal_distance beneath. If, quite the opposite, you’ve got adopted alongside within the article collection, you already find out about that perform, as we developed it (and made the case for it) in the article on computing distance matrices. Be at liberty to re-use that perform right here, to keep away from duplicated code. To get the space in meters between location loc1 and placement loc2, you simply have to:

from geoutils import GeoAnalyzer

distance = GeoAnalyzer.ellipsoidal_distance(loc1, loc2)

from geopy.distance import geodesic

_Location = Tuple[float, float]

def ellipsoidal_distance(point1: _Location, point2: _Location) -> float:
"""Calculate ellipsoidal distance (in meters) between point1 and
point2 the place every level is represented as a tuple (lat, lon)"""
return geodesic(point1, point2).meters

The perform ellipsoidal_distance accepts two places (two tuples of coordinates) and returns the geodesic distance between them, in meters. For a extra in-depth rationalization and justification of the geodesic distance, I invite you to learn the article the place we created it:

We will add the brand new column 'distance_seg' as a result of we’ve got each endpoints of the phase in every row:

df_route_segments['distance_seg'] = df_route_segments.apply(
lambda cease: ellipsoidal_distance(
(cease.latitude, cease.longitude),
(cease.latitude_next, cease.longitude_next)),
axis=1
)

df_route_segments

Having this new column, we are able to take their values and embrace them within the string we go to tooltip when creating the traces of the map, thereby making every phase’s distance accessible on the tip of the cursor. Whereas we’re at it, let’s add the “cease numbers” to the markers too, in order that extra context is given to every marker on the general route:

map_paris = folium.Map(location=avg_location, zoom_start=13)

for cease in df_route_segments.itertuples():
initial_stop = cease.Index == 0
# marker for present cease
icon = folium.Icon(icon='house' if initial_stop else 'info-sign',
shade='cadetblue' if initial_stop else 'crimson')
marker = folium.Marker(
location=(cease.latitude, cease.longitude),
icon=icon,
# show the title and cease quantity at every web site's marker
tooltip=f"<b>Title</b>: {cease.web site} <br>"
+ f"<b>Cease quantity</b>: {cease.Index} <br>"
)
# line for the route phase connecting present to subsequent cease
line = folium.PolyLine(
places=[(stop.latitude, stop.longitude),
(stop.latitude_next, stop.longitude_next)],
# show the beginning, finish, and distance of every phase
tooltip=f"<b>From</b>: {cease.web site} <br>"
+ f"<b>To</b>: {cease.site_next} <br>"
+ f"<b>Distance</b>: {cease.distance_seg:.0f} m",
)
# add parts to the map
marker.add_to(map_paris)
line.add_to(map_paris)

# add route's final marker, because it wasn't included in for loop
folium.Marker(
location=(cease.latitude_next, cease.longitude_next),
tooltip=f"<b>Title</b>: {cease.site_next} <br>"
+ f"<b>Cease quantity</b>: {cease.Index + 1} <br>",
icon = folium.Icon(icon='info-sign', shade='crimson')
).add_to(map_paris);

map_paris # present map

Determine 6.4. Interactive info popping up when hovering over a marker

Word how we’ve used a little bit of HTML to render the “hover textual content” displayed on markers and features extra properly.

Determine 6.5. Interactive info popping up when hovering over a route phase

The present conduct appears basic sufficient to be encapsulated into some helper features:

def _make_route_segments_df(df_route: pd.DataFrame) -> pd.DataFrame:
"""Given a dataframe whose rows are ordered stops in a route,
and the place the index has integers representing the go to order of these
stops, return a dataframe having new columns with the knowledge of
every cease's subsequent web site"""
df_route_segments = df_route.be part of(
df_route.shift(-1), # map every cease to its subsequent
rsuffix='_next').dropna()

df_route_segments['distance_seg'] = df_route_segments.apply(
lambda cease: ellipsoidal_distance(
(cease.latitude, cease.longitude),
(cease.latitude_next, cease.longitude_next)
), axis=1
)
return df_route_segments

def plot_route_on_map(df_route: pd.DataFrame) -> folium.Map:
"""Takes a dataframe of a route and shows it on a map, including
a marker for every cease and a line for every pair of consecutive
stops"""
df_route_segments = _make_route_segments_df(df_route)

# create empty map
avg_location = df_route[['latitude', 'longitude']].imply()
map_route = folium.Map(location=avg_location, zoom_start=13)

for cease in df_route_segments.itertuples():
initial_stop = cease.Index == 0
# marker for present cease
icon = folium.Icon(icon='house' if initial_stop else 'info-sign',
shade='cadetblue' if initial_stop else 'crimson')
marker = folium.Marker(
location=(cease.latitude, cease.longitude),
icon=icon,
tooltip=f"<b>Title</b>: {cease.web site} <br>"
+ f"<b>Cease quantity</b>: {cease.Index} <br>"
)
# line for the route phase connecting present to subsequent cease
line = folium.PolyLine(
places=[(stop.latitude, stop.longitude),
(stop.latitude_next, stop.longitude_next)],
# add to every line its begin, finish, and distance
tooltip=f"<b>From</b>: {cease.web site} <br>"
+ f"<b>To</b>: {cease.site_next} <br>"
+ f"<b>Distance</b>: {cease.distance_seg:.0f} m",
)
# add parts to the map
marker.add_to(map_route)
line.add_to(map_route)

# When for loop ends, the cease variable has the second-to-last cease in
# the route, so the marker for the final cease is lacking, and we add it
# now utilizing the "subsequent" columns of the final row
folium.Marker(
location=(cease.latitude_next, cease.longitude_next),
tooltip=f"<b>Title</b>: {cease.site_next} <br>"
+ f"<b>Cease quantity</b>: {cease.Index + 1} <br>",
icon = folium.Icon(icon='info-sign', shade='crimson')
).add_to(map_route)

return map_route

3.4 Coping with closed routes, a.ok.a. excursions

What occurs if we’ve got a dataframe df_route representing a closed tour, i.e., a route that begins and ends on the similar web site? How will the map mirror that? Let’s construct such a dataframe and take a look at it. We take the earlier df_route, having an open route, and add a brand new row on the finish with the identical information as within the first row, thus making the route closed:

df_route_closed = pd.concat(
[df_route, df_route.head(1)], ignore_index=True
)
df_route_closed.index.title = df_route.index.title

df_route_closed

Because of setting ignore_index=True, the index quantity, which represents the go to order, has been incremented in a single unit robotically. Now we simply proceed as earlier than, creating the map, however this time with our new helper perform plot_route_on_map:

plot_route_on_map(df_route_closed)
Determine 6.6. The blue marker indicating the preliminary web site is buried by the crimson marker of the ultimate web site

It really works nearly fantastic, as a result of the brand new line representing the “closing” phase is created because it ought to, however there’s an issue with the markers: a brand new crimson marker is added on high of the beforehand added blue marker for the preliminary web site, hiding it. That is merely how Folium works, including parts on high of parts (discover how the marker’s shadow is darker for the ‘resort’ than for the opposite markers, a delicate method of claiming that there’s multiple marker in that location). To keep away from dropping that useful blue “house” icon indicating the beginning cease of the route, we have to skip including the final marker (after the for-loop ends) solely when the final cease coincides with the beginning web site of the route. We will try this by checking the situation instantly, on df_route, and by solely including this final marker if the route is just not closed:

# NOTE: trimmed down perform for reference solely, don't copy-paste.
def plot_route_on_map(df_route: pd.DataFrame) -> folium.Map:
#----- map is created right here -----
# ...
#----- markers are created right here -----
for cease in df_route_segments.itertuples():
# ...
# ( ఠ ͟ʖ ఠ)
# ...
# for loop ends!

### ###
# examine if first web site's title and placement coincide with final's?
first_stop = df_route_closed.iloc[0][['site', 'latitude', 'longitude']]
last_stop = df_route_closed.iloc[-1][['site', 'latitude', 'longitude']]
is_closed_tour = (first_stop == last_stop).all()

# When for loop ends, the marker for the final cease is lacking
# (**until the route is closed**). if the route is just not closed,
# we add it now utilizing the "subsequent" columns of the final row
if not is_closed_tour:
folium.Marker(
location=(cease.latitude_next, cease.longitude_next),
tooltip=f"<b>Title</b>: {cease.site_next} <br>"
+ f"<b>Cease quantity</b>: {cease.Index + 1} <br>",
icon = folium.Icon(icon='info-sign', shade='crimson')
).add_to(map_route)

return map_route

After modifying plot_route_on_map as indicated above, the specified conduct is restored, i.e., the “house” icon for the preliminary web site is just not overridden by the final cease within the tour:

plot_route_on_map(df_route_closed)
Determine 6.7. Marker signaling the preliminary web site is seen once more

With this closing view, we are able to simply know the space between two consecutive stops (and the websites it’s connecting) with only one transfer of the cursor. In addition to, we are able to examine the chronological order through which websites are visited simply by hovering over them. This interactivity may help us consider the standard of the routes we cope with, whatever the routing drawback at hand.

3.5 Bonus: Including KPIs to the map

To cap all of it, let’s add a function that may make the map much more insightful.

There’s at all times info that isn’t instantly related to any web site or phase in a route, however with the route as a complete. This info is vital to find out about too, and tends to be international properties, like IDs or names, or related metrics related to the routes. We will show them too on a folium map with a easy trick.

If we need to show info that isn’t linked to any single marker or line, however that pertains to the route as a complete, we are able to add it as textual content blocks in folium maps. That is helpful for enterprise dashboards, for instance, the place one often desires to show issues like the title/ID of the route or varied metrics that try to summarize it in some related sense.

For instance, I’ll add on the top-left nook a title for the map, and on the bottom-right nook two very primary KPIs for routes: the variety of stops and the complete distance it traverses. We outline the names of the totally different items of textual content we need to show (within the TAG_ constants), and extract their respective values from df_route_segments, the dataframe having all of the “segments knowledge”:

TAG_ROUTE_NAME = "Title"
TAG_NUMBER_STOPS = "Num stops"
TAG_TOTAL_DISTANCE = "Distance"
_SPACE_HTML = "&nbsp" # wanted so as to add empty areas between KPIs

# get abstract information to show on map
title = df_route_segments.columns.title.capitalize()
n_stops = df_route_segments['site'].measurement
route_distance = df_route_segments['distance_seg'].sum().spherical(0)

For every bit of textual content we need to add, we have to convert it to HTML and add it to the map in a particular method. The string having the HTML code for the textual content must go inside a folium.Aspect. These are the steps: (1) Make a primary HTML string with the information to be displayed, (2) fashion it with extra HTML, and (3) add it to the map’s root HTML component. I do know, speaking about maps will get me misplaced too, so let’s simply do it.

💡Tip: For those who’re utilizing Jupyter Pocket book or Lab, I like to recommend you utilize the IPython.show.HTML class to achieve fast suggestions in your HTML code, like this:

from IPython.show import HTML, show
# present a whats up world message in blue and daring
show(HTML("<span fashion='shade:steelblue'>Good day <b>world</b></span>"))

Including a title to the map

First, create the fundamental HTML string for the title:

_html_text_title = f"<b>{TAG_ROUTE_NAME}</b>: {title}"

show(HTML(_html_text_title)) # [Out]: : Paris

Second, fashion this primary HTML string as you want. Right here, I need the title to be black, situated on the top-left of the map, and, for improved readability, I need the textual content to have some background white shadow:

STYLE_TITLE = (
"place:absolute;z-index:100000;left:5vw;shade:black;"
"font-size:30px;text-shadow:-1px 0 white, 0 1px white, 0 1px white"
)
html_title = f'<h3 fashion="{STYLE_TITLE}">{_html_text_title}</h3>'

# let's have a look at how that title appears like on the map (run all in similar cell):
map_with_title = plot_route_on_map(df_route)

root_map = map_with_title.get_root()
root_map.html.add_child(folium.Aspect(html_title))

map_with_title

Determine 6.8. Title of the map, invariant to zoom degree

That appears good. Now, the identical factor for the KPIs:

Including KPIs to the map

As we did earlier than, first, we create the fundamental HTML string for the KPIs. For this case, the show shall be totally different, and naturally, it will depend on private style: I like my KPIs displayed horizontally on the bottom-right of the map. Be at liberty to alter the fashion parameters to suite your aesthetic preferences.

_html_text_summary = f"""
<b>{TAG_NUMBER_STOPS}</b> <b>{TAG_TOTAL_DISTANCE}</b>
<br>
{n_stops} {16 * _SPACE_HTML} {route_distance:.0f} m
"""

show(HTML(_html_text_summary))
# [Out]:
#
# 8 25158 m

The fashion may be very comparable, the one change is a barely smaller font measurement. We preserve the white background shadow because it’s key for the readability of the KPIs displayed:

STYLE_SUMMARY = (
"place:absolute;z-index:100000;font-size:20px;"
"proper:0;backside:0;shade:black;"
"text-shadow:-1px 0 white, 0 1px white, 0 1px white"
)
html_summary = f'<h2 fashion="{STYLE_SUMMARY}">{_html_text_summary}</h2>'

# let's have a look at how the KPIs appear to be (run all in similar cell):
map_with_kpis = plot_route_on_map(df_route)

root_map = map_with_kpis.get_root()
root_map.html.add_child(folium.Aspect(html_summary))

map_with_kpis

Determine 6.9. KPIs on high of the map, invariant to zoom degree

Alright, that is beginning to look nice! Let’s mix each the title and KPIs now!

my_map = plot_route_on_map(df_route)

root_map = my_map.get_root()
root_map.html.add_child(folium.Aspect(html_title)) # add title
root_map.html.add_child(folium.Aspect(html_summary)) # add abstract KPIs
my_map # test it out

Determine 6.10. Abstract info of the route, title on the high and KPIs on the backside, invariant to zoom degree

Because of the textual content we’ve added, the map now conveys extra helpful info on the route (and appears extra skilled, doesn’t it?). With out even transferring the mouse, we are able to see worthwhile info on the corners; and due to interactivity, with a really fast hovering round, we are able to see the names and visiting order of the totally different websites, in addition to the space between any pair of stops. Let’s make this performance reusable by wrapping it up into helper features, and let’s create a brand new, extra basic perform, display_route_on_map, that may create a map like plot_route_on_map did, with the added choice of together with the title and KPI info to the map. First, the features to acquire the HTML strings:

def _get_text_for_title(df_route_segments):
"""Given a dataframe representing a route, the place the column index has
the title of the route, returns an HTML string with a pleasant show of
this title"""
# 1) get the information to show
title = df_route_segments.columns.title
title = title.capitalize() if title else ''

# 2) parse the information as HTML for addition to map
_html_text_title = f"<b>{TAG_ROUTE_NAME}</b>: {title}"
html_title = f'<h3 fashion="{STYLE_TITLE}">{_html_text_title}</h3>'
return html_title

def _get_kpis_to_display_on_map(df_route_segments):
"""Given a dataframe representing a route, and having columns 'web site'
and 'distance_seg', returns an HTML string with a pleasant show of
the variety of websites and the overall distance of the route"""
# 1) get the information to show
n_stops = df_route_segments['site'].measurement
route_distance = df_route_segments['distance_seg'].sum().spherical(0)

# 2) parse the information as HTML for addition to map
_html_text_summary = f"""
<b>{TAG_NUMBER_STOPS}</b> <b>{TAG_TOTAL_DISTANCE}</b>
<br>
{n_stops} {16 * _SPACE_HTML} {route_distance:.0f} m
"""
html_summary = f'<h2 fashion="{STYLE_SUMMARY}">{_html_text_summary}</h2>'
return html_summary

We will management the addition of the title and KPIs to the map with the attribute include_kpis of the brand new perform:

def display_route_on_map(df_route, include_kpis=True) -> folium.Map:
"""Given a dataframe representing a route, creates a folium map
and provides markers for the stops and features for the route segments,
with the choice to additionally add an automated title and a couple of KPIs:
- variety of stops within the route
- complete distance of route

Parameters
----------
df_route : pd.DataFrame
A dataframe representing a route, whereby every row comprises
info on a distinct cease of the route, and rows are sorted
by cease visiting order.
include_kpis : bool (default=True)
Whether or not to incorporate the title and the two KPIs within the map

Returns
-------
A folium map that may be displayed or re-used"""
# 1) create empty map
avg_location = df_route[['latitude', 'longitude']].imply()
map_route = folium.Map(location=avg_location, zoom_start=13)

# 2) create DF with phase info
df_route_segments = _make_route_segments_df(df_route)

# 3) add title and KPIs to the map
if include_kpis:
html_title = _get_text_for_title(df_route_segments)
html_summary = _get_kpis_to_display_on_map(df_route_segments)
root_map = map_route.get_root()
root_map.html.add_child(folium.Aspect(html_title)) # add title
root_map.html.add_child(folium.Aspect(html_summary)) # add KPIs

# 4) add path to the map
for cease in df_route_segments.itertuples():
initial_stop = cease.Index == 0
# marker for present cease
icon = folium.Icon(icon='house' if initial_stop else 'info-sign',
shade='cadetblue' if initial_stop else 'crimson')
marker = folium.Marker(
location=(cease.latitude, cease.longitude),
icon=icon,
tooltip=f"<b>Title</b>: {cease.web site} <br>"
+ f"<b>Cease quantity</b>: {cease.Index} <br>"
)
# line for the route phase connecting present to subsequent cease
line = folium.PolyLine(
places=[(stop.latitude, stop.longitude),
(stop.latitude_next, stop.longitude_next)],
# add to every line its begin, finish, and distance
tooltip=f"<b>From</b>: {cease.web site} <br>"
+ f"<b>To</b>: {cease.site_next} <br>"
+ f"<b>Distance</b>: {cease.distance_seg:.0f} m",
)
# add parts to the map
marker.add_to(map_route)
line.add_to(map_route)

# does the primary web site's title and placement coincide with the final's?
first_stop = df_route_closed.iloc[0][['site', 'latitude', 'longitude']]
last_stop = df_route_closed.iloc[-1][['site', 'latitude', 'longitude']]
is_closed_tour = (first_stop == last_stop).all()

# When for loop ends, the cease variable has the second-to-last
# cease within the route, so the marker for the final cease is lacking
# (**until the route is closed**). We add it now utilizing
# the "subsequent" columns of the final row, if the route is open
if not is_closed_tour:
folium.Marker(
location=(cease.latitude_next, cease.longitude_next),
tooltip=f"<b>Title</b>: {cease.site_next} <br>"
+ f"<b>Cease quantity</b>: {cease.Index + 1} <br>",
icon = folium.Icon(icon='info-sign', shade='crimson')
).add_to(map_route)

return map_route

Let’s do a closing take a look at of our display_route_on_map perform with a tour on a distinct metropolis: New York. The dataframe beneath has a set of iconic websites of town (and a random resort) in no specific order:

df_route_ny = pd.DataFrame(
[['hotel', 40.710400, -74.006670],
['One World Trade Center', 40.713658, -74.013285],
['Federal Hall', 40.708051, -74.010223],
['Tenement Museum', 40.718807, -73.990051],
['Flatiron Building', 40.739665, -73.989976],
['Chrysler Building', 40.750542, -73.975632],
['New York City Library', 40.752471, -73.982162],
['Empire State Building', 40.747814, -73.985289],
['Madison Square Garden', 40.749924, -73.993853],
['Chelsea Market', 40.742750, -74.006877]],
columns=pd.Index(['site', 'latitude', 'longitude'], title='big apple')
)
df_route_ny.index.title = 'visit_order'

df_route_ny

With the dataframe of the route prepared, a pleasant visualization is only a single perform name away:

display_route_on_map(df_route_ny)
Determine 6.11. Interactive map for a random route in New York

We get a easy, but highly effective visualization of the route on high of Manhattan. At a look, we all know how a lot distance the route covers, and we don’t even have to trouble counting crimson markers: the “Num stops” KPI is telling us already. For extra context, all markers and route segments are interactive and can show some helpful info in pop-ups after we caringly go our mouse over them. Mission achieved!

[ad_2]