Last active
January 26, 2019 04:59
-
-
Save davidbernat/a393cf6f6f35709b52c8452147b533bc to your computer and use it in GitHub Desktop.
Starlight Aether SDK application example: Visualizing the Profound Fury of Wildfires in Satellite Imagery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################### | |
# | |
# First things first. | |
# | |
# Please go to: network.runsonaether.com | |
# | |
# You will be prompted to log in with a | |
# gmail or email address. | |
# | |
# After log-in, you will see all the good | |
# stuff. | |
# 1) our Aether SDK documentation | |
# 2) our examples | |
# 3) your applications and UUID. | |
# | |
# Company url: www.runsonaether.com | |
# www.starlight.ai | |
# | |
# Demos, Pictures, & Videos: | |
# Posted to Twitter: https://twitter.com/astrorobotic | |
# | |
# Medium: https://medium.com/starlightteam | |
# | |
# Thank you, | |
# David Bernat | |
# @astrorobotic | |
# | |
############################################### | |
import aether as ae | |
import os | |
import logging | |
logger = logging.getLogger(__name__) | |
############################################### | |
# | |
# | |
# Visualizing the Profound Fury of Wildfires | |
# in Satellite Imagery | |
# | |
# An example of the Starlight Aether SDK | |
# Starlight: starlight.ai | |
# Aether SDK docs: docs.runsonaether.com | |
# Get a UUID in one-click: apply.runsonaether.com | |
# | |
# Author: David Bernat | |
# Twitter: @astrorobotic | |
# | |
# It will help us if you follow us on Twitter | |
# | |
# Report Issues to: | |
# https://github.com/TeamStarlight/aether-alpha-program | |
# | |
############################################### | |
# To install Aether SDK, simply pip install aether | |
# For documentation: docs.runsonaether.com | |
# Get a UUID in one-click: Log in to apply.runsonaether.com | |
uuid = "YOUR_UUID" | |
ae.GlobalConfig.set_user(uuid) | |
job_name = "paradise" | |
out_dir = "enhancedFire" | |
# The Paradise / Camp Fire started in Pulga, CA. So we use that as our center. | |
# | |
# There are several tunable parameters in the EnhancedFire function before. | |
# To reproduce the images in the Medium article, you simple need to change one | |
# line. Find that line by searching for [STARLIGHT NOTE]. | |
# Medium Article: Visualizing the Profound Fury of Wildfires in Satellite Imagery | |
# center = [39.754985, -121.609025] # paradise; November 10, 2018 | |
center = [39.802015, -121.448666] # pulga, center of fire | |
w = 0.15 # degrees | |
#################################### | |
# DEMO START | |
#################################### | |
coordinates = [[center[0] - 1.2*w, center[1] - 1.2*w], | |
[center[0] - 1.2*w, center[1] + 0.6*w], | |
[center[0] + 0.8*w, center[1] + 0.6*w], | |
[center[0] + 0.8*w, center[1] - 1.2*w], | |
[center[0] - 1.2*w, center[1] - 1.2*w]] | |
polygon = ae.AEPolygon(coordinates) | |
resource_name = "landsat" | |
query_parameters = dict( | |
bands=["BLUE", "GREEN", "RED", "NIR", "NIR", "SWIR1", "SWIR2"], | |
date_acquired=["2018-11-08", "2018-11-08"], | |
spacecraft_id=["LANDSAT_4", "LANDSAT_5", "LANDSAT_8"], | |
cloud_cover=[0.0, 100.0], | |
) | |
def EnhancedFire(s): | |
# FIRE NATURAL COLOR ENHANCED | |
# Pierre Markuse (@pierre_markuse) | |
def stretch(val, min, max): | |
return (val-min) / (max-min) | |
BITDEPTH = 16 | |
B02 = s.bands(0) / (2.0 ** BITDEPTH) | |
B03 = s.bands(1) / (2.0 ** BITDEPTH) | |
B04 = s.bands(2) / (2.0 ** BITDEPTH) | |
B05 = s.bands(3) / (2.0 ** BITDEPTH) | |
B08 = s.bands(4) / (2.0 ** BITDEPTH) | |
B11 = s.bands(5) / (2.0 ** BITDEPTH) | |
B12 = s.bands(6) / (2.0 ** BITDEPTH) | |
NaturalColors = [stretch(3.1*B04, 0.05, 0.9), | |
stretch(3.0*B03, 0.05, 0.9), | |
stretch(3.0*B02, 0.05, 0.9)] | |
EnhancedNaturalColors = [stretch(3.1*B04 + 0.10*B05, 0.05, 0.9), | |
stretch(3.0*B03 + 0.15*B08, 0.05, 0.9), | |
stretch(3.0*B02, 0.05, 0.9)] | |
NIRSSWIRColors = [stretch(2.6*B12, 0.05, 0.9), | |
stretch(1.9*B08, 0.05, 0.9), | |
stretch(2.7*B02, 0.05, 0.9)] | |
NaturalNIRSWIRMix = [stretch(2.1*B04 + 0.5*B12, 0.01, 0.99), | |
stretch(2.2*B03 + 0.5*B08, 0.01, 0.99), | |
stretch(3.2*B02, 0.01, 0.99)] | |
PanBand = [stretch(B08, 0.01, 0.99), | |
stretch(B08, 0.01, 0.99), | |
stretch(B08, 0.01, 0.99)] | |
PanTintedGreen = [B08 * 0.2, B08, B08 * 0.2] | |
Fire1OVL = [stretch(2.1*B04 + 0.5*B12, 0.01, 0.99) + 1.1, | |
stretch(2.2*B03 + 0.5*B08, 0.01, 0.99), | |
stretch(2.1*B02, 0.01, 0.99)] | |
Fire2OVL = [stretch(2.1*B04 + 0.5*B12, 0.01, 0.99) + 1.1, | |
stretch(2.2*B03 + 0.5*B08, 0.01, 0.99) + 0.5, | |
stretch(2.1*B02, 0.01, 0.99)] | |
FIRE1 = Fire1OVL | |
FIRE2 = Fire2OVL | |
# [STARLIGHT NOTE] | |
# TO CREATE NATURAL PHOTO OF SMOKE PLUME, USE NOFIRE = EnhancedNaturalColors | |
# TO CREATE BURN SCAR + FIRE OUTLINE, USE NOFIRE = NaturalNIRSWIRMix | |
NOFIRE = NaturalNIRSWIRMix | |
NOFIRE = EnhancedNaturalColors | |
SENSITIVITY = 0.5 | |
# Bookkeeping | |
import numpy as np | |
shape = B02.shape | |
FIRE1 = np.reshape(np.concatenate(FIRE1, axis=3), newshape=(shape[0], shape[1]*shape[2], 3)) | |
FIRE2 = np.reshape(np.concatenate(FIRE2, axis=3), newshape=(shape[0], shape[1]*shape[2], 3)) | |
NOFIRE = np.reshape(np.concatenate(NOFIRE, axis=3), newshape=(shape[0], shape[1]*shape[2], 3)) | |
B11 = np.reshape(B11, newshape=(shape[0], shape[1]*shape[2], 1)) | |
B12 = np.reshape(B12, newshape=(shape[0], shape[1]*shape[2], 1)) | |
IMAGE = NOFIRE | |
w1 = np.squeeze((B12+B11) > (1.0/SENSITIVITY), axis=2) | |
w2 = np.squeeze((B12+B11) > (2.0/SENSITIVITY), axis=2) | |
for ts_i in range(shape[0]): | |
IMAGE[ts_i, w1[ts_i]&w2[ts_i],:] = FIRE2[0,w1[ts_i]&w2[ts_i]] | |
IMAGE[ts_i, w1[ts_i]&~w2[ts_i],:] = FIRE1[0,w1[ts_i]&~w2[ts_i]] | |
IMAGE = np.reshape(IMAGE, newshape=(shape[0], shape[1], shape[2], 3)) | |
return s.update(IMAGE) | |
########### | |
# App Start | |
########### | |
out_full_path = "{}/{}/".format(out_dir, job_name) | |
# Write the essential data to file: | |
if not os.path.exists(out_full_path): | |
os.makedirs(out_full_path) | |
with ae.SkySession() as sky: | |
sb = sky.Resource(resource_name).search(polygon, query_parameters) | |
sb = sky.crop(sb) | |
spacetime = sky.download(sb, run=[EnhancedFire]) | |
spacetime.generate_image([0], [0,1,2], show_now=True, save_to="{}/{}.png".format(out_full_path, job_name)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment