eoghan edits + changed dir structure

This commit is contained in:
Ollie Ballinger
2023-04-17 11:28:29 +01:00
parent 6c5816d659
commit 7ccf85e48e
653 changed files with 44167 additions and 4363 deletions

View File

@@ -0,0 +1,606 @@
<!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.2.269">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - 1&nbsp; Remote Sensing</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 -1.6em;
vertical-align: middle;
}
</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="../chapters/A3_Data_Acquisition.html" rel="next">
<link href="../index.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 src="../site_libs/quarto-contrib/videojs/video.min.js"></script>
<link href="../site_libs/quarto-contrib/videojs/video-js.css" rel="stylesheet">
<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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<div class="container-fluid d-flex justify-content-between">
<h1 class="quarto-secondary-nav-title"><span class="chapter-title">Remote Sensing</span></h1>
<button type="button" class="quarto-btn-toggle btn" aria-label="Show secondary navigation">
<i class="bi bi-chevron-right"></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 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="sidebar-tool px-1"><i class="bi bi-github"></i></a>
<a href="" title="Download" id="sidebar-tool-dropdown-0" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" title="Share" id="sidebar-tool-dropdown-1" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" class="quarto-color-scheme-toggle sidebar-tool" 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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="true">A. Introduction</a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="true">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../index.html" class="sidebar-item-text sidebar-link">Overview</a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/A2_Remote_Sensing.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Remote Sensing</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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 collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">B. Google Earth Engine</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><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">C. Case Studies</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="false">
<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="../chapters/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="../chapters/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="../chapters/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="../chapters/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="../chapters/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>
<!-- 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="#active-and-passive-sensors" id="toc-active-and-passive-sensors" class="nav-link active" data-scroll-target="#active-and-passive-sensors">Active and Passive Sensors</a></li>
<li><a href="#resolution" id="toc-resolution" class="nav-link" data-scroll-target="#resolution">Resolution</a>
<ul class="collapse">
<li><a href="#spatial-resolution" id="toc-spatial-resolution" class="nav-link" data-scroll-target="#spatial-resolution">Spatial Resolution</a></li>
<li><a href="#spectral-resolution" id="toc-spectral-resolution" class="nav-link" data-scroll-target="#spectral-resolution">Spectral Resolution</a></li>
<li><a href="#temporal-resolution" id="toc-temporal-resolution" class="nav-link" data-scroll-target="#temporal-resolution">Temporal Resolution</a></li>
</ul></li>
<li><a href="#summary" id="toc-summary" class="nav-link" data-scroll-target="#summary">Summary</a></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/chapters/A2_Remote_Sensing.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 d-none d-lg-block"><span class="chapter-title">Remote Sensing</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>Before learning how to load, process, and analyze satellite imagery in Google Earth Engine, it will be helpful to know a few basic principles of remote sensing. This section provides a brief overview of some important concepts and terminology that will be used throughout the course, including active and passive sensors; spatial, spectral and temporal resolution; and orbits.</p>
<section id="active-and-passive-sensors" class="level2">
<h2 class="anchored" data-anchor-id="active-and-passive-sensors">Active and Passive Sensors</h2>
<p><a href="https://www.sciencedirect.com/topics/medicine-and-dentistry/remote-sensing">Remote sensing</a> is the science of obtaining information about an object or phenomenon without making physical contact with the object. Remote sensing can be done with various types of electromagnetic radiation such as visible, infrared or microwave. The electromagnetic radiation is either emitted or reflected from the object being sensed. The reflected radiation is then collected by a sensor and processed to obtain information about the object.</p>
<p><img src="../images/diagram.png" class="img-fluid"></p>
<p>While most satellite imagery is optical, meaning it captures sunlight reflected by the earths surface, Synthetic Aperture Radar (SAR) satellites such as Sentinel-1 work by emitting pulses of radio waves and measuring how much of the signal is reflected back. This is similar to the way a bat uses sonar to “see” in the dark: by emitting calls and listening to echoes.</p>
</section>
<section id="resolution" class="level2">
<h2 class="anchored" data-anchor-id="resolution">Resolution</h2>
<p>Resolution is one of the most important attributes of satellite imagery. There are three types of resolution: spatial, spectral, and temporal. Lets look at each of these.</p>
<section id="spatial-resolution" class="level3">
<h3 class="anchored" data-anchor-id="spatial-resolution">Spatial Resolution</h3>
<p>Spatial resolution governs how “sharp” an image looks. The Google Maps satellite basemap, for example, is really sharp. Most of the optical imagery that is freely available has relatively low spatial resolution (it looks more grainy than, for example, the Google satellite basemap),</p>
<p><img src="../images/Landsat.png" class="img-fluid"> <img src="../images/Sentinel2.png" class="img-fluid"> <img src="../images/Maxar.png" class="img-fluid"></p>
</section>
<section id="spectral-resolution" class="level3">
<h3 class="anchored" data-anchor-id="spectral-resolution">Spectral Resolution</h3>
<p>What open access imagery lacks in spatial resolution it often makes up for with <em>spectral</em> resolution. Really sharp imagery from MAXAR, for example, mostly collects light in the visible light spectrum, which is what our eyes can see. But there are other parts of the electromagnetic spectrum that we cant see, but which can be very useful for distinguishing between different materials. Many satellites that have a lower spatial resolution than MAXAR, such as Landsat and Sentinel-2, collect data in a wider range of the electromagnetic spectrum.</p>
<p>Different materials reflect light differently. An apple absorbs shorter wavelengths (e.g.&nbsp;blue and green), and reflects longer wavelengths (red). Our eyes use that information the color to distinguish between different objects. Below is a plot of the spectral profiles of different materials:</p>
<iframe title="Spectral Profiles of Different Materials" aria-label="Interactive line chart" id="datawrapper-chart-b1kcX" src="https://datawrapper.dwcdn.net/b1kcX/3/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="400">
</iframe>
<script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();
</script>
<p>The visible portion of the spectrum is highlighted on the left, ranging from 400 nanometers (violet) to 700nm (red). Our eyes (and satellite imagery in the visible light spectrum) can only see this portion of the light spectrum; we cant see UV or infrared wavelengths, for example, though the extent to which different materials reflect or absorb these wavelengths is just as useful for distinguishing between them. The European Space Agencys Sentinel-2 satellite collects spectral information well beyond the visible light spectrum, enabling this sort of analysis. It chops the electromagnetic spectrum up into “bands”, and measures how strongly wavelengths in each of those bands is reflected:</p>
<p><img src="images/S2_bands.png" class="img-fluid"></p>
<p>To illustrate why this is important, consider Astroturf (fake plastic grass). Astroturf and real grass will both look green to us, especially from a satellite image. But living plants strongly reflect radiation from the sun in a part of the light spectrum that we cant see (near-infrared). Theres a spectral index called the Normalized Difference Vegetation Index (NDVI) which exploits this fact to isolate vegetation in multispectral satellite imagery. So if we look at <a href="https://en.wikipedia.org/wiki/Gillette_Stadium">Gillette Stadium</a> near Boston, we can tell that the three training fields south of the stadium are real grass (they generate high NDVI values, showing up red), while the pitch in the stadium itself is astroturf (generating low NDVI values, showing up blue).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/NDVI.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">VHR image of Gillette Stadium with Sentinel-2 derived NDVI overlay</figcaption><p></p>
</figure>
</div>
<p>In other words, even though these fields are all green and indistinguishable to the human eye, their <em>spectral profiles</em> beyond the visible light spectrum differ, and we can use this information to distinguish between them.</p>
<p>Astroturf is a trivial example. But suppose we were interested in identifying makeshift oil refineries in Northern Syria that constitute a key source of rents for whichever group controls them. As demonstrated in the <a href="../chapters/C2_Refineries.html">Refinery Identification</a> case study, we can train an algorithm to identify the spectral signatures of oil, and use that to automatically detect them in satellite imagery.</p>
</section>
<section id="temporal-resolution" class="level3">
<h3 class="anchored" data-anchor-id="temporal-resolution">Temporal Resolution</h3>
<p>Finally, the frequency with which we can access new imagery is an important consideration. This is called the <strong>temporal resolution</strong>.</p>
<p>The Google Maps basemap is very high resolution, available globally, and is freely available. But it has no <em>temporal</em> dimension: its a snapshot from one particular point in time. If the thing were interested in involves <em>changes</em> over time, this basemap will be of limited use.</p>
<p>The <strong>“revisit rate”</strong> is the amount of time it takes for the satellite to pass over the same location twice. For example, the Sentinel-2 constellations two satellites can achieve a revisit rate of 5 days, as shown in this cool video from the European Space Agency:</p>
<div class="quarto-video"><video id="video_shortcode_videojs_video1" class="video-js vjs-default-skin vjs-fluid" controls="" preload="auto" data-setup="{}" title=""><source src="https://dlmultimedia.esa.int/download/public/videos/2016/08/004/1608_004_AR_EN.mp4"></video></div>
<p>Some satellite constellations are able to achieve much higher revisit rates. Sentinel-2 has a revisit rate of 5 days, but SkySat is capable of imaging the same point on earth around 12 times per day! How is that possible? Well, as the video above demonstrated, the Sentinel-2 constellation is composed of two satellites that share the same orbit, 180 degrees apart. In contrast, the SkySat constellation comprises 21 satellites, each with its own orbital path:</p>
<div class="quarto-video"><video id="video_shortcode_videojs_video2" class="video-js vjs-default-skin vjs-fluid" controls="" preload="auto" data-setup="{}" title=""><source src="https://assets.planet.com/products/hi-res/Planet_Block_3_HD_1080p.mp4"></video></div>
<p>This allows SkySat to achieve a revisit rate of 2-3 <em>hours</em>. The catch, however, is that you need to pay for it (and it <a href="https://apollomapping.com/blog/an-update-on-skysat-tasking-pricing-and-video-capabilities">aint cheap</a>). Below is a comparison of revisit rates for various other optical satellites:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/revisit_chart.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">A chart of revisit times for different satellites from <a href="https://link.springer.com/article/10.1007/s10712-021-09637-5">Sutlieff et. al.(2021)</a></figcaption><p></p>
</figure>
</div>
</section>
</section>
<section id="summary" class="level2">
<h2 class="anchored" data-anchor-id="summary">Summary</h2>
<p>You should hopefully have a better understanding of what satellite imagery is, and how it can be used to answer questions about the world. In the <a href="../chapters/A3_Data_Acquisition.html">next section</a>, well look at the various types of satellite imagery stored in the Google Earth Engine catalog.</p>
</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 clipboard = new window.ClipboardJS('.code-copy-button', {
target: function(trigger) {
return trigger.previousElementSibling;
}
});
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;
});
}
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="../index.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text">Overview</span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="../chapters/A3_Data_Acquisition.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">Data Acquisition</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
<script>videojs(video_shortcode_videojs_video1);</script>
<script>videojs(video_shortcode_videojs_video2);</script>
</body></html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,851 @@
<!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.2.269">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - 7&nbsp; War at Night</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 -1.6em;
vertical-align: middle;
}
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;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</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="../chapters/C2_Refineries.html" rel="next">
<link href="../chapters/B4_Vectors_Tables.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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<div class="container-fluid d-flex justify-content-between">
<h1 class="quarto-secondary-nav-title"><span class="chapter-title">War at Night</span></h1>
<button type="button" class="quarto-btn-toggle btn" aria-label="Show secondary navigation">
<i class="bi bi-chevron-right"></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 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="sidebar-tool px-1"><i class="bi bi-github"></i></a>
<a href="" title="Download" id="sidebar-tool-dropdown-0" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" title="Share" id="sidebar-tool-dropdown-1" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" class="quarto-color-scheme-toggle sidebar-tool" 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">A. Introduction</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<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">Overview</a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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 collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">B. Google Earth Engine</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">C. Case Studies</a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/C1_Lights.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">War at Night</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/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>
<!-- 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="#data" id="toc-data" class="nav-link active" data-scroll-target="#data">Data</a>
<ul class="collapse">
<li><a href="#pre-processing" id="toc-pre-processing" class="nav-link" data-scroll-target="#pre-processing">Pre-Processing</a></li>
<li><a href="#analysis" id="toc-analysis" class="nav-link" data-scroll-target="#analysis">Analysis</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/chapters/C1_Lights.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 d-none d-lg-block"><span class="chapter-title">War at Night</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<section id="data" class="level2">
<h2 class="anchored" data-anchor-id="data">Data</h2>
<p>Satellite images of Syria taken at night capture a subtle trace left by human civilization: lights. Apartment buildings, street lights, highways, power plants all are illuminated at night and can be seen from space. Researchers often use these nighttime lights signatures to track development; as cities grow, villages receive power and infrastructure is built, areas emit more light. But this works both ways. As cities are demolished, villages burned and highways cutoff, they stop emitting lights.</p>
<p>In this tutorial, well use satellite images of Iraq taken at night to track the destruction caused by the fight against the Islamic State. Well use the VIIRS nighttime lights dataset, which is a collection of satellite images taken by the Visible Infrared Imaging Radiometer Suite (VIIRS) on the Suomi NPP satellite. VIIRS is a sensor that can detect light in the visible and infrared spectrum, and is capable of taking images at night. A link to the GEE code for this section can be found <a href="https://code.earthengine.google.com/2cf77d8cb9afd76b73100637fbffdf5d">here</a>.</p>
<section id="pre-processing" class="level3">
<h3 class="anchored" data-anchor-id="pre-processing">Pre-Processing</h3>
<p>First, lets start by importing a few useful packages written by <a href="https://twitter.com/gena_d">Gennadii Donchyts</a>. Well use <code>utils</code> and <code>text</code> to annotate the date of each image on the timelapse. Well also define an Area of Interest (AOI), which is just a rectangle. You can do this manually by clicking the drawing tools in the top left. Ive drawn an AOI over the area covering Mosul, Irbil and Kirkuk in Northern Iraq.</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="kw">var</span> utils <span class="op">=</span> <span class="pp">require</span>(<span class="st">"users/gena/packages:utils"</span>)<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> text <span class="op">=</span> <span class="pp">require</span>(<span class="st">"users/gena/packages:text"</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>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">// define the Area of Interest (AOI)</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> AOI <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="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> [[[<span class="fl">42.555362833405326</span><span class="op">,</span> <span class="fl">36.62010778397765</span>]<span class="op">,</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> [<span class="fl">42.555362833405326</span><span class="op">,</span> <span class="fl">35.18296243288332</span>]<span class="op">,</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> [<span class="fl">44.681217325592826</span><span class="op">,</span> <span class="fl">35.18296243288332</span>]<span class="op">,</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> [<span class="fl">44.681217325592826</span><span class="op">,</span> <span class="fl">36.62010778397765</span>]]])</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="co">// start and end dates for our gif </span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> startDate <span class="op">=</span> <span class="st">'2013-01-01'</span><span class="op">;</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> endDate <span class="op">=</span> <span class="st">'2018-01-01'</span><span class="op">;</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="co">// a filename for when we export the gif</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> export_name<span class="op">=</span><span class="st">'qayyarah_viirs'</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="co">// A palette to visualize the VIIRS imagery. This one is similar to Matplotlib's "Magma" palette. </span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> viirs_palette <span class="op">=</span> [</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="st">"#000004"</span><span class="op">,</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> <span class="st">"#320a5a"</span><span class="op">,</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> <span class="st">"#781b6c"</span><span class="op">,</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> <span class="st">"#bb3654"</span><span class="op">,</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> <span class="st">"#ec6824"</span><span class="op">,</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a> <span class="st">"#fbb41a"</span><span class="op">,</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> <span class="st">"#fcffa4"</span><span class="op">,</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="co">// Visualisation parameters for the VIIRS imagery, defining a minimum and maximum value, and referencing the palette we just created</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> VIIRSvis <span class="op">=</span> { <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="fl">0.1</span><span class="op">,</span> <span class="dt">max</span><span class="op">:</span> <span class="fl">1.6</span><span class="op">,</span> <span class="dt">palette</span><span class="op">:</span> viirs_palette }<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Next, well load the VIIRS nighttime lights imagery. We want to select the <code>avg_rad</code> band of the image collection, and filter blank images. Sometimes, we get blank images over an area in VIIRS if our AOI is on the edge of the satellites imaging swath. We can filter these images, similarly to how we filter for cloudy images in Sentinel-2:</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="kw">var</span> VIIRS<span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">"NOAA/VIIRS/DNB/MONTHLY_V1/VCMCFG"</span>) </span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">select</span>(<span class="st">'avg_rad'</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the sum of the 'avg_rad' band within the AOI</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(image) { </span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> blank<span class="op">=</span>image<span class="op">.</span><span class="fu">reduceRegions</span>({ <span class="co">// reduceRegions is a function that allows us to reduce the values of a band within a</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> AOI<span class="op">,</span> <span class="co">// geometry. In this case, we're reducing the values of the 'avg_rad' band within the AOI</span></span>
<span id="cb2-7"><a href="#cb2-7" 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="co">// We're using the sum reducer, which will sum the values of the 'avg_rad' band</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">10</span>}) <span class="co">// We're reducing the values of the 'avg_rad' band at a scale of 10m</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>() <span class="co">// We only want the first element of the collection, which is the sum of the 'avg_rad' band within the AOI</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">get</span>(<span class="st">'sum'</span>) <span class="co">// We want the value of the 'sum' property, which is the sum of the 'avg_rad' band within the AOI</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// For each image, define a property 'blank' that stores the sum of the 'avg_rad' band within the AOI. </span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// We're also going to take a base 10 log of the image-- this will help us visualize the data by dampening extreme values </span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> image<span class="op">.</span><span class="fu">set</span>(<span class="st">'blank'</span><span class="op">,</span> blank)<span class="op">.</span><span class="fu">log10</span>()<span class="op">.</span><span class="fu">unmask</span>(<span class="dv">0</span>)</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// Now, we can filter images which are fully or partially blank over our AOI</span></span>
<span id="cb2-16"><a href="#cb2-16" 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">gt</span>(<span class="st">'blank'</span><span class="op">,</span> <span class="dv">10</span>))</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// Finally, we filter the collection to the specified date range</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(startDate<span class="op">,</span> endDate)</span>
<span id="cb2-19"><a href="#cb2-19" 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>Lets have a look at the first image in the collection to make sure everythings looking right. Well set the basemap to satellite and center our AOI:</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="bu">Map</span><span class="op">.</span><span class="fu">setOptions</span>(<span class="st">'HYBRID'</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(AOI)</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(VIIRS<span class="op">.</span><span class="fu">first</span>()<span class="op">,</span>VIIRSvis<span class="op">,</span><span class="st">'Nighttime Lights'</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="../images/iraq_check.png" class="img-fluid"></p>
<p>If we decrease the opacity of the VIIRS layer, we can see the cities of Mosul, Erbil and Kirkuk shining brightly at night. We can also see a string of bright lights between Kirkuk and Erbil these are methane flares from oil wells.</p>
</section>
<section id="analysis" class="level3">
<h3 class="anchored" data-anchor-id="analysis">Analysis</h3>
<p>Having pre-processed the VIIRS imagery, we can now define a function <code>gif</code> that will take:</p>
<ol type="1">
<li>An image collection (<code>col</code>, in this case the nighttime lights imagery <code>VIIRS</code>)</li>
<li>Visualization parameters (<code>col_vis</code>, in this case <code>VIIRSvis</code>)</li>
<li>An Area of Interest <code>AOI</code></li>
</ol>
<p>The function will then return a timelapse.</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="kw">var</span> gif <span class="op">=</span> <span class="kw">function</span> (col<span class="op">,</span> col_vis<span class="op">,</span> AOI) {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// Define the date annotations to be printed in the top left of the gif in white</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> annotations <span class="op">=</span> [</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">textColor</span><span class="op">:</span> <span class="st">"white"</span><span class="op">,</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">position</span><span class="op">:</span> <span class="st">"left"</span><span class="op">,</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">offset</span><span class="op">:</span> <span class="st">"1%"</span><span class="op">,</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">margin</span><span class="op">:</span> <span class="st">"1%"</span><span class="op">,</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">property</span><span class="op">:</span> <span class="st">"label"</span><span class="op">,</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// Dynamically size the annotations according to the size of the AOI</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> AOI<span class="op">.</span><span class="fu">area</span>(<span class="dv">100</span>)<span class="op">.</span><span class="fu">sqrt</span>()<span class="op">.</span><span class="fu">divide</span>(<span class="dv">200</span>)<span class="op">,</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a> ]<span class="op">;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Next, we want to map over the image collection,</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> rgbVis <span class="op">=</span> col<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span> (image) {</span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> <span class="co">// Get the date of the image and format it</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> start <span class="op">=</span> ee<span class="op">.</span><span class="fu">Date</span>(image<span class="op">.</span><span class="fu">get</span>(<span class="st">"system:time_start"</span>))<span class="op">;</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> label <span class="op">=</span> start<span class="op">.</span><span class="fu">format</span>(<span class="st">"YYYY-MM-dd"</span>)<span class="op">;</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// And visualize the image using the visualization parameters defined earlier.</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> <span class="co">// We also want to set a property called "label" that stores the formatted date </span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> image<span class="op">.</span><span class="fu">visualize</span>(col_vis)<span class="op">.</span><span class="fu">set</span>({ <span class="dt">label</span><span class="op">:</span> label })<span class="op">;</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a> <span class="co">// Now we use the label property and the annotateImage function from @gena_d to annotate each image with the date. </span></span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true" tabindex="-1"></a> rgbVis <span class="op">=</span> rgbVis<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span> (image) {</span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> text<span class="op">.</span><span class="fu">annotateImage</span>(image<span class="op">,</span> {}<span class="op">,</span> AOI<span class="op">,</span> annotations)<span class="op">;</span></span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a> <span class="co">// Define GIF visualization parameters.</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> gifParams <span class="op">=</span> {</span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxPixels</span><span class="op">:</span> <span class="dv">27017280</span><span class="op">,</span></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a> <span class="dt">region</span><span class="op">:</span> AOI<span class="op">,</span></span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a> <span class="dt">crs</span><span class="op">:</span> <span class="st">"EPSG:3857"</span><span class="op">,</span></span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a> <span class="dt">dimensions</span><span class="op">:</span> <span class="dv">640</span><span class="op">,</span></span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a> <span class="dt">framesPerSecond</span><span class="op">:</span> <span class="dv">5</span><span class="op">,</span></span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a> }<span class="op">;</span></span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a> <span class="co">// Export the gif to Google Drive</span></span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a> Export<span class="op">.</span><span class="at">video</span><span class="op">.</span><span class="fu">toDrive</span>({</span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> rgbVis<span class="op">,</span> <span class="co">// the image collection</span></span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a> <span class="dt">description</span><span class="op">:</span> export_name<span class="op">,</span> <span class="co">// the name of the file</span></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a> <span class="dt">dimensions</span><span class="op">:</span> <span class="dv">1080</span><span class="op">,</span> <span class="co">// the dimensions of the gif</span></span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a> <span class="dt">framesPerSecond</span><span class="op">:</span> <span class="dv">5</span><span class="op">,</span> <span class="co">// the number of frames per second</span></span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true" tabindex="-1"></a> <span class="dt">region</span><span class="op">:</span> AOI<span class="op">,</span> <span class="co">// the area of interest</span></span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb4-53"><a href="#cb4-53" aria-hidden="true" tabindex="-1"></a> <span class="co">// Print the GIF URL to the console.</span></span>
<span id="cb4-54"><a href="#cb4-54" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span>(rgbVis<span class="op">.</span><span class="fu">getVideoThumbURL</span>(gifParams))<span class="op">;</span></span>
<span id="cb4-55"><a href="#cb4-55" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-56"><a href="#cb4-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-57"><a href="#cb4-57" aria-hidden="true" tabindex="-1"></a> <span class="co">// Render the GIF animation in the console.</span></span>
<span id="cb4-58"><a href="#cb4-58" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span>(ui<span class="op">.</span><span class="fu">Thumbnail</span>(rgbVis<span class="op">,</span> gifParams))<span class="op">;</span></span>
<span id="cb4-59"><a href="#cb4-59" 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>Ok that was a pretty big chunk of code. But the good news is that we basically never have to touch it again, since we can just feed it different inputs. For example, if I want to generate a gif of night time lights over a different area, its as simple as dragging the AOI. If I want to look at a different time period, I can just edit the <code>startDate</code> and <code>endDate</code> variables. And if I want to visualize an entirely different type of satellite imagery Sentinel-1, Sentinel-2, or anything else, all I have to do is change the image collection (<code>col</code>) and visualization parameters (<code>col_vis</code>) variables. Now, lets look at some timelapses.</p>
<section id="the-fall-of-mosul" class="level4">
<h4 class="anchored" data-anchor-id="the-fall-of-mosul">The Fall of Mosul</h4>
<p>The function returns a timelapse of nighttime lights over Northern Iraq:</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">gif</span>(VIIRS<span class="op">,</span> VIIRSvis<span class="op">,</span> AOI)<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/Figure_1.gif" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Ive done a bit of post-processing to this gif, adding more annotations and blending between frames to make it a bit smoother. I typically use <a href="https://ffmpeg.org/">ffmpeg</a> and <a href="https://ezgif.com/">ezgif</a> for the finishing touches.</figcaption><p></p>
</figure>
</div>
<p>This timelapse gives a play-by-play of one of the most important campaigns in the war against the Islamic State. In the first few frames, Mosul is under the control of the Kurdistan Regional Government (KRG). In the summer of 2014, ISIS captures the city, and power is cut off. Mosul and many villages along the Tigris river are plunged into darkness. In 2015, the front line in the campaign to retake the city emerges around Mosul, advancing in 2016 and 2017. Mosul is eventually retaken by the KRG in 2017, after which it brightens once again as electricity is restored.</p>
</section>
<section id="the-qayyarah-fires" class="level4">
<h4 class="anchored" data-anchor-id="the-qayyarah-fires">The Qayyarah Fires</h4>
<p>Farther south, there is an interesting detail. Above the “h” in “Qayyarah”, a bright set of lights emerges just before Mosul is recaptured, around December 2016. Fleeing Islamic State fighters <a href="https://time.com/iraq-fires/">set fire to the Qayyarah oilfields</a>, which burned for months.</p>
<p>Using the VIIRS data weve already loaded, we can further analyze the effect of the conflict using a chart. First, lets define two rectangles (again, you can draw these) over Mosul and Qayyarah:</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="kw">var</span> mosul <span class="op">=</span> ee<span class="op">.</span><span class="fu">Feature</span>(</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Polygon</span>(</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> [[[<span class="fl">43.054977780266675</span><span class="op">,</span> <span class="fl">36.438274276521234</span>]<span class="op">,</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.054977780266675</span><span class="op">,</span> <span class="fl">36.290642221212416</span>]<span class="op">,</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.24792516796199</span><span class="op">,</span> <span class="fl">36.290642221212416</span>]<span class="op">,</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.24792516796199</span><span class="op">,</span> <span class="fl">36.438274276521234</span>]]]<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="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"label"</span><span class="op">:</span> <span class="st">"Mosul"</span><span class="op">,</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">"system:index"</span><span class="op">:</span> <span class="st">"0"</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> qayyarah <span class="op">=</span> ee<span class="op">.</span><span class="fu">Feature</span>(</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Polygon</span>(</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> [[[<span class="fl">43.08240275545117</span><span class="op">,</span> <span class="fl">35.8925587996721</span>]<span class="op">,</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.08240275545117</span><span class="op">,</span> <span class="fl">35.77899970860588</span>]<span class="op">,</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.26642375154492</span><span class="op">,</span> <span class="fl">35.77899970860588</span>]<span class="op">,</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> [<span class="fl">43.26642375154492</span><span class="op">,</span> <span class="fl">35.8925587996721</span>]]]<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="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> <span class="st">"label"</span><span class="op">:</span> <span class="st">"Qayyarah"</span><span class="op">,</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="st">"system:index"</span><span class="op">:</span> <span class="st">"0"</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="co">// Let's put these together in a list </span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> regions<span class="op">=</span>[qayyarah<span class="op">,</span> mosul]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Once weve got the rectangles, we can make a chart that will take the mean value of the VIIRS images in each rectangle over time:</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> chart <span class="op">=</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> ui<span class="op">.</span><span class="at">Chart</span><span class="op">.</span><span class="at">image</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">seriesByRegion</span>({</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">imageCollection</span><span class="op">:</span> VIIRS<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">regions</span><span class="op">:</span> regions<span class="op">,</span></span>
<span id="cb7-6"><a href="#cb7-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">mean</span>()<span class="op">,</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">seriesProperty</span><span class="op">:</span><span class="st">'label'</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> })<span class="op">.</span><span class="fu">setOptions</span>({</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Nighttime Lights'</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(chart)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="../images/qayyarah_chart.png" class="img-fluid"></p>
<p>We can clearly see Mosul (the red line) darkening in 2014 as the city is taken by ISIS. During this period the Qayyarah oil fields are, as we might expect, quite dark. All of a sudden in 2016 Qayyarah becomes brighter at night than the city of Mosul ever was, as the oilfields are set on fire. Then, almost exactly when the blaze in Qayyarah is extinguished and the area darkens (i.e.&nbsp;when the blue line falls back to near zero), Mosul brightens once again (i.e.&nbsp;the red line rises) as the city is liberated.</p>
<!--
### The Battle for Aleppo
The images below were taken between 2012 and 2014. Vast swaths of the city darken as neighborhoods are razed by fighting.
<timelapse>
Though this is a trend that can be observed across the country, nowhere is the decline in nightlights more visible than in Aleppo. Below is a comparison of longitudinal trends in nightlights signatures between several cities:
<graph>
The most salient trend is Aleppo plummeting over the course of 2012, and becoming steadily darker over the course of the next four years. Raqqa drops in 2012 as well, but remains in flux until 2017, when the battle to reclaim the city plunges it into near total darkness. Damascus also experiences a dip in 2012, but stabilizes relatively quickly. The Turkish city of Gaziantep -- less than 100km from Aleppo and roughly 1/5th the size -- stands in stark contrast to the Syrian cities, becoming progressively brighter over the entire period.
Another interesting pattern here is the difference in seasonal trends in nightlights. Under normal circumstances in this part of the world, cities become brighter at night during the summer months. Restaurants, bars, and markets stay open later and conduct business outdoors. Gaziantep, which still attracts scores of tourists every year, displays pronounced seasonality. Damascus, the most stable of the three Syrian cities, also maintains a seasonal trend throughout the war. In contrast, both Raqqa and Aleppo maintain extremely low and roughly constant levels of nightlights year-round during the periods following intense fighting.
Reliable economic data for Syria haven't been available for nearly a decade, and assessing the country's recovery is consequently difficult. But subtle indications of economic growth are visible above: all three Syrian cities have been on a steady upward trend since 2017, and beginning to display seasonal variation once again. -->
<!-- ### Fighting for Oil
Throughout the war, sudden massive spikes in nightlights signatures can be observed throughout the country. In the center of the map just west of Palmyra, some particularly large spikes occur in 2017:
These flashes of light show gas wells being set on fire, a common form of sabotage carried out by retreating Islamic State fighters. Modified Sentinel-2 imagery of the Hayyan gas field (indicated by the green box above) shows this in greater detail. Substituting the Red band in an RGB image with Near Infrared (NIR) highlights thermal signatures, showing fires burning brightly even during the day.
The large complex on the right is the Hayyan Gas Plant, which produced nearly one third of Syria's electricity. The plant and its associated wells changed hands several times throughout the war, but were under Islamic State control until February 2017. In the video below, Islamic State fighters can be seen rigging the plant with explosives and destroying it on January 8th:
In February, three Russian oil and gas companies (Zarubij Naft, Lukoil and Gazprom Neft) were given restoration, exploration and production rights to the hydrocarbon deposits West of Palmyra. On January 12th, 2017, the Syrian Army's 5th Legion and Russian special forces launched a counterattack known as the "Palmyra offensive", with the aim of retaking several important hydrocarbon deposits including Hayyan.
The timing of well fires aligns closely with a detailed timeline of the campaign.The Near Infrared Sentinel-2 image below shows the layout of the Hayyan Gas Plant and the wells in the Hayyan gas field:
The Syrian Army took the Hayyan gas field on [February 4th](https://www.almasdarnews.com/article/syrian-army-liberates-hayyan-gas-fields-west-palmyra/), and retreating ISIS fighters set fire to wells 1, and 3. However, ISIS managed to briefly retake the Hayyan field on [February 7th](https://www.almasdarnews.com/article/isis-retakes-hayyan-gas-fields-new-bid-expand-west-palmyra/), setting fire to wells 2 and 4. These moments in the Palmyra Offensive are captured in NIR signatures
Interestingly, despite the massive explosion caused by the bombing of the Hayyan Gas Plant, no prolonged thermal anomalies were detected over the area of the plant itself. The well fires, on the other hand, lasted for months. Below is an image of well fire at the Hayyan field taken from this [video](https://www.youtube.com/watch?v=WFe9abYyqK0); based on the nearby infrastructure and date (04/02/2017) of posting, it is likely Well-3.
-->
</section>
</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 clipboard = new window.ClipboardJS('.code-copy-button', {
target: function(trigger) {
return trigger.previousElementSibling;
}
});
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;
});
}
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="../chapters/B4_Vectors_Tables.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Vectors and Tables</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="../chapters/C2_Refineries.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">Refinery Identification</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
</body></html>

File diff suppressed because one or more lines are too long

987
docs/chapters/C3_Blast.html Normal file
View File

@@ -0,0 +1,987 @@
<!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.2.269">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - 9&nbsp; Blast Damage Assessment</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 -1.6em;
vertical-align: middle;
}
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;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</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="../chapters/C4_Ships.html" rel="next">
<link href="../chapters/C2_Refineries.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>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<div class="container-fluid d-flex justify-content-between">
<h1 class="quarto-secondary-nav-title"><span class="chapter-title">Blast Damage Assessment</span></h1>
<button type="button" class="quarto-btn-toggle btn" aria-label="Show secondary navigation">
<i class="bi bi-chevron-right"></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 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="sidebar-tool px-1"><i class="bi bi-github"></i></a>
<a href="" title="Download" id="sidebar-tool-dropdown-0" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" title="Share" id="sidebar-tool-dropdown-1" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" class="quarto-color-scheme-toggle sidebar-tool" 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">A. Introduction</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<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">Overview</a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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 collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">B. Google Earth Engine</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">C. Case Studies</a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/C3_Blast.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Blast Damage Assessment</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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>
<!-- 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="#change-detection" id="toc-change-detection" class="nav-link active" data-scroll-target="#change-detection">Change Detection</a></li>
<li><a href="#implementing-a-t-test-in-earth-engine" id="toc-implementing-a-t-test-in-earth-engine" class="nav-link" data-scroll-target="#implementing-a-t-test-in-earth-engine">Implementing a t-test in Earth Engine</a></li>
<li><a href="#filtering-the-sentinel-1-imagery" id="toc-filtering-the-sentinel-1-imagery" class="nav-link" data-scroll-target="#filtering-the-sentinel-1-imagery">Filtering the Sentinel-1 Imagery</a></li>
<li><a href="#validation" id="toc-validation" class="nav-link" data-scroll-target="#validation">Validation</a></li>
<li><a href="#conclusion" id="toc-conclusion" class="nav-link" data-scroll-target="#conclusion">Conclusion</a></li>
<li><a href="#extension-satellite-imagery-and-its-limits" id="toc-extension-satellite-imagery-and-its-limits" class="nav-link" data-scroll-target="#extension-satellite-imagery-and-its-limits">Extension: Satellite Imagery and its Limits</a>
<ul class="collapse">
<li><a href="#creating-a-3d-model-of-beirut" id="toc-creating-a-3d-model-of-beirut" class="nav-link" data-scroll-target="#creating-a-3d-model-of-beirut">Creating a 3D model of Beirut</a></li>
<li><a href="#using-a-viewshed-analysis-to-assess-blast-exposure" id="toc-using-a-viewshed-analysis-to-assess-blast-exposure" class="nav-link" data-scroll-target="#using-a-viewshed-analysis-to-assess-blast-exposure">Using a Viewshed Analysis to Assess Blast Exposure</a></li>
<li><a href="#accounting-for-diffraction" id="toc-accounting-for-diffraction" class="nav-link" data-scroll-target="#accounting-for-diffraction">Accounting for Diffraction</a></li>
<li><a href="#modeling-the-pressure-wave" id="toc-modeling-the-pressure-wave" class="nav-link" data-scroll-target="#modeling-the-pressure-wave">Modeling the Pressure Wave</a></li>
<li><a href="#assessing-damage-to-the-skyline-tower" id="toc-assessing-damage-to-the-skyline-tower" class="nav-link" data-scroll-target="#assessing-damage-to-the-skyline-tower">Assessing Damage to the Skyline Tower</a></li>
<li><a href="#further-research" id="toc-further-research" class="nav-link" data-scroll-target="#further-research">Further Research</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/chapters/C3_Blast.qmd" class="toc-action">Edit this page</a></p></div></div></nav>
</div>
<!-- main -->
<main class="content page-columns page-full" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title d-none d-lg-block"><span class="chapter-title">Blast Damage Assessment</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>On August 4th, 2020, a warehouse full of fertilizer in the port of Beirut exploded. The blast killed over 200 people, injured thousands, and caused widespread damage to the city:</p>
<div style="padding-bottom:56.25%; position:relative; display:block; width: 100%">
<p><iframe width="100%" height="100%" src="https://www.youtube.com/embed/LNDhIGR-83w?start=29" frameborder="0" allowfullscreen="" style="position:absolute; top:0; left: 0"> </iframe></p>
</div>
<p>Assessing blast damage is a common task for open source investigators, and satellite imagery analysis is one of the best tools we have at our disposal to analyze this sort of phenomenon. <a href="https://earthobservatory.nasa.gov/images/147098/scientists-map-beirut-blast-damage">NASA</a> used Sentinel-1 imagery in the aftermath of the explosion to generate an estimated damage map. They explain that Sentinel-1 Synthetic Aperture Radar (SAR) imagery is good for this sort of task:</p>
<p>“SAR instruments send pulses of microwaves toward Earths surface and listen for the reflections of those waves. The radar waves can penetrate cloud cover, vegetation, and the dark of night to detect changes that might not show up in visible light imagery. When Earths crust moves due to an earthquake, when dry land is suddenly covered by flood water, or when buildings have been damaged or toppled, the amplitude and phase of radar wave reflections changes in those areas and indicates to the satellite that something on the ground has changed.”</p>
<p>The NASA team produced this estimate the day after the explosion, which is very impressive. However, due to the quick turnaround, they were pretty light on the description of their methodology (they didnt provide any code, or even explicitly say how exactly they generated the estimate), making their analysis hard to replicate. They also failed to validate their results, which is a critical step in any analysis.</p>
<p>In this case study well be developing our own change detection algorithm from scratch, applying to Sentinel-1 imagery of Beirut before and after the blast, and validating our results using building footprints and U.N. damage estimates as the ground truth. Below is the final result of the analysis, which shows building footprints colored according to the predicted level of damage they sustained from the blast:</p>
<div class="column-page">
<iframe src="https://ollielballinger.users.earthengine.app/view/beirutsar" width="100%" height="700px">
</iframe>
</div>
<section id="change-detection" class="level2">
<h2 class="anchored" data-anchor-id="change-detection">Change Detection</h2>
<p>There are many ways to detect change between two images. The simplest way would be to take an image taken before an event, and subtract it from an image taken after. This is a good way to get a general sense of where change has occurred, but if you only use two images (one before an event and another after), it would be difficult to differentiate between areas that have changed as a result of the event in question, and areas that have changed for other reasons. Things in Beirut (and cities in general) are constantly changing: construction, cars/planes/ships moving, vegetation growing, etc. So we wouldnt know if the change were seeing is a result of the explosion or whether that area is generally prone to change. We can overcome this by comparing a bunch of pre-event images to a bunch of post-event images. This way we can see if the change were seeing is consistent across all of the images. If it is, then we can be fairly confident that the change is a result of the event in question. The mean is simply the sum of all the values (<span class="math inline">\(x_i\)</span>) in a set divided by the number of values (<span class="math inline">\(n\)</span>):</p>
<p><span class="math display">\[\large \overline{x} = \frac{1}{n} \sum_{i=1}^n x_i\]</span></p>
<p>But if we just take the average pixel value before and subtract the average pixel value after, were not accounting for the <em>variability</em> of that pixels values. For example, if we have a pixel that has had an average value of 1 for the month before the event, and a value of 2 in the month after the event, the difference is 1. If that pixels value is extremely consistent (it never varies by more than 0.1), such a change would be very significant. But if that pixels value is very variable (it varies by 2 or even 3 on a regular basis), then the change is not significant. So we need to account for the variability of the pixels values using the standard deviation. It is calculated as the square root of the variance, which is the average of the squared differences from the mean:</p>
<p><span class="math display">\[\large s = \sqrt{\frac{1}{n-1} \sum_{i=1}^n (x_i - \overline{x})^2}\]</span></p>
<p>With just the mean and the standard deviations of two sets of numbers, we can use a statistical test to determine whether the change in means is significant. The simplest way to do this is to use a pixelwise t-test, which is basically just a signal-to-noise ratio: it calculates the difference between two sample means (signal), and divides it by the standard deviations of both samples (noise). In this case, the two samples are the pre- and post-event images. The t-test is applied to each pixel in the image, allowing us to determine whether the change is statistically significant. Given two groups, <span class="math inline">\(x_1\)</span> before the event, and <span class="math inline">\(x_2\)</span> after the event, the <span class="math inline">\(t\)</span> statistic is calculated as:</p>
<p><span class="math display">\[ \Large t = {\frac{\overline{x_1}-\overline{x_2}} {\sqrt{\frac{s^2_1}{n_1} + \frac{s^2_2}{n_2}}}} \]</span></p>
<p>Where:</p>
<ul>
<li><span class="math inline">\(\overline{x}\)</span>: Sample Mean</li>
<li><span class="math inline">\(s^2\)</span>: Sample Standard Deviation</li>
<li><span class="math inline">\(n\)</span>: Number of observations</li>
</ul>
<p>This procedure gives us a number called a t-value, which is a measure of how many standard deviations the difference between the two means is. Were not going to get into the details here, but a rule of thumb is that if the t-value is greater than 2, then the difference between the two means is significant. If the t-value is less than 2, then the difference is not significant. Were going to calculate the t-value for each pixel in the image to determine whether that pixel has changed significantly following the event in question. You dont need to know the details of the t-test to understand the results (but hopefully youve got an intuition for what its doing). If youre interested in learning more about statistical tests of this sort, I teach a course on Data Science at the University College London, and have made all of the lectures and courseware open-source. The T-test lecture is here.</p>
</section>
<section id="implementing-a-t-test-in-earth-engine" class="level2">
<h2 class="anchored" data-anchor-id="implementing-a-t-test-in-earth-engine">Implementing a t-test in Earth Engine</h2>
<p>Now lets go about implementing this in Earth Engine. Well start by centering the map on the port of Beirut, and setting the map to satellite view, and defining an area of interest (AOI) as a 3km buffer around the port:</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="bu">Map</span><span class="op">.</span><span class="fu">setCenter</span>(<span class="fl">35.51898</span><span class="op">,</span> <span class="fl">33.90153</span><span class="op">,</span> <span class="dv">15</span>)<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></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="bu">Map</span><span class="op">.</span><span class="fu">setOptions</span>(<span class="st">"satellite"</span>)<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> aoi <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>(<span class="fl">35.51898</span><span class="op">,</span> <span class="fl">33.90153</span>)<span class="op">.</span><span class="fu">buffer</span>(<span class="dv">3000</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Next, lets define a function in earth engine that will perform the T-Test. The block of code below defines a function to implement a t-test for every pixel in a set of images. The function will be called ttest, and takes four arguments:</p>
<ul>
<li>s1: the image collection</li>
<li>shock: the date of the event</li>
<li>pre_interval: the number of months before the event</li>
<li>post_interval: the number of months after the event</li>
</ul>
<p>The function will return a t-value image, which we can use to determine whether a pixel has changed significantly following the event in question.</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="kw">function</span> <span class="fu">ttest</span>(s1<span class="op">,</span> shock<span class="op">,</span> pre_interval<span class="op">,</span> post_interval) {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Convert the shock date to a date object</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> shock <span class="op">=</span> ee<span class="op">.</span><span class="fu">Date</span>(shock)<span class="op">;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the image collection to the pre-event period</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> pre <span class="op">=</span> s1<span class="op">.</span><span class="fu">filterDate</span>(</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> shock<span class="op">.</span><span class="fu">advance</span>(ee<span class="op">.</span><span class="fu">Number</span>(pre_interval)<span class="op">.</span><span class="fu">multiply</span>(<span class="op">-</span><span class="dv">1</span>)<span class="op">,</span> <span class="st">"month"</span>)<span class="op">,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> shock</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the image collection to the post-event period</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> post <span class="op">=</span> s1<span class="op">.</span><span class="fu">filterDate</span>(shock<span class="op">,</span> shock<span class="op">.</span><span class="fu">advance</span>(post_interval<span class="op">,</span> <span class="st">"month"</span>))<span class="op">;</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the mean, standard deviation, and number of images for the pre-event period</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> pre_mean <span class="op">=</span> pre<span class="op">.</span><span class="fu">mean</span>()<span class="op">;</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> pre_sd <span class="op">=</span> pre<span class="op">.</span><span class="fu">reduce</span>(ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">stdDev</span>())<span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> pre_n <span class="op">=</span> ee<span class="op">.</span><span class="fu">Number</span>(pre<span class="op">.</span><span class="fu">filterBounds</span>(aoi)<span class="op">.</span><span class="fu">size</span>())<span class="op">;</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the mean, standard deviation, and number of images for the pre-event period</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> post_mean <span class="op">=</span> post<span class="op">.</span><span class="fu">mean</span>()<span class="op">;</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> post_sd <span class="op">=</span> post<span class="op">.</span><span class="fu">reduce</span>(ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">stdDev</span>())<span class="op">;</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> post_n <span class="op">=</span> ee<span class="op">.</span><span class="fu">Number</span>(post<span class="op">.</span><span class="fu">filterBounds</span>(aoi)<span class="op">.</span><span class="fu">size</span>())<span class="op">;</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the pooled standard deviation</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> pooled_sd <span class="op">=</span> pre_sd</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">multiply</span>(pre_sd)</span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">multiply</span>(pre_n<span class="op">.</span><span class="fu">subtract</span>(<span class="dv">1</span>))</span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">add</span>(post_sd<span class="op">.</span><span class="fu">multiply</span>(post_sd)<span class="op">.</span><span class="fu">multiply</span>(post_n<span class="op">.</span><span class="fu">subtract</span>(<span class="dv">1</span>)))</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">divide</span>(pre_n<span class="op">.</span><span class="fu">add</span>(post_n)<span class="op">.</span><span class="fu">subtract</span>(<span class="dv">2</span>))</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sqrt</span>()<span class="op">;</span></span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the denominator of the t-test</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> denom <span class="op">=</span> pooled_sd<span class="op">.</span><span class="fu">multiply</span>(</span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Number</span>(<span class="dv">1</span>)<span class="op">.</span><span class="fu">divide</span>(pre_n)<span class="op">.</span><span class="fu">add</span>(ee<span class="op">.</span><span class="fu">Number</span>(<span class="dv">1</span>)<span class="op">.</span><span class="fu">divide</span>(post_n))<span class="op">.</span><span class="fu">sqrt</span>()</span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-37"><a href="#cb2-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-38"><a href="#cb2-38" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the Degrees of Freedom, which is the number of observations minus 2</span></span>
<span id="cb2-39"><a href="#cb2-39" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> df <span class="op">=</span> pre_n<span class="op">.</span><span class="fu">add</span>(post_n)<span class="op">.</span><span class="fu">subtract</span>(<span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb2-40"><a href="#cb2-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-41"><a href="#cb2-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-42"><a href="#cb2-42" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span>(<span class="st">"Number of Images: "</span><span class="op">,</span> df)<span class="op">;</span></span>
<span id="cb2-43"><a href="#cb2-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-44"><a href="#cb2-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-45"><a href="#cb2-45" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the t-test using the:</span></span>
<span id="cb2-46"><a href="#cb2-46" aria-hidden="true" tabindex="-1"></a> <span class="co">// mean of the pre-event period, </span></span>
<span id="cb2-47"><a href="#cb2-47" aria-hidden="true" tabindex="-1"></a> <span class="co">// the mean of the post-event period, </span></span>
<span id="cb2-48"><a href="#cb2-48" aria-hidden="true" tabindex="-1"></a> <span class="co">// and the pooled standard deviation</span></span>
<span id="cb2-49"><a href="#cb2-49" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> change <span class="op">=</span> post_mean</span>
<span id="cb2-50"><a href="#cb2-50" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">abs</span>()</span>
<span id="cb2-51"><a href="#cb2-51" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">subtract</span>(pre_mean<span class="op">.</span><span class="fu">abs</span>())</span>
<span id="cb2-52"><a href="#cb2-52" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">divide</span>(denom)</span>
<span id="cb2-53"><a href="#cb2-53" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">abs</span>()</span>
<span id="cb2-54"><a href="#cb2-54" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">subtract</span>(<span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb2-55"><a href="#cb2-55" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-56"><a href="#cb2-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-57"><a href="#cb2-57" aria-hidden="true" tabindex="-1"></a> <span class="co">// return the t-values for each pixel</span></span>
<span id="cb2-58"><a href="#cb2-58" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> change<span class="op">;</span></span>
<span id="cb2-59"><a href="#cb2-59" 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>An important detail in the code above is that weve actually tweaked the t-test slightly, in two ways.</p>
<p>First, the algorithm above returns tha <em>absolute</em> value of t (i.e.&nbsp;the absolute value of the difference between the two means). This is because were interested in whether the pixel has changed at all, not whether its changed in a particular direction. Second, weve subtracted 2 from the t-value.</p>
<p>The t-value is a measure of how many standard deviations the difference between the two means is. Generally speaking, if the t-value is greater than 2, then the difference between the two means is considered statistically significant. 2 is a fairly arbitrary cutoff, but its the most commonly used one since it corresponds to the 95% confidence interval (i.e., theres less than a 5% chance of observing a difference that big due to random chance). Now weve got a function that can take an image collection and return a t-value image, where a value greater than 0 corresponds to a statistically significant change between the pre-event and post-event periods.</p>
</section>
<section id="filtering-the-sentinel-1-imagery" class="level2">
<h2 class="anchored" data-anchor-id="filtering-the-sentinel-1-imagery">Filtering the Sentinel-1 Imagery</h2>
<p>We cant just blindly apply this algorithm to the entire image collection, because the image collection contains images from both ascending and descending orbits. We need to filter the image collection to the ascending and descending orbits, and then calculate the t-value for each orbit separately: this is because the satellite is viewing the scene from a completely different angle when its in ascending and descending orbits, which will generate a lot of noise in our data. In fact, even when the satellite is either ascending or descending, we can have multiple images of the same place taken from slightly different orbital tracks because these overlap (see <a href="../ch1#orbits">this visualization of orbits</a>). We need to filter the image collection to the relative orbit number that is most common within the image collection. For that, we define a new function called filter_s1, which takes a single argument: the path (either ASCENDING or DESCENDING).</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="kw">function</span> <span class="fu">filter_s1</span>(path) {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the image collection to the ascending or descending orbit</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> s1 <span class="op">=</span> ee</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">"COPERNICUS/S1_GRD"</span>)</span>
<span id="cb3-6"><a href="#cb3-6" 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">listContains</span>(<span class="st">"transmitterReceiverPolarisation"</span><span class="op">,</span> <span class="st">"VH"</span>))</span>
<span id="cb3-7"><a href="#cb3-7" 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">eq</span>(<span class="st">"instrumentMode"</span><span class="op">,</span> <span class="st">"IW"</span>))</span>
<span id="cb3-8"><a href="#cb3-8" 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">eq</span>(<span class="st">"orbitProperties_pass"</span><span class="op">,</span> path))</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(aoi)</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">select</span>(<span class="st">"VH"</span>)<span class="op">;</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// Find the most common relative orbit number</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> orbit <span class="op">=</span> s1</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">aggregate_array</span>(<span class="st">"relativeOrbitNumber_start"</span>)</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">reduce</span>(ee<span class="op">.</span><span class="at">Reducer</span><span class="op">.</span><span class="fu">mode</span>())<span class="op">;</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the image collection to the most common relative orbit number</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> s1 <span class="op">=</span> s1<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">"relativeOrbitNumber_start"</span><span class="op">,</span> orbit))<span class="op">;</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// Calculate the t-test for the filtered image collection using the function we defined earlier</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> change <span class="op">=</span> <span class="fu">ttest</span>(s1<span class="op">,</span> <span class="st">"2020-08-04"</span><span class="op">,</span> <span class="dv">12</span><span class="op">,</span> <span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> <span class="co">// Return the t-values for each pixel</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> change<span class="op">;</span></span>
<span id="cb3-29"><a href="#cb3-29" 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>Youll notice that weve called the ttest function we defined earlier with four arguments:</p>
<ul>
<li>s1: the Sentinel-1 image collection filtered to the ascending or descending orbit</li>
<li>2020-08-04: the date of the explosion</li>
<li>12: the number of months before the explosion to use for the pre-event period; Im choosing to include the full year prior to the explosion to get a good baseline</li>
<li>2: the number of months after the explosion to use for the post-event period; Im including 2 months after the explosion. Much less than that, and we risk missing the effects of the explosion. Much more than that, and we risk including the effects other changes that happened after the explosion, including the reconstruction effort.</li>
</ul>
<p>Now we want to apply this function to the image collection twice (once for each orbit) and then combine the two images into a single image. After that, we can clip it to the area of interest and display 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>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Call the filter_s1 function twice, once for each orbit, and then combine the two images into a single image</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> composite <span class="op">=</span> ee</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">ImageCollection</span>([<span class="fu">filter_s1</span>(<span class="st">"ASCENDING"</span>)<span class="op">,</span> <span class="fu">filter_s1</span>(<span class="st">"DESCENDING"</span>)])</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">mean</span>()</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">clip</span>(aoi)<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Define a color palette</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> palette <span class="op">=</span> [<span class="st">"440154"</span><span class="op">,</span> <span class="st">"3b528b"</span><span class="op">,</span> <span class="st">"21918c"</span><span class="op">,</span> <span class="st">"5ec962"</span><span class="op">,</span> <span class="st">"fde725"</span>]<span class="op">;</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the composite to the map</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> composite<span class="op">,</span></span>
<span id="cb4-17"><a href="#cb4-17" 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 class="dt">max</span><span class="op">:</span> <span class="dv">4</span><span class="op">,</span> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.8</span><span class="op">,</span> <span class="dt">palette</span><span class="op">:</span> palette }<span class="op">,</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="st">"change"</span></span>
<span id="cb4-19"><a href="#cb4-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 visualization parameters correspond to the statistical significance of the change in pixel values. Using the Viridis color palette which ranges from purple to yellow, dark purple pixels indicate no significant change, and yellow pixels indicate a significant change with with 95% confidence. The brighter the yellow of a pixel, the more significant the change.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="../images/beirut/beirut_change_2020.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Pixelwise T-Test, 2020</figcaption><p></p>
</figure>
</div>
<p>This seems to be working quite well; but remember, ports are generally prone to change. The t-test is accounting for this by calculating each pixels variance over the entire time period, but its still possible that the change were seeing is due to the port rather than the explosion. To test this, we can run the same algorithm on the same area, using the same date cutoff (August 4th), but in a different year; Ive chosen 2018. This is whats known as a placebo test: if its still showing loads of statistically significant change around the cutoff, our algorithm is probably picking up on port activity rather than the explosion.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="../images/beirut/beirut_change_2018.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Pixelwise T-Test, 2018</figcaption><p></p>
</figure>
</div>
<p>Compared to the 2020 image, theres a lot less yellow (significant change). That being said, there are a few yellow areas. This could be due to a number of reasons: ships coming and going, cranes moving, and containers being loaded and unloaded would all register in the change detection algorithm. There are also a number of yellow specks throughout the city, which is also to be expected since cities are also generally in a state of flux. Construction, demolition, and even the growth of vegetation can all be detected by the algorithm.</p>
<p>However, the scale and quantity of the change is nowhere near what it was for the 2020 image. This is a good sign that the algorithm is detecting change resulting from the explosion.</p>
</section>
<section id="validation" class="level2">
<h2 class="anchored" data-anchor-id="validation">Validation</h2>
<p>Great. Weve developed our very own change detection algorithm in earth engine, applied it to the Beirut explosion, and it seems to be working after checking with a basic placebo test. But how do we know that its correctly predicting the <em>extent</em> of the damage, and not wildly over/underestimating?</p>
<p>Given that this was a few years ago, we have the benefit of hindsight. In particular, the United Nations and the Municipality of Beirut have <a href="https://unhabitat.org/sites/default/files/2020/10/municipality_of_beirut_-_beirut_explosion_rapid_assessment_report.pdf">published a report</a> on the damage caused by the explosion. This report includes estimates of the number of buildings damaged or destroyed, as well as the number of people displaced. The report states that approximately 10,000 buildings were damaged within a 3 kilometre radius of the port. If our algorithm suggests that only 1,000 buildings were damaged, its undershooting. If it suggests that 100,000 buildings were damaged, its overshooting.</p>
<p>Using building footprint data and the t-test image we just generated, we can createe an estimate of the number of damaged buildings according to our model. First, we want to generate a thresholded image, where pixels with a value greater than 0 are set to 1, and all other pixels are set to 0. We can then use this mask to reduce the building footprints to a single value for each building, where the value is the mean of the t-test image within the footprint. If the mean value is greater than 0, the building is damaged. If its less than 0, the building is not damaged.</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="co">// Create a mask of the t-test image, where pixels with a value greater than 0 are set to 1, and all other pixels are set to 0</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> threshold <span class="op">=</span> composite<span class="op">.</span><span class="fu">updateMask</span>(composite<span class="op">.</span><span class="fu">gt</span>(<span class="dv">0</span>))<span class="op">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Load the building footprints</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> buildings <span class="op">=</span> ee</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">FeatureCollection</span>(<span class="st">"projects/sat-io/open-datasets/MSBuildings/Lebanon"</span>)</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(aoi)<span class="op">;</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate the mean value of the t-test image within each building footprint</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> damaged_buildings <span class="op">=</span> threshold<span class="op">.</span><span class="fu">reduceRegions</span>({</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> buildings<span class="op">,</span></span>
<span id="cb5-14"><a href="#cb5-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">mean</span>()<span class="op">,</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="co">// Print the number of buildings with a mean value greater than 0</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="co">// i.e., those displaying statistically significant change</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(damaged_buildings<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">"mean"</span><span class="op">,</span> <span class="dv">0</span>))<span class="op">.</span><span class="fu">size</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 9,256, which is pretty damn close to 10,000. We can also visualize the building footprints on the map, colored according the mean value of the t-test image within the footprint, where:</p>
<ul>
<li>Blue = no damage</li>
<li>Green = low damage</li>
<li>Yellow/Orange = medium damage</li>
<li>Red = high levels of damage</li>
</ul>
<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>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Create an empty image</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> empty <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>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Paint the building footprints onto the empty image</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> outline <span class="op">=</span> empty<span class="op">.</span><span class="fu">paint</span>({</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> damaged_buildings<span class="op">,</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">color</span><span class="op">:</span> <span class="st">"mean"</span><span class="op">,</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">5</span><span class="op">,</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="co">// Define a color palette</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> building_palette <span class="op">=</span> [</span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="st">"0034f5"</span><span class="op">,</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="st">"1e7d83"</span><span class="op">,</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> <span class="st">"4da910"</span><span class="op">,</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> <span class="st">"b3c120"</span><span class="op">,</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="st">"fcc228"</span><span class="op">,</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> <span class="st">"ff8410"</span><span class="op">,</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a> <span class="st">"fd3000"</span><span class="op">,</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the image to the map</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(</span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> outline<span class="op">,</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> { <span class="dt">palette</span><span class="op">:</span> building_palette<span class="op">,</span> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> <span class="dt">max</span><span class="op">:</span> <span class="dv">2</span> }<span class="op">,</span></span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a> <span class="st">"Damaged Buildings"</span></span>
<span id="cb6-32"><a href="#cb6-32" 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 result naturally resembles the underlying t-test image, with high levels of damage concentrated around the port, and progressively decreasing damage with distance:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="../images/beirut/beirut_footprints.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Building Footprints colored according to estimated blast damage</figcaption><p></p>
</figure>
</div>
<p>To get a better sense of how these predicitions correspond to actual damage, we can zoom in and turn on the Google satellite basemap, which has imagery taken just after the explosion; you can still see capsized boats in the port. Zooming in to the epicenter, we can see several warehouses that were effectively vaporized. Our change detection algorithm picks up on a high degree of change, as indicated by the red outlines of the building footprints:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="../images/beirut/beirut_footprints_port.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Predicted damage and optical satellite imagery in the Port of Beirut, August 2020</figcaption><p></p>
</figure>
</div>
<p>This is pretty low-hanging fruit. Lets look at a different area, around 1.3km east from the epicenter with a mix of warehouses and residential buildings:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="../images/beirut/beirut_footprints_zoomed.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Area east of the port: 35.533194, 33.9024</figcaption><p></p>
</figure>
</div>
<p>Here, theres greater variation in the predictions. Ive highlighted three areas.</p>
<p>In Area A, we see a warehouse with a highly deformed roof; panels of corrugated iron are missing, and much of the roof is warped. The building footprint for this warehouse is red, suggesting that our algorithm correctly predicts a significant amount of blast damage.</p>
<p>In Area B, we see a medium-rise building. If you look closely at the southern edge of the building, youll see the siding has been completely torn off and is laying on the sidewalk. The building footprint is orange, suggesting a medium amount of change. We may be underestimating a bit here.</p>
<p>In Area C, there are a bunch of high rise buildings clustered together. The building footprints are all blue, suggesting little to no damage. This is a bit of a surprise given how damaged areas A and B are. If you squint at the satellite image, it is indeed hard to tell if these buildings are damaged because were looking at them from the top down, when much of the damage (e.g., the windows being blown out) would only be visible from the side. Indeed, our own estimate of the number of damaged buildings based on the algorithm we developed is about 8% shy of the U.N.s estimate. This may be why.</p>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>In this practical example, we created a custom change detection algorithm that conducts a pixelwise t-test to detect change resulting from the 2020 explosion in the port of Beirut. By defining our own functions to do most of this analysis, we can apply the same workflow quite easily to a different context by simply moving the AOI and inputting the date of the shock. A placebo test showed that its not just detecting general change in the area, but specifically change resulting from the explosion: when we keep everything the same but change the year of the shock, we see very little significant change being detected. Finally, by joining the predicted damage map to building footprints, we come up with an estimate of 9,256 damaged buildings, which is pretty close to the U.N.s estimate of 10,000. That concludes the portion of this case study that deals with Earth Engine, but if youre interested in learning more about why were coming up a bit short on the damage estimate (and some different ways of looking at the problem), read on.</p>
</section>
<section id="extension-satellite-imagery-and-its-limits" class="level2">
<h2 class="anchored" data-anchor-id="extension-satellite-imagery-and-its-limits">Extension: Satellite Imagery and its Limits</h2>
<p>Though satellite imagery analysis is undoubtedly one of the best tools we have at our disposal to analyze this sort of phenomenon, it appears to systematically underestimate the extent of damage in Beirut. I outline an alternative approach using Open Street Map data to create a 3D model of Beirut and the explosion to analyze directional blast damage. Again, were now leaving Earth Engine and moving to Blender, so if youre not interested in that feel free to skip ahead to the next case study.</p>
<p>Below is one of the most viewed videos of the explosion:</p>
<blockquote class="twitter-tweet blockquote">
<p lang="en" dir="ltr">
Stunning video shows explosions just minutes ago at Beirut port <a href="https://t.co/ZjltF0VcTr">pic.twitter.com/ZjltF0VcTr</a>
</p>
— Borzou Daragahi 🖊🗒 (<span class="citation" data-cites="borzou">@borzou</span>) <a href="https://twitter.com/borzou/status/1290675854767513600?ref_src=twsrc%5Etfw">August 4, 2020</a>
</blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Geolocating this video was pretty simple thanks to the Greek Orthodox church (highlighted in green below) and the road leading to it (highlighted in blue). The red box indicates the likely location (33.889061, 35.515909) from which the person was filming:</p>
<p><img src="../images/beirut/IMG_2.png" class="img-fluid"></p>
<p>The video shows heavy damage being sustained by areas well outside the zones classified as damaged in the maps above (both my own and NASAs). Indeed, substantial damage was reported several kilometers away.</p>
<p>Why are satellite images underestimating damage in Beirut? Satellite images are taken from above, and are two-dimensional. Much of the damage caused by the blast, however, was directional; the pressure wave hit the sides of buildings, as shown in this diagram from a FEMA manual:</p>
<p><img src="../images/beirut/IMG_3.png" class="img-fluid"></p>
<p>Areas close to the explosion suffered so much damage that it could be seen from above, but even if an apartment building had all of its windows blown out, this would not necessarily be visible in a top-down view. Even for radar, which does technically collect data in three dimensions, the angle problem remains; a high resolution radar might be able to tell you how tall an apartment complex is, but it wont give you a clear image of all sides. Case in point: the NASA damage map was created using Sentinel-1 SAR data. In a nutshell, damage assessment in this case is a three-dimensional problem, and remote sensing is a two-dimensional solution.</p>
<section id="creating-a-3d-model-of-beirut" class="level3">
<h3 class="anchored" data-anchor-id="creating-a-3d-model-of-beirut">Creating a 3D model of Beirut</h3>
<p>To create a more accurate rendering of directional blast damage, three dimensional data are required. Data from Open Street Maps (OSM) contains information on both the “footprints” (i.e., the location and shape) as well as the height of buildings, which is enough to create a three dimensional model of Beirut. 3D rendering was done in Blender using the Blender-OSM add-on to import a satellite basemap, terrain raster, and OSM data.</p>
<p>Geolocated videos of the blast can be used to verify and adjust the model. Below is a side-by-side comparison of the twitter video and a 3D rendition of OSM data:</p>
<p><img src="../images/beirut/IMG_4.png" class="img-fluid"></p>
<p>Some slight adjustments to the raw OSM data were made to achieve the image on the right. The building footprints are generally very accurate and comprehensive in coverage, but the building height data does occasionally have to be adjusted manually. A simple and reliable way of doing this is to look at the shadows cast by the building on the satellite base map and scale accordingly. I also added a rough texture to the buildings to help differentiate them, and added the domed roof of the Greek Orthodox church for reference.</p>
<p>For good measure, a second video is geolocated following the same procedure:</p>
<blockquote class="twitter-tweet blockquote">
<p lang="en" dir="ltr">
Another view of the explosions in Beirut <a href="https://t.co/efT5VlpMkj">pic.twitter.com/efT5VlpMkj</a>
</p>
— Borzou Daragahi 🖊🗒 (<span class="citation" data-cites="borzou">@borzou</span>) <a href="https://twitter.com/borzou/status/1290678580897251330?ref_src=twsrc%5Etfw">August 4, 2020</a>
</blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The second pier (highlighted in green) and the angle (in blue) serve as references:</p>
<p><img src="../images/beirut/IMG_5.png" class="img-fluid"></p>
<p>The video was taken from the rooftop of a Japanese restaurant called Clap Beirut (in red above). This is confirmed by a picture of the rooftop bar on google images, which matches the bar that can be seen at 0:02 in the twitter video. Below is a comparison of the video view and the 3D OSM model:</p>
<p><img src="../images/beirut/IMG_6.png" class="img-fluid"></p>
<p>Though somewhat grainy, the basemap on the OSM rendering shows the same parking lot in the foreground, the second pier, and the same two buildings highlighted in yellow. Having created a 3D model of Beirut using OSM data, we can now simulate how the explosion would interact with the cityscape.</p>
</section>
<section id="using-a-viewshed-analysis-to-assess-blast-exposure" class="level3">
<h3 class="anchored" data-anchor-id="using-a-viewshed-analysis-to-assess-blast-exposure">Using a Viewshed Analysis to Assess Blast Exposure</h3>
<p>As the pressure wave moved through the Beirut, some buildings bore the full force of the explosion, while others were partially shielded by taller structures. A viewshed analysis can be conducted to identify surfaces that were directly exposed to the explosion by creating a lighting object at ground zero; areas that are lit up experienced unobstructed exposure to the blast:</p>
<p><img src="../images/beirut/GIF_1.gif" class="img-fluid"></p>
<p>Pressure waves, like sound, are capable of diffraction (bending around small obstructions). To roughly simulate this, the lighting object is gradually raised, allowing the light to pass “around” obstructions. Warehouses on the Eastern side of the docks, as well as the first row of apartment buildings facing the docks are immediately affected. As the lighting object rises above the warehouse, more areas suffer direct exposure.</p>
<p>Using two lighting objects a red one at 10 meters and a blue one at 20 meters above the warehouse at ground zero the intensity of the blast in different areas is highlighted; red areas suffered direct exposure, blue areas suffered partially obstructed exposure, and black areas were indirectly exposed.</p>
<p><img src="../images/beirut/IMG_7.png" class="img-fluid"></p>
<p>In the immediate vicinity of the explosion the large “L” shaped building (Lebanons strategic grain reserve) is bright red, and was barely left standing. It absorbed a large amount of the blast, shielding areas behind it and thereby casting a long blue shadow to the West. If one refers back to the satellite damage maps above, there appears to be significantly less damage in the area just West of (“behind”) the grain silo, roughly corresponding to the blue shadow above. While these areas were still heavily damaged, they seem to have suffered less damage than areas of equal distance to the East.</p>
</section>
<section id="accounting-for-diffraction" class="level3">
<h3 class="anchored" data-anchor-id="accounting-for-diffraction">Accounting for Diffraction</h3>
<p>The viewshed analysis tells us which sides of a building are exposed to the blast, but its a pretty rough approximation of the way the pressure wave would respond to obstacles in its path. As previously mentioned, pressure waves behave much like sound waves or waves in water: they bounce off of objects, move around obstructions, and gradually fade.</p>
<p>To get a more precise idea of the way in which the blast interacted with the urban environment, we can model the blast as an actual wave using the “dynamic wave” feature in Blender. This effectively involves creating a two-dimensional plane, telling it to behave like water, and simulating an object being dropped into the water. By putting an obstruction in this plane, we can see how the wave responds to it. As an example, the grain silo has been isolated below:</p>
<p><img src="../images/beirut/GIF_2.gif" class="img-fluid"></p>
<p>As the blast hits the side of the silo, it is reflected. Two large waves can be seen traveling to the right: the initial blast wave, and the reflection from the silo which rivals the initial wave in magnitude. To the left, the wave travels around the silo but is significantly weakened.</p>
<p>Broadening the focus and adding the rest of the OSM data back in, we can observe how the pressure wave interacted with buildings on the waterfront:</p>
<p><img src="../images/beirut/GIF_3.gif" class="img-fluid"></p>
<p>The warehouses on the docks were omitted to emphasize the interaction between the pressure wave and the waterfront buildings; their light metal structure and low height means they would have caused little reflection anyway. The general pattern of the dynamic wave is consistent with the viewshed, but adds a layer of detail. The blast is reflected off of the silo towards the East, leading to a double hit. Though the wave still moves around the silo to the West, the pressure is diminished. Once the wave hits the highrises, the pattern becomes noisy as the wave both presses forward into the mainland and is reflected back towards the pier.</p>
</section>
<section id="modeling-the-pressure-wave" class="level3">
<h3 class="anchored" data-anchor-id="modeling-the-pressure-wave">Modeling the Pressure Wave</h3>
<p>Now that weve accounted for the directionality of the blast and the influence of buildings, we can model the pressure wave itself. An expanding sphere centered at ground zero is used to model the progression of the pressure wave through the city. To get a visual sense of the blasts force, the color of the sphere will be a function of the pressure exerted by the pressure wave.</p>
<p>The pressure exerted by the explosion in kilopascals (kPa) at various distances can be calculated using the U.S. Department of Defemses Blast Effects Computer, which allows users to input variables such as the TNT equivalent of the ordnance, storage method, and elevation. Though there are several estimates, the blast was likely equivalent to around 300 tons of TNT. The direct “incident pressure” of the pressure wave is shown in blue. However, pressure waves from explosions that occur on the ground are reflected upwards, amplifying the total pressure exerted by the blast. This “reflected pressure” is shown in orange:</p>
<iframe title="Blast Overpressure and Distance " aria-label="Interactive line chart" id="datawrapper-chart-J1Pb1" src="https://datawrapper.dwcdn.net/J1Pb1/1/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="400">
</iframe>
<script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(a){if(void 0!==a.data["datawrapper-height"])for(var e in a.data["datawrapper-height"]){var t=document.getElementById("datawrapper-chart-"+e)||document.querySelector("iframe[src*='"+e+"']");t&&(t.style.height=a.data["datawrapper-height"][e]+"px")}}))}();
</script>
<p>For reference, 137 kPa results in 99% fatalities, 68 kPa is enough to cause structural damage to most buildings, and 20 kPa results in serious injuries. 1-6 kPa is enough to break an average window. At 1km, the reflected pressure of the blast (18 kPa) was still enough to seriously injure. Precisely calculating the force exerted by an explosion is exceptionally complicated, however, so these numbers should be treated as rough estimates. Further analysis of the damage caused by the blast can be derived from the UNs Explosion Consequences Analysis calculator which provides distance values for different types of damage and injuries.</p>
<p>Linking the values in this graph to the color of the pressure wave sphere provides a visual representation of the blasts force as it expands. An RGB color scale corresponds to the blasts overpressure at three threshold values.</p>
<p><img src="../images/beirut/beirut.gif" class="img-fluid"></p>
<p>By keeping the lighting object from the viewshed analysis and placing it within the expanding sphere of the pressure wave, we combine two key pieces of information: the pressure exerted by the blast (the color of the sphere), and the level of directional exposure (brightness).</p>
<p>Now, referring back to the two geolocated twitter videos from earlier, we can recreate the blast in our 3D model and get some new insights. Below is a side-by-side comparison of the first video and the 3D model:</p>
<p><img src="../images/beirut/GIF_5.gif" class="img-fluid"></p>
<p>Judging by the twitter video alone, it would be very hard to tell the fate of the person filming or the damage caused to the building that they were in. However, the 3D model shows that despite having an unobstructed view of the explosion, the incident pressure of the pressure wave had decreased significantly by the time it reached the viewing point. The blue-green color corresponds to roughly 15 kPa enough to injure and break windows, but not enough to cause structural damage to the building.</p>
<p>The second twitter video was taken slightly closer to ground zero, but the view was partially obstructed by the grain silo:</p>
<p><img src="../images/beirut/GIF_6.gif" class="img-fluid"></p>
<p>Though the pressure wave probably exerted more pressure compared to the first angle, the partial obstruction of the grain silo likely tempered the force of the blast.</p>
</section>
<section id="assessing-damage-to-the-skyline-tower" class="level3">
<h3 class="anchored" data-anchor-id="assessing-damage-to-the-skyline-tower">Assessing Damage to the Skyline Tower</h3>
<p>As a concrete example of how this approach can be used to assess damage (or predict it, if one had the foresight), let us consider the Skyline Tower, pictured below following the explosion:</p>
<p><img src="../images/beirut/IMG_8.png" class="img-fluid"></p>
<p>This partial side view shows two faces of the building, labeled “A” and “B” above. Side A was nearly perpendicular to the blast, and just 600m from ground zero. Based on the previous modeling, the pressure wave exerted roughly 40 kPa on this side of the building. The corner where sides A and B meet, highlighted in green, shows total destruction of windows, removal of most siding panels, and structural damage. The back corner, highlighted in red, shows many windows still intact, indicating that the maximum overpressure on this side of the building likely didnt exceed 10 kPa. In other words, standing on the front balcony would likely have led to serious injury but standing on the back balcony would have been relatively safe.</p>
<p>The animation below shows the Skyline Tower as it is hit by the pressure wave, with sides A and B labeled:</p>
<p><img src="../images/beirut/GIF_7.gif" class="img-fluid"></p>
<p>The bright green color of the pressure wave indicates a strong likelihood of structural damage. Side A can be seen taking a direct hit, while side B is angled slightly away. Despite not being directly exposed to the blast, it likely still took reflective damage from some of the neighboring buildings. Both the incident overpressure indicated by the color of the sphere, as well as the relative brightness of sides A and B both correspond closely to the observed damage taken by the Skyline Tower.</p>
</section>
<section id="further-research" class="level3">
<h3 class="anchored" data-anchor-id="further-research">Further Research</h3>
<p>Though satellite imagery analysis is an indispensable tool in disaster response, it has limitations. Urban blast damage in particular is difficult to assess accurately because it is highly directional and much of it cannot be seen from a birds eye view. Using free and open source tools, an interactive 3D model of an urban explosion can be generated, allowing for a highly detailed investigation of directional blast damage. This can be achieved in three steps:</p>
<p>First, creating a 3D model of the urban area using Blender and Open Street Maps data. Second, conducting a viewshed analysis using lighting objects to gauge levels of unobstructed exposure to the pressure wave. Third, modeling the explosion using geolocated videos of the event and ordnance calculators. For added detail, a dynamic wave analysis can be used to more precisely model how the pressure wave interacts with buildings.</p>
<p>Once properly modeled, the explosion can be viewed from any angle in the city. The viewshed analysis can be calibrated more finely by ground-truthing various damage levels (e.g.&nbsp;broken windows) at different locations. In the absence of an official address registry in Beirut, OSM is already being used by the Lebanese Red Cross (donate here) to conduct neighborhood surveys assessing blast damage. As such, this type of damage analysis can quickly be integrated into relief efforts, adapted to model disasters in different cities, and can even be used to simulate the destructive potential of hypothetical explosions to promote readiness.</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 clipboard = new window.ClipboardJS('.code-copy-button', {
target: function(trigger) {
return trigger.previousElementSibling;
}
});
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;
});
}
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="../chapters/C2_Refineries.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Refinery Identification</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="../chapters/C4_Ships.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">Ship Detection</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
</body></html>

971
docs/chapters/C4_Ships.html Normal file
View File

@@ -0,0 +1,971 @@
<!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.2.269">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - 10&nbsp; Ship Detection</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 -1.6em;
vertical-align: middle;
}
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;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</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="../chapters/C5_Object_Detection.html" rel="next">
<link href="../chapters/C3_Blast.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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<div class="container-fluid d-flex justify-content-between">
<h1 class="quarto-secondary-nav-title"><span class="chapter-title">Ship Detection</span></h1>
<button type="button" class="quarto-btn-toggle btn" aria-label="Show secondary navigation">
<i class="bi bi-chevron-right"></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 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="sidebar-tool px-1"><i class="bi bi-github"></i></a>
<a href="" title="Download" id="sidebar-tool-dropdown-0" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" title="Share" id="sidebar-tool-dropdown-1" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" class="quarto-color-scheme-toggle sidebar-tool" 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">A. Introduction</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<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">Overview</a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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 collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">B. Google Earth Engine</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">C. Case Studies</a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/C4_Ships.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Ship Detection</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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>
<!-- 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="#how-it-works" id="toc-how-it-works" class="nav-link active" data-scroll-target="#how-it-works">How it Works</a></li>
<li><a href="#building-the-application" id="toc-building-the-application" class="nav-link" data-scroll-target="#building-the-application">Building the Application</a>
<ul class="collapse">
<li><a href="#setup" id="toc-setup" class="nav-link" data-scroll-target="#setup">Setup</a></li>
<li><a href="#ship-detection" id="toc-ship-detection" class="nav-link" data-scroll-target="#ship-detection">Ship Detection</a></li>
<li><a href="#visualization" id="toc-visualization" class="nav-link" data-scroll-target="#visualization">Visualization</a></li>
<li><a href="#putting-it-all-together" id="toc-putting-it-all-together" class="nav-link" data-scroll-target="#putting-it-all-together">Putting it all together</a></li>
<li><a href="#building-a-user-interface" id="toc-building-a-user-interface" class="nav-link" data-scroll-target="#building-a-user-interface">Building a User Interface</a>
<ul class="collapse">
<li><a href="#drawing-tools" id="toc-drawing-tools" class="nav-link" data-scroll-target="#drawing-tools">Drawing Tools</a></li>
<li><a href="#widgets" id="toc-widgets" class="nav-link" data-scroll-target="#widgets">Widgets</a></li>
<li><a href="#the-control-panel" id="toc-the-control-panel" class="nav-link" data-scroll-target="#the-control-panel">The Control Panel</a></li>
</ul></li>
<li><a href="#taking-it-for-a-spin" id="toc-taking-it-for-a-spin" class="nav-link" data-scroll-target="#taking-it-for-a-spin">Taking it for a spin</a>
<ul class="collapse">
<li><a href="#north-korea" id="toc-north-korea" class="nav-link" data-scroll-target="#north-korea">North Korea</a></li>
<li><a href="#ukraine" id="toc-ukraine" class="nav-link" data-scroll-target="#ukraine">Ukraine</a></li>
</ul></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/chapters/C4_Ships.qmd" class="toc-action">Edit this page</a></p></div></div></nav>
</div>
<!-- main -->
<main class="content page-columns page-full" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title d-none d-lg-block"><span class="chapter-title">Ship Detection</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>Theres a huge amount of data available on the internet about ship movements, most of which draws on the Automatic Identification System (AIS) which uses radio to broadcast the identity, position, course, speed and other data about ships. <a href="https://www.marinetraffic.com/en/ais-api-services">MarineTraffic</a>, for example, provides an API that allows you to query the location of ships in real time as well as historical vessel tracks and lots of other useful data. Unfortunately most sources of AIS data are paywalled, and AIS can be turned off or manipulated to hide the identity or position of the ship. In fact, most of the stuff were interested in investigating probably happens when AIS is turned off.</p>
<p>Though ships can hide by turning off their AIS transponders, they cant hide from satellites. In this tutorial, were going to build an application that uses Synthetic Aperture Radar (SAR) from the European Space Agencys Sentinel-1 satellite to automatically identify ships, regardless of whether theyve got their transponders turned on or off. Heres the finished application:</p>
<div class="column-page">
<iframe src="https://ollielballinger.users.earthengine.app/view/shipdetection" width="100%" height="700px">
</iframe>
</div>
<section id="how-it-works" class="level2">
<h2 class="anchored" data-anchor-id="how-it-works">How it Works</h2>
<p>The app has two main panels:</p>
<ol type="1">
<li>A control panel on the left that allows the user to interact with the application</li>
<li>A map on the right that displays the results</li>
</ol>
<p>The control panel has a date slider that allows the user to load imagery from a particular year. Below that is a graph that shows the number of ships detected over time within that year. A slider underneath the graph lets us toggle the sensitivity of the ship detection process. Finally, a button at the bottom lets the user draw their own area of interest on the map, and the app will automatically detect ships within that area.</p>
<p>The map panel visualizes the results of the ship detection process and has three layers. The bottom layer is the Sentinel-1 image that were using to detect ships; its blue/purple, and if you zoom in and look cloesly you can see bright specks in the sea, which are ships. When Sentinel-1 sends a pulse of radio waves onto a flat surface like the sea, there is very little to reflect the waves back to the satellite they just bounce off into space. A low return signal means well see a darker color on our map. But when the radio waves hit a ship they are reflected back to the satellite and generate a higher return signal, and therefore a much brighter color. The second layer on the map displays a bunch of green points; each one of these is a detected ship. The last layer shows the red outline of the area of interest that the user drew on the map. You can zoom in on the map by holding down the command button and scrolling up and down.</p>
<p>When the application is first loaded it is centered on an area just north of the Suez Canal, and is analyzing imagery from 2021. We can see a bunch of green dots in the AOI, which is the main waiting area for ships waiting to transit the canal. Its a bit crowded because its visualizing all of the ships detected in the entire year. We can display imagery from a single day by clicking on a point in the graph on the left, which you will notice displays a huge spike in the number of ships detected around March.</p>
<p>You might remember that on March 23rd, 2021, the Ever Given a 400m long container ship got stuck in the Suez Canal. The ship was blocking the canal for six days, and its estimated that it cost the global economy $400 million per day. If you click on the tip of the spike on March 30th, you can see a backup of around 150 ships waiting for the canal to be cleared. You can also zoom in on a particular date range by scrolling and dragging on the graph. If you zoom in on the spike, you can then select imagery from early April to compare the number of ships in the waiting area after the blockage was cleared. In normal times we can see a regular pattern in the number of ships in the waiting area ranging between 15 and 40 ships.</p>
<p>If youre closely zoomed in to the map and load imagery from different days by clicking on the graph, you can compare the bright spots on the Sentinel image and the green dots. The ship detection process is pretty accurate, and we typically see one green dot per ship. However, you may notice that we occasionally miss a ship. This is because the ship detection process is based on a threshold, and if the ship is too small it may not generate a high enough return signal to be detected. You can increase the sensitivity of the ship detection process by moving the slider below the graph. This will increase the number of ships detected, but it may also increase the number of false positives.</p>
<p>The next section focuses on building this application. After that, well have a look at a few different use cases for this sort of maritime surveillance.</p>
</section>
<section id="building-the-application" class="level1">
<h1>Building the Application</h1>
<section id="setup" class="level2">
<h2 class="anchored" data-anchor-id="setup">Setup</h2>
<p>The first step is to configure the map and import the necessary datasets. By default, we want the app to be centered on the Suez Canal. Then, we want to import the Digital Surface Model (DSM) from the ALOS World 3D-30 dataset. This dataset provides a 30m resolution elevation model of the Earth which we will use to mask out the land. Finally, we want to import the Sentinel 1 dataset. We will use the VV polarization and the Interferometric Wide (IW) mode. We will also sort the images by date.</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">// Center the map on the Suez Canal and set map options</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setCenter</span>(<span class="fl">32.327</span><span class="op">,</span> <span class="fl">31.4532</span><span class="op">,</span> <span class="dv">10</span>)<span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setOptions</span>(<span class="st">"Hybrid"</span>)<span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">setControlVisibility</span>({ <span class="dt">all</span><span class="op">:</span> <span class="kw">false</span> })<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the Digital Surface Model (DSM) from the ALOS World 3D-30 dataset</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> dem <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">"JAXA/ALOS/AW3D30/V3_2"</span>)<span class="op">.</span><span class="fu">mean</span>()<span class="op">.</span><span class="fu">select</span>(<span class="st">"DSM"</span>)<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the Sentinel 1 dataset</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> s1 <span class="op">=</span> ee</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">"COPERNICUS/S1_GRD"</span>)</span>
<span id="cb1-14"><a href="#cb1-14" 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">listContains</span>(<span class="st">"transmitterReceiverPolarisation"</span><span class="op">,</span> <span class="st">"VV"</span>))</span>
<span id="cb1-15"><a href="#cb1-15" 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">eq</span>(<span class="st">"instrumentMode"</span><span class="op">,</span> <span class="st">"IW"</span>))</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sort</span>(<span class="st">"system:time_start"</span>)<span class="op">;</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="co">// Define the default area of interest</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> suez <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="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> [</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> [<span class="fl">32.17388584692775</span><span class="op">,</span> <span class="fl">31.59541178442045</span>]<span class="op">,</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> [<span class="fl">32.17388584692775</span><span class="op">,</span> <span class="fl">31.327159861902278</span>]<span class="op">,</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> [<span class="fl">32.4787564523965</span><span class="op">,</span> <span class="fl">31.327159861902278</span>]<span class="op">,</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> [<span class="fl">32.4787564523965</span><span class="op">,</span> <span class="fl">31.59541178442045</span>]<span class="op">,</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> ]<span class="op">,</span></span>
<span id="cb1-27"><a href="#cb1-27" 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 that weve gotten that out of the way, we can move on to the actual detection of ships.</p>
</section>
<section id="ship-detection" class="level2">
<h2 class="anchored" data-anchor-id="ship-detection">Ship Detection</h2>
<p>You might expect the automatic identification of ships based on synthetic aperture radar satellite imagery to involve a complex machine learning algorithm or artificial intelligence. In fact, it can be done in one line of code which sets a cutoff. If the return signal is greater than 0, then we have a ship. If its less than 0, then we dont. Simple as that.</p>
<p>The main analytical function responsible for ship identification is the <code>getVectors</code> function shown below. It takes an image as an input and returns a FeatureCollection of points, each corresponding to a ship. The function clips the image to the area of interest, selects the VV polarization, and finally filters out areas where the VV value is smaller than 0. This results in a raster image where the sea is black and the ships are white. We then use the <code>reduceToVectors</code> function to convert the raster image to a FeatureCollection of points. The function returns this FeatureCollection, and sets a property called <code>count</code> which is the number of ships detected in the image.</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="kw">function</span> <span class="fu">getVectors</span>(img) {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Get the area of interest from the drawing tools widget. </span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> aoi <span class="op">=</span> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">getEeObject</span>()<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// Clip the image to the area of interest</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Select the VV polarization </span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter areas where the VV value is greater than 0</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> cutoff <span class="op">=</span> img<span class="op">.</span><span class="fu">clip</span>(aoi)<span class="op">.</span><span class="fu">select</span>(<span class="st">"VV"</span>)<span class="op">.</span><span class="fu">gt</span>(<span class="dv">0</span>)</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// Convert the raster image to a FeatureCollection of points</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> points <span class="op">=</span> cutoff<span class="op">.</span><span class="fu">reduceToVectors</span>({</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometry</span><span class="op">:</span> aoi<span class="op">,</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> scaleSlider<span class="op">.</span><span class="fu">getValue</span>()<span class="op">,</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometryType</span><span class="op">:</span> <span class="st">"centroid"</span><span class="op">,</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">eightConnected</span><span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">maxPixels</span><span class="op">:</span> <span class="dv">1653602926</span><span class="op">,</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// Set the number of ships detected in the image as a property called "count"</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> count <span class="op">=</span> points<span class="op">.</span><span class="fu">size</span>()<span class="op">;</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a> <span class="co">// Set the date of the image as a property called "system:time_start"</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> date <span class="op">=</span> ee<span class="op">.</span><span class="fu">Date</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="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> points<span class="op">.</span><span class="fu">set</span>(<span class="st">"count"</span><span class="op">,</span> count)<span class="op">.</span><span class="fu">set</span>(<span class="st">"system:time_start"</span><span class="op">,</span> date)<span class="op">;</span></span>
<span id="cb2-27"><a href="#cb2-27" 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>The <code>count</code> and <code>system:time_start</code> properties are used to create the graph of daily ship counts and allow the resulting vector (point) data to interact with the date slider widget. An important detail here is that the “scale” parameter of the <code>reduceToVectors</code> function is set to the value of the scale slider widget. This allows the user to adjust the resolution of the ship detection process; a smaller value will allow us to detect smaller ships.</p>
</section>
<section id="visualization" class="level2">
<h2 class="anchored" data-anchor-id="visualization">Visualization</h2>
<p>The <code>viz</code> function is responsible for displaying the results of the ship detection process. It takes the area of interest, the vector data, and the Sentinel 1 image as inputs. Nothing super complicated here; were just creating three layers and adding them to the map in order: the underlying Sentinel-1 image raster, the ship vector data in green, and the area of interest outline in red. Were using the <code>Map.layers().set()</code> function to replace the existing layers with the new ones, rather than adding new ones each time.</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="kw">function</span> <span class="fu">viz</span>(aoi<span class="op">,</span> vectors<span class="op">,</span> s1Filtered) {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Create an empty image into which to paint the features, cast to byte.</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> empty <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>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// Paint all the polygon edges with the same number and width, display.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> outline <span class="op">=</span> empty<span class="op">.</span><span class="fu">paint</span>({</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">featureCollection</span><span class="op">:</span> aoi<span class="op">,</span></span>
<span id="cb3-9"><a href="#cb3-9" 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="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">width</span><span class="op">:</span> <span class="dv">3</span><span class="op">,</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// Create a layer for the area of interest in red</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> aoi_layer <span class="op">=</span> ui<span class="op">.</span><span class="at">Map</span><span class="op">.</span><span class="fu">Layer</span>(outline<span class="op">,</span> { <span class="dt">palette</span><span class="op">:</span> <span class="st">"red"</span> }<span class="op">,</span> <span class="st">"AOI"</span>)<span class="op">;</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Create a layer for the vector data in green</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> vectorLayer <span class="op">=</span> ui<span class="op">.</span><span class="at">Map</span><span class="op">.</span><span class="fu">Layer</span>(</span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> vectors<span class="op">.</span><span class="fu">flatten</span>()<span class="op">,</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> { <span class="dt">color</span><span class="op">:</span> <span class="st">"#39ff14"</span> }<span class="op">,</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> <span class="st">"Vectors"</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> <span class="co">// Create a layer for the Sentinel 1 image in false color</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> sarLayer <span class="op">=</span> ui<span class="op">.</span><span class="at">Map</span><span class="op">.</span><span class="fu">Layer</span>(</span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a> s1Filtered<span class="op">,</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> { <span class="dt">min</span><span class="op">:</span> [<span class="op">-</span><span class="dv">25</span><span class="op">,</span> <span class="op">-</span><span class="dv">20</span><span class="op">,</span> <span class="op">-</span><span class="dv">25</span>]<span class="op">,</span> <span class="dt">max</span><span class="op">:</span> [<span class="dv">0</span><span class="op">,</span> <span class="dv">10</span><span class="op">,</span> <span class="dv">0</span>]<span class="op">,</span> <span class="dt">opacity</span><span class="op">:</span> <span class="fl">0.8</span> }<span class="op">,</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="st">"SAR"</span></span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a> <span class="co">// Add the layers in order</span></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="bu">Map</span><span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">set</span>(<span class="dv">0</span><span class="op">,</span> sarLayer)<span class="op">;</span></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a> <span class="bu">Map</span><span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">set</span>(<span class="dv">1</span><span class="op">,</span> vectorLayer)<span class="op">;</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> <span class="bu">Map</span><span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">set</span>(<span class="dv">2</span><span class="op">,</span> aoi_layer)<span class="op">;</span></span>
<span id="cb3-38"><a href="#cb3-38" 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>We want a function to handle the visualization because there are two different situations in which were going to visualize results, and we dont want to repeat our code. The first situation is when the user draws a new area of interest, moves the date slider or alters the scale. In this case, we want to visualize the results of the ship detection process for the entire years worth of Sentinel-1 imagery. The second situation is when the user clicks on the chart to analyze a particular day. In this case, we obviously only want to visualize the results of the ship detection process on that day. With this function, we can simply pass the appropriately filtered versions of the Sentinel-1 image and vector data to the function, and it will visualize the results, rather than having to write the same code twice.</p>
</section>
<section id="putting-it-all-together" class="level2">
<h2 class="anchored" data-anchor-id="putting-it-all-together">Putting it all together</h2>
<p>Having defined a few helper functions to handle the visualization and ship detection process, we can now move on to the main function that will perform the analysis. This will be performed by the <code>daterangeVectors</code> function. In a nutshell, it reads the user specified date range from the date slider widget, and filters the Sentinel 1 dataset to only include images within that period. Then, it will loop through each Sentinel-1 image from that year and apply the <code>getVectors</code> function to count the number of ships that fall within the area of interest and generate a dataset of points corresponding to detected ships. Well then use the <code>viz</code> function we just defined to visualize all of the ship detections and Sentinel-1 images in the AOI during that year stacked on top of each other. Finally, well create a chart based on the number of ships detected per day, and allow the user to click on the chart to visualize the results for a particular day.</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="kw">var</span> daterangeVectors <span class="op">=</span> <span class="kw">function</span> () {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Get the date range from the date slider widget.</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> range <span class="op">=</span> ee<span class="op">.</span><span class="fu">DateRange</span>(</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Date</span>(dateSlider<span class="op">.</span><span class="fu">getValue</span>()[<span class="dv">0</span>])<span class="op">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> ee<span class="op">.</span><span class="fu">Date</span>(dateSlider<span class="op">.</span><span class="fu">getValue</span>()[<span class="dv">1</span>])</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// Get the area of interest from the drawing tools widget.</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> aoi <span class="op">=</span> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">getEeObject</span>()<span class="op">;</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// Hide the user-drawn shape.</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">setShown</span>(<span class="kw">false</span>)<span class="op">;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the Sentinel 1 dataset to only include images within the date range, and within the area of interest.</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> s1Filtered <span class="op">=</span> s1<span class="op">.</span><span class="fu">filterDate</span>(range<span class="op">.</span><span class="fu">start</span>()<span class="op">,</span> range<span class="op">.</span><span class="fu">end</span>())<span class="op">.</span><span class="fu">filterBounds</span>(aoi)<span class="op">;</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> <span class="co">// Count the number of ships in each image using the getVectors function</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> vectors <span class="op">=</span> s1Filtered<span class="op">.</span><span class="fu">map</span>(getVectors)<span class="op">;</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> <span class="co">// Use the viz function to visualize the results </span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a> <span class="fu">viz</span>(aoi<span class="op">,</span> vectors<span class="op">,</span> s1Filtered<span class="op">.</span><span class="fu">max</span>()<span class="op">.</span><span class="fu">updateMask</span>(dem<span class="op">.</span><span class="fu">lte</span>(<span class="dv">0</span>)))<span class="op">;</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a> <span class="co">// Create a chart of the number of ships per day</span></span>
<span id="cb4-30"><a href="#cb4-30" 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="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">byFeature</span>({</span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> vectors<span class="op">,</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a> <span class="dt">xProperty</span><span class="op">:</span> <span class="st">"system:time_start"</span><span class="op">,</span></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a> <span class="dt">yProperties</span><span class="op">:</span> [<span class="st">"count"</span>]<span class="op">,</span></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">setOptions</span>({</span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">"Daily Number of Ships in Area of Interest"</span><span class="op">,</span></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a> <span class="dt">vAxis</span><span class="op">:</span> { <span class="dt">title</span><span class="op">:</span> <span class="st">"Ship Count"</span> }<span class="op">,</span></span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a> <span class="dt">explorer</span><span class="op">:</span> { <span class="dt">axis</span><span class="op">:</span> <span class="st">"horizontal"</span> }<span class="op">,</span></span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a> <span class="dt">lineWidth</span><span class="op">:</span> <span class="dv">2</span><span class="op">,</span></span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a> <span class="dt">series</span><span class="op">:</span> <span class="st">"Area of Interest"</span><span class="op">,</span></span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a> <span class="co">// Add the chart at a fixed position, so that new charts overwrite older ones.</span></span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a> controlPanel<span class="op">.</span><span class="fu">widgets</span>()<span class="op">.</span><span class="fu">set</span>(<span class="dv">4</span><span class="op">,</span> chart)<span class="op">;</span></span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a> <span class="co">// Add a click handler to the chart to filter the map by day.</span></span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a> chart<span class="op">.</span><span class="fu">onClick</span>(filterDay)<span class="op">;</span></span>
<span id="cb4-51"><a href="#cb4-51" 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>Theres one function referenced above <code>filterDay</code> that we havent defined yet. This function is called when the user clicks on the chart to analyze a particular day. It takes the date of the clicked day as an input, filters the Sentinel-1 dataset and vector data accordingly, and uses the <code>viz</code> function to display the results for that day.</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="kw">function</span> <span class="fu">filterDay</span> (callback) {</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// Get the date of the clicked day</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> date <span class="op">=</span> ee<span class="op">.</span><span class="fu">Date</span>(callback)<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the vector data to only include images from that day</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> vectorDay <span class="op">=</span> vectors<span class="op">.</span><span class="fu">filterDate</span>(date)<span class="op">;</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// Filter the Sentinel-1 imagery to only include images from that day</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> s1Day <span class="op">=</span> s1<span class="op">.</span><span class="fu">filterDate</span>(date)<span class="op">.</span><span class="fu">max</span>()<span class="op">.</span><span class="fu">updateMask</span>(dem<span class="op">.</span><span class="fu">lte</span>(<span class="dv">0</span>))<span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// Use the viz function to visualize the results</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a> <span class="fu">viz</span>(aoi<span class="op">,</span> vectorDay<span class="op">,</span> s1Day)<span class="op">;</span></span>
<span id="cb5-17"><a href="#cb5-17" 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 analytical portion of the application is now complete. Now we have to build a user interface that lets us interact with the application.</p>
</section>
<section id="building-a-user-interface" class="level2">
<h2 class="anchored" data-anchor-id="building-a-user-interface">Building a User Interface</h2>
<p>There are four main steps in the process of creating the User Interface (UI):</p>
<ol type="1">
<li>Configure the drawing tools that allow the user to draw a polygon on the map.</li>
<li>Create some widgets</li>
</ol>
<section id="drawing-tools" class="level3">
<h3 class="anchored" data-anchor-id="drawing-tools">Drawing Tools</h3>
<p>We eventually want to allow the user to draw a polygon on the map, and count the number of ships that fall within it. In order to do so, we need to set up a few functions related to the drawing tools that allow the user to do this. Among other things, we want to make sure that were clearing the old geometries so that were only ever conducting analysis inside the most recent user-drawn polygon, so well need to clear the old ones. We also want to specify the type of polygon the user can draw, which for ease will be a rectangle (you could change this to the actual “polygon” type if you wanted to draw more complex geometries).</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="kw">var</span> drawingTools <span class="op">=</span> <span class="bu">Map</span><span class="op">.</span><span class="fu">drawingTools</span>()<span class="op">;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Remove any existing layers</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> (drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">length</span>() <span class="op">&gt;</span> <span class="dv">0</span>) {</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> layer <span class="op">=</span> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">remove</span>(layer)<span class="op">;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Add a dummy layer to the drawing tools object (the Suez Canal box)</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> dummyGeometry <span class="op">=</span> ui<span class="op">.</span><span class="at">Map</span><span class="op">.</span><span class="fu">GeometryLayer</span>({</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">geometries</span><span class="op">:</span> <span class="kw">null</span><span class="op">,</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>})</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">fromGeometry</span>(suez)</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">setShown</span>(<span class="kw">false</span>)<span class="op">;</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the dummy layer to the drawing tools object</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">.</span><span class="fu">add</span>(dummyGeometry)<span class="op">;</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a function that clears existing geometries and lets the user draw a rectangle</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">drawPolygon</span>() {</span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> layers <span class="op">=</span> drawingTools<span class="op">.</span><span class="fu">layers</span>()<span class="op">;</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a> layers<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">geometries</span>()<span class="op">.</span><span class="fu">remove</span>(layers<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>)<span class="op">.</span><span class="fu">geometries</span>()<span class="op">.</span><span class="fu">get</span>(<span class="dv">0</span>))<span class="op">;</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> drawingTools<span class="op">.</span><span class="fu">setShape</span>(<span class="st">"rectangle"</span>)<span class="op">;</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> drawingTools<span class="op">.</span><span class="fu">draw</span>()<span class="op">;</span></span>
<span id="cb6-31"><a href="#cb6-31" 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="widgets" class="level3">
<h3 class="anchored" data-anchor-id="widgets">Widgets</h3>
<p>The control panel will eventually contain a few different widgets that allow the user to interact with the application. Well start by creating a button that allows the user to draw a polygon on the map. Well also create a slider that allows the user to adjust the size of the ships that are detected (remember, this manipulates the “scale” parameter in the <code>reduceToVectors</code> function used in the detection process). The slider will have an accompanying label that tells the user what it does.</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="co">// Create a button that allows the user to draw a polygon on the map</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> drawButton <span class="op">=</span> ui<span class="op">.</span><span class="fu">Button</span>({</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">label</span><span class="op">:</span> <span class="st">"🔺"</span> <span class="op">+</span> <span class="st">" Draw a Polygon"</span><span class="op">,</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">onClick</span><span class="op">:</span> drawPolygon<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">style</span><span class="op">:</span> { <span class="dt">stretch</span><span class="op">:</span> <span class="st">"horizontal"</span> }<span class="op">,</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a slider that allows the user to adjust the size of the ships that are detected</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> scaleSlider <span class="op">=</span> ui<span class="op">.</span><span class="fu">Slider</span>({</span>
<span id="cb7-11"><a href="#cb7-11" 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="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">100</span><span class="op">,</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">value</span><span class="op">:</span> <span class="dv">80</span><span class="op">,</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">step</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">onChange</span><span class="op">:</span> daterangeVectors<span class="op">,</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">style</span><span class="op">:</span> { <span class="dt">width</span><span class="op">:</span> <span class="st">"70%"</span> }<span class="op">,</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a label for the slider</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> scaleLabel <span class="op">=</span> ui<span class="op">.</span><span class="fu">Label</span>(<span class="st">"Ship Size: "</span>)<span class="op">;</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a panel that contains the slider and its label</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> scalePanel <span class="op">=</span> ui<span class="op">.</span><span class="fu">Panel</span>({</span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">widgets</span><span class="op">:</span> [scaleLabel<span class="op">,</span> scaleSlider]<span class="op">,</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a> <span class="dt">style</span><span class="op">:</span> { <span class="dt">stretch</span><span class="op">:</span> <span class="st">"horizontal"</span> }<span class="op">,</span></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a> <span class="dt">layout</span><span class="op">:</span> ui<span class="op">.</span><span class="at">Panel</span><span class="op">.</span><span class="at">Layout</span><span class="op">.</span><span class="fu">Flow</span>(<span class="st">"horizontal"</span>)<span class="op">,</span></span>
<span id="cb7-29"><a href="#cb7-29" 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 last widget were going to define is the date slider. This widget will trigger the <code>daterangeVectors</code> function, which will filter the Sentinel-1 dataset to only include images from the selected year, and then run the detection process on the filtered dataset.</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="co">// Specify the start and end dates for the date slider</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> start <span class="op">=</span> <span class="st">"2014-01-01"</span><span class="op">;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> now <span class="op">=</span> <span class="bu">Date</span><span class="op">.</span><span class="fu">now</span>()<span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a date slider that allows the user to select a year</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> dateSlider <span class="op">=</span> ui<span class="op">.</span><span class="fu">DateSlider</span>({</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">value</span><span class="op">:</span> <span class="st">"2021-03-01"</span><span class="op">,</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">start</span><span class="op">:</span> start<span class="op">,</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">end</span><span class="op">:</span> now<span class="op">,</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">period</span><span class="op">:</span> <span class="dv">365</span><span class="op">,</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">onChange</span><span class="op">:</span> daterangeVectors<span class="op">,</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">style</span><span class="op">:</span> { <span class="dt">width</span><span class="op">:</span> <span class="st">"95%"</span> }<span class="op">,</span></span>
<span id="cb8-14"><a href="#cb8-14" 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>
</section>
<section id="the-control-panel" class="level3">
<h3 class="anchored" data-anchor-id="the-control-panel">The Control Panel</h3>
<p>Now were going to assemble all of the widgets weve just defined into one panel, alongside some explanatory text. Im adding a blank label to the panel as a placeholder for the chart, since it will be re-added to the panel every time the user changes the date on the date slider, the AOI, or the scale.</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> controlPanel <span class="op">=</span> ui<span class="op">.</span><span class="fu">Panel</span>({</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">widgets</span><span class="op">:</span> [</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> ui<span class="op">.</span><span class="fu">Label</span>(<span class="st">"SAR Ship Detection"</span><span class="op">,</span> {</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">fontWeight</span><span class="op">:</span> <span class="st">"bold"</span><span class="op">,</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">fontSize</span><span class="op">:</span> <span class="st">"20px"</span><span class="op">,</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> })<span class="op">,</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> ui<span class="op">.</span><span class="fu">Label</span>(</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"This tool identifies ships using Synthetic Aperture Radar imagery. Use the date slider below to analyze a given year. Click on the graph to show ships on a given day."</span><span class="op">,</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> { <span class="dt">whiteSpace</span><span class="op">:</span> <span class="st">"wrap"</span> }</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> )<span class="op">,</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> dateSlider<span class="op">,</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> ui<span class="op">.</span><span class="fu">Label</span>()<span class="op">,</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a> scalePanel<span class="op">,</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> ui<span class="op">.</span><span class="fu">Label</span>(</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> <span class="st">"Click the button below and draw a rectangle on the map to count ships in a custom area."</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> )<span class="op">,</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a> drawButton</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a> ]<span class="op">,</span></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">style</span><span class="op">:</span> {<span class="dt">maxWidth</span><span class="op">:</span> <span class="st">"400px"</span>}<span class="op">,</span></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">layout</span><span class="op">:</span> ui<span class="op">.</span><span class="at">Panel</span><span class="op">.</span><span class="at">Layout</span><span class="op">.</span><span class="fu">flow</span>(<span class="st">"vertical"</span><span class="op">,</span> <span class="kw">true</span>)<span class="op">,</span></span>
<span id="cb9-21"><a href="#cb9-21" 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>Once the control panel has been defined, we can add it to</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="co">// Add the control panel to the map</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>ui<span class="op">.</span><span class="at">root</span><span class="op">.</span><span class="fu">insert</span>(<span class="dv">0</span><span class="op">,</span>controlPanel)<span class="op">;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Trigger the daterangeVectors function when the user draws a polygon</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>drawingTools<span class="op">.</span><span class="fu">onDraw</span>(ui<span class="op">.</span><span class="at">util</span><span class="op">.</span><span class="fu">debounce</span>(daterangeVectors<span class="op">,</span> <span class="dv">500</span>))<span class="op">;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="co">// Run the daterangeVectors function to initialize the map</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="fu">daterangeVectors</span>()<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>And there we have it. A fully functional, all weather, daytime/nighttime ship detection tool that doesnt rely on AIS data. Lets play around with it.</p>
</section>
</section>
<section id="taking-it-for-a-spin" class="level2">
<h2 class="anchored" data-anchor-id="taking-it-for-a-spin">Taking it for a spin</h2>
<section id="north-korea" class="level3">
<h3 class="anchored" data-anchor-id="north-korea">North Korea</h3>
<p>In 2020, North Korea implemented one of the most severe COVID-19 lockdowns in the world including a near-total ban on <a href="https://thediplomat.com/2023/01/north-korea-likely-to-lift-pandemic-border-restrictions-in-2023/">“all cross-border exchanges, including trade, traffic, and tourism”.</a>. Measures have been so severe that the country appears to have experienced a significant <a href="https://foreignpolicy.com/2022/05/16/kim-north-korea-covid-outbreak-pandemic/">famine</a>. Though there were signs that things have gradually returned to normal, information on North Koreas economy is pretty hard to come by. Ship traffic in and out of the countrys largest port, Nampo, is probably a pretty good indicator of the countrys economic activity.</p>
<p>But we cant just head on down to Marine Tracker or other services that use AIS data to track ship movements. According to the <a href="https://home.treasury.gov/system/files/126/dprk_vessel_advisory_02232018.pdf">U.S. Treasury</a>, “North Korean-flagged merchant vessels have been known to intentionally disable their AIS transponders to mask their movements. This tactic, whether employed by North Korean-flagged vessels or other vessels involved in trade with North Korea, could conceal the origin or destination of cargo destined for, or originating in, North Korea.” They should know theyre the ones imposing the sanctions that make it illegal to trade with North Korea.</p>
<p>A New York Times <a href="https://www.nytimes.com/2019/07/16/world/asia/north-korea-luxury-goods-sanctions.html">investigation</a> tracked the maritime voyage of luxury Mercedes cars from Germany to North Korea via the Netherlands, China, Japan, South Korea, and Russia. AIS transponders were turned off at several points throughout this journey, and the investigation had to rely on satellite imagery to fill in the gaps.</p>
<p>Though they used high resolution optical imagery to follow individual ships, we want to identify lots of ships in a large area over a long period. That would get very expensive, and automatic ship detection in optical imagery is relatively difficult. Heres how our SAR tool fares when we draw a box in the bay of Nampo:</p>
<p><img src="../images/ships_north_korea.jpg" class="img-fluid"></p>
<p>Looking at imagery from 2021, we can see ship traffic increasing from nearly zero to around 40 ships per day.</p>
</section>
<section id="ukraine" class="level3">
<h3 class="anchored" data-anchor-id="ukraine">Ukraine</h3>
<p>Odessa is Ukraines largest port. Following its invasion of Ukraine in February 2022, Russia instituted a naval blockade against Ukrainian ports. The impact of this blockade is clearly visible using the tool weve just built:</p>
<p><img src="../images/ships_ukraine.jpg" class="img-fluid"></p>
<p>The daily number of ships detected in the port of Odessa dropped from 40 to 50 to 0 to 5 following the invasion, and remained near zero until the blockade was lifted in September 2022.</p>
</section>
</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 clipboard = new window.ClipboardJS('.code-copy-button', {
target: function(trigger) {
return trigger.previousElementSibling;
}
});
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;
});
}
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="../chapters/C3_Blast.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Blast Damage Assessment</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="../chapters/C5_Object_Detection.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">Object Detection</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
</body></html>

View File

@@ -0,0 +1,850 @@
<!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.2.269">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - 11&nbsp; Object Detection</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 -1.6em;
vertical-align: middle;
}
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;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</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="../chapters/C4_Ships.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 src="../site_libs/quarto-contrib/videojs/video.min.js"></script>
<link href="../site_libs/quarto-contrib/videojs/video-js.css" rel="stylesheet">
<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>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<div class="container-fluid d-flex justify-content-between">
<h1 class="quarto-secondary-nav-title"><span class="chapter-title">Object Detection</span></h1>
<button type="button" class="quarto-btn-toggle btn" aria-label="Show secondary navigation">
<i class="bi bi-chevron-right"></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 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="sidebar-tool px-1"><i class="bi bi-github"></i></a>
<a href="" title="Download" id="sidebar-tool-dropdown-0" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" title="Share" id="sidebar-tool-dropdown-1" class="sidebar-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="sidebar-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>
<a href="" class="quarto-color-scheme-toggle sidebar-tool" 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">A. Introduction</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<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">Overview</a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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 collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">B. Google Earth Engine</a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="false">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><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" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">C. Case Studies</a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="true">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../chapters/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="../chapters/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="../chapters/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="../chapters/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="../chapters/C5_Object_Detection.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Object Detection</span></a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<!-- 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="#object-detection-in-satellite-imagery" id="toc-object-detection-in-satellite-imagery" class="nav-link active" data-scroll-target="#object-detection-in-satellite-imagery">Object Detection in Satellite Imagery</a>
<ul class="collapse">
<li><a href="#yolov5" id="toc-yolov5" class="nav-link" data-scroll-target="#yolov5">YOLOv5</a></li>
</ul></li>
<li><a href="#training" id="toc-training" class="nav-link" data-scroll-target="#training">Training</a>
<ul class="collapse">
<li><a href="#accuracy-assessment" id="toc-accuracy-assessment" class="nav-link" data-scroll-target="#accuracy-assessment">Accuracy Assessment</a></li>
</ul></li>
<li><a href="#inference" id="toc-inference" class="nav-link" data-scroll-target="#inference">Inference</a>
<ul class="collapse">
<li><a href="#loading-a-trained-model" id="toc-loading-a-trained-model" class="nav-link" data-scroll-target="#loading-a-trained-model">1. Loading a trained model</a></li>
<li><a href="#loading-the-input-imagery" id="toc-loading-the-input-imagery" class="nav-link" data-scroll-target="#loading-the-input-imagery">2. Loading the input imagery</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/chapters/C5_Object_Detection.qmd" class="toc-action">Edit this page</a></p></div></div></nav>
</div>
<!-- main -->
<main class="content page-columns page-full" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title d-none d-lg-block"><span class="chapter-title">Object Detection</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>The Ship Detection tutorial explored a use case in which we might want to monitor the activity of ships in a particular location. That was a fairly straightforward task: the sea is very flat, and ships (especially large cargo and military vessels) protrude significantly. Using radar imagery, we could just set a threshold because if anything on the water is reflecting radio waves, its probably a ship.</p>
<p>One shortcoming of this approach is that it doesnt tell us what <em>kind</em> of ship weve detected. Sure, you could use the shape and size to distinguish between a fishing vessel and an aircraft carrier. But what about ships of similar sizes? Or what if you wanted to use satellite imagery to identify things other than ships, like airplanes, cars, or bridges? This sort of task called <strong>“object detection”</strong> is a bit more complicated.</p>
<p>In this tutorial, well be using a deep learning model called <strong>YOLOv5</strong> to detect objects in satellite imagery. Well be training the model on a custom dataset, and then using it to dynamically identify objects in satellite imagery of different resolutions pulled from Google Earth Engine. The tutorial is broken up into three sections:</p>
<ol type="1">
<li>Object detection in satellite imagery<br>
</li>
<li>Training a deep learning model on a custom dataset</li>
<li>Dynamic inference using Google Earth Engine</li>
</ol>
<p>Unlike previous tutorials which used the GEE JavaScript API, <strong>this one will utilize Python</strong>; this is because these sorts of deep learning models arent available in GEE natively yet. By the end, well be able to generate images such as the one below:</p>
<div class="column-screen">
<p><img src="images/obj_det2.jpg" class="img-fluid"></p>
</div>
<section id="object-detection-in-satellite-imagery" class="level2">
<h2 class="anchored" data-anchor-id="object-detection-in-satellite-imagery">Object Detection in Satellite Imagery</h2>
<p>Object detection in satellite imagery has a variety of useful applications.</p>
<p>Theres the needle-in-a-haystack problem of needing to monitor a large area for a small number of objects. Immediately prior to the invasion of Ukraine, for example, a number of articles emerged showing Russian military vehicles and equipment popping up in small clearings in the forest near the border with Ukraine. Many of these deployments were spotted by painstakingly combing through high resolution satellite imagery, looking for things that look like trucks. One problem with this approach is that you need to know roughly where to look. The second, and more serious problem, is that you need to be on the lookout in the first place. Object detection, applied to satellite imagery, can automatically comb through vast areas and identify objects of interest. If planes and trucks start showing up in unexpected places, youll know about it.</p>
<p>Perhaps youre not monitoring that large of an area, but you want frequent updates about whats going on. What sorts of objects (planes, trucks, cars, etc.) are present? How many of each? Where are they located? Instead of having to manually look through new imagery as it becomes available, you could have a model automatically analyze new collections and output a summary.</p>
<section id="yolov5" class="level3">
<h3 class="anchored" data-anchor-id="yolov5">YOLOv5</h3>
<p>Object detection is a fairly complicated task, and there are a number of different approaches to it. In this tutorial, well be using a model called <strong>YOLOv5</strong>. YOLO stands for <strong>You Only Look Once</strong>, and its a model that was developed by <a href="https://pjreddie.com/">Joseph Redmon</a> et. al., and the full paper detailing the model can be found <a href="https://arxiv.org/abs/1506.02640">here</a>.</p>
<p>The YOLOv5 model is a <strong>convolutional neural network</strong> (CNN), which is a type of deep learning model. CNNs are very good at identifying patterns in images, particularly in small regions of images. This is important for object detection, because we want to be able to identify objects even if theyre partially obscured by other objects.</p>
<p>YOLO works by chopping an image up into a grid, and then predicting the location and size of objects in each grid cell:</p>
<p><img src="images/yolo.jpg" class="img-fluid"></p>
<p>It learns the locations of these objects by training on a dataset of images in which each object is indicated by a <strong>bounding box</strong>. Then, when its shown a new image, it will attempt to predict bounding boxes around the objects in that image. The standard YOLO model is trained on the <a href="https://cocodataset.org/#home">COCO dataset</a>, which contains over 200,000 images of 80 different objects ranging from people to cars to dogs. YOLO models pre-trained on this dataset work great out of the box to detect objects in videos, photographs, and live streams. But the nature of the objects were interested in is a bit different.</p>
<p>Luckily, we can simply re-train the YOLOv5 model on datasets of labeled satellite imagery. The rest of this tutorial will walk through the process of training YOLOv5 on a custom dataset, and then using it to dynamically identify objects in satellite imagery pulled from Google Earth Engine.</p>
</section>
</section>
<section id="training" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="training">Training</h2>
<p>The process of re-training the YOLOv5 model on satellite imagery is fairly straightforward and can be accomplished in just three steps; first, were going to clone the YOLOv5 repository which contains the model code and the training scripts. Then, well download a dataset of satellite imagery and labels from Roboflow, and finally, well train the model on that dataset.</p>
<p>Lets start by cloning the YOLOv5 repository. Note: well be using a fork of the original repository that Ive modified to include some pre-trained models that well be using later on.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="op">!</span>git clone https:<span class="op">//</span>github.com<span class="op">/</span>oballinger<span class="op">/</span>yolov5_RS <span class="co"># clone repo</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="op">%</span>cd yolov5_RS <span class="co"># change directory to repo</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="op">%</span>pip install <span class="op">-</span>qr requirements.txt <span class="co"># install dependencies</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="op">%</span>pip install <span class="op">-</span>q roboflow <span class="co"># install roboflow</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> torch <span class="co"># install pytorch</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> os <span class="co"># for os related operations</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> IPython.display <span class="im">import</span> Image, clear_output <span class="co"># to display images</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Once weve downloaded the YOLOv5 repository, well need to download a dataset of labeled satellite imagery. For this example, were going to stick with ship detection as our use case, but expand upon it. We want to be able to distinguish between different types of ships, and we want to use freely-available satellite imagery.</p>
<p>To that end, well be using <a href="https://universe.roboflow.com/ibl-huczk/ships-2fvbx">this dataset</a>, which contains 3400 labeled images taken from Sentinel-2 (10m/px) and PlanetScope (3m/px) satellites. Ships in these images are labeled by drawing an outline around them:</p>
<p><img src="images/sample_training_ships.jpg" class="img-fluid"></p>
<p>The image above shows three ships and what is known as an STS a “Ship-To-Ship” transfer which is when a ship is transferring cargo to another ship. There are a total of seven classes of ship in this dataset:</p>
<p><img src="images/label_freq.jpg" class="img-fluid"></p>
<p>This dataset can be downloaded directly from Roboflow using the following code:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> roboflow <span class="im">import</span> Roboflow</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>rf <span class="op">=</span> Roboflow(api_key<span class="op">=</span><span class="st">"&lt;YOUR API KEY&gt;"</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>project <span class="op">=</span> rf.workspace(<span class="st">'ibl-huczk'</span>).project(<span class="st">"ships-2fvbx"</span>)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>dataset <span class="op">=</span> project.version(<span class="st">"1"</span>).download(<span class="st">"yolov5"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Youll need to get your own API key from Roboflow, which you can do <a href="https://app.roboflow.com/account/api">here</a>, and insert it in the second line of code. Roboflow is a platform for managing and training deep learning models on custom datasets. Its free to use for up to three projects, and hosts a large number of datasets that you can use to train your models. To use a different dataset, you can simply change the project name and version number in the second and third lines of code.</p>
<p>Finally, we can train our YOLOv5 model on the dataset we just downloaded in just one line of code:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="op">!</span>python train.py <span class="op">--</span>data {dataset.location}<span class="op">/</span>data.yaml <span class="op">--</span>batch <span class="dv">32</span> <span class="op">--</span>cache</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>This should take about an hour.</p>
<section id="accuracy-assessment" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="accuracy-assessment">Accuracy Assessment</h3>
<p>Using Tensorboard, we can log the performance of our model over the course of the training process:</p>
<div class="column-page">
<iframe src="https://tensorboard.dev/experiment/Yiyl7AsoQcyJ3uw699CR8A/#scalars" width="100%" height="700px">
</iframe>
</div>
<p>One metric in particular, <strong>mAP 0.5</strong>, is a good indicator of how well our model is performing. We can see it increasing rapidly at first, and then leveling off after around 30 epochs of training. The rest of this subsection will explain what exactly the mAP 0.5 value represents in this context. If youre interested in training your own model at some point, the rest of this subsection will be of interest. If youre just interested in deploying a pre-trained model, you can skip ahead to the next subsection.</p>
<p>In the past when weve worked on machine learning projects (for example in the makeshift refinery identifion tutorial), our training and validation data was a set of points geographic coordinates which we labeled as either being a refinery or not. Calculating the accuracy of that model was fairly straightforward, since predictions were either true positives, true negatives, false positives or false negatives.</p>
<p>This is slightly more complicated for object detection. Were not going pixel-by-pixel and trying to say “this is a ship” or “this is not a ship.” Instead, were looking at a larger image, and trying to draw boxes around the ships. The problem is that there are many ways to draw a box around a ship. The image below shows the labels used in our training data to indicate the location of ships.</p>
<p><img src="images/val_batch0_labels.jpg" class="img-fluid"> <img src="images/val_batch0_pred.jpg" class="img-fluid"></p>
<p>The predicted bounding boxes are very close to the actual bounding boxes, but theyre not exactly the same. The first step in evaluating the performance of our model is to determine how close the predicted boxes are to the actual boxes. We can do this by calculating the <strong>intersection over union</strong> (IoU) of the predicted and actual boxes. This is essentially a measure of how much overlap there is between the the predicted and actual boxes:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/iou.png" height="400" class="figure-img"></p>
<p></p><figcaption class="figure-caption">Intersection over Union</figcaption><p></p>
</figure>
</div>
<p>The IoU is a value between 0 and 1, where 0 means that the boxes dont overlap at all, and 1 means that the boxes overlap perfectly. Now we can set a threshold value for the IoU, and say that if the IoU is greater than that threshold, then well count that as a correct prediction. Now that we can classify a prediction as correct or incorrect, we can calculate two important metrics: <span class="math display">\[\text{Precision} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Positives}}\]</span></p>
<p>This is the proportion of positive identifications that are actually correct. If my model detects 100 ships and 90 of them are actually ships, then my precision is 90%.</p>
<p><span class="math display">\[\text{Recall} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Negatives}}\]</span></p>
<p>This is the proportion of actual positives that are identified correctly. If there are 100 ships in the image, and my model detects 90 of them, then my recall is 90%.</p>
<p>These two metrics are inversely related; I could easily get 100% recall by drawing lots of boxes everywhere to increase my chances of detecting all the ships. Conversely, I could get 100% precision by being extremely conservative and just drawing one or two boxes around the ships Im most confident about. The key is to maximize <em>both</em>: we want our model to be sensitive enough to detect as many ships as possible (high recall), but also precise enough to only draw boxes around the ships that are actually there (high precision). Researchers find this balance using a <strong>Precision-Recall curve</strong> (PR curve), which plots precision on the y-axis and recall on the x-axis. Below is the Precision-Recall curve for our final model, for each class:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/pr_curve.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Precision-Recall curve from the best</figcaption><p></p>
</figure>
</div>
<p>Starting from the top left corner, we set a very high confidence threshold: precision is 1, meaning that every box we draw is a ship, but recall is near 0 meaning that were not detecting any ships. As we lower the confidence threshold, we start to detect more ships, but we also start to draw boxes around things that arent ships. Towards the middle of the curve, were detecting most of the ships, but were also drawing boxes around a lot of false positives. Towards the bottom right corner, were detecting all the ships, but were also generating lots of false positives.</p>
<p>The goal is to find the point on the curve where precision and recall are both high; the closer the peak of our curve is to the top right corner, the better. A perfect model would touch the top right corner: it would have precision of 1 and recall of 1, detecting all of the ships without making any false positives. The area under this curve is called the <strong>Average Precision</strong> (AP), and is a measure of how close the curve is to the top right corner. The perfect model would have an AP of 1.</p>
<p>Some classes have a very high AP the value for the Aircraft Carrier class is 0.995, which is very high (though this could be down to the fact that we have a relatively small number of images with aircraft carriers in them). Ship-To-Ship (STS) transfer operations also have a high AP, at 0.951. However, other classes notably the “Ship” class have a low AP. This may be because the “Ship” class is a catch-all for any ship that doesnt fit into one of the other classes, so it encompasses lots of weird looking ships.</p>
<p>Finally, the <strong>mean Average Precision</strong> (mAP) is the average of the AP for each class, shown as the thick blue line above. Remember, all of this is premised on using a 0.5 threshold in the overlap (IoU) between our predicted boxes and the labels, which is why the final metric is called <strong>mAP 0.5</strong>. The mAP 0.5 for our model is 0.775, which is pretty good.</p>
<p>This number is very useful when training a model in several different ways using the same dataset, in order to select the best performing one. Its not that useful for comparing models trained on different datasets, since the mAP 0.5 is dependent on the number of classes in the dataset and the nature of those classes. For example, in the next section well be using a different model trained on the DOTA dataset which has a mAP 0.5 of around 0.68, largely due to the fact that it has around twice as many classes and many of them are similar to each other.</p>
</section>
</section>
<section id="inference" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="inference">Inference</h2>
<p>Now that weve got a trained model, we can use it to conduct object detection on new images. well build a data processing pipeline in three steps by:</p>
<ol type="1">
<li>Loading our trained model</li>
<li>Creating an interactive map to define the area we want to analyze.</li>
<li>Defining a function to run object detection within this area.</li>
</ol>
<section id="loading-a-trained-model" class="level3">
<h3 class="anchored" data-anchor-id="loading-a-trained-model">1. Loading a trained model</h3>
<p>During the training process, YOLO is iteratively tweaking the model to try to maximize mAP 0.5. It automatically saves the best version of the model in the following location: <code>YOLOv5_RS/runs/train/exp/weights/best.pt</code>. You can save this file for later use, which I have done in case you just want to use this model without having to train it yourself. Ive also included several other pre-trained models which you can find in the <code>YOLOv5_RS/weights/</code> directory, including:</p>
<ul>
<li><p><code>lowres_ships.pt</code>: the model we just trained on Sentinel-2 imagery.</p></li>
<li><p><code>aircraft.pt</code>: trained on the high resolution <a href="https://www.kaggle.com/datasets/airbusgeo/airbus-aircrafts-sample-dataset">Airbus Aircraft Detection Dataset</a>.</p></li>
<li><p><code>general.pt</code>: trained on the <a href="https://captain-whu.github.io/DOTA/dataset.html">DOTA dataset</a> by <a href="https://github.com/KevinMuyaoGuo/yolov5s_for_satellite_imagery#readme">Kevin Guo</a>. This model works great on high resolution satellite imagery, and can detect the following classes: plane, ship, storage tank, baseball diamond, tennis court, basketball court, ground track field, harbor, bridge, large vehicle, small vehicle, helicopter, roundabout, soccer field, swimming pool, container crane, airport and helipad.</p></li>
</ul>
<p>So far, weve trained a model to detect ships in Sentinel-2 imagery. But to show the versatility of this general approach, the rest of this tutorial will load up the <code>general.pt</code> model, and use it to detect a wide range of aircraft in high resolution imagery.</p>
</section>
<section id="loading-the-input-imagery" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="loading-the-input-imagery">2. Loading the input imagery</h3>
<p>To get started with object detection on satellite imagery using these pre-trained models, we need to define an Area of Interest (AOI) and load satellite imagery. Well do this by accessing Google Earth Engine from the Python notebook were working in, and creating an interactive map that will let us draw an AOI for analysis.</p>
<p>First, we first need to import a few packages:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="op">!</span>pip install geemap <span class="op">-</span>q</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> ee</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> geemap</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> requests</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> PIL <span class="im">import</span> Image</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> PIL <span class="im">import</span> ImageDraw</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> io <span class="im">import</span> BytesIO</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> torch</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> PIL</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Once weve done this, well also need to log in to Google Earth Engine using its Python API in order to access the satellite imagery. Running these two lines of code will generate a prompt with instructions; you have to click the link, confirm that you give the notebook permission to access your Earth Engine account, and paste the authentication code in the provided dialogue box.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>ee.Authenticate()</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>ee.Initialize()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Great, now we can load high resolution imagery from the National Agriculture Imagery Program (NAIP) and create an interactive map. For this example, Im centering the map on the <a href="https://en.wikipedia.org/wiki/309th_Aerospace_Maintenance_and_Regeneration_Group">Davis-Monthan Airplane Boneyard</a>. This is where the US Air force retires and restores aircraft, so it will have lots of airplanes of different kinds for us to identify.</p>
<p>First, we want to define a function called <code>detect</code> that will accept four arguments:</p>
<ul>
<li><code>input</code>: the satellite imagery we want to analyze.</li>
<li><code>visParams</code>: a dictionary of visualization parameters for the imagery.</li>
<li><code>weight</code>: the name of the pre-trained model we want to use.</li>
<li><code>labels</code>: a boolean indicating whether we want to display the labels on the processed image.</li>
</ul>
<div class="sourceCode" id="cb6"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> detect(<span class="bu">input</span>, visParams, weight, labels<span class="op">=</span><span class="va">True</span>):</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="co"># Get the AOI from the map</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> aoi <span class="op">=</span> ee.FeatureCollection(Map.draw_features)</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> mapScale<span class="op">=</span>Map.getScale()</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="co"># Visualize the raster in Earth Engine and get a download URL</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> image_url<span class="op">=</span><span class="bu">input</span>.visualize(bands<span class="op">=</span>visParams[<span class="st">'bands'</span>], <span class="bu">max</span><span class="op">=</span>visParams[<span class="st">'max'</span>]).getThumbURL({<span class="st">"region"</span>:aoi.geometry(), <span class="st">'scale'</span>:mapScale})</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="co"># Load the image into a PIL image</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> requests.get(image_url)</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> img <span class="op">=</span> Image.<span class="bu">open</span>(BytesIO(response.content))</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="co"># Load the model</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span>torch.hub.load(<span class="st">'.'</span>,<span class="st">'custom'</span>, path<span class="op">=</span><span class="st">'weights/</span><span class="sc">{}</span><span class="st">.pt'</span>.<span class="bu">format</span>(weight),source<span class="op">=</span><span class="st">'local'</span>,_verbose<span class="op">=</span><span class="va">False</span>)</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="co"># Run inference</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> results <span class="op">=</span> model(img)</span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a> <span class="co"># Count the number of detections</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a> counts<span class="op">=</span>pd.DataFrame(results.pandas().xyxy[<span class="dv">0</span>].groupby(<span class="st">'name'</span>).size()).reset_index().rename(columns<span class="op">=</span>{<span class="dv">0</span>:<span class="st">'count'</span>,<span class="st">'name'</span>:<span class="st">'detected'</span>}).set_index(<span class="st">'count'</span>)</span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> <span class="co"># Display the results</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> results.show(labels<span class="op">=</span>labels)</span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a> <span class="co"># Print the number of detections and the date of the image</span></span>
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(ee.Date(<span class="bu">input</span>.get(<span class="st">'system:time_start'</span>)).<span class="bu">format</span>(<span class="st">"dd-MM-yyyy"</span>).getInfo())</span>
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(counts)</span>
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> counts</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now, we can load the NAIP imagery and create an interactive map.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># load the past 10 years of NAIP imagery</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>naip <span class="op">=</span> ee.ImageCollection(<span class="st">'USDA/NAIP/DOQQ'</span>).<span class="bu">filter</span>(ee.Filter.date(<span class="st">'2012-01-01'</span>, <span class="st">'2022-01-01'</span>))</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="co"># set some thresholds</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>trueColorVis <span class="op">=</span> {</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="st">'bands'</span>:[<span class="st">'R'</span>, <span class="st">'G'</span>, <span class="st">'B'</span>],</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="st">'min'</span>: <span class="dv">0</span>,</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="st">'max'</span>: <span class="dv">300</span>,</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a><span class="co"># initialize our map</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>Map <span class="op">=</span> geemap.Map()</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>Map.setCenter(<span class="op">-</span><span class="fl">110.84</span>,<span class="fl">32.16</span>,<span class="dv">17</span>)</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>Map.addLayer(naip.first(), trueColorVis, <span class="st">"naip"</span>)</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>Map</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>This will generate a small map with some drawing tools on the left side. We can use these tools to draw a polygon around the area we want to analyze. Use the drawing tools to draw a rectangle around an area of interest.</p>
<p>Finally, we can run the detection on the imagery. Well do this by iterating through the collection of images, and running the <code>detect</code> function on each one. Well also store the results in a dataframe so we can analyze them later.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Get the polygon we just drew on the map </span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>aoi<span class="op">=</span>ee.FeatureCollection(Map.draw_features)</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Get a list of all the images in the collection</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>naip_list<span class="op">=</span>naip.filterBounds(aoi).toList(naip.size())</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Iterate through the list of images and run detection on each one</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> num <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>,(img_list.size()).getInfo()):</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> detect(ee.Image(naip_list.get(num)), trueColorVis,<span class="st">'general'</span>,labels<span class="op">=</span><span class="va">False</span>)</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> df<span class="op">=</span>df.append(detection) <span class="co"># store the results in a dataframe</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Below is the result of the detection on the latest image in the collection:</p>
<div class="column-screen">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/boneyard.jpg" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Davis-Monthan Airplane Boneyard, Tucson AZ. 32.139498, -110.868549</figcaption><p></p>
</figure>
</div>
</div>
<p>This image shows a remarkable degree of accuracy being achieved by our model. Inference took just 822.2 milliseconds, and it seems to be doing pretty well. The model identifies over 100 different kinds of aircraft (orange boxes) of many shapes and sizes, civilian and military, without missing a single one. It also identifies around 20 different types of helicopter (blue boxes) in the top right and even spots the cars on the highway and in the parking lots (red boxes). Its not perfect it thinks theres a ship in the bottom left corner near the shed (yellow box); in reality this appears to be half of a planes fuselage, an understandable mistake given how long it took <em>me</em> to figure out what it was.</p>
<!--
Even though we trained our model on Sentinel-2 imagery (10 meters per pixel), it can still be used on imagery from different satellites as long as they have a broadly similar resolution. A ship in PlanetScope imagery (3 meters per pixel) will look roughly similar to a ship in Sentinel-2 imagery. Using PlanetScope has another big advantage over Sentinel-2 beyond its higher spatial resolution: it has a much higher revisit rate (daily instead of 5 days). Though *downloading* PlanetScope imagery isn't free, you *can* generate a timelapse image of any area on Earth using Planet's [Planet Stories](https://www.planet.com/stories/create) tool. Simply create a free account and follow the instructions to generate a timelapse of an area of interest. You can then download the timelapse video and use it as input to our model.
Once you've done this, you can run the following line of code to automatically identify ships in the timelapse video:
![](../images/mikolayiv.mp4)
-->
</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 clipboard = new window.ClipboardJS('.code-copy-button', {
target: function(trigger) {
return trigger.previousElementSibling;
}
});
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;
});
}
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="../chapters/C4_Ships.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Ship Detection</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
</div>
</nav>
</div> <!-- /content -->
<script>videojs(video_shortcode_videojs_video1);</script>
</body></html>