Visualizing Dynamic World#

  • Dynamic World data provide a global near-real time monitoring solution for land cover and land use change

  • Each Sentinel-2 image tile has a corresponding output

  • This notebook will run through how to utilize geeViz to visualize and summarize Dynamic World data

Copyright 2024 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

ee = geeView.ee
Map = geeView.Map

Map.port = 1234
print('done')
Initializing GEE
Cached project id file path: C:\Users\ihousman\.config\earthengine\credentials.proj_id
Cached project id: lcms-292214
Successfully initialized
geeViz package folder: c:\Users\ihousman\AppData\Local\Programs\Python\Python311\Lib\site-packages\geeViz
done

Make a basic viewer#

  • First, we’ll adapt the example script to use geeViz

  • Adapted from: https://code.earthengine.google.com/?scriptPath=Examples%3ADatasets%2FGOOGLE%2FGOOGLE_DYNAMICWORLD_V1

Map.clearMapLayers()


# Construct a collection of corresponding Dynamic World and Sentinel-2 for
# inspection. Filter the DW and S2 collections by region and date.
START = ee.Date('2021-04-02')
END = START.advance(1, 'day')

colFilter = ee.Filter.And(
    ee.Filter.bounds(ee.Geometry.Point(20.6729, 52.4305)),
    ee.Filter.date(START, END))

dwCol = ee.ImageCollection('GOOGLE/DYNAMICWORLD/V1').filter(colFilter)
s2Col = ee.ImageCollection('COPERNICUS/S2').filter(colFilter)

# Join corresponding DW and S2 images (by system:index).
DwS2Col = ee.Join.saveFirst('s2_img').apply(dwCol, s2Col,
    ee.Filter.equals(**{'leftField': 'system:index', 'rightField': 'system:index'}))

# Extract an example DW image and its source S2 image.
dwImage = ee.Image(DwS2Col.first())
s2Image = ee.Image(dwImage.get('s2_img'))

# Create a visualization that blends DW class label with probability.
# Define list pairs of DW LULC label and color.
CLASS_NAMES = [
    'water', 'trees', 'grass', 'flooded_vegetation', 'crops',
    'shrub_and_scrub', 'built', 'bare', 'snow_and_ice']

VIS_PALETTE = [
    '419bdf', '397d49', '88b053', '7a87c6', 'e49635', 'dfc35a', 'c4281b',
    'a59b8f', 'b39fe1']

# Create an RGB image of the label (most likely class) on [0, 1].
dwRgb = dwImage.select('label').visualize(**{'min': 0, 'max': 8, 'palette': VIS_PALETTE}).divide(255)

# Get the most likely class probability.
top1Prob = dwImage.select(CLASS_NAMES).reduce(ee.Reducer.max())

# Create a hillshade of the most likely class probability on [0, 1];
top1ProbHillshade =ee.Terrain.hillshade(top1Prob.multiply(100)).divide(255)

# Combine the RGB image with the hillshade.
dwRgbHillshade = dwRgb.multiply(top1ProbHillshade);

# Display the Dynamic World visualization with the source Sentinel-2 image.
Map.setCenter(20.6729, 52.4305, 12)
Map.addLayer(
    s2Image, {'min': 0, 'max': 3000, 'bands': ['B4', 'B3', 'B2']}, 'Sentinel-2 L1C');
Map.addLayer(
    dwRgbHillshade, {'min': 0, 'max': 0.65}, 'Dynamic World V1 - label hillshade');

Map.turnOnInspector()
Map.view()
c:\Users\ihousman\AppData\Local\Programs\Python\Python311\Lib\site-packages\ee\deprecation.py:207: DeprecationWarning: 

Attention required for COPERNICUS/S2! You are using a deprecated asset.
To ensure continued functionality, please update it.
Learn more: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2

  warnings.warn(warning, category=DeprecationWarning)
Adding layer: Sentinel-2 L1C
Adding layer: Dynamic World V1 - label hillshade
Starting webmap
Using default refresh token for geeView
Starting local web server at: http://localhost:1234/geeView/
HTTP server command: "c:\Users\ihousman\AppData\Local\Programs\Python\Python311\python.exe" -m http.server  1234
Done
cwd a:\GEE\gee_py_modules_package\geeViz\examples
geeView URL: http://localhost:1234/geeView/?projectID=lcms-292214&accessToken=ya29.a0AcM612yGCqy4-XqRhQqvY_FFaYjrnoQatPaJ93ai0qIqeAtYt_yKOa0_PpRTbgMjzSmUyCEDWYS-gLn4UNL5NmPWwC1KgxJGVhOfj0FTG-s5FEnuukGbvLRYZVH1lMJsFYiIMhaKb9PgfcAwbxVxFjs8l_fuccsBd75lQBepoQMaCgYKAasSARESFQHGX2Mi1F_7ZzfGG7VXwGv70He2qQ0178

Visualizing Dynamic World over time#

  • Next, we’ll visualize Dynamic World as a seasonal time lapse and summarize zonal stats

  • 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 can take some time to load

Map.clearMap()

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

class_band_name = 'label'

# Specify where to visualize
study_area = ee.Geometry.Polygon(
        [[[4.9269985485747725, 61.11936200243072],
          [4.9269985485747725, 60.187959630772305],
          [7.2561001110747725, 60.187959630772305],
          [7.2561001110747725, 61.11936200243072]]], None, False)

