Visualizing Global Land Cover

  • The Global Land Cover 30m product is a powerful dataset for global land cover change monitoring

  • See https://gee-community-catalog.org/tutorials/examples/glc_fcs30d_lulc for source documentation

Copyright 2025 Ian Housman

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

github github

try:
    import  geeViz.geeView as geeView
except:
    !python -m pip install geeViz
    import  geeViz.geeView as geeView

import geeViz.geePalettes as palettes
ee = geeView.ee
Map = geeView.Map

print('done')

Make a basic viewer

  • This is a slight adaptation of the example provided here: https://gee-community-catalog.org/tutorials/examples/glc_fcs30d_lulc/#steps

  • geeViz will automatically generate legends and query lookups using "autoViz" : True in the visualization params

  • Double-clicking on the map will show the values for the pixel you clicked on

Map.clearMap()
Map.port = 1234
# Specify projection to use for zonal summaries and map querying
# Be sure to leave one of scale or transform as None
crs= 'EPSG:4326'
transform = None
scale = 30


Map.setQueryCRS(crs)
if transform == None:
    Map.setQueryScale(scale)
else:
    Map.setQueryTransform(transform)
Map.clearMapLayers()

####################################################################
# Adapted from: https://gee-community-catalog.org/tutorials/examples/glc_fcs30d_lulc/#steps
annual = ee.ImageCollection('projects/sat-io/open-datasets/GLC-FCS30D/annual')



# Five-Yearly data for 1985-90, 1990-95 and 1995-2000
fiveyear = ee.ImageCollection('projects/sat-io/open-datasets/GLC-FCS30D/five-years-map')

# Classification scheme has 36 classes (35 landcover classes and 1 fill value)
classValues = [10, 11, 12, 20, 51, 52, 61, 62, 71, 72, 81, 82, 91, 92, 120, 121, 122, 130, 140, 150, 152, 153, 181, 182, 183, 184, 185, 186, 187, 190, 200, 201, 202, 210, 220, 0]
classNames = ['Rainfed_cropland', 'Herbaceous_cover_cropland', 'Tree_or_shrub_cover_cropland', 'Irrigated_cropland', 'Open_evergreen_broadleaved_forest', 'Closed_evergreen_broadleaved_forest', 'Open_deciduous_broadleaved_forest', 'Closed_deciduous_broadleaved_forest', 'Open_evergreen_needle_leaved_forest', 'Closed_evergreen_needle_leaved_forest', 'Open_deciduous_needle_leaved_forest', 'Closed_deciduous_needle_leaved_forest', 'Open_mixed_leaf_forest', 'Closed_mixed_leaf_forest', 'Shrubland', 'Evergreen_shrubland', 'Deciduous_shrubland', 'Grassland', 'Lichens_and_mosses', 'Sparse_vegetation', 'Sparse_shrubland', 'Sparse_herbaceous', 'Swamp', 'Marsh', 'Flooded_flat', 'Saline', 'Mangrove', 'Salt_marsh', 'Tidal_flat', 'Impervious_surfaces', 'Bare_areas', 'Consolidated_bare_areas', 'Unconsolidated_bare_areas', 'Water_body', 'Permanent_ice_and_snow', 'Filled_value']
classColors = ['ffff64', 'ffff64', 'ffff00', 'aaf0f0', '4c7300', '006400', 'a8c800', '00a000', '005000', '003c00', '286400', '285000', 'a0b432', '788200', '966400', '964b00', '966400', 'ffb432', 'ffdcd2', 'ffebaf', 'ffd278', 'ffebaf', '00a884', '73ffdf', '9ebb3b', '828282', 'f57ab6', '66cdab', '444f89', 'c31400', 'fff5d7', 'dcdcdc', 'fff5d7', '0046c8', 'ffffff', 'ffffff']

# Mosaic the data into a single image
annualMosaic = annual.mosaic()
fiveYearMosaic = fiveyear.mosaic()

# Rename bands from b1, b2, etc. to 2000, 2001, etc.
fiveYearsList = [str(i) for i in list(range(1985, 1995+1, 5))]
fiveyearMosaicRenamed = fiveYearMosaic.rename(fiveYearsList)
yearsList = [str(i) for i in list(range(2000, 2022+1))]
annualMosaicRenamed = annualMosaic.rename(yearsList)
years = fiveYearsList + yearsList
# Convert the multiband image to an ImageCollection
def getFiveYear(year):
    date = ee.Date.fromYMD(ee.Number.parse(year), 1, 1)
    return fiveyearMosaicRenamed.select([year]).set({'system:time_start': date.millis(), 'system:index': year, 'year': ee.Number.parse(year)})
fiveYearlyMosaics = ee.List(fiveYearsList).map(getFiveYear)

def getAnnualYear(year):
    date = ee.Date.fromYMD(ee.Number.parse(year), 1, 1)
    return annualMosaicRenamed.select([year]).set({'system:time_start': date.millis(), 'system:index': year, 'year': ee.Number.parse(year)})
yearlyMosaics = ee.List(yearsList).map(getAnnualYear)

allMosaics = fiveYearlyMosaics.cat(yearlyMosaics)
mosaicsCol = ee.ImageCollection.fromImages(allMosaics)
mosaicsCol = ee.ImageCollection.fromImages(allMosaics)
print(mosaicsCol.size().getInfo())
# Set up viz properties
out_band_name = "lulc"


# Function to convert a given band into a time-enabled image object
def setupLulc(img):
    img = img.set(
        {
            f"{out_band_name}_class_names": classNames,
            f"{out_band_name}_class_palette": classColors,
            f"{out_band_name}_class_values": classValues,
        }
    )
    return img

# Rename band and set the properties for visualization
landcoverCol = mosaicsCol.select([0],[out_band_name]).map(setupLulc)

print('Pre-processed Collection', landcoverCol.getInfo())


# Visualize the most common class
Map.addLayer(landcoverCol, {'reducer':ee.Reducer.mode(),'autoViz':True}, 'Global Land Cover' )

# Querying the data by double-clicking on the map will show the full time series of values
Map.turnOnInspector()
Map.view()

Calculating and Exporting Landcover Statistics¶

  • geeViz streamlines calculating and exporting statistics

  • Once the map loads, you can use the TOOLS under Area Tools to generate charts

  • Charts can be downloaded as a PNG and the underlying data can be downloaded as a CSV

  • This example shows the imageCollection as a time lapse

  • Once the map loads, you can annimate the time series using the time lapse controls

Map.clearMap()
Map.turnOffLayersWhenTimeLapseIsOn = False # Set this to False to avoid layers automatically turning off when a time lapse is turned on

Map.addTimeLapse(landcoverCol, {'autoViz':True,'years':years,'canAreaChart':True,'areaChartParams':{'crs':crs,'scale':scale,'transform':transform}}, 'Global Land Cover' )

# Add country boundaries as a layer to select with
countries = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level0")
Map.addSelectLayer(countries, {}, "Global Country Boundaries")

Map.turnOnAutoAreaCharting()
Map.view()

Create Sankey Charts

  • You can also create Sankey charts and download transition matrices with geeViz

  • Once the map loads, you can use the TOOLS under Area Tools to generate charts

  • You can customize the years to include in the sankey charts under the Area Tools Parameters -> Transition Charting Periods

  • Charts can be downloaded as a PNG and the underlying data can be downloaded as a CSV

Map.clearMapLayers()

Map.addTimeLapse(landcoverCol, {'autoViz':True,'years':years,'canAreaChart':True,'areaChartParams':{'line':True,'sankey':True,'crs':crs,'transform':transform,'scale':scale}}, 'Global Land Cover' )

countries = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level0")
Map.addSelectLayer(countries, {}, "Global Country Boundaries")

Map.turnOnAutoAreaCharting()
Map.view()
Map.clearMap()
# Adapted from: https://code.earthengine.google.com/f9f56ceb38ed9e911767c4014eeb536d
print("This data is made available by the Global Land Analysis and Discovery (GLAD) lab at the University of Maryland.","P.V. Potapov, M.C. Hansen, A.H. Pickens, A. Hernandez-Serna, A. Tyukavina, S. Turubanova, V. Zalles, X. Li, A. Khan, F. Stolle, N. Harris, X.-P. Song, A. Baggett, I. Kommareddy, A. Komareddy (2022).")
# print("For more information please visit:",ui.Label(" https://glad.umd.edu/dataset/GLCLUC2020",{},"https://glad.umd.edu/dataset/GLCLUC2020"),
 # "The legend is available here:",ui.Label(" https://storage.googleapis.com/earthenginepartners-hansen/GLCLU2000-2020/v2/legend.xlsx",{},"https://storage.googleapis.com/earthenginepartners-hansen/GLCLU2000-2020/v2/legend.xlsx"))


