Files
RS4OSINT/docs/B4_Vectors_Tables.html
Ollie Ballinger 9eb020dab7 fixed image paths
2023-04-17 11:46:07 +01:00

2587 lines
321 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.3.326">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - Vectors and Tables</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="site_libs/quarto-nav/quarto-nav.js"></script>
<script src="site_libs/quarto-nav/headroom.min.js"></script>
<script src="site_libs/clipboard/clipboard.min.js"></script>
<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="site_libs/quarto-search/fuse.min.js"></script>
<script src="site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="./">
<link href="./C1_Lights.html" rel="next">
<link href="./B3_Image_Series.html" rel="prev">
<link href="./../favicon.ico" rel="icon">
<script src="site_libs/quarto-html/quarto.js"></script>
<script src="site_libs/quarto-html/popper.min.js"></script>
<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="site_libs/quarto-html/anchor.min.js"></script>
<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="site_libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-dark.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<script src="site_libs/bootstrap/bootstrap.min.js"></script>
<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="site_libs/bootstrap/bootstrap.min.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="site_libs/bootstrap/bootstrap-dark.min.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
<script id="quarto-search-options" type="application/json">{
"location": "sidebar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "start",
"type": "textbox",
"limit": 20,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit"
}
}</script>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-RK9ZLZQ6GL"></script>
<script type="text/javascript">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-RK9ZLZQ6GL', { 'anonymize_ip': true});
</script>
</head>
<body class="nav-sidebar floating">
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./B1_Getting_Started.html">B. Google Earth Engine</a></li><li class="breadcrumb-item"><a href="./B4_Vectors_Tables.html"><span class="chapter-number">7</span>&nbsp; <span class="chapter-title">Vectors and Tables</span></a></li></ol></nav>
<a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i>
</button>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal sidebar-navigation floating overflow-auto">
<div class="pt-lg-2 mt-2 text-left sidebar-header sidebar-header-stacked">
<a href="./index.html" class="sidebar-logo-link">
<img src="./../images/logo_white.png" alt="" class="sidebar-logo py-0 d-lg-inline d-none">
</a>
<div class="sidebar-title mb-0 py-0">
<a href="./">Remote Sensing for OSINT</a>
<div class="sidebar-tools-main tools-wide">
<a href="https://github.com/oballinger/RS4OSINT/" title="Source Code" class="quarto-navigation-tool px-1" aria-label="Source Code"><i class="bi bi-github"></i></a>
<div class="dropdown">
<a href="" title="Download" id="quarto-navigation-tool-dropdown-0" class="quarto-navigation-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Download"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="quarto-navigation-tool-dropdown-0">
<li>
<a class="dropdown-item sidebar-tools-main-item" href="./Remote-Sensing-
-for-OSINT.pdf">
<i class="bi bi-bi-file-pdf pe-1"></i>
Download PDF
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="./Remote-Sensing-
-for-OSINT.epub">
<i class="bi bi-bi-journal pe-1"></i>
Download ePub
</a>
</li>
</ul>
</div>
<div class="dropdown">
<a href="" title="Share" id="quarto-navigation-tool-dropdown-1" class="quarto-navigation-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Share"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="quarto-navigation-tool-dropdown-1">
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://twitter.com/intent/tweet?url=|url|">
<i class="bi bi-bi-twitter pe-1"></i>
Twitter
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://www.facebook.com/sharer/sharer.php?u=|url|">
<i class="bi bi-bi-facebook pe-1"></i>
Facebook
</a>
</li>
</ul>
</div>
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
</div>
</div>
</div>
<div class="mt-2 flex-shrink-0 align-items-center">
<div class="sidebar-search">
<div id="quarto-search" class="" title="Search"></div>
</div>
</div>
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<span class="menu-text">A. Introduction</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./index.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Overview</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./A2_Remote_Sensing.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Remote Sensing</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./A3_Data_Acquisition.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Data Acquisition</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="true">
<span class="menu-text">B. Google Earth Engine</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B1_Getting_Started.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Getting Started</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B2_Interpreting_Images.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Interpreting Images</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B3_Image_Series.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Image Series</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Vectors and Tables</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="false">
<span class="menu-text">C. Case Studies</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C1_Lights.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">War at Night</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C2_Refineries.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Refinery Identification</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C3_Blast.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Blast Damage Assessment</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C4_Ships.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Ship Detection</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C5_Object_Detection.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Object Detection</span></a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 id="toc-title">Table of contents</h2>
<ul>
<li><a href="#exploring-vectors" id="toc-exploring-vectors" class="nav-link active" data-scroll-target="#exploring-vectors">Exploring Vectors</a>
<ul class="collapse">
<li><a href="#using-geometry-tools-to-create-features-in-earth-engine" id="toc-using-geometry-tools-to-create-features-in-earth-engine" class="nav-link" data-scroll-target="#using-geometry-tools-to-create-features-in-earth-engine">Using Geometry Tools to Create Features in Earth Engine</a></li>
<li><a href="#loading-existing-features-and-feature-collections-in-earth-engine" id="toc-loading-existing-features-and-feature-collections-in-earth-engine" class="nav-link" data-scroll-target="#loading-existing-features-and-feature-collections-in-earth-engine">Loading Existing Features and Feature Collections in Earth Engine</a></li>
<li><a href="#importing-features-into-earth-engine" id="toc-importing-features-into-earth-engine" class="nav-link" data-scroll-target="#importing-features-into-earth-engine">Importing Features into Earth Engine</a></li>
<li><a href="#filtering-feature-collections-by-attributes" id="toc-filtering-feature-collections-by-attributes" class="nav-link" data-scroll-target="#filtering-feature-collections-by-attributes">Filtering Feature Collections by Attributes</a></li>
<li><a href="#reducing-images-using-feature-geometry" id="toc-reducing-images-using-feature-geometry" class="nav-link" data-scroll-target="#reducing-images-using-feature-geometry">Reducing Images Using Feature Geometry</a></li>
<li><a href="#identifying-the-block-in-the-neighborhood-surrounding-usf-with-the-highest-ndvi" id="toc-identifying-the-block-in-the-neighborhood-surrounding-usf-with-the-highest-ndvi" class="nav-link" data-scroll-target="#identifying-the-block-in-the-neighborhood-surrounding-usf-with-the-highest-ndvi">Identifying the Block in the Neighborhood Surrounding USF with the Highest NDVI</a></li>
<li><a href="#conclusion" id="toc-conclusion" class="nav-link" data-scroll-target="#conclusion">Conclusion</a></li>
</ul></li>
<li><a href="#rastervector-conversions" id="toc-rastervector-conversions" class="nav-link" data-scroll-target="#rastervector-conversions">Raster/Vector Conversions</a>
<ul class="collapse">
<li><a href="#raster-to-vector-conversion" id="toc-raster-to-vector-conversion" class="nav-link" data-scroll-target="#raster-to-vector-conversion">Raster to Vector Conversion</a></li>
<li><a href="#a-more-complex-example" id="toc-a-more-complex-example" class="nav-link" data-scroll-target="#a-more-complex-example">3. A More Complex Example</a></li>
<li><a href="#vector-to-raster-conversion" id="toc-vector-to-raster-conversion" class="nav-link" data-scroll-target="#vector-to-raster-conversion">Vector-to-Raster Conversion</a></li>
<li><a href="#conclusion-1" id="toc-conclusion-1" class="nav-link" data-scroll-target="#conclusion-1">Conclusion</a></li>
</ul></li>
<li><a href="#zonal-statistics" id="toc-zonal-statistics" class="nav-link" data-scroll-target="#zonal-statistics">Zonal Statistics</a>
<ul class="collapse">
<li><a href="#functions" id="toc-functions" class="nav-link" data-scroll-target="#functions">Functions</a></li>
<li><a href="#point-collection-creation" id="toc-point-collection-creation" class="nav-link" data-scroll-target="#point-collection-creation">Point Collection Creation</a></li>
<li><a href="#neighborhood-statistic-examples" id="toc-neighborhood-statistic-examples" class="nav-link" data-scroll-target="#neighborhood-statistic-examples">Neighborhood Statistic Examples</a></li>
<li><a href="#additional-notes" id="toc-additional-notes" class="nav-link" data-scroll-target="#additional-notes">Additional Notes</a></li>
<li><a href="#conclusion-2" id="toc-conclusion-2" class="nav-link" data-scroll-target="#conclusion-2">Conclusion</a></li>
<li><a href="#references" id="toc-references" class="nav-link" data-scroll-target="#references">References</a></li>
</ul></li>
<li><a href="#advanced-vector-operations" id="toc-advanced-vector-operations" class="nav-link" data-scroll-target="#advanced-vector-operations">Advanced Vector Operations</a>
<ul class="collapse">
<li><a href="#visualizing-feature-collections" id="toc-visualizing-feature-collections" class="nav-link" data-scroll-target="#visualizing-feature-collections">Visualizing Feature Collections</a></li>
<li><a href="#joins-with-feature-collections" id="toc-joins-with-feature-collections" class="nav-link" data-scroll-target="#joins-with-feature-collections">Joins with Feature Collections</a></li>
<li><a href="#conclusion-3" id="toc-conclusion-3" class="nav-link" data-scroll-target="#conclusion-3">Conclusion</a></li>
</ul></li>
</ul>
<div class="toc-actions"><div><i class="bi bi-github"></i></div><div class="action-links"><p><a href="https://github.com/oballinger/RS4OSINT/edit/main/B4_Vectors_Tables.qmd" class="toc-action">Edit this page</a></p></div></div></nav>
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title"><span class="chapter-title">Vectors and Tables</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>In addition to raster data processing, Earth Engine supports a rich set of vector processing tools. This Part introduces you to the vector framework in Earth Engine, shows you how to create and to import your vector data, and how to combine vector and raster data for analyses.</p>
<section id="exploring-vectors" class="level2">
<h2 class="anchored" data-anchor-id="exploring-vectors">Exploring Vectors</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author">Author</h4>
<p>AJ Purdy, Ellen Brock, David Saah</p>
</section>
<section id="overview" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview">Overview</h4>
<p>In this chapter, you will learn about features and feature collections and how to use them in conjunction with images and image collections in Earth Engine. Maps are useful for understanding spatial patterns, but scientists often need to extract statistics to answer a question. For example, you may make a false-color composite showing which areas of San Francisco are more “green”—i.e., have more healthy vegetation—than others, but you will likely not be able to directly determine which block in a neighborhood is the most green. This tutorial will demonstrate how to do just that by utilizing vectors.</p>
<p>As described in Chap. F4.0, an important way to summarize and simplify data in Earth Engine is through the use of reducers. Reducers operating across space were used in Chap. F3.0, for example, to enable image regression between bands. More generally, chapters in Part F3 and Part F4 used reducers mostly to summarize the values across bands or images on a pixel-by-pixel basis. What if you wanted to summarize information within the confines of given spatial elements- for example, within a set of polygons? In this chapter, we will illustrate and explore Earth Engines method for doing that, which is through a reduceRegions call.</p>
</section>
<section id="learning-outcomes" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes">Learning Outcomes</h4>
<ul>
<li>Uploading and working with a shapefile as an asset to use in Earth Engine.</li>
<li>Creating a new feature using the geometry tools.</li>
<li>Importing and filtering a feature collection in Earth Engine.</li>
<li>Using a feature to clip and reduce image values within a geometry.</li>
<li>Use reduceRegions to summarize an image in irregular neighborhoods.</li>
<li>Exporting calculated data to tables with Tasks.</li>
</ul>
</section>
<section id="assumes-you-know-how-to" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to">Assumes you know how to:</h3>
<ul>
<li>Import images and image collections, filter, and visualize (Part F1).</li>
<li>Calculate and interpret vegetation indices (Chap. F2.0).</li>
<li>Use drawing tools to create points, lines, and polygons (Chap. F2.1).</li>
</ul>
</section>
</div>
</div>
<section id="introduction" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction">Introduction</h3>
<p>In the world of geographic information systems (GIS), data is typically thought of in one of two basic data structures: raster and vector. In previous chapters, we have principally been focused on raster data—data using the remote sensing vocabulary of pixels, spatial resolution, images, and image collections. Working within the vector framework is also a crucial skill to master. If you dont know much about GIS, you can find any number of online explainers of the distinctions between these data types, their strengths and limitations, and analyses using both data types. Being able to move fluidly between a raster conception and a vector conception of the world is powerful, and is facilitated with specialized functions and approaches in Earth Engine.</p>
<p>For our purposes, you can think of vector data as information represented as points (e.g., locations of sample sites), lines (e.g., railroad tracks), or polygons (e.g., the boundary of a national park or a neighborhood). Line data and polygon data are built up from points: for example, the latitude and longitude of the sample sites, the points along the curve of the railroad tracks, and the corners of the park that form its boundary. These points each have a highly specific location on Earths surface, and the vector data formed from them can be used for calculations with respect to other layers. As will be seen in this chapter, for example, a polygon can be used to identify which pixels in an image are contained within its borders. Point-based data have already been used in earlier chapters for filtering image collections by location (see Part F1), and can also be used to extract values from an image at a point or a set of points (see Chap. F5.2). Lines possess the dimension of length and have similar capabilities for filtering image collections and accessing their values along a transect. In addition to using polygons to summarize values within a boundary, they can be used for other, similar purposes—for example, to clip an image.</p>
<p>As you have seen, raster features in Earth Engine are stored as an Image or as part of an ImageCollection. Using a similar conceptual model, vector data in Earth Engine is stored as a Feature or as part of a FeatureCollection. Features and feature collections provide useful data to filter images and image collections by their location, clip images to a boundary, or statistically summarize the pixel values within a region.</p>
<p>In the following example, you will use features and feature collections to identify which city block near the University of San Francisco (USF) campus is the most green.</p>
</section>
<section id="using-geometry-tools-to-create-features-in-earth-engine" class="level3">
<h3 class="anchored" data-anchor-id="using-geometry-tools-to-create-features-in-earth-engine">Using Geometry Tools to Create Features in Earth Engine</h3>
<p>To demonstrate how geometry tools in Earth Engine work, lets start by creating a point, and two polygons to represent different elements on the USF campus.</p>
<p>Click on the geometry tools in the top left of the Map pane and create a point feature. Place a new point where USF is located (see Fig. F5.0.1).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image54.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.1 Location of the USF campus in San Francisco, California. Your first point should be in this vicinity. The red arrow points to the geometry tools.</figcaption><p></p>
</figure>
</div>
<p>Use Google Maps to search for “Harney Science Center” or “Lo Schiavo Center for Science.” Hover your mouse over the Geometry Imports to find the +new layer menu item and add a new layer to delineate the boundary of a building on campus.</p>
<p>Next, create another new layer to represent the entire campus as a polygon.</p>
<p>After you create these layers, rename the geometry imports at the top of your script. Name the layers usf_point, usf_building, and usf_campus. These names are used within the script shown in Fig. F5.0.2.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image10.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.2 Rename the default variable names for each layer in the Imports section of the code at the top of your script</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F50a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="loading-existing-features-and-feature-collections-in-earth-engine" class="level3">
<h3 class="anchored" data-anchor-id="loading-existing-features-and-feature-collections-in-earth-engine">Loading Existing Features and Feature Collections in Earth Engine</h3>
<p>If you wish to have the exact same geometry imports in this chapter for the rest of this exercise, begin this section using the code at the Code Checkpoint above.</p>
<p>Next, you will load a city block dataset to determine the amount of vegetation on blocks near USF. The code below imports an existing feature dataset in Earth Engine. The Topologically Integrated Geographic Encoding and Referencing (TIGER) boundaries are census-designated boundaries that are a useful resource when comparing socioeconomic and diversity metrics with environmental datasets in the United States.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the Census Tiger Boundaries from GEE. </span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> tiger <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'TIGER/2010/Blocks'</span>)<span class="op">;</span> </span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the new feature collection to the map, but do not display. </span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(tiger<span class="op">,</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'black'</span>}<span class="op">,</span> <span class="st">'Tiger'</span><span class="op">,</span> <span class="kw">false</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>You should now have the geometry for USFs campus and a layer added to your map that is not visualized for census blocks across the United States. Next, we will use neighborhood data to spatially filter the TIGER feature collection for blocks near USFs campus.</p>
</section>
<section id="importing-features-into-earth-engine" class="level3">
<h3 class="anchored" data-anchor-id="importing-features-into-earth-engine">Importing Features into Earth Engine</h3>
<p>There are many image collections loaded in Earth Engine, and they can cover a very large area that you might want to study. Borders can be quite intricate (for example, a detailed coastline), and fortunately there is no need for you to digitize the intricate boundary of a large geographic area. Instead, we will show how to find a spatial dataset online, download the data, and load this into Earth Engine as an asset for use.</p>
<section id="find-a-spatial-dataset-of-san-francisco-neighborhoods" class="level4">
<h4 class="anchored" data-anchor-id="find-a-spatial-dataset-of-san-francisco-neighborhoods">Find a Spatial Dataset of San Francisco Neighborhoods</h4>
<p>Use your internet searching skills to locate the “Analysis Neighborhoods” dataset covering San Francisco. This data might be located in a number of places, including DataSF, the City of San Franciscos public-facing data repository.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image27.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.3 DataSF website neighborhood shapefile to download</figcaption><p></p>
</figure>
</div>
<p>After you find the Analysis Neighborhoods layer, click Export and select Shapefile (Fig. F5.0.3). Keep track of where you save the zipped file, as we will load this into Earth Engine. Shapefiles contain vector-based data—points, lines, polygons—and include a number of files, such as the location information, attribute information, and others.</p>
<p>Extract the folder to your computer. When you open the folder, you will see that there are actually many files. The extensions (.shp, .dbf, .shx, .prj) all provide a different piece of information to display vector-based data. The .shp file provides data on the geometry. The .dbf file provides data about the attributes. The .shx file is an index file. Lastly, the .prj file describes the map projection of the coordinate information for the shapefile. You will need to load all four files to create a new feature asset in Earth Engine.</p>
</section>
<section id="upload-sf-neighborhoods-file-as-an-asset" class="level4">
<h4 class="anchored" data-anchor-id="upload-sf-neighborhoods-file-as-an-asset">Upload SF Neighborhoods File as an Asset</h4>
<p>Navigate to the Assets tab (near Scripts). Select New &gt; Table Upload &gt; Shape files (Fig. F5.0.4).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image52.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.4 Import an asset as a zipped folder</figcaption><p></p>
</figure>
</div>
</section>
<section id="select-files-and-name-asset" class="level4">
<h4 class="anchored" data-anchor-id="select-files-and-name-asset">Select Files and Name Asset</h4>
<p>Click the Select button and then use the file navigator to select the component files of the shapefile structure (i.e., .shp, .dbf, .shx, and .prj) (Fig. F5.0.5). Assign an Asset Name so you can recognize this asset.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image43.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.5 Select the four files extracted from the zipped folder. Make sure each file has the same name and that there are no spaces in the file names of the component files of the shapefile structure.</figcaption><p></p>
</figure>
</div>
<p>Uploading the asset may take a few minutes. The status of the upload is presented under the Tasks tab. After your asset has been successfully loaded, click on the asset in the Assets folder and find the collection ID. Copy this text and use it to import the file into your Earth Engine analysis.</p>
<p>Assign the asset to the table (collection) ID using the script below. Note that you will need to replace path/to/your/asset/assetname with the actual path copied in the previous step.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Assign the feature collection to the variable sfNeighborhoods. </span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfNeighborhoods <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>( <span class="st">'path/to/your/asset/assetname'</span>)<span class="op">;</span> </span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Print the size of the feature collection. </span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">// (Answers the question how many features?) </span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(sfNeighborhoods<span class="op">.</span><span class="fu">size</span>())<span class="op">;</span> </span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfNeighborhoods<span class="op">,</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'blue'</span>}<span class="op">,</span> <span class="st">'sfNeighborhoods'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note that if you have any trouble with loading the FeatureCollection using the technique above, you can follow directions in the Checkpoint script below to use an existing asset loaded for this exercise.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F50b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="filtering-feature-collections-by-attributes" class="level3">
<h3 class="anchored" data-anchor-id="filtering-feature-collections-by-attributes">Filtering Feature Collections by Attributes</h3>
<section id="filter-by-geometry-of-another-feature" class="level4">
<h4 class="anchored" data-anchor-id="filter-by-geometry-of-another-feature">Filter by Geometry of Another Feature</h4>
<p>First, lets find the neighborhood associated with USF. Use the first point you created to find the neighborhood that intersects this point; filterBounds is the tool that does that, returning a filtered feature.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter sfNeighborhoods by USF. </span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> usfNeighborhood <span class="op">=</span> sfNeighborhoods<span class="op">.</span><span class="fu">filterBounds</span>(usf_point)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now, filter the blocks layer by USFs neighborhood and visualize it on the map.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter the Census blocks by the boundary of the neighborhood layer. </span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> usfTiger <span class="op">=</span> tiger<span class="op">.</span><span class="fu">filterBounds</span>(usfNeighborhood)<span class="op">;</span> </span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(usfTiger<span class="op">,</span> {}<span class="op">,</span> <span class="st">'usf_Tiger'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="filter-by-feature-attribute-properties" class="level4">
<h4 class="anchored" data-anchor-id="filter-by-feature-attribute-properties">Filter by Feature (Attribute) Properties</h4>
<p>In addition to filtering a FeatureCollection by the location of another feature, you can also filter it by its properties. First, lets print the usfTiger variable to the Console and inspect the object.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(usfTiger)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>You can click on the feature collection name in the Console to uncover more information about the dataset. Click on the columns to learn about what attribute information is contained in this dataset. You will notice this feature collection contains information on both housing (housing10) and population (pop10).</p>
<p>Now you will filter for blocks with just the right amount of housing units. You dont want it too dense, nor do you want too few neighbors.</p>
<p>Filter the blocks to have fewer than 250 housing units.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter for census blocks by housing units. </span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> housing10_l250 <span class="op">=</span> usfTiger </span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">lt</span>(<span class="st">'housing10'</span><span class="op">,</span> <span class="dv">250</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now filter the already-filtered blocks to have more than 50 housing units.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> housing10_g50_l250 <span class="op">=</span> housing10_l250<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">gt</span>( <span class="st">'housing10'</span><span class="op">,</span> <span class="dv">50</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now, lets visualize what this looks like.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(housing10_g50_l250<span class="op">,</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'Magenta'</span>}<span class="op">,</span> <span class="st">'housing'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We have combined spatial and attribute information to narrow the set to only those blocks that meet our criteria of having between 50 and 250 housing units.</p>
</section>
<section id="print-feature-attribute-properties-to-console" class="level4">
<h4 class="anchored" data-anchor-id="print-feature-attribute-properties-to-console">Print Feature (Attribute) Properties to Console</h4>
<p>We can print out attribute information about these features. The block of code below prints out the area of the resultant geometry in square meters.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> housing_area <span class="op">=</span> housing10_g50_l250<span class="op">.</span><span class="fu">geometry</span>()<span class="op">.</span><span class="fu">area</span>()<span class="op">;</span> </span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'housing_area:'</span><span class="op">,</span> housing_area)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The next block of code reduces attribute information and prints out the mean of the housing10 column.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> housing10_mean <span class="op">=</span> usfTiger<span class="op">.</span><span class="fu">reduceColumns</span>({ </span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">mean</span>()<span class="op">,</span> </span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">selectors</span><span class="op">:</span> [<span class="st">'housing10'</span>] </span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'housing10_mean'</span><span class="op">,</span> housing10_mean)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Both of the above sections of code provide meaningful information about each feature, but they do not tell us which block is the most green. The next section will address that question.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F50c. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="reducing-images-using-feature-geometry" class="level3">
<h3 class="anchored" data-anchor-id="reducing-images-using-feature-geometry">Reducing Images Using Feature Geometry</h3>
<p>Now that we have identified the blocks around USFs campus that have the right housing density, lets find which blocks are the greenest.</p>
<p>The Normalized Difference Vegetation Index (NDVI), presented in detail in Chap. F2.0, is often used to compare the greenness of pixels in different locations. Values on land range from 0 to 1, with values closer to 1 representing healthier and greener vegetation than values near 0.</p>
<section id="create-an-ndvi-image" class="level4">
<h4 class="anchored" data-anchor-id="create-an-ndvi-image">Create an NDVI Image</h4>
<p>The code below imports the Landsat 8 ImageCollection as landsat8. Then, the code filters for images in 2021. Lastly, the code sorts the images from 2021 to find the least cloudy day.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the Landsat 8 TOA image collection. </span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> landsat8 <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LC08/C02/T1_TOA'</span>)<span class="op">;</span> </span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Get the least cloudy image in 2015. </span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> image <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>( </span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> landsat8 </span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(usf_point) </span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(<span class="st">'2015-01-01'</span><span class="op">,</span> <span class="st">'2015-12-31'</span>) </span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sort</span>(<span class="st">'CLOUD_COVER'</span>) </span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>())<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The next section of code assigns the near-infrared band (B5) to variable nir and assigns the red band (B4) to red. Then the bands are combined together to compute NDVI as (nir red)/(nir + red).</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> nir <span class="op">=</span> image<span class="op">.</span><span class="fu">select</span>(<span class="st">'B5'</span>)<span class="op">;</span> </span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> red <span class="op">=</span> image<span class="op">.</span><span class="fu">select</span>(<span class="st">'B4'</span>)<span class="op">;</span> </span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndvi <span class="op">=</span> nir<span class="op">.</span><span class="fu">subtract</span>(red)<span class="op">.</span><span class="fu">divide</span>(nir<span class="op">.</span><span class="fu">add</span>(red))<span class="op">.</span><span class="fu">rename</span>(<span class="st">'NDVI'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="clip-the-ndvi-image-to-the-blocks-near-usf" class="level4">
<h4 class="anchored" data-anchor-id="clip-the-ndvi-image-to-the-blocks-near-usf">Clip the NDVI Image to the Blocks Near USF</h4>
<p>Next, you will clip the NDVI layer to only show NDVI over USFs neighborhood.</p>
<p>The first section of code provides visualization settings.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviParams <span class="op">=</span> { </span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="dv">1</span><span class="op">,</span> </span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'blue'</span><span class="op">,</span> <span class="st">'white'</span><span class="op">,</span> <span class="st">'green'</span>] </span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The second block of code clips the image to our filtered housing layer.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviUSFblocks <span class="op">=</span> ndvi<span class="op">.</span><span class="fu">clip</span>(housing10_g50_l250)<span class="op">;</span> </span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(ndviUSFblocks<span class="op">,</span> ndviParams<span class="op">,</span> <span class="st">'NDVI image'</span>)<span class="op">;</span> </span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(usf_point<span class="op">,</span> <span class="dv">14</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The NDVI map for all of San Francisco is interesting, and shows variability across the region. Now, lets compute mean NDVI values for each block of the city.</p>
</section>
<section id="compute-ndvi-statistics-by-block" class="level4">
<h4 class="anchored" data-anchor-id="compute-ndvi-statistics-by-block">Compute NDVI Statistics by Block</h4>
<p>The code below uses the clipped image ndviUSFblocks and computes the mean NDVI value within each boundary. The scale provides a spatial resolution for the mean values to be computed on.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Reduce image by feature to compute a statistic e.g. mean, max, min etc. </span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviPerBlock <span class="op">=</span> ndviUSFblocks<span class="op">.</span><span class="fu">reduceRegions</span>({ </span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> housing10_g50_l250<span class="op">,</span> </span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">mean</span>()<span class="op">,</span> </span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">30</span><span class="op">,</span> </span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now well use Earth Engine to find out which block is greenest.</p>
</section>
<section id="export-table-of-ndvi-data-by-block-from-earth-engine-to-google-drive" class="level4">
<h4 class="anchored" data-anchor-id="export-table-of-ndvi-data-by-block-from-earth-engine-to-google-drive">Export Table of NDVI Data by Block from Earth Engine to Google Drive</h4>
<p>Just as we loaded a feature into Earth Engine, we can export information from Earth Engine. Here, we will export the NDVI data, summarized by block, from Earth Engine to a Google Drive space so that we can interpret it in a program like Google Sheets or Excel.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Get a table of data out of Google Earth Engine. </span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>Export<span class="op">.</span><span class="at">table</span><span class="op">.</span><span class="fu">toDrive</span>({ </span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> ndviPerBlock<span class="op">,</span> </span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">description</span><span class="op">:</span> <span class="st">'NDVI_by_block_near_USF'</span> </span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>When you run this code, you will notice that you have the Tasks tab highlighted on the top right of the Earth Engine Code Editor (Fig. F5.0.6). You will be prompted to name the directory when exporting the data.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image4.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.0.6 Under the Tasks tab, select Run to initiate download</figcaption><p></p>
</figure>
</div>
<p>After you run the task, the file will be saved to your Google Drive. You have now brought a feature into Earth Engine and also exported data from Earth Engine.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F50d. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="identifying-the-block-in-the-neighborhood-surrounding-usf-with-the-highest-ndvi" class="level3">
<h3 class="anchored" data-anchor-id="identifying-the-block-in-the-neighborhood-surrounding-usf-with-the-highest-ndvi">Identifying the Block in the Neighborhood Surrounding USF with the Highest NDVI</h3>
<p>You are already familiar with filtering datasets by their attributes. Now you will sort a table and select the first element of the table.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>ndviPerBlock <span class="op">=</span> ndviPerBlock<span class="op">.</span><span class="fu">select</span>([<span class="st">'blockid10'</span><span class="op">,</span> <span class="st">'mean'</span>])<span class="op">;</span> </span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'ndviPerBlock'</span><span class="op">,</span> ndviPerBlock)<span class="op">;</span> </span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviPerBlockSorted <span class="op">=</span> ndviPerBlock<span class="op">.</span><span class="fu">sort</span>(<span class="st">'mean'</span><span class="op">,</span> <span class="kw">false</span>)<span class="op">;</span> </span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviPerBlockSortedFirst <span class="op">=</span> ee<span class="op">.</span><span class="fu">Feature</span>(ndviPerBlock<span class="op">.</span><span class="fu">sort</span>(<span class="st">'mean'</span><span class="op">,</span><span class="kw">false</span>) <span class="co">//Sort by NDVI mean in descending order. </span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>())<span class="op">;</span> <span class="co">//Select the block with the highest NDVI. </span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'ndviPerBlockSortedFirst'</span><span class="op">,</span> ndviPerBlockSortedFirst)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>If you expand the feature of ndviPerBlockSortedFirst in the Console, you will be able to identify the blockid10 value of the greenest block and the mean NDVI value for that area.</p>
<p>Another way to look at the data is by exporting the data to a table. Open the table using Google Sheets or Excel. You should see a column titled “mean.” Sort the mean column in descending order from highest NDVI to lowest NDVI, then use the blockid10 attribute to filter our feature collection one last time and display the greenest block near USF.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Now filter by block and show on map! </span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> GreenHousing <span class="op">=</span> usfTiger<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">eq</span>(<span class="st">'blockid10'</span><span class="op">,</span> </span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="st">'###'</span>))<span class="op">;</span> <span class="co">//&lt; Put your id here prepend a 0! </span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(GreenHousing<span class="op">,</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'yellow'</span>}<span class="op">,</span> <span class="st">'Green Housing!'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F50e. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="conclusion" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion">Conclusion</h3>
<p>In this chapter, you learned how to import features into Earth Engine. In Sect. 1, you created new features using the geometry tools and loaded a feature from Earth Engines Data Catalog. In Sect. 2, you loaded a shapefile to an Earth Engine asset. In Sect. 3, you filtered feature collections based on their properties and locations. Finally, in Sects. 4 and 5, you used a feature collection to reduce an image, then exported the data from Earth Engine. Now you have all the tools you need to load, filter, and apply features to extract meaningful information from images using vector features in Earth Engine.</p>
</section>
</section>
<section id="rastervector-conversions" class="level2">
<h2 class="anchored" data-anchor-id="rastervector-conversions">Raster/Vector Conversions</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author-1">Author</h4>
<p>Keiko Nomura, Samuel Bowers</p>
</section>
<section id="overview-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview-1">Overview</h4>
<p>The purpose of this chapter is to review methods of converting between raster and vector data formats, and to understand the circumstances in which this is useful. By way of example, this chapter focuses on topographic elevation and forest cover change in Colombia, but note that these are generic methods that can be applied in a wide variety of situations.</p>
</section>
<section id="learning-outcomes-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes-1">Learning Outcomes</h4>
<ul>
<li>Understanding raster and vector data in Earth Engine and their differing properties.</li>
<li>Knowing how and why to convert from raster to vector.</li>
<li>Knowing how and why to convert from vector to raster.</li>
<li>Write a function and map it over a FeatureCollection.</li>
</ul>
</section>
<section id="assumes-you-know-how-to-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to-1">Assumes you know how to:</h4>
<ul>
<li>Import images and image collections, filter, and visualize (Part F1).</li>
<li>Understand distinctions among Image, ImageCollection, Feature and FeatureCollection Earth Engine objects (Part F1, Part F2, Part F5).</li>
<li>Perform basic image analysis: select bands, compute indices, create masks (Part F2).</li>
<li>Perform image morphological operations (Chap. F3.2).</li>
<li>Understand the filter, map, reduce paradigm (Chap. F4.0).</li>
<li>Write a function and map it over an ImageCollection (Chap. F4.0).</li>
<li>Use reduceRegions to summarize an image in irregular shapes (Chap. F5.0).</li>
</ul>
</section>
</div>
</div>
<section id="introduction-1" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction-1">Introduction</h3>
<p>Raster data consists of regularly spaced pixels arranged into rows and columns, familiar as the format of satellite images. Vector data contains geometry features (i.e., points, lines, and polygons) describing locations and areas. Each data format has its advantages, and both will be encountered as part of GIS operations.</p>
<p>Raster and vector data are commonly combined (e.g., extracting image information for a given location or clipping an image to an area of interest); however, there are also situations in which conversion between the two formats is useful. In making such conversions, it is important to consider the key advantages of each format. Rasters can store data efficiently where each pixel has a numerical value, while vector data can more effectively represent geometric features where homogenous areas have shared properties. Each format lends itself to distinctive analytical operations, and combining them can be powerful.</p>
<p>In this exercise, well use topographic elevation and forest change images in Colombia as well as a protected area feature collection to practice the conversion between raster and vector formats, and to identify situations in which this is worthwhile.</p>
</section>
<section id="raster-to-vector-conversion" class="level3">
<h3 class="anchored" data-anchor-id="raster-to-vector-conversion">Raster to Vector Conversion</h3>
<section id="raster-to-polygons" class="level4">
<h4 class="anchored" data-anchor-id="raster-to-polygons">Raster to Polygons</h4>
<p>In this section we will convert an elevation image (raster) to a feature collection (vector). We will start by loading the Global Multi-Resolution Terrain Elevation Data 2010 and the Global Administrative Unit Layers 2015 dataset to focus on Colombia. The elevation image is a raster at 7.5 arc-second spatial resolution containing a continuous measure of elevation in meters in each pixel.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Load raster (elevation) and vector (colombia) datasets. </span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevation <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'USGS/GMTED2010'</span>)<span class="op">.</span><span class="fu">rename</span>(<span class="st">'elevation'</span>)<span class="op">;</span> </span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> colombia <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>( <span class="st">'FAO/GAUL_SIMPLIFIED_500m/2015/level0'</span>) </span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">equals</span>(<span class="st">'ADM0_NAME'</span><span class="op">,</span> <span class="st">'Colombia'</span>))<span class="op">;</span> </span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="co">// Display elevation image. </span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(colombia<span class="op">,</span> <span class="dv">7</span>)<span class="op">;</span> </span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(elevation<span class="op">,</span> { </span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">4000</span>}<span class="op">,</span> <span class="st">'Elevation'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>When converting an image to a feature collection, we will aggregate the categorical elevation values into a set of categories to create polygon shapes of connected pixels with similar elevations. For this exercise, we will create four zones of elevation by grouping the altitudes to 0-100 m = 0, 100200 m = 1, 200500 m = 2, and &gt;500 m = 3.</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Initialize image with zeros and define elevation zones. </span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> zones <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="dv">0</span>) </span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(elevation<span class="op">.</span><span class="fu">gt</span>(<span class="dv">100</span>)<span class="op">,</span> <span class="dv">1</span>) </span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(elevation<span class="op">.</span><span class="fu">gt</span>(<span class="dv">200</span>)<span class="op">,</span> <span class="dv">2</span>) </span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(elevation<span class="op">.</span><span class="fu">gt</span>(<span class="dv">500</span>)<span class="op">,</span> <span class="dv">3</span>)<span class="op">;</span> </span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Mask pixels below sea level (&lt;= 0 m) to retain only land areas. </span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Name the band with values 0-3 as 'zone'. </span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>zones <span class="op">=</span> zones<span class="op">.</span><span class="fu">updateMask</span>(elevation<span class="op">.</span><span class="fu">gt</span>(<span class="dv">0</span>))<span class="op">.</span><span class="fu">rename</span>(<span class="st">'zone'</span>)<span class="op">;</span> </span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(zones<span class="op">,</span> { </span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">3</span><span class="op">,</span> </span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'white'</span><span class="op">,</span> <span class="st">'yellow'</span><span class="op">,</span> <span class="st">'lime'</span><span class="op">,</span> <span class="st">'green'</span>]<span class="op">,</span> </span>
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.7</span>}<span class="op">,</span> <span class="st">'Elevation zones'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We will convert this zonal elevation image in Colombia to polygon shapes, which is a vector format (termed a FeatureCollection in Earth Engine), using the ee.Image.reduceToVectors method. This will create polygons delineating connected pixels with the same value. In doing so, we will use the same projection and spatial resolution as the image. Please note that loading the vectorized image in the native resolution (231.92 m) takes time to execute. For faster visualization, we set a coarse scale of 1,000 m.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> projection <span class="op">=</span> elevation<span class="op">.</span><span class="fu">projection</span>()<span class="op">;</span> </span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> scale <span class="op">=</span> elevation<span class="op">.</span><span class="fu">projection</span>()<span class="op">.</span><span class="fu">nominalScale</span>()<span class="op">;</span> </span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevationVector <span class="op">=</span> zones<span class="op">.</span><span class="fu">reduceToVectors</span>({ </span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> colombia<span class="op">.</span><span class="fu">geometry</span>()<span class="op">,</span> </span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> projection<span class="op">,</span> </span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">1000</span><span class="op">,</span> <span class="co">// scale geometryType: 'polygon', </span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">eightConnected</span><span class="op">:</span> <span class="kw">false</span><span class="op">,</span> </span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">labelProperty</span><span class="op">:</span> <span class="st">'zone'</span><span class="op">,</span> </span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">bestEffort</span><span class="op">:</span> <span class="kw">true</span><span class="op">,</span> </span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxPixels</span><span class="op">:</span> <span class="fl">1e13</span><span class="op">,</span> </span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">tileScale</span><span class="op">:</span> <span class="dv">3</span> <span class="co">// In case of error. </span></span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb21-15"><a href="#cb21-15" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(elevationVector<span class="op">.</span><span class="fu">limit</span>(<span class="dv">10</span>))<span class="op">;</span> </span>
<span id="cb21-16"><a href="#cb21-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevationDrawn <span class="op">=</span> elevationVector<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'black'</span><span class="op">,</span> </span>
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb21-20"><a href="#cb21-20" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb21-21"><a href="#cb21-21" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(elevationDrawn<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Elevation zone polygon'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="images/F5/image50.png" class="img-fluid"></p>
<p><img src="images/F5/image33.png" class="img-fluid"></p>
<p><img src="images/F5/image36.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image7.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.1 Raster-based elevation (top left) and zones (top right), vectorized elevation zones overlaid on the raster (bottom-left) and vectorized elevation zones only (bottom-right)</figcaption><p></p>
</figure>
</div>
<p>You may have realized that polygons consist of complex lines, including some small polygons with just one pixel. That happens when there are no surrounding pixels of the same elevation zone. You may not need a vector map with such details—if, for instance, you want to produce a regional or global map. We can use a morphological reducer focalMode to simplify the shape by defining a neighborhood size around a pixel. In this example, we will set the kernel radius as four pixels. This operation makes the resulting polygons look much smoother, but less precise (Fig. F5.1.2).</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> zonesSmooth <span class="op">=</span> zones<span class="op">.</span><span class="fu">focalMode</span>(<span class="dv">4</span><span class="op">,</span> <span class="st">'square'</span>)<span class="op">;</span> </span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>zonesSmooth <span class="op">=</span> zonesSmooth<span class="op">.</span><span class="fu">reproject</span>(projection<span class="op">.</span><span class="fu">atScale</span>(scale))<span class="op">;</span> </span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(zonesSmooth<span class="op">,</span> { </span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">3</span><span class="op">,</span> </span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'lime'</span><span class="op">,</span> <span class="st">'green'</span>]<span class="op">,</span> </span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.7</span>}<span class="op">,</span> <span class="st">'Elevation zones (smooth)'</span>)<span class="op">;</span> </span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevationVectorSmooth <span class="op">=</span> zonesSmooth<span class="op">.</span><span class="fu">reduceToVectors</span>({ </span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> colombia<span class="op">.</span><span class="fu">geometry</span>()<span class="op">,</span> </span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> projection<span class="op">,</span> </span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> scale<span class="op">,</span> </span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometryType</span><span class="op">:</span> <span class="st">'polygon'</span><span class="op">,</span> </span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">eightConnected</span><span class="op">:</span> <span class="kw">false</span><span class="op">,</span> </span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">labelProperty</span><span class="op">:</span> <span class="st">'zone'</span><span class="op">,</span> </span>
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">bestEffort</span><span class="op">:</span> <span class="kw">true</span><span class="op">,</span> </span>
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxPixels</span><span class="op">:</span> <span class="fl">1e13</span><span class="op">,</span> </span>
<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">tileScale</span><span class="op">:</span> <span class="dv">3</span> </span>
<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> smoothDrawn <span class="op">=</span> elevationVectorSmooth<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb22-24"><a href="#cb22-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'black'</span><span class="op">,</span> </span>
<span id="cb22-25"><a href="#cb22-25" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb22-26"><a href="#cb22-26" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb22-27"><a href="#cb22-27" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(smoothDrawn<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Elevation zone polygon (smooth)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We can see now that the polygons have more distinct shapes with many fewer small polygons in the new map (Fig. F5.1.2). It is important to note that when you use methods like focalMode (or other, similar methods such as connectedComponents and connectedPixelCount), you need to reproject according to the original image in order to display properly with zoom using the interactive Code Editor.</p>
<p><img src="images/F5/image20.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image37.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.2 Before (left) and after (right) applying focalMode</figcaption><p></p>
</figure>
</div>
</section>
<section id="raster-to-points" class="level4">
<h4 class="anchored" data-anchor-id="raster-to-points">Raster to Points</h4>
<p>Lastly, we will convert a small part of this elevation image into a point vector dataset. For this exercise, we will use the same example and build on the code from the previous subsection. This might be useful when you want to use geospatial data in a tabular format in combination with other conventional datasets such as economic indicators (Fig. F5.1.3).</p>
<p><img src="images/F5/image24.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image11.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.3 Elevation point values with latitude and longitude</figcaption><p></p>
</figure>
</div>
<p>The easiest way to do this is to use sample while activating the geometries parameter. This will extract the points at the centroid of the elevation pixel.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> geometry <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Polygon</span>([ </span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">89.553</span><span class="op">,</span> <span class="op">-</span><span class="fl">0.929</span>]<span class="op">,</span> </span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">89.436</span><span class="op">,</span> <span class="op">-</span><span class="fl">0.929</span>]<span class="op">,</span> </span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">89.436</span><span class="op">,</span> <span class="op">-</span><span class="fl">0.866</span>]<span class="op">,</span> </span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">89.553</span><span class="op">,</span> <span class="op">-</span><span class="fl">0.866</span>]<span class="op">,</span> </span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">89.553</span><span class="op">,</span> <span class="op">-</span><span class="fl">0.929</span>] </span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a>])<span class="op">;</span> </span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a><span class="co">// To zoom into the area, un-comment and run below </span></span>
<span id="cb23-11"><a href="#cb23-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Map.centerObject(geometry,12); </span></span>
<span id="cb23-12"><a href="#cb23-12" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(geometry<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Areas to extract points'</span>)<span class="op">;</span> </span>
<span id="cb23-13"><a href="#cb23-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-14"><a href="#cb23-14" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevationSamples <span class="op">=</span> elevation<span class="op">.</span><span class="fu">sample</span>({ </span>
<span id="cb23-15"><a href="#cb23-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">region</span><span class="op">:</span> geometry<span class="op">,</span> </span>
<span id="cb23-16"><a href="#cb23-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">projection</span><span class="op">:</span> projection<span class="op">,</span> </span>
<span id="cb23-17"><a href="#cb23-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> scale<span class="op">,</span> </span>
<span id="cb23-18"><a href="#cb23-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometries</span><span class="op">:</span> <span class="kw">true</span><span class="op">,</span> </span>
<span id="cb23-19"><a href="#cb23-19" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb23-20"><a href="#cb23-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-21"><a href="#cb23-21" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(elevationSamples<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Points extracted'</span>)<span class="op">;</span> </span>
<span id="cb23-22"><a href="#cb23-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-23"><a href="#cb23-23" aria-hidden="true" tabindex="-1"></a><span class="co">// Add three properties to the output table: </span></span>
<span id="cb23-24"><a href="#cb23-24" aria-hidden="true" tabindex="-1"></a><span class="co">// 'Elevation', 'Longitude', and 'Latitude'. </span></span>
<span id="cb23-25"><a href="#cb23-25" aria-hidden="true" tabindex="-1"></a>elevationSamples <span class="op">=</span> elevationSamples<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(feature) { <span class="kw">var</span> geom <span class="op">=</span> feature<span class="op">.</span><span class="fu">geometry</span>()<span class="op">.</span><span class="fu">coordinates</span>()<span class="op">;</span> <span class="cf">return</span> ee<span class="op">.</span><span class="fu">Feature</span>(<span class="kw">null</span><span class="op">,</span> { <span class="st">'Elevation'</span><span class="op">:</span> ee<span class="op">.</span><span class="fu">Number</span>(feature<span class="op">.</span><span class="fu">get</span>( <span class="st">'elevation'</span>))<span class="op">,</span> <span class="st">'Long'</span><span class="op">:</span> ee<span class="op">.</span><span class="fu">Number</span>(geom<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>))<span class="op">,</span> <span class="st">'Lat'</span><span class="op">:</span> ee<span class="op">.</span><span class="fu">Number</span>(geom<span class="op">.</span><span class="fu">get</span>(<span class="dv">1</span>)) </span>
<span id="cb23-26"><a href="#cb23-26" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> </span>
<span id="cb23-27"><a href="#cb23-27" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb23-28"><a href="#cb23-28" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-29"><a href="#cb23-29" aria-hidden="true" tabindex="-1"></a><span class="co">// Export as CSV. </span></span>
<span id="cb23-30"><a href="#cb23-30" aria-hidden="true" tabindex="-1"></a>Export<span class="op">.</span><span class="at">table</span><span class="op">.</span><span class="fu">toDrive</span>({ </span>
<span id="cb23-31"><a href="#cb23-31" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> elevationSamples<span class="op">,</span> </span>
<span id="cb23-32"><a href="#cb23-32" aria-hidden="true" tabindex="-1"></a> <span class="dt">description</span><span class="op">:</span> <span class="st">'extracted_points'</span><span class="op">,</span> </span>
<span id="cb23-33"><a href="#cb23-33" aria-hidden="true" tabindex="-1"></a> <span class="dt">fileFormat</span><span class="op">:</span> <span class="st">'CSV'</span> </span>
<span id="cb23-34"><a href="#cb23-34" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We can also extract sample points per elevation zone. Below is an example of extracting 10 randomly selected points per elevation zone (Fig. F5.1.4). You can also set different values for each zone using classValues and classPoints parameters to modify the sampling intensity in each class. This may be useful, for instance, to generate point samples for a validation effort.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elevationSamplesStratified <span class="op">=</span> zones<span class="op">.</span><span class="fu">stratifiedSample</span>({ </span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">numPoints</span><span class="op">:</span> <span class="dv">10</span><span class="op">,</span> </span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">classBand</span><span class="op">:</span> <span class="st">'zone'</span><span class="op">,</span> </span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">region</span><span class="op">:</span> geometry<span class="op">,</span> </span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> scale<span class="op">,</span> </span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">projection</span><span class="op">:</span> projection<span class="op">,</span> </span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometries</span><span class="op">:</span> <span class="kw">true</span> </span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(elevationSamplesStratified<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Stratified samples'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image23.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.4 Stratified sampling over different elevation zones</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F51a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="a-more-complex-example" class="level3">
<h3 class="anchored" data-anchor-id="a-more-complex-example">3. A More Complex Example</h3>
<p>In this section well use two global datasets, one to represent raster formats and the other vectors:</p>
<ul>
<li>The Global Forest Change (GFC) dataset: a raster dataset describing global tree cover and change for 2001present.</li>
<li>The World Protected Areas Database: a vector database of global protected areas.</li>
</ul>
<p>The objective will be to combine these two datasets to quantify rates of deforestation in protected areas in the “arc of deforestation” of the Colombian Amazon. The datasets can be loaded into Earth Engine with the following code:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Read input data. </span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="co">// Note: these datasets are periodically updated. </span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Consider searching the Data Catalog for newer versions. </span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> gfc <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'UMD/hansen/global_forest_change_2020_v1_8'</span>)<span class="op">;</span> </span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpa <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'WCMC/WDPA/current/polygons'</span>)<span class="op">;</span> </span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Print assets to show available layers and properties. </span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(gfc)<span class="op">;</span> </span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(wdpa<span class="op">.</span><span class="fu">limit</span>(<span class="dv">10</span>))<span class="op">;</span> <span class="co">// Show first 10 records.</span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a>The GFC <span class="fu">dataset</span> (first presented <span class="kw">in</span> detail <span class="kw">in</span> Chap<span class="op">.</span> <span class="at">F1</span><span class="op">.</span><span class="dv">1</span>) is a <span class="bu">global</span> <span class="kw">set</span> <span class="kw">of</span> rasters that quantify tree cover and change <span class="cf">for</span> the period beginning <span class="kw">in</span> <span class="fl">2001.</span> Well use a single image <span class="im">from</span> <span class="kw">this</span> dataset<span class="op">:</span></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a><span class="op">*</span> <span class="st">'lossyear'</span><span class="op">:</span> a categorical raster <span class="kw">of</span> forest <span class="fu">loss</span> (<span class="dv">1</span><span class="er">20</span><span class="op">,</span> corresponding to deforestation <span class="cf">for</span> the period <span class="dv">2001</span><span class="er">2020</span>)<span class="op">,</span> and <span class="dv">0</span> <span class="cf">for</span> no change</span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-15"><a href="#cb25-15" aria-hidden="true" tabindex="-1"></a>The World Database on Protected <span class="fu">Areas</span> (WDPA) is a harmonized dataset <span class="kw">of</span> <span class="bu">global</span> terrestrial and marine <span class="kw">protected</span> area locations<span class="op">,</span> along <span class="cf">with</span> details on the classification and management <span class="kw">of</span> each<span class="op">.</span> <span class="at">In</span> addition to <span class="kw">protected</span> area outlines<span class="op">,</span> well use two fields <span class="im">from</span> <span class="kw">this</span> database<span class="op">:</span></span>
<span id="cb25-16"><a href="#cb25-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-17"><a href="#cb25-17" aria-hidden="true" tabindex="-1"></a><span class="op">*</span> <span class="st">'NAME'</span><span class="op">:</span> the name <span class="kw">of</span> each <span class="kw">protected</span> area</span>
<span id="cb25-18"><a href="#cb25-18" aria-hidden="true" tabindex="-1"></a><span class="op">*</span> WDPA_PID<span class="op">:</span> a unique numerical ID <span class="cf">for</span> each <span class="kw">protected</span> area</span>
<span id="cb25-19"><a href="#cb25-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-20"><a href="#cb25-20" aria-hidden="true" tabindex="-1"></a>To begin <span class="cf">with</span><span class="op">,</span> well focus on forest change dynamics <span class="kw">in</span> La Paya<span class="op">,</span> a small <span class="kw">protected</span> area <span class="kw">in</span> the Colombian Amazon<span class="op">.</span> <span class="at">Well</span> first visualize these data using the paint command<span class="op">,</span> which is discussed <span class="kw">in</span> more detail <span class="kw">in</span> Chap<span class="op">.</span> <span class="at">F5</span><span class="op">.</span><span class="dv">3</span><span class="op">:</span></span>
<span id="cb25-21"><a href="#cb25-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-22"><a href="#cb25-22" aria-hidden="true" tabindex="-1"></a><span class="co">// Display deforestation. </span></span>
<span id="cb25-23"><a href="#cb25-23" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation <span class="op">=</span> gfc<span class="op">.</span><span class="fu">select</span>(<span class="st">'lossyear'</span>)<span class="op">;</span> </span>
<span id="cb25-24"><a href="#cb25-24" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-25"><a href="#cb25-25" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestation<span class="op">,</span> { </span>
<span id="cb25-26"><a href="#cb25-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb25-27"><a href="#cb25-27" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20</span><span class="op">,</span> </span>
<span id="cb25-28"><a href="#cb25-28" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'red'</span>] </span>
<span id="cb25-29"><a href="#cb25-29" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'Deforestation raster'</span>)<span class="op">;</span> </span>
<span id="cb25-30"><a href="#cb25-30" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-31"><a href="#cb25-31" aria-hidden="true" tabindex="-1"></a><span class="co">// Display WDPA data. </span></span>
<span id="cb25-32"><a href="#cb25-32" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> protectedArea <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">equals</span>(<span class="st">'NAME'</span><span class="op">,</span> <span class="st">'La Paya'</span>))<span class="op">;</span> </span>
<span id="cb25-33"><a href="#cb25-33" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-34"><a href="#cb25-34" aria-hidden="true" tabindex="-1"></a><span class="co">// Display protected area as an outline (see F5.3 for paint()). </span></span>
<span id="cb25-35"><a href="#cb25-35" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> protectedAreaOutline <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>()<span class="op">.</span><span class="fu">byte</span>()<span class="op">.</span><span class="fu">paint</span>({ </span>
<span id="cb25-36"><a href="#cb25-36" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> protectedArea<span class="op">,</span> </span>
<span id="cb25-37"><a href="#cb25-37" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb25-38"><a href="#cb25-38" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">3</span> </span>
<span id="cb25-39"><a href="#cb25-39" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb25-40"><a href="#cb25-40" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-41"><a href="#cb25-41" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(protectedAreaOutline<span class="op">,</span> { </span>
<span id="cb25-42"><a href="#cb25-42" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> <span class="st">'white'</span>}<span class="op">,</span> <span class="st">'Protected area'</span>)<span class="op">;</span> </span>
<span id="cb25-43"><a href="#cb25-43" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-44"><a href="#cb25-44" aria-hidden="true" tabindex="-1"></a><span class="co">// Set up map display. </span></span>
<span id="cb25-45"><a href="#cb25-45" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(protectedArea)<span class="op">;</span> </span>
<span id="cb25-46"><a href="#cb25-46" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setOptions</span>(<span class="st">'SATELLITE'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>This will display the boundary of the La Paya protected area and deforestation in the region (Fig. F5.1.5).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image55.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.5 View of the La Paya protected area in the Colombian Amazon (in white), and deforestation over the period 20012020 (in yellows and reds, with darker colors indicating more recent changes)</figcaption><p></p>
</figure>
</div>
<p>We can use Earth Engine to convert the deforestation raster to a set of polygons. The deforestation data are appropriate for this transformation as each deforestation event is labeled categorically by year, and change events are spatially contiguous. This is performed in Earth Engine using the ee.Image.reduceToVectors method, as described earlier in this section.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Convert from a deforestation raster to vector. </span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationVector <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">reduceToVectors</span>({ </span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> deforestation<span class="op">.</span><span class="fu">projection</span>()<span class="op">.</span><span class="fu">nominalScale</span>()<span class="op">,</span> </span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> protectedArea<span class="op">.</span><span class="fu">geometry</span>()<span class="op">,</span> </span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">labelProperty</span><span class="op">:</span> <span class="st">'lossyear'</span><span class="op">,</span> <span class="co">// Label polygons with a change year. maxPixels: 1e13 </span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Count the number of individual change events </span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Number of change events:'</span><span class="op">,</span> deforestationVector<span class="op">.</span><span class="fu">size</span>())<span class="op">;</span> </span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Display deforestation polygons. Color outline by change year. </span></span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationVectorOutline <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>()<span class="op">.</span><span class="fu">byte</span>()<span class="op">.</span><span class="fu">paint</span>({ </span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> deforestationVector<span class="op">,</span> </span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'lossyear'</span><span class="op">,</span> </span>
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb26-18"><a href="#cb26-18" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestationVectorOutline<span class="op">,</span> { </span>
<span id="cb26-19"><a href="#cb26-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'red'</span>]<span class="op">,</span> </span>
<span id="cb26-20"><a href="#cb26-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb26-21"><a href="#cb26-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20</span>}<span class="op">,</span> <span class="st">'Deforestation vector'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Fig. F5.1.6 shows a comparison of the raster versus vector representations of deforestation within the protected area.</p>
<p><img src="images/F5/image42.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image13.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.6 Raster (left) versus vector (right) representations of deforestation data of the La Paya protected area</figcaption><p></p>
</figure>
</div>
<p>Having converted from raster to vector, a new set of operations becomes available for post-processing the deforestation data. We might, for instance, be interested in the number of individual change events each year (Fig. F5.1.7):</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> chart <span class="op">=</span> ui<span class="op">.</span><span class="at">Chart</span><span class="op">.</span><span class="at">feature</span> </span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">histogram</span>({ </span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> deforestationVector<span class="op">,</span> </span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">property</span><span class="op">:</span> <span class="st">'lossyear'</span> }) </span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">setOptions</span>({ </span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">hAxis</span><span class="op">:</span> { </span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Year'</span> }<span class="op">,</span> </span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">vAxis</span><span class="op">:</span> { </span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Number of deforestation events'</span> }<span class="op">,</span> </span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">legend</span><span class="op">:</span> { </span>
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">position</span><span class="op">:</span> <span class="st">'none'</span> } </span>
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span><span class="fu">print</span>(chart)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image15.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.7 Plot of the number of deforestation events in La Paya for the years 20012020</figcaption><p></p>
</figure>
</div>
<p>There might also be interest in generating point locations for individual change events (e.g., to aid a field campaign):</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Generate deforestation point locations. </span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationCentroids <span class="op">=</span> deforestationVector<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(feat) { <span class="cf">return</span> feat<span class="op">.</span><span class="fu">centroid</span>()<span class="op">;</span> </span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestationCentroids<span class="op">,</span> { </span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'darkblue'</span>}<span class="op">,</span> <span class="st">'Deforestation centroids'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The vector format allows for easy filtering to only deforestation events of interest, such as only the largest deforestation events:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Add a new property to the deforestation FeatureCollection </span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="co">// describing the area of the change polygon. </span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>deforestationVector <span class="op">=</span> deforestationVector<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(feat) { <span class="cf">return</span> feat<span class="op">.</span><span class="fu">set</span>(<span class="st">'area'</span><span class="op">,</span> feat<span class="op">.</span><span class="fu">geometry</span>()<span class="op">.</span><span class="fu">area</span>({ </span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxError</span><span class="op">:</span> <span class="dv">10</span> })<span class="op">.</span><span class="fu">divide</span>(<span class="dv">10000</span>))<span class="op">;</span> <span class="co">// Convert m^2 to hectare. </span></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter the deforestation FeatureCollection for only large-scale (&gt;10 ha) changes </span></span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationLarge <span class="op">=</span> deforestationVector<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">gt</span>( <span class="st">'area'</span><span class="op">,</span> <span class="dv">10</span>))<span class="op">;</span> </span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Display deforestation area outline by year. </span></span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationLargeOutline <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>()<span class="op">.</span><span class="fu">byte</span>()<span class="op">.</span><span class="fu">paint</span>({ </span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> deforestationLarge<span class="op">,</span> </span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'lossyear'</span><span class="op">,</span> </span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestationLargeOutline<span class="op">,</span> { </span>
<span id="cb29-18"><a href="#cb29-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'red'</span>]<span class="op">,</span> </span>
<span id="cb29-19"><a href="#cb29-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb29-20"><a href="#cb29-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20</span>}<span class="op">,</span> <span class="st">'Deforestation (&gt;10 ha)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F51b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
<section id="raster-properties-to-vector-fields" class="level4">
<h4 class="anchored" data-anchor-id="raster-properties-to-vector-fields">Raster Properties to Vector Fields</h4>
<p>Sometimes we want to extract information from a raster to be included in an existing vector dataset. An example might be estimating a deforestation rate for a set of protected areas. Rather than perform this task on a case-by-case basis, we can attach information generated from an image as a property of a feature.</p>
<p>The following script shows how this can be used to quantify a deforestation rate for a set of protected areas in the Colombian Amazon.</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Load required datasets. </span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> gfc <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'UMD/hansen/global_forest_change_2020_v1_8'</span>)<span class="op">;</span> </span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpa <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'WCMC/WDPA/current/polygons'</span>)<span class="op">;</span> </span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Display deforestation. </span></span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation <span class="op">=</span> gfc<span class="op">.</span><span class="fu">select</span>(<span class="st">'lossyear'</span>)<span class="op">;</span> </span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestation<span class="op">,</span> { </span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20</span><span class="op">,</span> </span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'red'</span>] </span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'Deforestation raster'</span>)<span class="op">;</span> </span>
<span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-14"><a href="#cb30-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Select protected areas in the Colombian Amazon. </span></span>
<span id="cb30-15"><a href="#cb30-15" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> amazonianProtectedAreas <span class="op">=</span> [ <span class="st">'Cordillera de los Picachos'</span><span class="op">,</span> <span class="st">'La Paya'</span><span class="op">,</span> <span class="st">'Nukak'</span><span class="op">,</span> <span class="st">'Serrania de Chiribiquete'</span><span class="op">,</span> <span class="st">'Sierra de la Macarena'</span><span class="op">,</span> <span class="st">'Tinigua'</span> </span>
<span id="cb30-16"><a href="#cb30-16" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span> </span>
<span id="cb30-17"><a href="#cb30-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-18"><a href="#cb30-18" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpaSubset <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">inList</span>(<span class="st">'NAME'</span><span class="op">,</span> </span>
<span id="cb30-19"><a href="#cb30-19" aria-hidden="true" tabindex="-1"></a> amazonianProtectedAreas))<span class="op">;</span> </span>
<span id="cb30-20"><a href="#cb30-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-21"><a href="#cb30-21" aria-hidden="true" tabindex="-1"></a><span class="co">// Display protected areas as an outline. </span></span>
<span id="cb30-22"><a href="#cb30-22" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> protectedAreasOutline <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>()<span class="op">.</span><span class="fu">byte</span>()<span class="op">.</span><span class="fu">paint</span>({ </span>
<span id="cb30-23"><a href="#cb30-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> wdpaSubset<span class="op">,</span> </span>
<span id="cb30-24"><a href="#cb30-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb30-25"><a href="#cb30-25" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb30-26"><a href="#cb30-26" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb30-27"><a href="#cb30-27" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-28"><a href="#cb30-28" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(protectedAreasOutline<span class="op">,</span> { </span>
<span id="cb30-29"><a href="#cb30-29" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> <span class="st">'white'</span>}<span class="op">,</span> <span class="st">'Amazonian protected areas'</span>)<span class="op">;</span> </span>
<span id="cb30-30"><a href="#cb30-30" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-31"><a href="#cb30-31" aria-hidden="true" tabindex="-1"></a><span class="co">// Set up map display. </span></span>
<span id="cb30-32"><a href="#cb30-32" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(wdpaSubset)<span class="op">;</span> </span>
<span id="cb30-33"><a href="#cb30-33" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setOptions</span>(<span class="st">'SATELLITE'</span>)<span class="op">;</span> </span>
<span id="cb30-34"><a href="#cb30-34" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-35"><a href="#cb30-35" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> scale <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">projection</span>()<span class="op">.</span><span class="fu">nominalScale</span>()<span class="op">;</span> </span>
<span id="cb30-36"><a href="#cb30-36" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-37"><a href="#cb30-37" aria-hidden="true" tabindex="-1"></a><span class="co">// Use 'reduceRegions' to sum together pixel areas in each protected area. </span></span>
<span id="cb30-38"><a href="#cb30-38" aria-hidden="true" tabindex="-1"></a>wdpaSubset <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">gte</span>(<span class="dv">1</span>) </span>
<span id="cb30-39"><a href="#cb30-39" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">multiply</span>(ee<span class="op">.</span><span class="at">Image</span><span class="op">.</span><span class="fu">pixelArea</span>()<span class="op">.</span><span class="fu">divide</span>(<span class="dv">10000</span>))<span class="op">.</span><span class="fu">reduceRegions</span>({ </span>
<span id="cb30-40"><a href="#cb30-40" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> wdpaSubset<span class="op">,</span> </span>
<span id="cb30-41"><a href="#cb30-41" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">sum</span>()<span class="op">.</span><span class="fu">setOutputs</span>([ <span class="st">'deforestation_area'</span>])<span class="op">,</span> </span>
<span id="cb30-42"><a href="#cb30-42" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> scale </span>
<span id="cb30-43"><a href="#cb30-43" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> </span>
<span id="cb30-44"><a href="#cb30-44" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb30-45"><a href="#cb30-45" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(wdpaSubset)<span class="op">;</span> <span class="co">// Note the new 'deforestation_area' property.</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The output of this script is an estimate of deforested area in hectares for each reserve. However, as reserve sizes vary substantially by area, we can normalize by the total area of each reserve to quantify rates of change.</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Normalize by area. </span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>wdpaSubset <span class="op">=</span> wdpaSubset<span class="op">.</span><span class="fu">map</span>( <span class="kw">function</span>(feat) { <span class="cf">return</span> feat<span class="op">.</span><span class="fu">set</span>(<span class="st">'deforestation_rate'</span><span class="op">,</span> ee<span class="op">.</span><span class="fu">Number</span>(feat<span class="op">.</span><span class="fu">get</span>(<span class="st">'deforestation_area'</span>)) </span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">divide</span>(feat<span class="op">.</span><span class="fu">area</span>()<span class="op">.</span><span class="fu">divide</span>(<span class="dv">10000</span>)) <span class="co">// m2 to ha .divide(20) // number of years .multiply(100)); // to percentage points });// Print to identify rates of change per protected area. </span></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Which has the fastest rate of loss? </span></span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(wdpaSubset<span class="op">.</span><span class="fu">reduceColumns</span>({ </span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">toList</span>()<span class="op">.</span><span class="fu">repeat</span>(<span class="dv">2</span>)<span class="op">,</span> </span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">selectors</span><span class="op">:</span> [<span class="st">'NAME'</span><span class="op">,</span> <span class="st">'deforestation_rate'</span>] </span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a>}))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F51c. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="vector-to-raster-conversion" class="level3">
<h3 class="anchored" data-anchor-id="vector-to-raster-conversion">Vector-to-Raster Conversion</h3>
<p>In Sect. 1, we used the protected area feature collection as its original vector format. In this section, we will rasterize the protected area polygons to produce a mask and use this to assess rates of forest change.</p>
<section id="polygons-to-a-mask" class="level4">
<h4 class="anchored" data-anchor-id="polygons-to-a-mask">Polygons to a Mask</h4>
<p>The most common operation to convert from vector to raster is the production of binary image masks, describing whether a pixel intersects a line or falls within a polygon. To convert from vector to a raster mask, we can use the ee.FeatureCollection.reduceToImage method. Lets continue with our example of the WDPA database and Global Forest Change data from the previous section:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Load required datasets. </span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> gfc <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'UMD/hansen/global_forest_change_2020_v1_8'</span>)<span class="op">;</span> </span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpa <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'WCMC/WDPA/current/polygons'</span>)<span class="op">;</span> </span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Get deforestation. </span></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation <span class="op">=</span> gfc<span class="op">.</span><span class="fu">select</span>(<span class="st">'lossyear'</span>)<span class="op">;</span> </span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Generate a new property called 'protected' to apply to the output mask. </span></span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpa <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(feat) { <span class="cf">return</span> feat<span class="op">.</span><span class="fu">set</span>(<span class="st">'protected'</span><span class="op">,</span> <span class="dv">1</span>)<span class="op">;</span> </span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a><span class="co">// Rasterize using the new property. </span></span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a><span class="co">// unmask() sets areas outside protected area polygons to 0. </span></span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpaMask <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">reduceToImage</span>([<span class="st">'protected'</span>]<span class="op">,</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">first</span>()) </span>
<span id="cb32-15"><a href="#cb32-15" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">unmask</span>()<span class="op">;</span> </span>
<span id="cb32-16"><a href="#cb32-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-17"><a href="#cb32-17" aria-hidden="true" tabindex="-1"></a><span class="co">// Center on Colombia. </span></span>
<span id="cb32-18"><a href="#cb32-18" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setCenter</span>(<span class="op">-</span><span class="dv">75</span><span class="op">,</span> <span class="dv">3</span><span class="op">,</span> <span class="dv">6</span>)<span class="op">;</span> </span>
<span id="cb32-19"><a href="#cb32-19" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-20"><a href="#cb32-20" aria-hidden="true" tabindex="-1"></a><span class="co">// Display on map. </span></span>
<span id="cb32-21"><a href="#cb32-21" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(wdpaMask<span class="op">,</span> { </span>
<span id="cb32-22"><a href="#cb32-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb32-23"><a href="#cb32-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span>}<span class="op">,</span> <span class="st">'Protected areas (mask)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We can use this mask to, for example, highlight only deforestation that occurs within a protected area using logical operations:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Set the deforestation layer to 0 where outside a protected area. </span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationProtected <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">where</span>(wdpaMask<span class="op">.</span><span class="fu">eq</span>(<span class="dv">0</span>)<span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span> </span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Update mask to hide where deforestation layer = 0 </span></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationProtected <span class="op">=</span> deforestationProtected </span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">updateMask</span>(deforestationProtected<span class="op">.</span><span class="fu">gt</span>(<span class="dv">0</span>))<span class="op">;</span> </span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb33-8"><a href="#cb33-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Display deforestation in protected areas </span></span>
<span id="cb33-9"><a href="#cb33-9" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestationProtected<span class="op">,</span> { </span>
<span id="cb33-10"><a href="#cb33-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb33-11"><a href="#cb33-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20</span><span class="op">,</span> </span>
<span id="cb33-12"><a href="#cb33-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'yellow'</span><span class="op">,</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'red'</span>] </span>
<span id="cb33-13"><a href="#cb33-13" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'Deforestation protected'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>In the above example we generated a simple binary mask, but reduceToImage can also preserve a numerical property of the input polygons. For example, we might want to be able to determine which protected area each pixel represents. In this case, we can produce an image with the unique ID of each protected area:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Produce an image with unique ID of protected areas. </span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpaId <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">reduceToImage</span>([<span class="st">'WDPAID'</span>]<span class="op">,</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">first</span>())<span class="op">;</span> </span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(wdpaId<span class="op">,</span> { </span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">100000</span>}<span class="op">,</span> <span class="st">'Protected area ID'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>This output can be useful when performing large-scale raster operations, such as efficiently calculating deforestation rates for multiple protected areas.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F51d. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="a-more-complex-example-1" class="level4">
<h4 class="anchored" data-anchor-id="a-more-complex-example-1">A More Complex Example</h4>
<p>The reduceToImage method is not the only way to convert a feature collection to an image. We will create a distance image layer from the boundary of the protected area using distance. For this example, we return to the La Paya protected area explored in Sect. 1.</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Load required datasets. </span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> gfc <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'UMD/hansen/global_forest_change_2020_v1_8'</span>)<span class="op">;</span> </span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wdpa <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'WCMC/WDPA/current/polygons'</span>)<span class="op">;</span> </span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Select a single protected area. </span></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> protectedArea <span class="op">=</span> wdpa<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">equals</span>(<span class="st">'NAME'</span><span class="op">,</span> <span class="st">'La Paya'</span>))<span class="op">;</span> </span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Maximum distance in meters is set in the brackets. </span></span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> distance <span class="op">=</span> protectedArea<span class="op">.</span><span class="fu">distance</span>(<span class="dv">1000000</span>)<span class="op">;</span> </span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(distance<span class="op">,</span> { </span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20000</span><span class="op">,</span> </span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'white'</span><span class="op">,</span> <span class="st">'grey'</span><span class="op">,</span> <span class="st">'black'</span>]<span class="op">,</span> </span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.6</span>}<span class="op">,</span> <span class="st">'Distance'</span>)<span class="op">;</span> </span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(protectedArea)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We can also show the distance inside and outside of the boundary by using the rasterized protected area (Fig. F5.1.8).</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Produce a raster of inside/outside the protected area. </span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> protectedAreaRaster <span class="op">=</span> protectedArea<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(feat) { <span class="cf">return</span> feat<span class="op">.</span><span class="fu">set</span>(<span class="st">'protected'</span><span class="op">,</span> <span class="dv">1</span>)<span class="op">;</span> </span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>})<span class="op">.</span><span class="fu">reduceToImage</span>([<span class="st">'protected'</span>]<span class="op">,</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">first</span>())<span class="op">;</span> </span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(distance<span class="op">.</span><span class="fu">updateMask</span>(protectedAreaRaster)<span class="op">,</span> { </span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20000</span>}<span class="op">,</span> <span class="st">'Distance inside protected area'</span>)<span class="op">;</span> </span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(distance<span class="op">.</span><span class="fu">updateMask</span>(protectedAreaRaster<span class="op">.</span><span class="fu">unmask</span>() </span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">not</span>())<span class="op">,</span> { </span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb36-12"><a href="#cb36-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">20000</span>}<span class="op">,</span> <span class="st">'Distance outside protected area'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="images/F5/image56.png" class="img-fluid"></p>
<p><img src="images/F5/image9.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image25.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.8 Distance from the La Paya boundary (left), distance within the La Paya (middle), and distance outside the La Paya (right)</figcaption><p></p>
</figure>
</div>
<p>Sometimes it makes sense to work with objects in raster imagery. This is an unusual case of vector-like operations conducted with raster data. There is a good reason for this where the vector equivalent would be computationally burdensome.</p>
<p>An example of this is estimating deforestation rates by distance to the edge of the protected area, as it is common that rates of change will be higher at the boundary of a protected area. We will create a distance raster with three zones from the La Paya boundary (&gt;1 km, &gt;2 km, &gt;3 km, and &gt;4 km) and to estimate the deforestation by distance from the boundary (Fig. F5.1.9).</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> distanceZones <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="dv">0</span>) </span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(distance<span class="op">.</span><span class="fu">gt</span>(<span class="dv">0</span>)<span class="op">,</span> <span class="dv">1</span>) </span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(distance<span class="op">.</span><span class="fu">gt</span>(<span class="dv">1000</span>)<span class="op">,</span> <span class="dv">2</span>) </span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">where</span>(distance<span class="op">.</span><span class="fu">gt</span>(<span class="dv">3000</span>)<span class="op">,</span> <span class="dv">3</span>) </span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">updateMask</span>(distance<span class="op">.</span><span class="fu">lte</span>(<span class="dv">5000</span>))<span class="op">;</span> </span>
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(distanceZones<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Distance zones'</span>)<span class="op">;</span> </span>
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation <span class="op">=</span> gfc<span class="op">.</span><span class="fu">select</span>(<span class="st">'loss'</span>)<span class="op">;</span> </span>
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation1km <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">updateMask</span>(distanceZones<span class="op">.</span><span class="fu">eq</span>(<span class="dv">1</span>))<span class="op">;</span> </span>
<span id="cb37-11"><a href="#cb37-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation3km <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">updateMask</span>(distanceZones<span class="op">.</span><span class="fu">lte</span>(<span class="dv">2</span>))<span class="op">;</span> </span>
<span id="cb37-12"><a href="#cb37-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation5km <span class="op">=</span> deforestation<span class="op">.</span><span class="fu">updateMask</span>(distanceZones<span class="op">.</span><span class="fu">lte</span>(<span class="dv">3</span>))<span class="op">;</span> </span>
<span id="cb37-13"><a href="#cb37-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-14"><a href="#cb37-14" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestation1km<span class="op">,</span> { </span>
<span id="cb37-15"><a href="#cb37-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb37-16"><a href="#cb37-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span>}<span class="op">,</span> <span class="st">'Deforestation within a 1km buffer'</span>)<span class="op">;</span> </span>
<span id="cb37-17"><a href="#cb37-17" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestation3km<span class="op">,</span> { </span>
<span id="cb37-18"><a href="#cb37-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb37-19"><a href="#cb37-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb37-20"><a href="#cb37-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.5</span>}<span class="op">,</span> <span class="st">'Deforestation within a 3km buffer'</span>)<span class="op">;</span> </span>
<span id="cb37-21"><a href="#cb37-21" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(deforestation5km<span class="op">,</span> { </span>
<span id="cb37-22"><a href="#cb37-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb37-23"><a href="#cb37-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb37-24"><a href="#cb37-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.5</span>}<span class="op">,</span> <span class="st">'Deforestation within a 5km buffer'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="images/F5/image22.png" class="img-fluid"></p>
<p><img src="images/F5/image6.png" class="img-fluid"></p>
<p><img src="images/F5/image21.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image26.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.1.9 Distance zones (top left) and deforestation by zone (&lt;1 km, &lt;3 km, and &lt;5 km)</figcaption><p></p>
</figure>
</div>
<p>Lastly, we can estimate the deforestation area within 1 km of the protected area but only outside of the boundary.</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation1kmOutside <span class="op">=</span> deforestation1km </span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">updateMask</span>(protectedAreaRaster<span class="op">.</span><span class="fu">unmask</span>()<span class="op">.</span><span class="fu">not</span>())<span class="op">;</span> </span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Get the value of each pixel in square meters </span></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a><span class="co">// and divide by 10000 to convert to hectares. </span></span>
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestation1kmOutsideArea <span class="op">=</span> deforestation1kmOutside<span class="op">.</span><span class="fu">eq</span>(<span class="dv">1</span>) </span>
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">multiply</span>(ee<span class="op">.</span><span class="at">Image</span><span class="op">.</span><span class="fu">pixelArea</span>())<span class="op">.</span><span class="fu">divide</span>(<span class="dv">10000</span>)<span class="op">;</span> </span>
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a><span class="co">// We need to set a larger geometry than the protected area </span></span>
<span id="cb38-11"><a href="#cb38-11" aria-hidden="true" tabindex="-1"></a><span class="co">// for the geometry parameter in reduceRegion(). </span></span>
<span id="cb38-12"><a href="#cb38-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> deforestationEstimate <span class="op">=</span> deforestation1kmOutsideArea </span>
<span id="cb38-13"><a href="#cb38-13" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">reduceRegion</span>({ </span>
<span id="cb38-14"><a href="#cb38-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">sum</span>()<span class="op">,</span> </span>
<span id="cb38-15"><a href="#cb38-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> protectedArea<span class="op">.</span><span class="fu">geometry</span>()<span class="op">.</span><span class="fu">buffer</span>(<span class="dv">1000</span>)<span class="op">,</span> </span>
<span id="cb38-16"><a href="#cb38-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> deforestation<span class="op">.</span><span class="fu">projection</span>()<span class="op">.</span><span class="fu">nominalScale</span>() </span>
<span id="cb38-17"><a href="#cb38-17" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> </span>
<span id="cb38-18"><a href="#cb38-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb38-19"><a href="#cb38-19" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Deforestation within a 1km buffer outside the protected area (ha)'</span><span class="op">,</span> </span>
<span id="cb38-20"><a href="#cb38-20" aria-hidden="true" tabindex="-1"></a> deforestationEstimate)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F51e. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="conclusion-1" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion-1">Conclusion</h3>
<p>In this chapter, you learned how to convert raster to vector and vice versa. More importantly, you now have a better understanding of why and when such conversions are useful. Our examples should give you practical applications and ideas for using these techniques.</p>
</section>
</section>
<section id="zonal-statistics" class="level2">
<h2 class="anchored" data-anchor-id="zonal-statistics">Zonal Statistics</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author-2">Author</h4>
<p>Sara Winsemius and Justin Braaten</p>
</section>
<section id="overview-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview-2">Overview</h4>
<p>The purpose of this chapter is to extract values from rasters for intersecting points or polygons. We will lay out the process and a function to calculate zonal statistics, which includes optional parameters to modify the function, and then apply the process to three examples using different raster datasets and combinations of parameters.</p>
</section>
<section id="learning-outcomes-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes-2">Learning Outcomes</h4>
<ul>
<li>Buffering points as square or circular regions.</li>
<li>Writing and applying functions with optional parameters.</li>
<li>Learning what zonal statistics are and how to use reducers.</li>
<li>Exporting computation results to a table.</li>
<li>Copying properties from one image to another.</li>
</ul>
</section>
<section id="assumes-you-know-how-to-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to-2">Assumes you know how to:</h4>
<ul>
<li>Recognize similarities and differences among Landsat 5, 7, and 8 spectral bands (Part F1, Part F2, Part F3).</li>
<li>Understand distinctions among Image, ImageCollection, Feature and FeatureCollection Earth Engine objects (Part F1, Part F2, Part F5).</li>
<li>Use drawing tools to create points, lines, and polygons (Chap. F2.1).</li>
<li>Write a function and map it over an ImageCollection (Chap. F4.0).</li>
<li>Mask cloud, cloud shadow, snow/ice, and other undesired pixels (Chap. F4.3).</li>
<li>Export calculated data to tables with Tasks (Chap. F5.0).</li>
<li>Understand the differences between raster and vector data (Chap. F5.0, Chap. F5.1).</li>
<li>Write a function and map it over a FeatureCollection (Chap. F5.1).</li>
</ul>
</section>
</div>
</div>
<section id="introduction-2" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction-2">Introduction</h3>
<p>Anyone working with field data collected at plots will likely need to summarize raster-based data associated with those plots. For instance, they need to know the Normalized Difference Vegetation Index (NDVI), precipitation, or elevation for each plot (or surrounding region). Calculating statistics from a raster within given regions is called zonal statistics. Zonal statistics were calculated in Chaps. F5.0 and F5.1 using ee.Image.ReduceRegions. Here, we present a more general approach to calculating zonal statistics with a custom function that works for both ee.Image and ee.ImageCollection objects. In addition to its flexibility, the reduction method used here is less prone to “Computed value is too large” errors that can occur when using ReduceRegions with very large or complex ee.FeatureCollection object inputs.</p>
<p>The zonal statistics function in this chapter works for an Image or an ImageCollection. Running the function over an ImageCollection will produce a table with values from each image in the collection per point. Image collections can be processed before extraction as needed—for example, by masking clouds from satellite imagery or by constraining the dates needed for a particular research question. In this tutorial, the data extracted from rasters are exported to a table for analysis, where each row of the table corresponds to a unique point-image combination.</p>
<p>In fieldwork, researchers often work with plots, which are commonly recorded as polygon files or as a center point with a set radius. It is rare that plots will be set directly in the center of pixels from your desired raster dataset, and many field GPS units have positioning errors. Because of these issues, it may be important to use a statistic of adjacent pixels (as described in Chap. F3.2) to estimate the central value in whats often called a neighborhood mean or focal mean (Cansler and McKenzie 2012, Miller and Thode 2007).</p>
<p>To choose the size of your neighborhood, you will need to consider your research questions, the spatial resolution of the dataset, the size of your field plot, and the error from your GPS. For example, the raster value extracted for randomly placed 20 m diameter plots would likely merit use of a neighborhood mean when using Sentinel-2 or Landsat 8—at 10 m and 30 m spatial resolution, respectively—while using a thermal band from MODIS (Moderate Resolution Imaging Spectroradiometer) at 1000 m may not. While much of this tutorial is written with plot points and buffers in mind, a polygon asset with predefined regions will serve the same purpose.</p>
</section>
<section id="functions" class="level3">
<h3 class="anchored" data-anchor-id="functions">Functions</h3>
<p>Two functions are provided; copy and paste them into your script:</p>
<ul>
<li>A function to generate circular or square regions from buffered points</li>
<li>A function to extract image pixel neighborhood statistics for a given region</li>
</ul>
<section id="function-bufferpointsradius-bounds" class="level4">
<h4 class="anchored" data-anchor-id="function-bufferpointsradius-bounds">Function: bufferPoints(radius, bounds)</h4>
<p>Our first function, bufferPoints, returns a function for adding a buffer to points and optionally transforming to rectangular bounds</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">bufferPoints</span>(radius<span class="op">,</span> bounds) {</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">function</span>(pt) {</span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a> pt <span class="op">=</span> ee<span class="op">.</span><span class="fu">Feature</span>(pt)<span class="op">;</span></span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> bounds <span class="op">?</span> pt<span class="op">.</span><span class="fu">buffer</span>(radius)<span class="op">.</span><span class="fu">bounds</span>() <span class="op">:</span> pt<span class="op">.</span><span class="fu">buffer</span>(</span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a> radius)<span class="op">;</span></span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a> }<span class="op">;</span></span>
<span id="cb39-7"><a href="#cb39-7" aria-hidden="true" tabindex="-1"></a>}</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="function-zonalstatsfc-params" class="level4">
<h4 class="anchored" data-anchor-id="function-zonalstatsfc-params">Function: zonalStats(fc, params)</h4>
<p>The second function, zonalStats, reduces images in an ImageCollection by regions defined in a FeatureCollection. Note that reductions can return null statistics that you might want to filter out of the resulting feature collection. Null statistics occur when there are no valid pixels intersecting the region being reduced. This situation can be caused by points that are outside of an image or in regions that are masked for quality or clouds.</p>
<p>This function is written to include many optional parameters (see Table F5.2.2). Look at the function carefully and note how it is written to include defaults that make it easy to apply the basic function while allowing customization.</p>
<p>The desired datetime format. Use ISO 8601 data string standards. The datetime string is derived from the system:time_start value of the ee.Image being reduced. Optional.</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">zonalStats</span>(ic<span class="op">,</span> fc<span class="op">,</span> params) {</span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Initialize internal params dictionary.</span></span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> _params <span class="op">=</span> {</span>
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">mean</span>()<span class="op">,</span></span>
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-8"><a href="#cb40-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">bandsRename</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-9"><a href="#cb40-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">imgProps</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-10"><a href="#cb40-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">imgPropsRename</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb40-11"><a href="#cb40-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeName</span><span class="op">:</span> <span class="st">'datetime'</span><span class="op">,</span></span>
<span id="cb40-12"><a href="#cb40-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeFormat</span><span class="op">:</span> <span class="st">'YYYY-MM-dd HH:mm:ss'</span></span>
<span id="cb40-13"><a href="#cb40-13" aria-hidden="true" tabindex="-1"></a> }<span class="op">;</span></span>
<span id="cb40-14"><a href="#cb40-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-15"><a href="#cb40-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// Replace initialized params with provided params.</span></span>
<span id="cb40-16"><a href="#cb40-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (params) {</span>
<span id="cb40-17"><a href="#cb40-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> (<span class="kw">var</span> param <span class="kw">in</span> params) {</span>
<span id="cb40-18"><a href="#cb40-18" aria-hidden="true" tabindex="-1"></a> _params[param] <span class="op">=</span> params[param] <span class="op">||</span> _params[param]<span class="op">;</span></span>
<span id="cb40-19"><a href="#cb40-19" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb40-20"><a href="#cb40-20" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb40-21"><a href="#cb40-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-22"><a href="#cb40-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// Set default parameters based on an image representative.</span></span>
<span id="cb40-23"><a href="#cb40-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> imgRep <span class="op">=</span> ic<span class="op">.</span><span class="fu">first</span>()<span class="op">;</span></span>
<span id="cb40-24"><a href="#cb40-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> nonSystemImgProps <span class="op">=</span> ee<span class="op">.</span><span class="fu">Feature</span>(<span class="kw">null</span>)</span>
<span id="cb40-25"><a href="#cb40-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">copyProperties</span>(imgRep)<span class="op">.</span><span class="fu">propertyNames</span>()<span class="op">;</span></span>
<span id="cb40-26"><a href="#cb40-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>_params<span class="op">.</span><span class="at">bands</span>) _params<span class="op">.</span><span class="at">bands</span> <span class="op">=</span> imgRep<span class="op">.</span><span class="fu">bandNames</span>()<span class="op">;</span></span>
<span id="cb40-27"><a href="#cb40-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>_params<span class="op">.</span><span class="at">bandsRename</span>) _params<span class="op">.</span><span class="at">bandsRename</span> <span class="op">=</span> _params<span class="op">.</span><span class="at">bands</span><span class="op">;</span></span>
<span id="cb40-28"><a href="#cb40-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>_params<span class="op">.</span><span class="at">imgProps</span>) _params<span class="op">.</span><span class="at">imgProps</span> <span class="op">=</span> nonSystemImgProps<span class="op">;</span></span>
<span id="cb40-29"><a href="#cb40-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>_params<span class="op">.</span><span class="at">imgPropsRename</span>) _params<span class="op">.</span><span class="at">imgPropsRename</span> <span class="op">=</span> _params</span>
<span id="cb40-30"><a href="#cb40-30" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="at">imgProps</span><span class="op">;</span></span>
<span id="cb40-31"><a href="#cb40-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-32"><a href="#cb40-32" aria-hidden="true" tabindex="-1"></a> <span class="co">// Map the reduceRegions function over the image collection.</span></span>
<span id="cb40-33"><a href="#cb40-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> results <span class="op">=</span> ic<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(img) {</span>
<span id="cb40-34"><a href="#cb40-34" aria-hidden="true" tabindex="-1"></a> <span class="co">// Select bands (optionally rename), set a datetime &amp; timestamp property.</span></span>
<span id="cb40-35"><a href="#cb40-35" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(img<span class="op">.</span><span class="fu">select</span>(_params<span class="op">.</span><span class="at">bands</span><span class="op">,</span> _params</span>
<span id="cb40-36"><a href="#cb40-36" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="at">bandsRename</span>))</span>
<span id="cb40-37"><a href="#cb40-37" aria-hidden="true" tabindex="-1"></a> <span class="co">// Add datetime and timestamp features.</span></span>
<span id="cb40-38"><a href="#cb40-38" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">set</span>(_params<span class="op">.</span><span class="at">datetimeName</span><span class="op">,</span> img<span class="op">.</span><span class="fu">date</span>()<span class="op">.</span><span class="fu">format</span>(</span>
<span id="cb40-39"><a href="#cb40-39" aria-hidden="true" tabindex="-1"></a> _params<span class="op">.</span><span class="at">datetimeFormat</span>))</span>
<span id="cb40-40"><a href="#cb40-40" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">set</span>(<span class="st">'timestamp'</span><span class="op">,</span> img<span class="op">.</span><span class="fu">get</span>(<span class="st">'system:time_start'</span>))<span class="op">;</span></span>
<span id="cb40-41"><a href="#cb40-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-42"><a href="#cb40-42" aria-hidden="true" tabindex="-1"></a> <span class="co">// Define final image property dictionary to set in output features.</span></span>
<span id="cb40-43"><a href="#cb40-43" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> propsFrom <span class="op">=</span> ee<span class="op">.</span><span class="fu">List</span>(_params<span class="op">.</span><span class="at">imgProps</span>)</span>
<span id="cb40-44"><a href="#cb40-44" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">cat</span>(ee<span class="op">.</span><span class="fu">List</span>([_params<span class="op">.</span><span class="at">datetimeName</span><span class="op">,</span></span>
<span id="cb40-45"><a href="#cb40-45" aria-hidden="true" tabindex="-1"></a> <span class="st">'timestamp'</span>]))<span class="op">;</span></span>
<span id="cb40-46"><a href="#cb40-46" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> propsTo <span class="op">=</span> ee<span class="op">.</span><span class="fu">List</span>(_params<span class="op">.</span><span class="at">imgPropsRename</span>)</span>
<span id="cb40-47"><a href="#cb40-47" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">cat</span>(ee<span class="op">.</span><span class="fu">List</span>([_params<span class="op">.</span><span class="at">datetimeName</span><span class="op">,</span></span>
<span id="cb40-48"><a href="#cb40-48" aria-hidden="true" tabindex="-1"></a> <span class="st">'timestamp'</span>]))<span class="op">;</span></span>
<span id="cb40-49"><a href="#cb40-49" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> imgProps <span class="op">=</span> img<span class="op">.</span><span class="fu">toDictionary</span>(propsFrom)<span class="op">.</span><span class="fu">rename</span>(</span>
<span id="cb40-50"><a href="#cb40-50" aria-hidden="true" tabindex="-1"></a> propsFrom<span class="op">,</span> propsTo)<span class="op">;</span></span>
<span id="cb40-51"><a href="#cb40-51" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-52"><a href="#cb40-52" aria-hidden="true" tabindex="-1"></a> <span class="co">// Subset points that intersect the given image.</span></span>
<span id="cb40-53"><a href="#cb40-53" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> fcSub <span class="op">=</span> fc<span class="op">.</span><span class="fu">filterBounds</span>(img<span class="op">.</span><span class="fu">geometry</span>())<span class="op">;</span></span>
<span id="cb40-54"><a href="#cb40-54" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-55"><a href="#cb40-55" aria-hidden="true" tabindex="-1"></a> <span class="co">// Reduce the image by regions.</span></span>
<span id="cb40-56"><a href="#cb40-56" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> img<span class="op">.</span><span class="fu">reduceRegions</span>({</span>
<span id="cb40-57"><a href="#cb40-57" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> fcSub<span class="op">,</span></span>
<span id="cb40-58"><a href="#cb40-58" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> _params<span class="op">.</span><span class="at">reducer</span><span class="op">,</span></span>
<span id="cb40-59"><a href="#cb40-59" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> _params<span class="op">.</span><span class="at">scale</span><span class="op">,</span></span>
<span id="cb40-60"><a href="#cb40-60" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> _params<span class="op">.</span><span class="at">crs</span></span>
<span id="cb40-61"><a href="#cb40-61" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb40-62"><a href="#cb40-62" aria-hidden="true" tabindex="-1"></a> <span class="co">// Add metadata to each feature.</span></span>
<span id="cb40-63"><a href="#cb40-63" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(f) {</span>
<span id="cb40-64"><a href="#cb40-64" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> f<span class="op">.</span><span class="fu">set</span>(imgProps)<span class="op">;</span></span>
<span id="cb40-65"><a href="#cb40-65" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb40-66"><a href="#cb40-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-67"><a href="#cb40-67" aria-hidden="true" tabindex="-1"></a> <span class="co">// Converts the feature collection of feature collections to a single</span></span>
<span id="cb40-68"><a href="#cb40-68" aria-hidden="true" tabindex="-1"></a> <span class="co">//feature collection.</span></span>
<span id="cb40-69"><a href="#cb40-69" aria-hidden="true" tabindex="-1"></a> })<span class="op">.</span><span class="fu">flatten</span>()<span class="op">;</span></span>
<span id="cb40-70"><a href="#cb40-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-71"><a href="#cb40-71" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> results<span class="op">;</span></span>
<span id="cb40-72"><a href="#cb40-72" aria-hidden="true" tabindex="-1"></a>}</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
</section>
<section id="point-collection-creation" class="level3">
<h3 class="anchored" data-anchor-id="point-collection-creation">Point Collection Creation</h3>
<p>Below, we create a set of points that form the basis of the zonal statistics calculations. Note that a unique plot_id property is added to each point. A unique plot or point ID is important to include in your vector dataset for future filtering and joining.</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> pts <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>([</span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Feature</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="op">-</span><span class="fl">118.6010</span><span class="op">,</span> <span class="fl">37.0777</span>])<span class="op">,</span> {</span>
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">plot_id</span><span class="op">:</span> <span class="dv">1</span></span>
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Feature</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="op">-</span><span class="fl">118.5896</span><span class="op">,</span> <span class="fl">37.0778</span>])<span class="op">,</span> {</span>
<span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">plot_id</span><span class="op">:</span> <span class="dv">2</span></span>
<span id="cb41-7"><a href="#cb41-7" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb41-8"><a href="#cb41-8" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Feature</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="op">-</span><span class="fl">118.5842</span><span class="op">,</span> <span class="fl">37.0805</span>])<span class="op">,</span> {</span>
<span id="cb41-9"><a href="#cb41-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">plot_id</span><span class="op">:</span> <span class="dv">3</span></span>
<span id="cb41-10"><a href="#cb41-10" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb41-11"><a href="#cb41-11" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Feature</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="op">-</span><span class="fl">118.5994</span><span class="op">,</span> <span class="fl">37.0936</span>])<span class="op">,</span> {</span>
<span id="cb41-12"><a href="#cb41-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">plot_id</span><span class="op">:</span> <span class="dv">4</span></span>
<span id="cb41-13"><a href="#cb41-13" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb41-14"><a href="#cb41-14" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Feature</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="op">-</span><span class="fl">118.5861</span><span class="op">,</span> <span class="fl">37.0567</span>])<span class="op">,</span> {</span>
<span id="cb41-15"><a href="#cb41-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">plot_id</span><span class="op">:</span> <span class="dv">5</span></span>
<span id="cb41-16"><a href="#cb41-16" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb41-17"><a href="#cb41-17" aria-hidden="true" tabindex="-1"></a>])<span class="op">;</span></span>
<span id="cb41-18"><a href="#cb41-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-19"><a href="#cb41-19" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Points of interest'</span><span class="op">,</span> pts)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F52a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="neighborhood-statistic-examples" class="level3">
<h3 class="anchored" data-anchor-id="neighborhood-statistic-examples">Neighborhood Statistic Examples</h3>
<p>The following examples demonstrate extracting raster neighborhood statistics for the following:</p>
<ul>
<li>A single raster with elevation and slope bands</li>
<li>A multiband MODIS time series</li>
<li>A multiband Landsat time series</li>
</ul>
<p>In each example, the points created in the previous section will be buffered and then used as regions to extract zonal statistics for each image in the image collection.</p>
<section id="topographic-variables" class="level4">
<h4 class="anchored" data-anchor-id="topographic-variables">Topographic Variables</h4>
<p>This example demonstrates how to calculate zonal statistics for a single multiband image. This Digital Elevation Model (DEM) contains a single topographic band representing elevation.</p>
<p>####Buffer the Points</p>
<p>Nex, we will apply a 45 m radius buffer to the points defined previously by mapping the bufferPoints function over the feature collection. The radius is set to 45 m to correspond to the 90 m pixel resolution of the DEM. In this case, circles are used instead of squares (set the second argument as false, i.e., do not use bounds).</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Buffer the points. </span></span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ptsTopo <span class="op">=</span> pts<span class="op">.</span><span class="fu">map</span>(<span class="fu">bufferPoints</span>(<span class="dv">45</span><span class="op">,</span> <span class="kw">false</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>####Calculate Zonal Statistics</p>
<p>There are two important things to note about the zonalStats function that this example addresses:</p>
<ul>
<li>It accepts only an ee.ImageCollection, not an ee.Image; single images must be wrapped in an ImageCollection.</li>
<li>It expects every image in the input image collection to have a timestamp property named system:time_start with values representing milliseconds from 00:00:00 UTC on 1 January 1970. Most datasets should have this property, if not, one should be added.</li>
</ul>
<div class="sourceCode" id="cb43"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the MERIT global elevation dataset. </span></span>
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elev <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'MERIT/DEM/v1_0_3'</span>)<span class="op">;</span> </span>
<span id="cb43-3"><a href="#cb43-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb43-4"><a href="#cb43-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate slope from the DEM. </span></span>
<span id="cb43-5"><a href="#cb43-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> slope <span class="op">=</span> ee<span class="op">.</span><span class="at">Terrain</span><span class="op">.</span><span class="fu">slope</span>(elev)<span class="op">;</span> </span>
<span id="cb43-6"><a href="#cb43-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb43-7"><a href="#cb43-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Concatenate elevation and slope as two bands of an image. </span></span>
<span id="cb43-8"><a href="#cb43-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> topo <span class="op">=</span> ee<span class="op">.</span><span class="at">Image</span><span class="op">.</span><span class="fu">cat</span>(elev<span class="op">,</span> slope) </span>
<span id="cb43-9"><a href="#cb43-9" aria-hidden="true" tabindex="-1"></a> <span class="co">// Computed images do not have a 'system:time_start' property; add one based </span></span>
<span id="cb43-10"><a href="#cb43-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// on when the data were collected. .set('system:time_start', ee.Date('2000-01-01').millis()); </span></span>
<span id="cb43-11"><a href="#cb43-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb43-12"><a href="#cb43-12" aria-hidden="true" tabindex="-1"></a><span class="co">// Wrap the single image in an ImageCollection for use in the </span></span>
<span id="cb43-13"><a href="#cb43-13" aria-hidden="true" tabindex="-1"></a><span class="co">// zonalStats function. </span></span>
<span id="cb43-14"><a href="#cb43-14" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> topoCol <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>([topo])<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Define arguments for the zonalStats function and then run it. Note that we are accepting defaults for the reducer, scale, Coordinate Reference System (CRS), and image properties to copy over to the resulting feature collection. Refer to the function definition above for defaults.</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define parameters for the zonalStats function. </span></span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> params <span class="op">=</span> { </span>
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> [<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span>]<span class="op">,</span> </span>
<span id="cb44-4"><a href="#cb44-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">bandsRename</span><span class="op">:</span> [<span class="st">'elevation'</span><span class="op">,</span> <span class="st">'slope'</span>] </span>
<span id="cb44-5"><a href="#cb44-5" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb44-6"><a href="#cb44-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb44-7"><a href="#cb44-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Extract zonal statistics per point per image. </span></span>
<span id="cb44-8"><a href="#cb44-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ptsTopoStats <span class="op">=</span> <span class="fu">zonalStats</span>(topoCol<span class="op">,</span> ptsTopo<span class="op">,</span> params)<span class="op">;</span><span class="fu">print</span>(<span class="st">'Topo zonal stats table'</span><span class="op">,</span> ptsTopoStats)<span class="op">;</span> </span>
<span id="cb44-9"><a href="#cb44-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb44-10"><a href="#cb44-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Display the layers on the map. </span></span>
<span id="cb44-11"><a href="#cb44-11" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setCenter</span>(<span class="op">-</span><span class="fl">118.5957</span><span class="op">,</span> <span class="fl">37.0775</span><span class="op">,</span> <span class="dv">13</span>)<span class="op">;</span> </span>
<span id="cb44-12"><a href="#cb44-12" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(topoCol<span class="op">.</span><span class="fu">select</span>(<span class="dv">0</span>)<span class="op">,</span> { </span>
<span id="cb44-13"><a href="#cb44-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">2400</span><span class="op">,</span> </span>
<span id="cb44-14"><a href="#cb44-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">4200</span>}<span class="op">,</span> <span class="st">'Elevation'</span>)<span class="op">;</span> </span>
<span id="cb44-15"><a href="#cb44-15" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(topoCol<span class="op">.</span><span class="fu">select</span>(<span class="dv">1</span>)<span class="op">,</span> { </span>
<span id="cb44-16"><a href="#cb44-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb44-17"><a href="#cb44-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">60</span>}<span class="op">,</span> <span class="st">'Slope'</span>)<span class="op">;</span> </span>
<span id="cb44-18"><a href="#cb44-18" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(pts<span class="op">,</span> { </span>
<span id="cb44-19"><a href="#cb44-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'purple'</span>}<span class="op">,</span> <span class="st">'Points'</span>)<span class="op">;</span> </span>
<span id="cb44-20"><a href="#cb44-20" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(ptsTopo<span class="op">,</span> { </span>
<span id="cb44-21"><a href="#cb44-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'yellow'</span>}<span class="op">,</span> <span class="st">'Points w/ buffer'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The result is a copy of the buffered point feature collection with new properties added for the region reduction of each selected image band according to the given reducer. A part of the FeatureCollection is shown in Fig. F5.2.1. The data in that FeatureCollection corresponds to a table containing the information of Table F5.2.3. See Fig. F5.2.2 for a graphical representation of the points and the topographic data being summarized.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image29.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.2.1 A part of the FeatureCollection produced by calculating the zonal statistics</figcaption><p></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image5.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.2.2 Sample points and topographic slope. Elevation and slope values for regions intersecting each buffered point are reduced and attached as properties of the points.</figcaption><p></p>
</figure>
</div>
<p>Table F5.2.3 Example output from zonalStats organized as a table. Rows correspond to collection features and columns are feature properties. Note that elevation and slope values in this table are rounded to the nearest tenth for brevity.</p>
<table class="table">
<thead>
<tr class="header">
<th>plot_id</th>
<th>timestamp</th>
<th>datetime</th>
<th>elevation</th>
<th>slope</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>1</td>
<td>946684800000</td>
<td>2000-01-01 00:00:00</td>
<td>2648.1</td>
<td>29.7</td>
</tr>
<tr class="even">
<td>2</td>
<td>946684800000</td>
<td>2000-01-01 00:00:00</td>
<td>2888.2</td>
<td>33.9</td>
</tr>
<tr class="odd">
<td>3</td>
<td>946684800000</td>
<td>2000-01-01 00:00:00</td>
<td>3267.8</td>
<td>35.8</td>
</tr>
<tr class="even">
<td>4</td>
<td>946684800000</td>
<td>2000-01-01 00:00:00</td>
<td>2790.7</td>
<td>25.1</td>
</tr>
<tr class="odd">
<td>5</td>
<td>946684800000</td>
<td>2000-01-01 00:00:00</td>
<td>2559.4</td>
<td>29.4</td>
</tr>
</tbody>
</table>
</section>
<section id="modis-time-series" class="level4">
<h4 class="anchored" data-anchor-id="modis-time-series">MODIS Time Series</h4>
<p>A time series of MODIS eight-day surface reflectance composites demonstrates how to calculate zonal statistics for a multiband ImageCollection that requires no preprocessing, such as cloud masking or computation. Note that there is no built-in function for performing region reductions on ImageCollection objects. The zonalStats function that we are using for reduction is mapping the reduceRegions function over an ImageCollection.</p>
</section>
<section id="buffer-the-points" class="level4">
<h4 class="anchored" data-anchor-id="buffer-the-points">Buffer the Points</h4>
<p>In this example, suppose the point collection represents center points for field plots that are 100 m x 100 m, and apply a 50 m radius buffer to the points to match the size of the plot. Since we want zonal statistics for square plots, set the second argument of the bufferPoints function to true, so that the bounds of the buffered points are returned.</p>
<p>var ptsModis = pts.map(bufferPoints(50, true));</p>
</section>
<section id="calculate-zonal-statistic" class="level4">
<h4 class="anchored" data-anchor-id="calculate-zonal-statistic">Calculate Zonal Statistic</h4>
<p>Import the MODIS 500 m global eight-day surface reflectance composite collection and filter the collection to include data for July, August, and September from 2015 through 2019.</p>
<div class="sourceCode" id="cb45"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> modisCol <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'MODIS/006/MOD09A1'</span>) </span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(<span class="st">'2015-01-01'</span><span class="op">,</span> <span class="st">'2020-01-01'</span>) </span>
<span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">calendarRange</span>(<span class="dv">183</span><span class="op">,</span> <span class="dv">245</span><span class="op">,</span> <span class="st">'DAY_OF_YEAR'</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Reduce each image in the collection by each plot according to the following parameters. Note that this time the reducer is defined as the neighborhood median (ee.Reducer.median) instead of the default mean, and that scale, CRS, and properties for the datetime are explicitly defined.</p>
<div class="sourceCode" id="cb46"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define parameters for the zonalStats function. </span></span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> params <span class="op">=</span> { </span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">median</span>()<span class="op">,</span> </span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">500</span><span class="op">,</span> </span>
<span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> <span class="st">'EPSG:5070'</span><span class="op">,</span> </span>
<span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> [<span class="st">'sur_refl_b01'</span><span class="op">,</span> <span class="st">'sur_refl_b02'</span><span class="op">,</span> <span class="st">'sur_refl_b06'</span>]<span class="op">,</span> </span>
<span id="cb46-7"><a href="#cb46-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">bandsRename</span><span class="op">:</span> [<span class="st">'modis_red'</span><span class="op">,</span> <span class="st">'modis_nir'</span><span class="op">,</span> <span class="st">'modis_swir'</span>]<span class="op">,</span> </span>
<span id="cb46-8"><a href="#cb46-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeName</span><span class="op">:</span> <span class="st">'date'</span><span class="op">,</span> </span>
<span id="cb46-9"><a href="#cb46-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeFormat</span><span class="op">:</span> <span class="st">'YYYY-MM-dd'</span> </span>
<span id="cb46-10"><a href="#cb46-10" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb46-11"><a href="#cb46-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb46-12"><a href="#cb46-12" aria-hidden="true" tabindex="-1"></a><span class="co">// Extract zonal statistics per point per image. </span></span>
<span id="cb46-13"><a href="#cb46-13" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ptsModisStats <span class="op">=</span> <span class="fu">zonalStats</span>(modisCol<span class="op">,</span> ptsModis<span class="op">,</span> params)<span class="op">;</span><span class="fu">print</span>(<span class="st">'Limited MODIS zonal stats table'</span><span class="op">,</span> ptsModisStats<span class="op">.</span><span class="fu">limit</span>(<span class="dv">50</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The result is a feature collection with a feature for all combinations of plots and images. Interpreted as a table, the result has 200 rows (5 plots times 40 images) and as many columns as there are feature properties. Feature properties include those from the plot asset and the image, and any associated non-system image properties. Note that the printed results are limited to the first 50 features for brevity.</p>
</section>
<section id="landsat-time-series" class="level4">
<h4 class="anchored" data-anchor-id="landsat-time-series">Landsat Time Series</h4>
<p>This example combines Landsat surface reflectance imagery across three instruments: Thematic Mapper (TM) from Landsat 5, Enhanced Thematic Mapper Plus (ETM+) from Landsat 7, and Operational Land Imager (OLI) from Landsat 8.</p>
<p>The following section prepares these collections so that band names are consistent and cloud masks are applied. Reflectance among corresponding bands are roughly congruent for the three sensors when using the surface reflectance product; therefore the processing steps that follow do not address inter-sensor harmonization. Review the current literature on inter-sensor harmonization practices if youd like to apply a correction.</p>
</section>
<section id="prepare-the-landsat-image-collection" class="level4">
<h4 class="anchored" data-anchor-id="prepare-the-landsat-image-collection">Prepare the Landsat Image Collection</h4>
<p>First, define the function to mask cloud and shadow pixels (See Chap. F4.3 for more detail on cloud masking).</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Mask clouds from images and apply scaling factors.</span></span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">maskScale</span>(img) {</span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> qaMask <span class="op">=</span> img<span class="op">.</span><span class="fu">select</span>(<span class="st">'QA_PIXEL'</span>)<span class="op">.</span><span class="fu">bitwiseAnd</span>(<span class="pp">parseInt</span>(<span class="st">'11111'</span><span class="op">,</span></span>
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a> <span class="dv">2</span>))<span class="op">.</span><span class="fu">eq</span>(<span class="dv">0</span>)<span class="op">;</span></span>
<span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> saturationMask <span class="op">=</span> img<span class="op">.</span><span class="fu">select</span>(<span class="st">'QA_RADSAT'</span>)<span class="op">.</span><span class="fu">eq</span>(<span class="dv">0</span>)<span class="op">;</span></span>
<span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb47-7"><a href="#cb47-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Apply the scaling factors to the appropriate bands.</span></span>
<span id="cb47-8"><a href="#cb47-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> getFactorImg <span class="op">=</span> <span class="kw">function</span>(factorNames) {</span>
<span id="cb47-9"><a href="#cb47-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> factorList <span class="op">=</span> img<span class="op">.</span><span class="fu">toDictionary</span>()<span class="op">.</span><span class="fu">select</span>(factorNames)</span>
<span id="cb47-10"><a href="#cb47-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">values</span>()<span class="op">;</span></span>
<span id="cb47-11"><a href="#cb47-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ee<span class="op">.</span><span class="at">Image</span><span class="op">.</span><span class="fu">constant</span>(factorList)<span class="op">;</span></span>
<span id="cb47-12"><a href="#cb47-12" aria-hidden="true" tabindex="-1"></a> }<span class="op">;</span></span>
<span id="cb47-13"><a href="#cb47-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> scaleImg <span class="op">=</span> <span class="fu">getFactorImg</span>([<span class="st">'REFLECTANCE_MULT_BAND_.'</span>])<span class="op">;</span></span>
<span id="cb47-14"><a href="#cb47-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> offsetImg <span class="op">=</span> <span class="fu">getFactorImg</span>([<span class="st">'REFLECTANCE_ADD_BAND_.'</span>])<span class="op">;</span></span>
<span id="cb47-15"><a href="#cb47-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> scaled <span class="op">=</span> img<span class="op">.</span><span class="fu">select</span>(<span class="st">'SR_B.'</span>)<span class="op">.</span><span class="fu">multiply</span>(scaleImg)<span class="op">.</span><span class="fu">add</span>(</span>
<span id="cb47-16"><a href="#cb47-16" aria-hidden="true" tabindex="-1"></a> offsetImg)<span class="op">;</span></span>
<span id="cb47-17"><a href="#cb47-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb47-18"><a href="#cb47-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Replace the original bands with the scaled ones and apply the masks.</span></span>
<span id="cb47-19"><a href="#cb47-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> img<span class="op">.</span><span class="fu">addBands</span>(scaled<span class="op">,</span> <span class="kw">null</span><span class="op">,</span> <span class="kw">true</span>)</span>
<span id="cb47-20"><a href="#cb47-20" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">updateMask</span>(qaMask)</span>
<span id="cb47-21"><a href="#cb47-21" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">updateMask</span>(saturationMask)<span class="op">;</span></span>
<span id="cb47-22"><a href="#cb47-22" aria-hidden="true" tabindex="-1"></a>}</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Next, define functions to select and rename the bands of interest for the Operational Land Imager (OLI) aboard Landsat 8, and for the TM/ETM+ imagers aboard earlier Landsats. This is important because the band numbers are different for OLI and TM/ETM+, and it will make future index calculations easier.</p>
<div class="sourceCode" id="cb48"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Selects and renames bands of interest for Landsat OLI. </span></span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">renameOli</span>(img) { <span class="cf">return</span> img<span class="op">.</span><span class="fu">select</span>( </span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a> [<span class="st">'SR_B2'</span><span class="op">,</span> <span class="st">'SR_B3'</span><span class="op">,</span> <span class="st">'SR_B4'</span><span class="op">,</span> <span class="st">'SR_B5'</span><span class="op">,</span> <span class="st">'SR_B6'</span><span class="op">,</span> <span class="st">'SR_B7'</span>]<span class="op">,</span> </span>
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a> [<span class="st">'Blue'</span><span class="op">,</span> <span class="st">'Green'</span><span class="op">,</span> <span class="st">'Red'</span><span class="op">,</span> <span class="st">'NIR'</span><span class="op">,</span> <span class="st">'SWIR1'</span><span class="op">,</span> <span class="st">'SWIR2'</span>])<span class="op">;</span> </span>
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a>} </span>
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb48-7"><a href="#cb48-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Selects and renames bands of interest for TM/ETM+. </span></span>
<span id="cb48-8"><a href="#cb48-8" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">renameEtm</span>(img) { <span class="cf">return</span> img<span class="op">.</span><span class="fu">select</span>( </span>
<span id="cb48-9"><a href="#cb48-9" aria-hidden="true" tabindex="-1"></a> [<span class="st">'SR_B1'</span><span class="op">,</span> <span class="st">'SR_B2'</span><span class="op">,</span> <span class="st">'SR_B3'</span><span class="op">,</span> <span class="st">'SR_B4'</span><span class="op">,</span> <span class="st">'SR_B5'</span><span class="op">,</span> <span class="st">'SR_B7'</span>]<span class="op">,</span> </span>
<span id="cb48-10"><a href="#cb48-10" aria-hidden="true" tabindex="-1"></a> [<span class="st">'Blue'</span><span class="op">,</span> <span class="st">'Green'</span><span class="op">,</span> <span class="st">'Red'</span><span class="op">,</span> <span class="st">'NIR'</span><span class="op">,</span> <span class="st">'SWIR1'</span><span class="op">,</span> <span class="st">'SWIR2'</span>])<span class="op">;</span> </span>
<span id="cb48-11"><a href="#cb48-11" aria-hidden="true" tabindex="-1"></a>}</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Combine the cloud mask and band renaming functions into preparation functions for OLI and TM/ETM+. Add any other sensor-specific preprocessing steps that youd like to the functions below.</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Prepares (cloud masks and renames) OLI images. </span></span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">prepOli</span>(img) { </span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> <span class="fu">maskScale</span>(img)<span class="op">;</span> </span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> <span class="fu">renameOli</span>(img)<span class="op">;</span> <span class="cf">return</span> img<span class="op">;</span> </span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a>}<span class="co">// Prepares (cloud masks and renames) TM/ETM+ images. </span></span>
<span id="cb49-6"><a href="#cb49-6" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">prepEtm</span>(img) { </span>
<span id="cb49-7"><a href="#cb49-7" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> <span class="fu">maskScale</span>(img)<span class="op">;</span> </span>
<span id="cb49-8"><a href="#cb49-8" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> <span class="fu">renameEtm</span>(img)<span class="op">;</span> <span class="cf">return</span> img<span class="op">;</span> </span>
<span id="cb49-9"><a href="#cb49-9" aria-hidden="true" tabindex="-1"></a>}</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Get the Landsat surface reflectance collections for OLI, ETM+, and TM sensors. Filter them by the bounds of the point feature collection and apply the relevant image preparation function.</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ptsLandsat <span class="op">=</span> pts<span class="op">.</span><span class="fu">map</span>(<span class="fu">bufferPoints</span>(<span class="dv">15</span><span class="op">,</span> <span class="kw">true</span>))<span class="op">;</span> </span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> oliCol <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LC08/C02/T1_L2'</span>) </span>
<span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(ptsLandsat) </span>
<span id="cb50-5"><a href="#cb50-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(prepOli)<span class="op">;</span> </span>
<span id="cb50-6"><a href="#cb50-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb50-7"><a href="#cb50-7" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> etmCol <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LE07/C02/T1_L2'</span>) </span>
<span id="cb50-8"><a href="#cb50-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(ptsLandsat) </span>
<span id="cb50-9"><a href="#cb50-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(prepEtm)<span class="op">;</span> </span>
<span id="cb50-10"><a href="#cb50-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb50-11"><a href="#cb50-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> tmCol <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LT05/C02/T1_L2'</span>) </span>
<span id="cb50-12"><a href="#cb50-12" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(ptsLandsat) </span>
<span id="cb50-13"><a href="#cb50-13" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(prepEtm)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Merge the prepared sensor collections.</p>
<div class="sourceCode" id="cb51"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> landsatCol <span class="op">=</span> oliCol<span class="op">.</span><span class="fu">merge</span>(etmCol)<span class="op">.</span><span class="fu">merge</span>(tmCol)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="calculate-zonal-statistics" class="level4">
<h4 class="anchored" data-anchor-id="calculate-zonal-statistics">Calculate Zonal Statistics</h4>
<p>Reduce each image in the collection by each plot according to the following parameters. Note that this example defines the imgProps and imgPropsRename parameters to copy over and rename just two selected image properties: Landsat image ID and the satellite that collected the data. It also uses the max reducer, which, as an unweighted reducer, will return the maximum value from pixels that have their centroid within the buffer (see Sect. 4.1 below for more details).</p>
<div class="sourceCode" id="cb52"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define parameters for the zonalStats function. </span></span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> params <span class="op">=</span> { </span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">max</span>()<span class="op">,</span> </span>
<span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">30</span><span class="op">,</span> </span>
<span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> <span class="st">'EPSG:5070'</span><span class="op">,</span> </span>
<span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> [<span class="st">'Blue'</span><span class="op">,</span> <span class="st">'Green'</span><span class="op">,</span> <span class="st">'Red'</span><span class="op">,</span> <span class="st">'NIR'</span><span class="op">,</span> <span class="st">'SWIR1'</span><span class="op">,</span> <span class="st">'SWIR2'</span>]<span class="op">,</span> </span>
<span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">bandsRename</span><span class="op">:</span> [<span class="st">'ls_blue'</span><span class="op">,</span> <span class="st">'ls_green'</span><span class="op">,</span> <span class="st">'ls_red'</span><span class="op">,</span> <span class="st">'ls_nir'</span><span class="op">,</span> <span class="st">'ls_swir1'</span><span class="op">,</span> <span class="st">'ls_swir2'</span> ]<span class="op">,</span> </span>
<span id="cb52-8"><a href="#cb52-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">imgProps</span><span class="op">:</span> [<span class="st">'SENSOR_ID'</span><span class="op">,</span> <span class="st">'SPACECRAFT_ID'</span>]<span class="op">,</span> </span>
<span id="cb52-9"><a href="#cb52-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">imgPropsRename</span><span class="op">:</span> [<span class="st">'img_id'</span><span class="op">,</span> <span class="st">'satellite'</span>]<span class="op">,</span> </span>
<span id="cb52-10"><a href="#cb52-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeName</span><span class="op">:</span> <span class="st">'date'</span><span class="op">,</span> </span>
<span id="cb52-11"><a href="#cb52-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">datetimeFormat</span><span class="op">:</span> <span class="st">'YYYY-MM-dd'</span> </span>
<span id="cb52-12"><a href="#cb52-12" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb52-13"><a href="#cb52-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb52-14"><a href="#cb52-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Extract zonal statistics per point per image. </span></span>
<span id="cb52-15"><a href="#cb52-15" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ptsLandsatStats <span class="op">=</span> <span class="fu">zonalStats</span>(landsatCol<span class="op">,</span> ptsLandsat<span class="op">,</span> params) <span class="co">// Filter out observations where image pixels were all masked. .filter(ee.Filter.notNull(params.bandsRename)); </span></span>
<span id="cb52-16"><a href="#cb52-16" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Limited Landsat zonal stats table'</span><span class="op">,</span> ptsLandsatStats<span class="op">.</span><span class="fu">limit</span>(<span class="dv">50</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The result is a feature collection with a feature for all combinations of plots and images.</p>
</section>
<section id="dealing-with-large-collections" class="level4">
<h4 class="anchored" data-anchor-id="dealing-with-large-collections">Dealing with Large Collections</h4>
<p>If your browser times out, try exporting the results (as described in Chap. F6.2). Its likely that point feature collections that cover a large area or contain many points (point-image observations) will need to be exported as a batch task by either exporting the final feature collection as an asset or as a CSV/shapefile/GeoJSON to Google Drive or GCS.</p>
<p>Here is how you would export the above Landsat image-point feature collection to an asset and to Google Drive. Run the following code, activate the Code Editor Tasks tab, and then click the Run button. If you dont specify your own existing folder in Drive, the folder “EEFA_outputs” will be created.</p>
<div class="sourceCode" id="cb53"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>Export<span class="op">.</span><span class="at">table</span><span class="op">.</span><span class="fu">toAsset</span>({ </span>
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> ptsLandsatStats<span class="op">,</span> </span>
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">description</span><span class="op">:</span> <span class="st">'EEFA_export_Landsat_to_points'</span><span class="op">,</span> </span>
<span id="cb53-4"><a href="#cb53-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">assetId</span><span class="op">:</span> <span class="st">'EEFA_export_values_to_points'</span> </span>
<span id="cb53-5"><a href="#cb53-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb53-6"><a href="#cb53-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb53-7"><a href="#cb53-7" aria-hidden="true" tabindex="-1"></a>Export<span class="op">.</span><span class="at">table</span><span class="op">.</span><span class="fu">toDrive</span>({ </span>
<span id="cb53-8"><a href="#cb53-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> ptsLandsatStats<span class="op">,</span> </span>
<span id="cb53-9"><a href="#cb53-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">folder</span><span class="op">:</span> <span class="st">'EEFA_outputs'</span><span class="op">,</span> <span class="co">// this will create a new folder if it doesn't exist description: 'EEFA_export_values_to_points', </span></span>
<span id="cb53-10"><a href="#cb53-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">fileFormat</span><span class="op">:</span> <span class="st">'CSV'</span> </span>
<span id="cb53-11"><a href="#cb53-11" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F52b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="additional-notes" class="level3">
<h3 class="anchored" data-anchor-id="additional-notes">Additional Notes</h3>
<section id="weighted-versus-unweighted-region-reduction" class="level4">
<h4 class="anchored" data-anchor-id="weighted-versus-unweighted-region-reduction">Weighted Versus Unweighted Region Reduction</h4>
<p>A region used for calculation of zonal statistics often bisects multiple pixels. Should partial pixels be included in zonal statistics? Earth Engine lets you decide by allowing you to define a reducer as either weighted or unweighted (or you can provide per-pixel weight specification as an image band). A weighted reducer will include partial pixels in the zonal statistic calculation by weighting each pixels contribution according to the fraction of the area intersecting the region. An unweighted reducer, on the other hand, gives equal weight to all pixels whose cell center intersects the region; all other pixels are excluded from calculation of the statistic.</p>
<p>For aggregate reducers like ee.Reducer.mean and ee.Reducer.median, the default mode is weighted, while identifier reducers such as ee.Reducer.min and ee.Reducer.max are unweighted. You can adjust the behavior of weighted reducers by calling unweighted on them, as in ee.Reducer.mean.unweighted. You may also specify the weights by modifying the reducer with splitWeights; however, that is beyond the scope of this book.</p>
</section>
<section id="copy-properties-to-computed-images" class="level4">
<h4 class="anchored" data-anchor-id="copy-properties-to-computed-images">Copy Properties to Computed Images</h4>
<p>Derived, computed images do not retain the properties of their source image, so be sure to copy properties to computed images if you want them included in the region reduction table. For instance, consider the simple computation of unscaling Landsat SR data:</p>
<div class="sourceCode" id="cb54"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define a Landsat image. </span></span>
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> img <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LC08/C02/T1_L2'</span>)<span class="op">.</span><span class="fu">first</span>()<span class="op">;</span> </span>
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb54-4"><a href="#cb54-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Print its properties. </span></span>
<span id="cb54-5"><a href="#cb54-5" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'All image properties'</span><span class="op">,</span> img<span class="op">.</span><span class="fu">propertyNames</span>())<span class="op">;</span> </span>
<span id="cb54-6"><a href="#cb54-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb54-7"><a href="#cb54-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Subset the reflectance bands and unscale them. </span></span>
<span id="cb54-8"><a href="#cb54-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> computedImg <span class="op">=</span> img<span class="op">.</span><span class="fu">select</span>(<span class="st">'SR_B.'</span>)<span class="op">.</span><span class="fu">multiply</span>(<span class="fl">0.0000275</span>)<span class="op">.</span><span class="fu">add</span>(<span class="op">-</span><span class="fl">0.2</span>)<span class="op">;</span> </span>
<span id="cb54-9"><a href="#cb54-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb54-10"><a href="#cb54-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Print the unscaled image's properties. </span></span>
<span id="cb54-11"><a href="#cb54-11" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Lost original image properties'</span><span class="op">,</span> computedImg<span class="op">.</span><span class="fu">propertyNames</span>())<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Notice how the computed image does not have the source images properties and only retains the bands information. To fix this, use the copyProperties function to add desired source properties to the derived image. It is best practice to copy only the properties you really need because some properties, such as those containing geometry objects, lists, or feature collections, can significantly increase the computational burden for large collections.</p>
<div class="sourceCode" id="cb55"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Subset the reflectance bands and unscale them, keeping selected </span></span>
<span id="cb55-2"><a href="#cb55-2" aria-hidden="true" tabindex="-1"></a><span class="co">// source properties. </span></span>
<span id="cb55-3"><a href="#cb55-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> computedImg <span class="op">=</span> img<span class="op">.</span><span class="fu">select</span>(<span class="st">'SR_B.'</span>)<span class="op">.</span><span class="fu">multiply</span>(<span class="fl">0.0000275</span>)<span class="op">.</span><span class="fu">add</span>(<span class="op">-</span><span class="fl">0.2</span>) </span>
<span id="cb55-4"><a href="#cb55-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">copyProperties</span>(img<span class="op">,</span> [<span class="st">'system:time_start'</span><span class="op">,</span> <span class="st">'LANDSAT_PRODUCT_ID'</span>])<span class="op">;</span> </span>
<span id="cb55-5"><a href="#cb55-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb55-6"><a href="#cb55-6" aria-hidden="true" tabindex="-1"></a><span class="co">// Print the unscaled image's properties. </span></span>
<span id="cb55-7"><a href="#cb55-7" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Selected image properties retained'</span><span class="op">,</span> computedImg </span>
<span id="cb55-8"><a href="#cb55-8" aria-hidden="true" tabindex="-1"></a><span class="op">.</span><span class="fu">propertyNames</span>())<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now selected properties are included. Use this technique when returning computed, derived images in a mapped function, and in single-image operations.</p>
</section>
<section id="understanding-which-pixels-are-included-in-polygon-statistics" class="level4">
<h4 class="anchored" data-anchor-id="understanding-which-pixels-are-included-in-polygon-statistics">Understanding Which Pixels are Included in Polygon Statistics</h4>
<p>If you want to visualize what pixels are included in a polygon for a region reducer, you can adapt the following code to use your own region (by replacing geometry), dataset, desired scale, and CRS parameters. The important part to note is that the image data you are adding to the map is reprojected using the same scale and CRS as that used in your region reduction (see Fig. F5.2.3).</p>
<div class="sourceCode" id="cb56"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define polygon geometry. </span></span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> geometry <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Polygon</span>( </span>
<span id="cb56-3"><a href="#cb56-3" aria-hidden="true" tabindex="-1"></a> [ </span>
<span id="cb56-4"><a href="#cb56-4" aria-hidden="true" tabindex="-1"></a> [ </span>
<span id="cb56-5"><a href="#cb56-5" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">118.6019835717645</span><span class="op">,</span> <span class="fl">37.079867782687884</span>]<span class="op">,</span> </span>
<span id="cb56-6"><a href="#cb56-6" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">118.6019835717645</span><span class="op">,</span> <span class="fl">37.07838698844939</span>]<span class="op">,</span> </span>
<span id="cb56-7"><a href="#cb56-7" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">118.60036351751951</span><span class="op">,</span> <span class="fl">37.07838698844939</span>]<span class="op">,</span> </span>
<span id="cb56-8"><a href="#cb56-8" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">118.60036351751951</span><span class="op">,</span> <span class="fl">37.079867782687884</span>] </span>
<span id="cb56-9"><a href="#cb56-9" aria-hidden="true" tabindex="-1"></a> ] </span>
<span id="cb56-10"><a href="#cb56-10" aria-hidden="true" tabindex="-1"></a> ]<span class="op">,</span> <span class="kw">null</span><span class="op">,</span> <span class="kw">false</span>)<span class="op">;</span> </span>
<span id="cb56-11"><a href="#cb56-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb56-12"><a href="#cb56-12" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the MERIT global elevation dataset. </span></span>
<span id="cb56-13"><a href="#cb56-13" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> elev <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="st">'MERIT/DEM/v1_0_3'</span>)<span class="op">;</span> </span>
<span id="cb56-14"><a href="#cb56-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb56-15"><a href="#cb56-15" aria-hidden="true" tabindex="-1"></a><span class="co">// Define desired scale and crs for region reduction (for image display too). </span></span>
<span id="cb56-16"><a href="#cb56-16" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> proj <span class="op">=</span> { </span>
<span id="cb56-17"><a href="#cb56-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">90</span><span class="op">,</span> </span>
<span id="cb56-18"><a href="#cb56-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> <span class="st">'EPSG:5070'</span> </span>
<span id="cb56-19"><a href="#cb56-19" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The count reducer will return how many pixel centers are overlapped by the polygon region, which would be the number of pixels included in any unweighted reducer statistic. You can also visualize which pixels will be included in the reduction by using the toCollection reducer on a latitude/longitude image and adding resulting coordinates as feature geometry. Be sure to specify CRS and scale for both the region reducers and the reprojected layer added to the map (see bullet list below for more details).</p>
<div class="sourceCode" id="cb57"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a><span class="co">// A count reducer will return how many pixel centers are overlapped by the </span></span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a><span class="co">// polygon region. </span></span>
<span id="cb57-3"><a href="#cb57-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> count <span class="op">=</span> elev<span class="op">.</span><span class="fu">select</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">reduceRegion</span>({ </span>
<span id="cb57-4"><a href="#cb57-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">count</span>()<span class="op">,</span> </span>
<span id="cb57-5"><a href="#cb57-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> geometry<span class="op">,</span> </span>
<span id="cb57-6"><a href="#cb57-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> proj<span class="op">.</span><span class="at">scale</span><span class="op">,</span> <span class="dt">crs</span><span class="op">:</span> proj<span class="op">.</span><span class="at">crs</span> </span>
<span id="cb57-7"><a href="#cb57-7" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb57-8"><a href="#cb57-8" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'n pixels in the reduction'</span><span class="op">,</span> count<span class="op">.</span><span class="fu">get</span>(<span class="st">'dem'</span>))<span class="op">;</span> </span>
<span id="cb57-9"><a href="#cb57-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb57-10"><a href="#cb57-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Make a feature collection of pixel center points for those that are </span></span>
<span id="cb57-11"><a href="#cb57-11" aria-hidden="true" tabindex="-1"></a><span class="co">// included in the reduction. </span></span>
<span id="cb57-12"><a href="#cb57-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> pixels <span class="op">=</span> ee<span class="op">.</span><span class="at">Image</span><span class="op">.</span><span class="fu">pixelLonLat</span>()<span class="op">.</span><span class="fu">reduceRegion</span>({ </span>
<span id="cb57-13"><a href="#cb57-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">reducer</span><span class="op">:</span> ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">toCollection</span>([<span class="st">'lon'</span><span class="op">,</span> <span class="st">'lat'</span>])<span class="op">,</span> </span>
<span id="cb57-14"><a href="#cb57-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> geometry<span class="op">,</span> </span>
<span id="cb57-15"><a href="#cb57-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> proj<span class="op">.</span><span class="at">scale</span><span class="op">,</span> <span class="dt">crs</span><span class="op">:</span> proj<span class="op">.</span><span class="at">crs</span> </span>
<span id="cb57-16"><a href="#cb57-16" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb57-17"><a href="#cb57-17" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> pixelsFc <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(pixels<span class="op">.</span><span class="fu">get</span>(<span class="st">'features'</span>))<span class="op">.</span><span class="fu">map</span>( <span class="kw">function</span>(f) { <span class="cf">return</span> f<span class="op">.</span><span class="fu">setGeometry</span>(ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([f<span class="op">.</span><span class="fu">get</span>(<span class="st">'lon'</span>)<span class="op">,</span> f </span>
<span id="cb57-18"><a href="#cb57-18" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">get</span>(<span class="st">'lat'</span>) </span>
<span id="cb57-19"><a href="#cb57-19" aria-hidden="true" tabindex="-1"></a> ]))<span class="op">;</span> </span>
<span id="cb57-20"><a href="#cb57-20" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> </span>
<span id="cb57-21"><a href="#cb57-21" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb57-22"><a href="#cb57-22" aria-hidden="true" tabindex="-1"></a><span class="co">// Display layers on the map. </span></span>
<span id="cb57-23"><a href="#cb57-23" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(geometry<span class="op">,</span> <span class="dv">18</span>)<span class="op">;</span> </span>
<span id="cb57-24"><a href="#cb57-24" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>( </span>
<span id="cb57-25"><a href="#cb57-25" aria-hidden="true" tabindex="-1"></a> elev<span class="op">.</span><span class="fu">reproject</span>({ </span>
<span id="cb57-26"><a href="#cb57-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> proj<span class="op">.</span><span class="at">crs</span><span class="op">,</span> </span>
<span id="cb57-27"><a href="#cb57-27" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> proj<span class="op">.</span><span class="at">scale</span> })<span class="op">,</span> </span>
<span id="cb57-28"><a href="#cb57-28" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb57-29"><a href="#cb57-29" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">2500</span><span class="op">,</span> </span>
<span id="cb57-30"><a href="#cb57-30" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">3000</span><span class="op">,</span> </span>
<span id="cb57-31"><a href="#cb57-31" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'blue'</span><span class="op">,</span> <span class="st">'white'</span><span class="op">,</span> <span class="st">'red'</span>] </span>
<span id="cb57-32"><a href="#cb57-32" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'Image'</span>)<span class="op">;</span> </span>
<span id="cb57-33"><a href="#cb57-33" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(geometry<span class="op">,</span> { </span>
<span id="cb57-34"><a href="#cb57-34" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'white'</span>}<span class="op">,</span> <span class="st">'Geometry'</span>)<span class="op">;</span> </span>
<span id="cb57-35"><a href="#cb57-35" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(pixelsFc<span class="op">,</span> { </span>
<span id="cb57-36"><a href="#cb57-36" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'purple'</span>}<span class="op">,</span> <span class="st">'Pixels in reduction'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image44.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.2.3 Identifying pixels used in zonal statistics. By mapping the image and vector together, you can see which pixels are included in the unweighted statistic. For this example, three pixels would be included in the statistic because the polygon covers the center point of three pixels.</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F52c. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
<p>Finally, here are some notes on CRS and scale:</p>
<ul>
<li>Earth Engine runs reduceRegion using the projection of the images first band if the CRS is unspecified in the function. For imagery spanning multiple UTM zones, for example, this would lead to different origins. For some functions Earth Engine uses the default EPSG:4326. Therefore, when the opportunity is presented, such as by the reduceRegion function, it is important to specify the scale and CRS explicitly.</li>
<li>The Map default CRS is EPSG:3857. When looking closely at pixels on the map, the data layer scale and CRS should also be set explicitly. Note that zooming out after setting a relatively small scale when reprojecting may result in memory and/or timeout errors because optimized pyramid layers for each zoom level will not be used.</li>
<li>Specifying the CRS and scale in both the reduceRegion and addLayer functions allows the map visualization to align with the information printed in the Console.</li>
<li>The Earth Engine default, WGS 84 lat long (EPSG:4326), is a generic CRS that works worldwide. The code above reprojects to EPSG:5070, North American Equal Albers, which is a CRS that preserves area for North American locations. Use the CRS that is best for your use case when adapting this to your own project, or maintain (and specify) the CRS of the image using, for example, crs: img.projection().crs().</li>
</ul>
</section>
</section>
<section id="conclusion-2" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion-2">Conclusion</h3>
<p>In this chapter, you used functions containing optional parameters to extract raster values for collocated points. You also learned how to buffer points, and apply weighted and unweighted reducers to get different types of zonal statistics. These functions were applied to three examples that differed by raster dataset, reducer, spatial resolution, and scale. Lastly, you covered related topics like weighting of reducers and buffer visualization. Now youre ready to apply these ideas to your own work!</p>
</section>
<section id="references" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="references">References</h3>
<p>Cansler CA, McKenzie D (2012) How robust are burn severity indices when applied in a new region? Evaluation of alternate field-based and remote-sensing methods. Remote Sens 4:456483. https://doi.org/10.3390/rs4020456</p>
<p>Miller JD, Thode AE (2007) Quantifying burn severity in a heterogeneous landscape with a relative version of the delta Normalized Burn Ratio (dNBR). Remote Sens Environ 109:6680. https://doi.org/10.1016/j.rse.2006.12.006</p>
</section>
</section>
<section id="advanced-vector-operations" class="level2">
<h2 class="anchored" data-anchor-id="advanced-vector-operations">Advanced Vector Operations</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author-3" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author-3">Author</h4>
<p>Ujaval Gandhi</p>
</section>
<section id="overview-3" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview-3">Overview</h4>
<p>This chapter covers advanced techniques for visualizing and analyzing vector data in Earth Engine. There are many ways to visualize feature collections, and you will learn how to pick the appropriate method to create visualizations, such as a choropleth map. We will also cover geoprocessing techniques involving multiple vector layers, such as selecting features in one layer by their proximity to features in another layer and performing spatial joins.</p>
</section>
<section id="learning-outcomes-3" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes-3">Learning Outcomes</h4>
<ul>
<li>Visualizing any vector dataset and creating a thematic map.</li>
<li>Understanding joins in Earth Engine.</li>
<li>Carrying out geoprocessing tasks with vector layers in Earth Engine.</li>
</ul>
</section>
<section id="assumes-you-know-how-to-3" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to-3">Assumes you know how to:</h4>
<ul>
<li>Filter a FeatureCollection to obtain a subset (Chap. F5.0, Chap. F5.1).</li>
<li>Write a function and map it over a FeatureCollection (Chap. F5.1, Chap. F5.2).</li>
</ul>
</section>
</div>
</div>
<section id="visualizing-feature-collections" class="level3">
<h3 class="anchored" data-anchor-id="visualizing-feature-collections">Visualizing Feature Collections</h3>
<p>There is a distinct difference between how rasters and vectors are visualized. While images are typically visualized based on pixel values, vector layers use feature properties (i.e., attributes) to create a visualization. Vector layers are rendered on the Map by assigning a value to the red, green, and blue channels for each pixel on the screen based on the geometry and attributes of the features. The functions used for vector data visualization in Earth Engine are listed below in increasing order of complexity.</p>
<ul>
<li>Map.addLayer: As with raster layers, you can add a FeatureCollection to the Map by specifying visualization parameters. This method supports only one visualization parameter: color. All features are rendered with the specified color.</li>
<li>draw: This function supports the parameters pointRadius and strokeWidth in addition to color. It renders all features of the layer with the specified parameters.</li>
<li>paint: This is a more powerful function that can render each feature with a different color and width based on the values in the specified property.</li>
<li>style: This is the most versatile function. It can apply a different style to each feature, including color, pointSize, pointShape, width, fillColor, and lineType.</li>
</ul>
<p>In the exercises below, we will learn how to use each of these functions and see how they can generate different types of maps.</p>
<section id="creating-a-choropleth-map" class="level4">
<h4 class="anchored" data-anchor-id="creating-a-choropleth-map">Creating a Choropleth Map</h4>
<p>We will use the TIGER: US Census Blocks layer, which stores census block boundaries and their characteristics within the United States, along with the San Francisco neighborhoods layer from Chap. F5.0 to create a population density map for the city of San Francisco.</p>
<p>We start by loading the census blocks and San Francisco neighborhoods layers. We use ee.Filter.bounds to filter the census blocks layer to the San Francisco boundary.</p>
<div class="sourceCode" id="cb58"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> blocks <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'TIGER/2010/Blocks'</span>)<span class="op">;</span> </span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> roads <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'TIGER/2016/Roads'</span>)<span class="op">;</span> </span>
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfNeighborhoods <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>( <span class="st">'projects/gee-book/assets/F5-0/SFneighborhoods'</span>)<span class="op">;</span> </span>
<span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> geometry <span class="op">=</span> sfNeighborhoods<span class="op">.</span><span class="fu">geometry</span>()<span class="op">;</span> </span>
<span id="cb58-6"><a href="#cb58-6" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(geometry)<span class="op">;</span> </span>
<span id="cb58-7"><a href="#cb58-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb58-8"><a href="#cb58-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter blocks to the San Francisco boundary. </span></span>
<span id="cb58-9"><a href="#cb58-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfBlocks <span class="op">=</span> blocks<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">bounds</span>(geometry))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The simplest way to visualize this layer is to use Map.addLayer (Fig. F5.3.1). We can specify a color value in the visParams parameter of the function. Each census block polygon will be rendered with stroke and fill of the specified color. The fill color is the same as the stroke color but has a 66% opacity.</p>
<div class="sourceCode" id="cb59"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Visualize with a single color. </span></span>
<span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfBlocks<span class="op">,</span> { </span>
<span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'#de2d26'</span>}<span class="op">,</span> <span class="st">'Census Blocks (single color)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image34.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.1 San Francisco census blocks</figcaption><p></p>
</figure>
</div>
<p>The census blocks table has a property named pop10 containing the population totals as of the 2010 census. We can use this to create a choropleth map showing population density. We first need to compute the population density for each feature and add it as a property. To add a new property to each feature, we can map a function over the FeatureCollection and calculate the new property called pop_density. Earth Engine provides the area function, which can calculate the area of a feature in square meters. We convert it to square miles and calculate the population density per square mile.</p>
<div class="sourceCode" id="cb60"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Add a pop_density column. </span></span>
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfBlocks <span class="op">=</span> sfBlocks<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(f) { <span class="co">// Get the polygon area in square miles. </span></span>
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> area_sqmi <span class="op">=</span> f<span class="op">.</span><span class="fu">area</span>()<span class="op">.</span><span class="fu">divide</span>(<span class="fl">2.59e6</span>)<span class="op">;</span> </span>
<span id="cb60-4"><a href="#cb60-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> population <span class="op">=</span> f<span class="op">.</span><span class="fu">get</span>(<span class="st">'pop10'</span>)<span class="op">;</span> <span class="co">// Calculate population density. </span></span>
<span id="cb60-5"><a href="#cb60-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> density <span class="op">=</span> ee<span class="op">.</span><span class="fu">Number</span>(population)<span class="op">.</span><span class="fu">divide</span>(area_sqmi)<span class="op">;</span> </span>
<span id="cb60-6"><a href="#cb60-6" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> f<span class="op">.</span><span class="fu">set</span>({ </span>
<span id="cb60-7"><a href="#cb60-7" aria-hidden="true" tabindex="-1"></a> <span class="st">'area_sqmi'</span><span class="op">:</span> area_sqmi<span class="op">,</span> </span>
<span id="cb60-8"><a href="#cb60-8" aria-hidden="true" tabindex="-1"></a> <span class="st">'pop_density'</span><span class="op">:</span> density </span>
<span id="cb60-9"><a href="#cb60-9" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> </span>
<span id="cb60-10"><a href="#cb60-10" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now we can use the paint function to create an image from this FeatureCollection using the pop_density property. The paint function needs an empty image that needs to be cast to the appropriate data type. Lets use the aggregate_stats function to calculate basic statistics for the given column of a FeatureCollection.</p>
<div class="sourceCode" id="cb61"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate the statistics of the newly computed column. </span></span>
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> stats <span class="op">=</span> sfBlocks<span class="op">.</span><span class="fu">aggregate_stats</span>(<span class="st">'pop_density'</span>)<span class="op">;</span> </span>
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(stats)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>You will see that the population density values have a large range. We also have values that are greater than 100,000, so we need to make sure we select a data type that can store values of this size. We create an empty image and cast it to int32, which is able to hold large integer values.</p>
<p>The result is an image with pixel values representing the population density of the polygons. We can now use the standard image visualization method to add this layer to the Map (Fig. F5.3.2). Then, we need to determine minimum and maximum values for the visualization parameters.A reliable technique to produce a good visualization is to find minimum and maximum values that are within one standard deviation. From the statistics that we calculated earlier, we can estimate good minimum and maximum values to be 0 and 50000, respectively.</p>
<div class="sourceCode" id="cb62"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> palette <span class="op">=</span> [<span class="st">'fee5d9'</span><span class="op">,</span> <span class="st">'fcae91'</span><span class="op">,</span> <span class="st">'fb6a4a'</span><span class="op">,</span> <span class="st">'de2d26'</span><span class="op">,</span> <span class="st">'a50f15'</span>]<span class="op">;</span> </span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> visParams <span class="op">=</span> { </span>
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb62-4"><a href="#cb62-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">50000</span><span class="op">,</span> </span>
<span id="cb62-5"><a href="#cb62-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> palette </span>
<span id="cb62-6"><a href="#cb62-6" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb62-7"><a href="#cb62-7" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfBlocksPaint<span class="op">.</span><span class="fu">clip</span>(geometry)<span class="op">,</span> visParams<span class="op">,</span> <span class="st">'Population Density'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image41.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.2 San Francisco population density</figcaption><p></p>
</figure>
</div>
</section>
<section id="creating-a-categorical-map" class="level4">
<h4 class="anchored" data-anchor-id="creating-a-categorical-map">Creating a Categorical Map</h4>
<p>Continuing the exploration of styling methods, we will now learn about draw and style. These are the preferred methods of styling for points and line layers. Lets see how we can visualize the TIGER: US Census Roads layer to create a categorical map.</p>
<p>We start by filtering the roads layer to the San Francisco boundary and using Map.addLayer to visualize it.</p>
<div class="sourceCode" id="cb63"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter roads to San Francisco boundary. </span></span>
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfRoads <span class="op">=</span> roads<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">bounds</span>(geometry))<span class="op">;</span> </span>
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb63-4"><a href="#cb63-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfRoads<span class="op">,</span> { </span>
<span id="cb63-5"><a href="#cb63-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'blue'</span>}<span class="op">,</span> <span class="st">'Roads (default)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The default visualization renders each line using a width of 2 pixels. The draw function provides a way to specify a different line width. Lets use it to render the layer with the same color as before but with a line width of 1 pixel (Fig. F5.3.3).</p>
<div class="sourceCode" id="cb64"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Visualize with draw(). </span></span>
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfRoadsDraw <span class="op">=</span> sfRoads<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'blue'</span><span class="op">,</span> </span>
<span id="cb64-4"><a href="#cb64-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb64-5"><a href="#cb64-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb64-6"><a href="#cb64-6" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfRoadsDraw<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Roads (Draw)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="images/F5/image28.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image31.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.3 San Francisco roads rendered with a line width of 2 pixels (left) and and a line width of 1 pixel (right)</figcaption><p></p>
</figure>
</div>
<p>The road layer has a column called “MTFCC” (standing for the MAF/TIGER Feature Class Code). This contains the road priority codes, representing the various types of roads, such as primary and secondary. We can use this information to render each road segment according to its priority. The draw function doesnt allow us to specify different styles for each feature. Instead, we need to make use of the style function.</p>
<p>The column contains string values indicating different road types as indicated in at the MAF/TIGER Feature Class Code Definitions page on the US Census Bureau website. Lets say we want to create a map with rules based on the MTFCC values.</p>
<p>Lets define a dictionary containing the styling information.</p>
<div class="sourceCode" id="cb65"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> styles <span class="op">=</span> ee<span class="op">.</span><span class="fu">Dictionary</span>({ <span class="st">'S1100'</span><span class="op">:</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'blue'</span><span class="op">,</span> <span class="st">'width'</span><span class="op">:</span> <span class="dv">3</span> }<span class="op">,</span> <span class="st">'S1200'</span><span class="op">:</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'green'</span><span class="op">,</span> <span class="st">'width'</span><span class="op">:</span> <span class="dv">2</span> }<span class="op">,</span> <span class="st">'S1400'</span><span class="op">:</span> { <span class="st">'color'</span><span class="op">:</span> <span class="st">'orange'</span><span class="op">,</span> <span class="st">'width'</span><span class="op">:</span> <span class="dv">1</span> } </span>
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span><span class="kw">var</span> defaultStyle <span class="op">=</span> { </span>
<span id="cb65-3"><a href="#cb65-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'gray'</span><span class="op">,</span> <span class="st">'width'</span><span class="op">:</span> <span class="dv">1</span> </span>
<span id="cb65-4"><a href="#cb65-4" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The style function needs a property in the FeatureCollection that contains a dictionary with the style parameters. This allows you to specify a different style for each feature. To create a new property, we map a function over the FeatureCollection and assign an appropriate style dictionary to a new property named style. Note the use of the get function, which allows us to fetch the value for a key in the dictionary. It also takes a default value in case the specified key does not exist. We make use of this to assign different styles to the three road classes specified in Table 5.3.2 and a default style to all others.</p>
<div class="sourceCode" id="cb66"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfRoads <span class="op">=</span> sfRoads<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(f) { <span class="kw">var</span> classcode <span class="op">=</span> f<span class="op">.</span><span class="fu">get</span>(<span class="st">'mtfcc'</span>)<span class="op">;</span> <span class="kw">var</span> style <span class="op">=</span> styles<span class="op">.</span><span class="fu">get</span>(classcode<span class="op">,</span> defaultStyle)<span class="op">;</span> <span class="cf">return</span> f<span class="op">.</span><span class="fu">set</span>(<span class="st">'style'</span><span class="op">,</span> style)<span class="op">;</span> </span>
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Our collection is now ready to be styled. We call the style function to specify the property that contains the dictionary of style parameters. The output of the style function is an RGB image rendered from the FeatureCollection (Fig. F5.3.4).</p>
<div class="sourceCode" id="cb67"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb67-1"><a href="#cb67-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfRoadsStyle <span class="op">=</span> sfRoads<span class="op">.</span><span class="fu">style</span>({ </span>
<span id="cb67-2"><a href="#cb67-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">styleProperty</span><span class="op">:</span> <span class="st">'style'</span> </span>
<span id="cb67-3"><a href="#cb67-3" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb67-4"><a href="#cb67-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfRoadsStyle<span class="op">.</span><span class="fu">clip</span>(geometry)<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Roads (Style)'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image46.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.4 San Francisco roads rendered according to road priority</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F53a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
<p>Save your script for your own future use, as outlined in Chap. F1.0. Then, refresh the Code Editor to begin with a new script for the next section.</p>
</section>
</section>
<section id="joins-with-feature-collections" class="level3">
<h3 class="anchored" data-anchor-id="joins-with-feature-collections">Joins with Feature Collections</h3>
<p>Earth Engine was designed as a platform for processing raster data, and that is where it shines. Over the years, it has acquired advanced vector data processing capabilities, and users are now able to carry out complex geoprocessing tasks within Earth Engine. You can leverage the distributed processing power of Earth Engine to process large vector layers in parallel.</p>
<p>This section shows how you can do spatial queries and spatial joins using multiple large feature collections. This requires the use of joins. As described for Image Collections in Chap. F4.9, a join allows you to match every item in a collection with items in another collection based on certain conditions. While you can achieve similar results using map and filter, joins perform better and give you more flexibility. We need to define the following items to perform a join on two collections.</p>
<ol type="1">
<li>Filter: A filter defines the condition used to select the features from the two collections. There is a suite of filters in the ee.Filters module that work on two collections, such as ee.Filter.equals and ee.Filter.withinDistance.</li>
<li>Join type: While the filter determines which features will be joined, the join type determines how they will be joined. There are many join types, including simple join, inner join, and save-all join.</li>
</ol>
<p>Joins are one of the harder skills to master, but doing so will help you perform many complex analysis tasks within Earth Engine. We will go through practical examples that will help you understand these concepts and the workflow better.</p>
<section id="selecting-by-location" class="level4">
<h4 class="anchored" data-anchor-id="selecting-by-location">Selecting by Location</h4>
<p>In this section, we will learn how to select features from one layer that are within a specified distance from features in another layer. We will continue to work with the San Francisco census blocks and roads datasets from the previous section. We will implement a join to select all blocks in San Francisco that are within 1 km of an interstate highway.</p>
<p>We start by loading the census blocks and roads collections and filtering the roads layer to the San Francisco boundary.</p>
<div class="sourceCode" id="cb68"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb68-1"><a href="#cb68-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> blocks <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'TIGER/2010/Blocks'</span>)<span class="op">;</span> </span>
<span id="cb68-2"><a href="#cb68-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> roads <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">'TIGER/2016/Roads'</span>)<span class="op">;</span> </span>
<span id="cb68-3"><a href="#cb68-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfNeighborhoods <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>( <span class="st">'projects/gee-book/assets/F5-0/SFneighborhoods'</span>)<span class="op">;</span> </span>
<span id="cb68-4"><a href="#cb68-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb68-5"><a href="#cb68-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> geometry <span class="op">=</span> sfNeighborhoods<span class="op">.</span><span class="fu">geometry</span>()<span class="op">;</span> </span>
<span id="cb68-6"><a href="#cb68-6" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(geometry)<span class="op">;</span> </span>
<span id="cb68-7"><a href="#cb68-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb68-8"><a href="#cb68-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter blocks and roads to San Francisco boundary. </span></span>
<span id="cb68-9"><a href="#cb68-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfBlocks <span class="op">=</span> blocks<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">bounds</span>(geometry))<span class="op">;</span> </span>
<span id="cb68-10"><a href="#cb68-10" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfRoads <span class="op">=</span> roads<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">bounds</span>(geometry))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>As we want to select all blocks within 1 km of an interstate highway, we first filter the sfRoads collection to select all segments with the rttyp property value of I.</p>
<div class="sourceCode" id="cb69"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> interstateRoads <span class="op">=</span> sfRoads<span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">eq</span>(<span class="st">'rttyp'</span><span class="op">,</span> <span class="st">'I'</span>))<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We use the draw function to visualize the sfBlocks and interstateRoads layers (Fig. F5.3.5).</p>
<div class="sourceCode" id="cb70"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfBlocksDrawn <span class="op">=</span> sfBlocks<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'gray'</span><span class="op">,</span> </span>
<span id="cb70-3"><a href="#cb70-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">1</span> }) </span>
<span id="cb70-4"><a href="#cb70-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">clip</span>(geometry)<span class="op">;</span> </span>
<span id="cb70-5"><a href="#cb70-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfBlocksDrawn<span class="op">,</span> {}<span class="op">,</span> <span class="st">'All Blocks'</span>)<span class="op">;</span> </span>
<span id="cb70-6"><a href="#cb70-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> interstateRoadsDrawn <span class="op">=</span> interstateRoads<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb70-7"><a href="#cb70-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'blue'</span><span class="op">,</span> </span>
<span id="cb70-8"><a href="#cb70-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">3</span> }) </span>
<span id="cb70-9"><a href="#cb70-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">clip</span>(geometry)<span class="op">;</span> </span>
<span id="cb70-10"><a href="#cb70-10" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(interstateRoadsDrawn<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Interstate Roads'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image2.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.5 San Francisco blocks and interstate highways</figcaption><p></p>
</figure>
</div>
<p>Lets define a join that will select all the features from the sfBlocks layer that are within 1 km of any feature from the interstateRoads layer. We start by defining a filter using the ee.Filter.withinDistance filter. We want to compare the geometries of features in both layers, so we use a special property called .geo to compare the collections. By default, the filter will work with exact distances between the geometries. If your analysis does not require a very precise tolerance of spatial uncertainty, specifying a small non-zero maxError distance value will help speed up the spatial operations. A larger tolerance also helps when testing or debugging code so you can get the result quickly instead of waiting longer for a more precise output.</p>
<div class="sourceCode" id="cb71"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb71-1"><a href="#cb71-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> joinFilter <span class="op">=</span> ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">withinDistance</span>({ </span>
<span id="cb71-2"><a href="#cb71-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">distance</span><span class="op">:</span> <span class="dv">1000</span><span class="op">,</span> </span>
<span id="cb71-3"><a href="#cb71-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">leftField</span><span class="op">:</span> <span class="st">'.geo'</span><span class="op">,</span> </span>
<span id="cb71-4"><a href="#cb71-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">rightField</span><span class="op">:</span> <span class="st">'.geo'</span><span class="op">,</span> </span>
<span id="cb71-5"><a href="#cb71-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxError</span><span class="op">:</span> <span class="dv">10</span> </span>
<span id="cb71-6"><a href="#cb71-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We will use a simple join as we just want features from the first (primary) collection that match the features from the other (secondary) collection.</p>
<div class="sourceCode" id="cb72"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb72-1"><a href="#cb72-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> closeBlocks <span class="op">=</span> ee<span class="op">.</span><span class="at">Join</span><span class="op">.</span><span class="fu">simple</span>()<span class="op">.</span><span class="fu">apply</span>({ </span>
<span id="cb72-2"><a href="#cb72-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">primary</span><span class="op">:</span> sfBlocks<span class="op">,</span> </span>
<span id="cb72-3"><a href="#cb72-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">secondary</span><span class="op">:</span> interstateRoads<span class="op">,</span> </span>
<span id="cb72-4"><a href="#cb72-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">condition</span><span class="op">:</span> joinFilter </span>
<span id="cb72-5"><a href="#cb72-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We can visualize the results in a different color and verify that the join worked as expected (Fig. F5.3.6).</p>
<div class="sourceCode" id="cb73"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> closeBlocksDrawn <span class="op">=</span> closeBlocks<span class="op">.</span><span class="fu">draw</span>({ </span>
<span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'orange'</span><span class="op">,</span> </span>
<span id="cb73-3"><a href="#cb73-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">strokeWidth</span><span class="op">:</span> <span class="dv">1</span> }) </span>
<span id="cb73-4"><a href="#cb73-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">clip</span>(geometry)<span class="op">;</span> </span>
<span id="cb73-5"><a href="#cb73-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(closeBlocksDrawn<span class="op">,</span> {}<span class="op">,</span> <span class="st">'Blocks within 1km'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image40.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.6 Selected blocks within 1 km of an interstate highway</figcaption><p></p>
</figure>
</div>
</section>
<section id="spatial-joins" class="level4">
<h4 class="anchored" data-anchor-id="spatial-joins">Spatial Joins</h4>
<p>A spatial join allows you to query two collections based on the spatial relationship. We will now implement a spatial join to count points in polygons. We will work with a dataset of tree locations in San Francisco and polygons of neighborhoods to produce a CSV file with the total number of trees in each neighborhood.</p>
<p>The San Francisco Open Data Portal maintains a street tree map dataset that has a list of street trees with their latitude and longitude. We will also use the San Francisco neighborhood dataset from the same portal. We downloaded, processed, and uploaded these layers as Earth Engine assets for use in this exercise. We start by loading both layers and using the paint and style functions, covered in Sect. 1, to visualize them (Fig. F5.3.7).</p>
<p>var sfNeighborhoods = ee.FeatureCollection( projects/gee-book/assets/F5-0/SFneighborhoods);<br>
var sfTrees = ee.FeatureCollection( projects/gee-book/assets/F5-3/SFTrees);</p>
<div class="sourceCode" id="cb74"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb74-1"><a href="#cb74-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Use paint() to visualize the polygons with only outline </span></span>
<span id="cb74-2"><a href="#cb74-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfNeighborhoodsOutline <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>()<span class="op">.</span><span class="fu">byte</span>()<span class="op">.</span><span class="fu">paint</span>({ </span>
<span id="cb74-3"><a href="#cb74-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> sfNeighborhoods<span class="op">,</span> </span>
<span id="cb74-4"><a href="#cb74-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb74-5"><a href="#cb74-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">3</span> </span>
<span id="cb74-6"><a href="#cb74-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb74-7"><a href="#cb74-7" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfNeighborhoodsOutline<span class="op">,</span> { </span>
<span id="cb74-8"><a href="#cb74-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'blue'</span>] </span>
<span id="cb74-9"><a href="#cb74-9" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'SF Neighborhoods'</span>)<span class="op">;</span> </span>
<span id="cb74-10"><a href="#cb74-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb74-11"><a href="#cb74-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Use style() to visualize the points </span></span>
<span id="cb74-12"><a href="#cb74-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfTreesStyled <span class="op">=</span> sfTrees<span class="op">.</span><span class="fu">style</span>({ </span>
<span id="cb74-13"><a href="#cb74-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">'green'</span><span class="op">,</span> </span>
<span id="cb74-14"><a href="#cb74-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">pointSize</span><span class="op">:</span> <span class="dv">2</span><span class="op">,</span> </span>
<span id="cb74-15"><a href="#cb74-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">pointShape</span><span class="op">:</span> <span class="st">'triangle'</span><span class="op">,</span> </span>
<span id="cb74-16"><a href="#cb74-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">2</span> </span>
<span id="cb74-17"><a href="#cb74-17" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb74-18"><a href="#cb74-18" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfTreesStyled<span class="op">,</span> {}<span class="op">,</span> <span class="st">'SF Trees'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image35.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.7 San Francisco neighborhoods and trees</figcaption><p></p>
</figure>
</div>
<p>To find the tree points in each neighborhood polygon, we will use an ee.Filter.intersects filter.</p>
<div class="sourceCode" id="cb75"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> intersectFilter <span class="op">=</span> ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">intersects</span>({ </span>
<span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">leftField</span><span class="op">:</span> <span class="st">'.geo'</span><span class="op">,</span> </span>
<span id="cb75-3"><a href="#cb75-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">rightField</span><span class="op">:</span> <span class="st">'.geo'</span><span class="op">,</span> </span>
<span id="cb75-4"><a href="#cb75-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxError</span><span class="op">:</span> <span class="dv">10</span> </span>
<span id="cb75-5"><a href="#cb75-5" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>We need a join that can give us a list of all tree features that intersect each neighborhood polygon, so we need to use a saving join. A saving join will find all the features from the secondary collection that match the filter and store them in a property in the primary collection. Once you apply this join, you will get a version of the primary collection with an additional property that has the matching features from the secondary collection. Here we use the ee.Join.saveAll join, since we want to store all matching features. We specify the matchesKey property that will be added to each feature with the results.</p>
<div class="sourceCode" id="cb76"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> saveAllJoin <span class="op">=</span> ee<span class="op">.</span><span class="at">Join</span><span class="op">.</span><span class="fu">saveAll</span>({ </span>
<span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">matchesKey</span><span class="op">:</span> <span class="st">'trees'</span><span class="op">,</span> </span>
<span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Lets apply the join and print the first feature of the resulting collection to verify (Fig. F5.3.8).</p>
<div class="sourceCode" id="cb77"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> joined <span class="op">=</span> saveAllJoin </span>
<span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">apply</span>(sfNeighborhoods<span class="op">,</span> sfTrees<span class="op">,</span> intersectFilter)<span class="op">;</span> </span>
<span id="cb77-3"><a href="#cb77-3" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(joined<span class="op">.</span><span class="fu">first</span>())<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image1.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.8 Result of the save-all join</figcaption><p></p>
</figure>
</div>
<p>You will see that each feature of the sfNeighborhoods collection now has an additional property called trees. This contains all the features from the sfTrees collection that were matched using the intersectFilter. We can now map a function over the results and post-process the collection. As our analysis requires the computation of the total number of trees in each neighborhood, we extract the matching features and use the size function to get the count (Fig. F5.3.9).</p>
<div class="sourceCode" id="cb78"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb78-1"><a href="#cb78-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate total number of trees within each feature. </span></span>
<span id="cb78-2"><a href="#cb78-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfNeighborhoods <span class="op">=</span> joined<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(f) { <span class="kw">var</span> treesWithin <span class="op">=</span> ee<span class="op">.</span><span class="fu">List</span>(f<span class="op">.</span><span class="fu">get</span>(<span class="st">'trees'</span>))<span class="op">;</span> <span class="kw">var</span> totalTrees <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>(treesWithin)<span class="op">.</span><span class="fu">size</span>()<span class="op">;</span> <span class="cf">return</span> f<span class="op">.</span><span class="fu">set</span>(<span class="st">'total_trees'</span><span class="op">,</span> totalTrees)<span class="op">;</span> </span>
<span id="cb78-3"><a href="#cb78-3" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb78-4"><a href="#cb78-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb78-5"><a href="#cb78-5" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(sfNeighborhoods<span class="op">.</span><span class="fu">first</span>())<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image18.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.9 Final FeatureCollection with the new property</figcaption><p></p>
</figure>
</div>
<p>The results now have a property called total_trees containing the count of intersecting trees in each neighborhood polygon.</p>
<p>The final step in the analysis is to export the results as a CSV file using the Export.table.toDrive function. Note that as described in detail in F6.2, you should output only the columns you need to the CSV file. Suppose we do not need all the properties to appear in the output; imagine that wedo not need the trees property, for example, in the output. In that case, we can create only those columns we want in the manner below, by specifying the other selectors parameters with the list of properties to export.</p>
<div class="sourceCode" id="cb79"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb79-1"><a href="#cb79-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Export the results as a CSV. </span></span>
<span id="cb79-2"><a href="#cb79-2" aria-hidden="true" tabindex="-1"></a>Export<span class="op">.</span><span class="at">table</span><span class="op">.</span><span class="fu">toDrive</span>({ </span>
<span id="cb79-3"><a href="#cb79-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> sfNeighborhoods<span class="op">,</span> </span>
<span id="cb79-4"><a href="#cb79-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">description</span><span class="op">:</span> <span class="st">'SF_Neighborhood_Tree_Count'</span><span class="op">,</span> </span>
<span id="cb79-5"><a href="#cb79-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">folder</span><span class="op">:</span> <span class="st">'earthengine'</span><span class="op">,</span> </span>
<span id="cb79-6"><a href="#cb79-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">fileNamePrefix</span><span class="op">:</span> <span class="st">'tree_count'</span><span class="op">,</span> </span>
<span id="cb79-7"><a href="#cb79-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">fileFormat</span><span class="op">:</span> <span class="st">'CSV'</span><span class="op">,</span> </span>
<span id="cb79-8"><a href="#cb79-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">selectors</span><span class="op">:</span> [<span class="st">'nhood'</span><span class="op">,</span> <span class="st">'total_trees'</span>] </span>
<span id="cb79-9"><a href="#cb79-9" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The final result is a CSV file with the neighborhood names and total numbers of trees counted using the join (Fig. F5.3.10).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F5/image3.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F5.3.10 Exported CSV file with tree counts for San Francisco neighborhoods</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F53b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="conclusion-3" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion-3">Conclusion</h3>
<p>This chapter covered visualization and analysis using vector data in Earth Engine. You should now understand different functions for FeatureCollection visualization and be able to create thematic maps with vector layers. You also learned techniques for doing spatial queries and spatial joins within Earth Engine. Earth Engine is capable of handling large feature collections and can be effectively used for many spatial analysis tasks.</p>
</section>
</section>
</main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
toggleBodyColorPrimary();
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'prefetch';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'stylesheet';
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
const hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
let localAlternateSentinel = 'alternate';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
};
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
// Switch to dark mode if need be
if (hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const clipboard = new window.ClipboardJS('.code-copy-button', {
text: function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
});
clipboard.on('success', function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
});
function tippyHover(el, contentFn) {
const config = {
allowHTML: true,
content: contentFn,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start'
};
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
return note.innerHTML;
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
<nav class="page-navigation">
<div class="nav-page nav-page-previous">
<a href="./B3_Image_Series.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Image Series</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="./C1_Lights.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">War at Night</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
</body></html>