# Which years to include
startYear = 2018
endYear = 2023

# Number of months to include in each frame of the time lapse
nMonths = 3

# Provide visualization info
viz_dict = {f'{class_band_name}_class_names': [
    'water', 'trees', 'grass', 'flooded_vegetation', 'crops',
    'shrub_and_scrub', 'built', 'bare', 'snow_and_ice'],
f'{class_band_name}_class_palette': [
    '419bdf', '397d49', '88b053', '7a87c6', 'e49635', 'dfc35a', 'c4281b',
    'a59b8f', 'b39fe1'],
    f'{class_band_name}_class_values':list(range(0,9))
}

# Get DW
dwCol = ee.ImageCollection('GOOGLE/DYNAMICWORLD/V1')\
        .filterBounds(study_area)\
        .filter(ee.Filter.calendarRange(startYear,endYear,'year'))\
        .select([class_band_name])

# Pull projection for area charting
proj = dwCol.first().projection().getInfo()
crs = proj['crs']
transform = proj['transform']

# Create seasonal mode composites
def getMonths(imgs,yr,startMonth):
    startMonth = ee.Number(startMonth)
    endMonth = startMonth.add(nMonths).subtract(1)
    date = ee.Date.fromYMD(yr,startMonth,1)
    dateString = date.format('YYYY-MM')
    return imgs.filter(ee.Filter.calendarRange(startMonth,endMonth,'month'))\
        .mode().set(viz_dict).set({'system:time_start':date.millis(),'dateString':dateString})
def getYr(yr):
    yrImgs = dwCol.filter(ee.Filter.calendarRange(yr,yr,'year'))
    return ee.FeatureCollection(ee.List.sequence(1,12-nMonths+1,nMonths).map(lambda m:getMonths(yrImgs,yr,m)))
    
# Apply above functions
dwCompSeasonal = ee.FeatureCollection(ee.List.sequence(startYear,endYear).map(getYr)).flatten()
dwCompSeasonal = ee.ImageCollection(dwCompSeasonal)
dates = dwCompSeasonal.aggregate_histogram('dateString').keys().getInfo()

# Add layer to map as a time lapse
Map.addTimeLapse(dwCompSeasonal,{'autoViz':True,'canAreaChart':True,"dateFormat": "YYYY-MM",'years':dates,
        "advanceInterval": "month",'areaChartParams':{'transform':transform,'crs':crs}},'Dynamic World Seasonal Time-Lapse')

# Set up map and view it
Map.centerObject(study_area)
Map.turnOnAutoAreaCharting()
Map.view()
Adding layer: Dynamic World Time-Lapse
Starting webmap
Using default refresh token for geeView
Local web server at: http://localhost:1234/geeView/ already serving.
cwd a:\GEE\gee_py_modules_package\geeViz\examples
geeView URL: http://localhost:1234/geeView/?projectID=lcms-292214&accessToken=ya29.a0AcM612xqiGDcaycnSbAGRNruGn9HQEonDtnU8NC3ZU-4c11MRNqY2FyDVexAiOkUoJQFvOJuby2EDrQ3pYSCW9ABfTJypYe2uqcpNuz2V1zBRQnGPAy1X8gyUlhIOb8lOzeIjAhAV7AL9MHYCQHFCJKJ38TTUd7iOMm3b4YXGjEaCgYKASgSARESFQHGX2MixCTtvcDehFDfDtuskh4RcA0178

Create Sankey Charts#

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

  • Currently, geeViz only supports annual time series (ImageCollection) for Sankey charts

  • 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.clearMap()


# Create annual mode (geeViz only supports annual time series - seasonal time series currently will not work for Sankey charts)
def getYr(yr):
    yrImgs = dwCol.filter(ee.Filter.calendarRange(yr,yr,'year'))
    return yrImgs.mode().set(viz_dict).set('system:time_start',ee.Date.fromYMD(yr,6,1).millis())

# Apply annual composite function
dwCompAnnual = ee.ImageCollection(ee.List.sequence(startYear,endYear).map(getYr))


# Add layer to map as a time lapse
Map.addTimeLapse(dwCompAnnual,{'autoViz':True,'canAreaChart':True,"dateFormat": "YYYY",'years':list(range(startYear,endYear+1)),'areaChartParams':{'transform':transform,'crs':crs,'line':True,'sankey':True}},'Dynamic World Annual Time-Lapse')

# Set up map and view it
Map.centerObject(study_area)
Map.turnOnAutoAreaCharting()
Map.view()
Adding layer: Dynamic World Annual Time-Lapse
Starting webmap
Using default refresh token for geeView
Local web server at: http://localhost:1234/geeView/ already serving.
cwd a:\GEE\gee_py_modules_package\geeViz\examples
geeView URL: http://localhost:1234/geeView/?projectID=lcms-292214&accessToken=ya29.a0AcM612zGxlR7g2m06p2w_g97qFinVqTn0jWBwkUd7Ajsww2oZAou5J3XU8mLwFQe70M8qw64OeJPA6b5NAoUJxq5Uzxm7IvlyvOwROguzYwXXoIuNhdZswlG7AcNILR05Ut_74LWjabkPQjKkvs1n4D7UKpntSAgsRlCAoVRCWEaCgYKAX0SARESFQHGX2MiA7fum6YIB_wnXdrpRp3weg0178