16  Interactive maps

Author

Andres Patrignani

Published

March 16, 2024

Keywords

gee, interactive maps, images, folium

Interactive maps provide a dynamic user experience, allowing for real-time zooming, panning, and exploration of geospatial data layers. In contrast, Matplotlib’s static figures, while useful for publication-quality visuals, lack the ability to explore data or to easily integrate multiple layers of information. Additionally, interactive maps can be easily integrated within websites and are ideal for collaborative projects and public dissemination of geospatial research findings.

In this tutorial we will create an interactive map to explore different land covers across the state of Kansas. We will use the folium Python library, which is a wrapper around the popular Leaflet library implemented in JavaScript.

In this tutorial we will learn:

# Import modules
import ee
import folium
import numpy as np
from matplotlib import colors, colormaps

Define some helper functions

Let’s define some functions to avoid repeating the following code every time we want to create a raster image or every time we want to specify a given colormap for our map.

def get_raster_map(ee_object, vis, name):
    """
    Function that retrieves raster map from GEE and applies some style.
    """
    if isinstance(ee_object, ee.Image):
        layer = folium.TileLayer(ee_object.getMapId(vis)['tile_fetcher'].url_format,
                           name=name,
                           overlay=True,
                           control=True,
                           attr='Map Data &copy; <a href="https://earthengine.google.com/">Google Earth Engine</a>')
        
    return layer



def get_vector_map(ee_object,vis,name):
    """
    Function that retrieves vector map from GEE and applies some style.
    """
    if isinstance(ee_object, ee.FeatureCollection):
        layer = folium.GeoJson(ee_object.getInfo(),
                               name=name,
                               style_function=lambda feature: {
                                   'fillColor': vis['face_color'],
                                   'color': vis['edge_color'],
                                   'weight': vis['line_width']})
    return layer
# Authenticate GEE (follow the link and copy-paste the token in the notebook)
#ee.Authenticate()

# Initialize the library.
ee.Initialize()

State land cover

# Read US states
US_states = ee.FeatureCollection("TIGER/2018/States")

# Select Kansas
region = US_states.filter(ee.Filter.inList('NAME',['Kansas']))

# Read Kansas counties (Kansas FIPS code is "20")
counties = ee.FeatureCollection("TIGER/2018/Counties").filter(ee.Filter.eq('STATEFP','20'))
# Land use for 2021
land_use = ee.ImageCollection('USDA/NASS/CDL')\
             .filter(ee.Filter.date('2021-01-01', '2021-12-31')).first().clip(region)

# Select cropland layer
cropland = land_use.select('cropland')
# Get layer metadata
info = cropland.getInfo()

# Remove comment to print the entire information (output is long!)
# print(info)
print(info.keys())
dict_keys(['type', 'bands', 'version', 'id', 'properties'])
# Get land cover names, values, and colors from metadata
# The metadata stores each property as a single string of comma-separated items
class_names = info['properties']['cropland_class_names'].split(',')
class_values = [int(v) for v in info['properties']['cropland_class_values'].split(',')]
class_colors = info['properties']['cropland_class_palette'].split(',')
# Create dictionary to easily access properties by land cover name
class_props = {}
for k,name in enumerate(class_names):
    class_props[name] = {'value':class_values[k], 'color':class_colors[k]}

# Print example
class_props['Corn']
{'value': 1, 'color': 'ffd400'}
# Create self masks for each land cover, so that only 
# the pixels for this land cover are visible

corn = cropland.eq(class_props['Corn']['value']).selfMask()
sorghum = cropland.eq(class_props['Sorghum']['value']).selfMask()
soybeans = cropland.eq(class_props['Soybeans']['value']).selfMask()
wheat = cropland.eq(class_props['Winter Wheat']['value']).selfMask()
grassland = cropland.eq(class_props['Grass/Pasture']['value']).selfMask()
TipTip

Since we created a dictionary using the name of the land cover class as the key, in the previous cell you can use Tab-autocompletion.

# Initialize map centered at a specific location and zoom level
m = folium.Map(location=[38, -98], zoom_start=7)

get_raster_map(corn, {'palette':[class_props['Corn']['color']]}, 'Corn').add_to(m)
get_raster_map(sorghum, {'palette':[class_props['Sorghum']['color']]}, 'Sorghum').add_to(m)
get_raster_map(soybeans, {'palette':[class_props['Soybeans']['color']]}, 'Soybeans').add_to(m)
get_raster_map(wheat, {'palette':[class_props['Winter Wheat']['color']]}, 'Wheat').add_to(m)
get_raster_map(grassland, {'palette':[class_props['Grass/Pasture']['color']]}, 'Grassland').add_to(m)

# Add vector layers
get_vector_map(region, vis={'face_color':'#00FFFF00',
                     'edge_color':'black',
                     'line_width':2}, name='State boundary').add_to(m)

get_vector_map(counties, vis={'face_color':'#00FFFF00',
                     'edge_color':'grey',
                     'line_width':1}, name='County boundary').add_to(m)


# Add a layer control panel to the map.
m.add_child(folium.LayerControl())

# Display the map.
display(m)
Make this Notebook Trusted to load map: File -> Trust Notebook