landmask = ee.Image("projects/glad/OceanMask").lte(1)
change =ee.Image('projects/glad/GLCLU2020/v2/LCLUC').updateMask(landmask)
m00 = ee.Image('projects/glad/GLCLU2020/v2/LCLUC_2000').updateMask(landmask)
m05 = ee.Image('projects/glad/GLCLU2020/v2/LCLUC_2005').updateMask(landmask)
m10 = ee.Image('projects/glad/GLCLU2020/v2/LCLUC_2010').updateMask(landmask)
m15 = ee.Image('projects/glad/GLCLU2020/v2/LCLUC_2015').updateMask(landmask)
m20 = ee.Image('projects/glad/GLCLU2020/v2/LCLUC_2020').updateMask(landmask)
  
visParamMap = {"min":0,"max":255,"palette":["FEFECC","FAFAC3","F7F7BB","F4F4B3","F1F1AB","EDEDA2","EAEA9A","E7E792","E4E48A",
"E0E081","DDDD79","DADA71","D7D769","D3D360","D0D058","CDCD50","CACA48","C6C63F","C3C337","C0C02F","BDBD27","B9B91E","B6B616",
"B3B30E","B0B006","609C60","5C985C","589558","549254","508E50","4C8B4C","488848","448544","408140","3C7E3C","387B38","347834",
"317431","2D712D","296E29","256B25","216721","1D641D","196119","155E15","115A11","0D570D","095409","065106","643700","643a00",
"643d00","644000","644300","644600","644900","654c00","654f00","655200","655500","655800","655a00","655d00","656000","656300",
"666600","666900","666c00","666f00","667200","667500","667800","667b00","ff99ff","FC92FC","F98BF9","F685F6","F37EF3","F077F0",
"ED71ED","EA6AEA","E763E7","E45DE4","E156E1","DE4FDE","DB49DB","D842D8","D53BD5","D235D2","CF2ECF","CC27CC","C921C9","C61AC6",
"C313C3","C00DC0","BD06BD","bb00bb","000003","000004","000005","BFC0C0","B7BDC2","AFBBC4","A8B8C6","A0B6C9","99B3CB","91B1CD",
"89AFD0","82ACD2","7AAAD4","73A7D6","6BA5D9","64A3DB","5CA0DD","549EE0","4D9BE2","4599E4","3E96E6","3694E9","2E92EB","278FED",
"1F8DF0","188AF2","1088F4","0986F7","55A5A5","53A1A2","519E9F","4F9B9C","4D989A","4B9597","499294","478F91","458B8F","43888C",
"418589","3F8286","3D7F84","3B7C81","39797E","37767B","357279","336F76","316C73","2F6970","2D666E","2B636B","296068","285D66",
"bb93b0","B78FAC","B48CA9","B189A6","AE85A2","AA829F","A77F9C","A47B99","A17895","9E7592","9A718F","976E8C","946B88","916885",
"8D6482","8A617F","875E7B","845A78","815775","7D5472","7A506E","774D6B","744A68","714765","de7cbb","DA77B7","D772B3","D46EAF",
"D169AB","CE64A8","CB60A4","C85BA0","C4579C","C15298","BE4D95","BB4991","B8448D","B54089","B23B86","AF3682","AB327E","A82D7A",
"A52976","A22473","9F1F6F","9C1B6B","991667","961264","000000","000000","000000",
"1964EB","1555E4","1147DD","0E39D6","0A2ACF","071CC8","030EC1","0000BA",
"0000BA","040464","0000FF","3051cf","000000","000000","000000","000000",
"000000","000000","000000","000000","000000","000000","000000","000000",
"000000","000000","000000","000000","000000","000000","000000","000000",
"547FC4","4D77BA","466FB1","4067A7","395F9E","335895","335896","335897","ff2828","ffffff","d0ffff","ffe0d0","ff7d00","fac800","c86400",
"fff000","afcd96","afcd96","64dcdc","00ffff","00ffff","00ffff","111133","000000"]}
  

Map.addLayer(m00,visParamMap,'2000 land cover and land use')
Map.addLayer(m05,visParamMap,'2005 land cover and land use')
Map.addLayer(m10,visParamMap,'2010 land cover and land use')
Map.addLayer(m15,visParamMap,'2015 land cover and land use')
Map.addLayer(m20,visParamMap,'2020 land cover and land use')
Map.addLayer(change,visParamMap,'2000-2020 land cover and land use change')


Map.turnOnInspector()
Map.view()