Before starting working with hvPlot, we need to install and import a few libraries. Specifically, we will need GeoPandas and GeoViews. For the installation of GeoPandas, it has already been discussed in the previous article on GeoPandas. GeoViews and its dependencies can be installed using conda with:
conda install -c pyviz hvplot geoviews
It may also be installed using pip as follows if the cartopy
dependency has been satisfied in some other way.
pip install geoviews
Next, we import all the required libraries as follows:
import pandas as pd
import geopandas as gpd
import hvplot.pandas
We also need a geospatial dataset to work with. For this tutorial, we will use the same dataset used in the article of GeoPandas in order to compare the methods and results with GeoPandas. You do not need to download them, and we can read them directly from the web.
url = 'https://raw.githubusercontent.com/MinnPost/simple-map-d3/master/example-data/world-population.geo.json'
world = gpd.read_file(url)
world.head()
NAME | ISO_3_CODE | ISO_2_CODE | AREA | NAME_1 | POP2005 | REGION | GMI_CNTRY | NAME_12 | geometry | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Afghanistan | AFG | AF | 65209 | Afghanistan | 25067407 | Asia | AFG | Afghanistan | POLYGON ((65.62730 37.33320, 65.64693 37.45888... |
1 | Albania | ALB | AL | 2740 | Albania | 3153731 | Europe | ALB | Albania | POLYGON ((19.39732 42.31707, 19.46971 42.39999... |
2 | Algeria | DZA | DZ | 238174 | Algeria | 32854159 | NorthAfrica | DZA | Algeria | POLYGON ((-1.25389 32.21471, -1.25000 32.32693... |
3 | Andorra | AND | AD | 0 | Andorra | 73483 | Europe | AND | Andorra | POLYGON ((1.71097 42.47350, 1.53333 42.43610, ... |
4 | Angola | AGO | AO | 124670 | Angola | 16095214 | Sub Saharan Africa | AGO | Angola | MULTIPOLYGON (((12.01007 -5.02062, 12.16639 -4... |
Next, we can create a basic map by calling the hvplot method on the GeoDataFrame.
world.hvplot(geo=True)
C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:245: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(multi_line_string) > 1: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:297: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. for line in multi_line_string: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:364: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(p_mline) > 0: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:245: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(multi_line_string) > 1: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:297: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. for line in multi_line_string: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:364: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(p_mline) > 0: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:245: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(multi_line_string) > 1: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:297: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. for line in multi_line_string: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:364: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(p_mline) > 0: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:402: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. line_strings.extend(multi_line_string) C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:402: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. line_strings.extend(multi_line_string) C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:256: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. line_strings = list(multi_line_string) C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:256: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. line_strings = list(multi_line_string) C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:245: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(multi_line_string) > 1: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:297: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry. for line in multi_line_string: C:\Users\shouk\anaconda3\lib\site-packages\cartopy\crs.py:364: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry. if len(p_mline) > 0:
You might also face a similar Warning message on the Windows sytem due to lower Shapely version, which is one of common dependencies of Geopandas and Cartopy for GeoViews. In the future, the problem may be solved when Geopandas and Cartopy both use an update Shapely version >=2.0. But at the moment, my recommend solution is to remove the warning message using the following method. If you have a better solution, please leave a message.
import warnings
from shapely.errors import ShapelyDeprecationWarning
warnings.filterwarnings("ignore", category=ShapelyDeprecationWarning)
Let's run it again.
world.hvplot(geo=True)
This time, we should only display a map of the world with default styling. Here, geo
(default=False) denotes whether the plot should be treated as geographic (and assume PlateCarree, i.e. lat/lon coordinates).
world.hvplot(geo=True, hover_cols=['NAME'])
Now, when you hover over a country on the map, a tooltip will appear displaying the name of the country.
The map size is small, and we can change the map size easily by specifying frame_height
and frame_width
. Besides, the legend here seemly makes no sense, because there are so many countries, and we can hide it using legend=False
.
world.hvplot(geo=True, hover_cols=['NAME'],
frame_height=450,frame_width=600,
legend=False)
Or you can just use height
and width
to adjust the map size. In all the following examples, we will use this method for simplicity.
world.hvplot(geo=True, hover_cols=['NAME'],
height=450,width=600,
legend=False)
We can also add zooming and panning capabilities to our map using the tiles parameter. Tiles (default=False) is whether to overlay the plot on a tile source.
Tiles sources can be selected by name, the default is ‘Wikipedia’. Other options are: ‘CartoDark’, ‘CartoEco’, ‘CartoLight’, ‘CartoMidnight’, ‘EsriImagery’, ‘EsriNatGeo’, ‘EsriReference’’EsriTerrain’, ‘EsriUSATopo’, ‘OSM’, ‘StamenLabels’, ‘StamenTerrain’, ‘StamenTerrainRetina’, ‘StamenToner’, ‘StamenTonerBackground’, ‘StamenWatercolor’
world.hvplot(geo=True, hover_cols=['NAME'],
height=450,width=600,
legend=False,
tiles='StamenWatercolor')
This adds OpenStreetMap tiles to our map, which allows us to zoom and pan around the world.
We can also use global_extent (default=False)
to choose whether to expand the plot extent to span the whole globe.
world.hvplot(geo=True, global_extent=True,
hover_cols=['NAME'],
height=450,width=600,
legend=False)
world.hvplot(color='red',geo=True,
global_extent=True,
height=450,width=600,
legend=False)
You can use the colormaps of matplotlib or the colormaps of holoViews to change the map colors.
world.hvplot(cmap='jet',geo=True,
global_extent=True,
hover_cols=['NAME'],
frame_height=450,frame_width=600,
legend=False)
We can also easily declare a specific column to use as color with the c keyword. For example, we specify the REGION column to use as color.
world.hvplot(c='AREA',cmap='jet',geo=True,
global_extent=True,
hover_cols=['NAME'],
height=450,width=600,
legend=False)
hvPlot also provides several built-in widgets that allow you to add user-driven interactivity to your map. We have already displayed these methods in the previous article of hvPlot interactive plot widgets and GUI. Here, we just see 2 example as follows.
Similar to other plots of hvPlot, we can easily apply a groupby
along a particular column or dimension to automatically create plot widgets. For example, we can add a selector widget that filters the countries by 'REGION' column.
world.hvplot(c='OP2005',cmap='jet',
geo=True,
global_extent=True,
hover_cols=['POP2005'],
groupby='REGION',
height=450,width=550,
legend=False)
For the panel installation and use the Panel library to customize the interactivity of the hvPlot output, please refer to this previous article. For example, we can add a color select box that can change the map colors.
import panel as pn
def plot_map(color):
return world.hvplot(geo=True, color=color, line_color='white', hover_color='yellow')
color_selector = pn.widgets.Select(options=['red', 'green', 'blue'], name='Color')
@pn.depends(color_selector.param.value)
def update_map(color):
return plot_map(color)
pn.Row(color_selector, update_map)
airports_url = 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson'
airports = gpd.read_file(airports_url)
airports.head()
scalerank | type | name | abbrev | location | gps_code | iata_code | wikipedia | natlscale | featureclass | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 9 | small | Sahnewal | LUH | terminal | VILD | LUH | http://en.wikipedia.org/wiki/Sahnewal_Airport | 8 | Airport | POINT (75.95707 30.85036) |
1 | 9 | mid | Solapur | SSE | terminal | VASL | SSE | http://en.wikipedia.org/wiki/Solapur_Airport | 8 | Airport | POINT (75.93306 17.62542) |
2 | 9 | mid | Birsa Munda | IXR | terminal | VERC | IXR | http://en.wikipedia.org/wiki/Birsa_Munda_Airport | 8 | Airport | POINT (85.32360 23.31772) |
3 | 9 | mid | Ahwaz | AWZ | terminal | OIAW | AWZ | http://en.wikipedia.org/wiki/Ahwaz_Airport | 8 | Airport | POINT (48.74711 31.34316) |
4 | 9 | mid and military | Gwalior | GWL | terminal | VIGR | GWL | http://en.wikipedia.org/wiki/Gwalior_Airport | 8 | Airport | POINT (78.21722 26.28549) |
Now, we create world map and airport distribution points map layer, and then overlay the airport layer on the top of the world map.
world_map = world.hvplot(geo=True, tiles='OSM',
xlabel='longitude',
ylabel='latitude',
height=500,width=700)
airports_map = airports.hvplot(geo=True,color='red',
size=30)
world_airports_map = world_map * airports_map
world_airports_map
In this example, we add world cities through a CSV file. First, let's read the data online using pandas.
cities_url = 'https://gist.githubusercontent.com/curran/13d30e855d48cdd6f22acdf0afe27286/raw/0635f14817ec634833bb904a47594cc2f5f9dbf8/worldcities_clean.csv'
cities = pd.read_csv(cities_url)
cities.head()
city | lat | lng | country | population | |
---|---|---|---|---|---|
0 | Tokyo | 35.6850 | 139.7514 | Japan | 35676000 |
1 | New York | 40.6943 | -73.9249 | United States | 19354922 |
2 | Mexico City | 19.4424 | -99.1310 | Mexico | 19028000 |
3 | Mumbai | 19.0170 | 72.8570 | India | 18978000 |
4 | São Paulo | -23.5587 | -46.6250 | Brazil | 18845000 |
We use a scatter plot to glance over the data.
cities.hvplot.scatter(x='lng', y='lat',color='orange')
Next, we converse the DataFrame into GeoDataFrame.
cities = gpd.GeoDataFrame(cities,crs="EPSG:4326",
geometry=gpd.points_from_xy(
cities["lng"], cities["lat"]))
cities
city | lat | lng | country | population | geometry | |
---|---|---|---|---|---|---|
0 | Tokyo | 35.6850 | 139.7514 | Japan | 35676000 | POINT (139.75140 35.68500) |
1 | New York | 40.6943 | -73.9249 | United States | 19354922 | POINT (-73.92490 40.69430) |
2 | Mexico City | 19.4424 | -99.1310 | Mexico | 19028000 | POINT (-99.13100 19.44240) |
3 | Mumbai | 19.0170 | 72.8570 | India | 18978000 | POINT (72.85700 19.01700) |
4 | São Paulo | -23.5587 | -46.6250 | Brazil | 18845000 | POINT (-46.62500 -23.55870) |
... | ... | ... | ... | ... | ... | ... |
4655 | Pa-an | 16.8500 | 97.6167 | Burma | 50000 | POINT (97.61670 16.85000) |
4656 | Puzi | 23.4611 | 120.2419 | Taiwan | 50000 | POINT (120.24190 23.46110) |
4657 | Korogwe | -5.0896 | 38.5400 | Tanzania | 50000 | POINT (38.54000 -5.08960) |
4658 | Kahemba | -7.2829 | 19.0000 | Congo (Kinshasa) | 50000 | POINT (19.00000 -7.28290) |
4659 | Bairin Zuoqi | 43.9837 | 119.1834 | China | 50000 | POINT (119.18340 43.98370) |
4660 rows × 6 columns
Now, we create cities map layer and add it on the top of our world map.
cities_map = cities.hvplot(geo=True, color='orange')
world_cities_map = world_map * cities_map
world_cities_map
This creates a distribution map of world cities on top of our base map.
We can also add other types of overlays, such as polygons or lines, using the polygons and lines overlays, respectively. In summary, hvPlot makes it easy to add layers and overlays to your maps, allowing you to create complex and interactive visualizations of your geospatial data.
We can also easily layout or create subplots using hvPlot syntax. For example, we layout world airport map and world cities map vertically or horizontally.
Let's repeat the above process a bit in order to display the process of overlay and layout together.
world_map = world.hvplot(geo=True, tiles='OSM',
width=700,height=500,
xlabel='longitude',
ylabel='latitude')
airports_map = airports.hvplot(geo=True, color='red')
cities_map = cities.hvplot(geo=True, color='orange')
layout = (world_map*airports_map + world_map*cities_map).cols(1)
layout
In this example, we layout the map horizontally.
world_map = world.hvplot(geo=True, tiles='OSM',
width=450,height=400,
xlabel='longitude',
ylabel='latitude')
airports_map = airports.hvplot(geo=True,color='red')
cities_map = cities.hvplot(geo=True, color='orange')
layout = (world_map*airports_map + world_map*cities_map).cols(2)
layout
In this tutorial, we learned how to create interactive maps with hvPlot. We started by creating a basic map and then added interactivity using hover tooltips, zooming and panning, and widgets. We also learned how to add layers and overlays to our map and explored some of the advanced features of hvPlot.
hvPlot is a powerful library that can be used to create a wide variety of interactive visualizations from geospatial datasets. We hope that this tutorial has provided you with a good starting point for creating your own interactive maps using hvPlot.