From a3e0b5315b76acfa76edb92d7baf2910806f85e8 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 30 Aug 2020 11:16:29 -0600 Subject: [PATCH] Parse landsat scene ids --- src/components/deck-layers/tile-layer-2d.js | 6 +- src/components/options/index.js | 12 +- src/components/util/image.js | 18 ++- src/components/util/landsat.js | 34 +++++- src/components/util/webgl.js | 3 +- src/pages/index.js | 121 +++++++++++++++++--- 6 files changed, 166 insertions(+), 28 deletions(-) diff --git a/src/components/deck-layers/tile-layer-2d.js b/src/components/deck-layers/tile-layer-2d.js index 2b33f0f..a337310 100644 --- a/src/components/deck-layers/tile-layer-2d.js +++ b/src/components/deck-layers/tile-layer-2d.js @@ -12,6 +12,8 @@ export function TileLayer2d(props) { landsatBands, landsatBandCombination, landsatColormapName, + onViewportLoad, + onTileLoad, } = props || {}; return new TileLayer({ @@ -32,6 +34,8 @@ export function TileLayer2d(props) { ...landsatBands, ], }, + onViewportLoad, + onTileLoad, }); } @@ -49,7 +53,7 @@ function renderSubLayers(props) { return null; } - const { modules, images, ...moduleProps } = data; + const { modules, images, landsatAssetIds, ...moduleProps } = data; return new RasterLayer(props, { modules, diff --git a/src/components/options/index.js b/src/components/options/index.js index 96cbbaa..de5301c 100644 --- a/src/components/options/index.js +++ b/src/components/options/index.js @@ -27,6 +27,8 @@ export default class Options extends React.Component { render() { const { activeIndex } = this.state; const { + _landsatMaxAssetDate, + _landsatMinAssetDate, landsatMosaicId, naipMosaicId, useNaip, @@ -58,7 +60,7 @@ export default class Options extends React.Component { >
Landsat8.earth
Map Options
- + {map3d && } @@ -80,7 +82,6 @@ export default class Options extends React.Component { onChange={onChange} /> - + {_landsatMinAssetDate && _landsatMaxAssetDate && ( +

+ Date range:{' '} + {_landsatMinAssetDate.format('MMMM D, YYYY')} to{' '} + {_landsatMaxAssetDate.format('MMMM D, YYYY')} +

+ )} ); diff --git a/src/components/util/image.js b/src/components/util/image.js index 1030317..078e089 100644 --- a/src/components/util/image.js +++ b/src/components/util/image.js @@ -92,7 +92,7 @@ async function loadLandsatImages(options) { ); // Note: imageBands (will be) an Array of objects, not direct textures - const imageBands = bandsUrls.map(url => loadSingleBandImage(url)); + let imageBands = bandsUrls.map(url => loadSingleBandImage(url)); // Load colormap // Only load if landsatBandCombination is not RGB @@ -106,17 +106,27 @@ async function loadLandsatImages(options) { // Await all images together await Promise.all([imagePan, imageBands, imageColormap]); + // Split each image object into its `imageData` and `assets` identifiers + // https://stackoverflow.com/a/27386370/7319250 + ({ imageData: imagePan } = (await imagePan) || {}); + ({ imageData: imageColormap } = (await imageColormap) || {}); + + const imageBandsObjects = await Promise.all(imageBands); + imageBands = imageBandsObjects.map(({ imageData }) => imageData); + const landsatAssetIds = imageBandsObjects.map(({ assets }) => assets); + // The `Promise.all(imageBands)` converts an array of Promises to an array of // objects const images = { - imageBands: await Promise.all(imageBands), - imageColormap: await imageColormap, - imagePan: await imagePan, + imageBands, + imageColormap, + imagePan, }; return { images, modules, + landsatAssetIds, }; } diff --git a/src/components/util/landsat.js b/src/components/util/landsat.js index 9e8505f..344e5f0 100644 --- a/src/components/util/landsat.js +++ b/src/components/util/landsat.js @@ -1,6 +1,38 @@ import dayjs from 'dayjs'; -// NOTE change this to switch between precollection/collection based on string length, use substrings instead of regexp +// Simpler, faster landsat asset ID parser that uses string lengths instead of a +// regex +export function simpleLandsatParser(sceneid) { + if (sceneid.length === 40) { + return simpleLandsatCollection1Parser(sceneid); + } else if (sceneid.length == 21) { + return simpleLandsatPreCollectionParser(sceneid); + } + + return null; +} + +function simpleLandsatPreCollectionParser(sceneid) { + const acquisitionYear = sceneid.substring(9, 13); + const acquisitionJulianDay = sceneid.substring(13, 16); + + // Note, this might be in local timezone + const acquisitionDate = dayjs(new Date(acquisitionYear, 0, 1)).add( + Number(acquisitionJulianDay) - 1, + 'day' + ); + return { acquisitionDate }; +} + +function simpleLandsatCollection1Parser(sceneid) { + const acquisitionYear = sceneid.substring(17, 21); + const acquisitionMonth = sceneid.substring(21, 23); + const acquisitionDay = sceneid.substring(23, 25); + const acquisitionDate = dayjs( + new Date(acquisitionYear, acquisitionMonth - 1, acquisitionDay) + ); + return { acquisitionDate }; +} /** * Parse Landsat-8 scene id diff --git a/src/components/util/webgl.js b/src/components/util/webgl.js index 76e8642..9841cf8 100644 --- a/src/components/util/webgl.js +++ b/src/components/util/webgl.js @@ -48,8 +48,7 @@ export async function loadSingleBandImage(url) { // Load colormaps as RGB; all others as LUMINANCE format: image && image.height === 10 ? GL.RGB : GL.LUMINANCE, }; - // return { imageData, assets }; - return imageData; + return { imageData, assets }; } async function loadImageUrl(url) { diff --git a/src/pages/index.js b/src/pages/index.js index 069905a..8042139 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -16,8 +16,15 @@ import { setQueryParams, getQueryParams, } from '../components/util/view-state'; +import { simpleLandsatParser } from '../components/util/landsat'; + +import dayjs from 'dayjs'; +import minMax from 'dayjs/plugin/minMax'; + import '../css/semantic-ui.css'; +dayjs.extend(minMax); + const initialViewState = { longitude: -112.1861, latitude: 36.1284, @@ -43,6 +50,8 @@ class IndexPage extends React.Component { landsatBands: [4, 3, 2], landsatColormapName: 'cfastie', landsatBandCombination: 'rgb', + _landsatMinAssetDate: null, + _landsatMaxAssetDate: null, // NAIP options // Show NAIP imagery at zoom >= 12 @@ -68,8 +77,79 @@ class IndexPage extends React.Component { setHashFromViewState(viewState); }; + onTileLoad = tile => { + if (!tile) { + return; + } + + const { landsatAssetIds } = tile.content; + + if (!landsatAssetIds || landsatAssetIds.length === 0) { + return; + } + + const uniqueIds = [...new Set(landsatAssetIds.flat())]; + const parsedIds = uniqueIds.map(sceneid => simpleLandsatParser(sceneid)); + + // console.log(parsedIds); + + const minDate = dayjs.min( + ...parsedIds.map(({ acquisitionDate }) => acquisitionDate) + ); + const maxDate = dayjs.max( + ...parsedIds.map(({ acquisitionDate }) => acquisitionDate) + ); + + // this.setState({ + // _landsatMinAssetDate: dayjs.min(minDate, this.state._landsatMinAssetDate), + // _landsatMaxAssetDate: dayjs.max(maxDate, this.state._landsatMaxAssetDate), + // }); + + this.setState(prevState => { + const { _landsatMinAssetDate, _landsatMaxAssetDate } = prevState; + if (!_landsatMinAssetDate || !_landsatMaxAssetDate) { + return; + } + return { + _landsatMinAssetDate: dayjs.min( + minDate, + prevState._landsatMinAssetDate + ), + _landsatMaxAssetDate: dayjs.max( + maxDate, + prevState._landsatMaxAssetDate + ), + }; + }); + }; + + onViewportLoad = tileData => { + if (!tileData) { + return; + } + + const landsatAssetIds = tileData + .map(({ landsatAssetIds }) => landsatAssetIds) + .flat(2); + const uniqueIds = [...new Set(landsatAssetIds)]; + const parsedIds = uniqueIds.map(sceneid => simpleLandsatParser(sceneid)); + + const minDate = dayjs.min( + ...parsedIds.map(({ acquisitionDate }) => acquisitionDate) + ); + const maxDate = dayjs.max( + ...parsedIds.map(({ acquisitionDate }) => acquisitionDate) + ); + this.setState({ + _landsatMinAssetDate: minDate, + _landsatMaxAssetDate: maxDate, + }); + }; + render() { const { + _landsatMaxAssetDate, + _landsatMinAssetDate, landsatBandCombination, landsatBandPreset, landsatBands, @@ -85,43 +165,48 @@ class IndexPage extends React.Component {
{map3d ? ( ) : ( )} { this.setState(value); setQueryParams(value); }} - map3d={map3d} />
);