From 2c70a111591200dbd48fd3862ea8299ee180909c Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sat, 6 May 2023 10:26:17 -0700 Subject: [PATCH 01/29] Adding zarr layers --- .gitignore | 4 +- .../components/common/mapbox/layers/utils.ts | 4 +- .../common/mapbox/layers/zarr-timeseries.tsx | 351 ++++++++++++++++++ .../common/mapbox/map-options/basemaps.ts | 4 +- app/scripts/context/layer-data.tsx | 32 +- app/scripts/context/power-stac.js | 115 ++++++ mock/datasets/power.data.mdx | 128 +++++++ parcel-resolver-veda/index.d.ts | 3 +- 8 files changed, 628 insertions(+), 13 deletions(-) create mode 100644 app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx create mode 100644 app/scripts/context/power-stac.js create mode 100644 mock/datasets/power.data.mdx diff --git a/.gitignore b/.gitignore index 12fd79316..78eb27585 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,6 @@ nbproject temp tmp .tmp -dist \ No newline at end of file +distparcel-resolver-thematics/veda-thematic.out.js +parcel-resolver-thematics/veda-thematic.out.js +dist/ diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index 0e171f7ac..8db83034a 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -21,6 +21,7 @@ import { } from 'veda'; import { MapLayerRasterTimeseries, StacFeature } from './raster-timeseries'; import { MapLayerVectorTimeseries } from './vector-timeseries'; +import { MapLayerZarrTimeseries } from './zarr-timeseries'; import { userTzDate2utcString, utcString2userTzDate } from '$utils/date'; import { AsyncDatasetLayer } from '$context/layer-data'; @@ -29,11 +30,12 @@ import { HintedError } from '$utils/hinted-error'; export const getLayerComponent = ( isTimeseries: boolean, - layerType: 'raster' | 'vector' + layerType: 'raster' | 'vector' | 'zarr' ): FunctionComponent | null => { if (isTimeseries) { if (layerType === 'raster') return MapLayerRasterTimeseries; if (layerType === 'vector') return MapLayerVectorTimeseries; + if (layerType === 'zarr') return MapLayerZarrTimeseries; } return null; diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx new file mode 100644 index 000000000..14c2d88ff --- /dev/null +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -0,0 +1,351 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import qs from 'qs'; +import { + Map as MapboxMap, + AnyLayer, + AnySourceImpl, + GeoJSONSourceRaw, + LngLatBoundsLike, + RasterLayer, + RasterSource, + SymbolLayer +} from 'mapbox-gl'; +import { useTheme } from 'styled-components'; +import { featureCollection, point } from '@turf/helpers'; + +import { useMapStyle } from './styles'; +import { + checkFitBoundsFromLayer, + getFilterPayload, + getMergedBBox, + requestQuickCache, + useLayerInteraction +} from './utils'; +import { useCustomMarker } from './custom-marker'; + +import { + ActionStatus, + S_FAILED, + S_IDLE, + S_LOADING, + S_SUCCEEDED +} from '$utils/status'; + +// Whether or not to print the request logs. +const LOG = true; + +const FIT_BOUNDS_PADDING = 32; + +interface MapLayerZarrTimeseriesProps { + id: string; + stacCol: string; + date?: Date; + mapInstance: MapboxMap; + sourceParams: object; + zoomExtent?: [number, number]; + assetUrl?: string; + onStatusChange?: (result: { status: ActionStatus; id: string }) => void; + isHidden: boolean; +} + +export interface StacFeature { + bbox: [number, number, number, number]; +} + +enum STATUS_KEY { + Global, + Layer, + StacSearch +} + +interface Statuses { + [STATUS_KEY.Global]: ActionStatus; + [STATUS_KEY.Layer]: ActionStatus; + [STATUS_KEY.StacSearch]: ActionStatus; +} + +export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { + const { + id, + stacCol, + date, + mapInstance, + sourceParams, + zoomExtent, + assetUrl, + onStatusChange, + isHidden + } = props; + + const theme = useTheme(); + const { updateStyle } = useMapStyle(); + + const minZoom = zoomExtent?.[0] ?? 0; + + // Status tracking. + // A raster timeseries layer has a base layer and may have markers. + // The status is succeeded only if all requests succeed. + const statuses = useRef({ + [STATUS_KEY.Global]: S_IDLE, + [STATUS_KEY.Layer]: S_IDLE, + [STATUS_KEY.StacSearch]: S_IDLE + }); + + const changeStatus = useCallback( + ({ + status, + context + }: { + status: ActionStatus; + context: STATUS_KEY.StacSearch | STATUS_KEY.Layer; + }) => { + // Set the new status + statuses.current[context] = status; + + const layersToCheck = [ + statuses.current[STATUS_KEY.Layer] + ]; + + let newStatus = statuses.current[STATUS_KEY.Global]; + // All must succeed to be considered successful. + if (layersToCheck.every((s) => s === S_SUCCEEDED)) { + newStatus = S_SUCCEEDED; + + // One failed status is enough for all. + // Failed takes priority over loading. + } else if (layersToCheck.some((s) => s === S_FAILED)) { + newStatus = S_FAILED; + // One loading status is enough for all. + } else if (layersToCheck.some((s) => s === S_LOADING)) { + newStatus = S_LOADING; + } else if (layersToCheck.some((s) => s === S_IDLE)) { + newStatus = S_IDLE; + } + + // Only emit on status change. + if (newStatus !== statuses.current[STATUS_KEY.Global]) { + statuses.current[STATUS_KEY.Global] = newStatus; + onStatusChange?.({ status: newStatus, id }); + } + }, + [id, onStatusChange] + ); + + + // + // Markers + // + // TODO(aimee): Pull these from Zarr metadata + const points = useMemo(() => { + const [w, s, e, n] = [-180, -90, 180, 90]; + return [{ + bounds: [ + [w, s], + [e, n] + ] as LngLatBoundsLike, + center: [(w + e) / 2, (s + n) / 2] as [number, number] + }]; + }, []); + + // + // Tiles + // + let [tilesUrl, setTilesUrl] = useState(null); + useEffect(() => { + if (!id || !sourceParams['variable'] || !date) return; + + const controller = new AbortController(); + + const load = async () => { + changeStatus({ status: S_LOADING, context: STATUS_KEY.Layer }); + try { + const payload = {}; + + /* eslint-disable no-console */ + LOG && + console.groupCollapsed( + 'MapLayerZarrTimeseries %cLoading Zarr', + 'color: orange;', + id + ); + LOG && console.log('Payload', payload); + LOG && console.groupEnd(); + /* eslint-enable no-console */ + + // TODO(aimee: fetch CATALOG) + + setTilesUrl('https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json'); + + /* eslint-disable no-console */ + LOG && + console.groupCollapsed( + 'MapLayerRasterTimeseries %cAdding Mosaic', + 'color: green;', + id + ); + // links[0] : metadata , links[1]: tile + // LOG && console.log('Url', responseData.links[1].href); + // LOG && console.log('STAC response', responseData); + LOG && console.groupEnd(); + /* eslint-enable no-console */ + changeStatus({ status: S_SUCCEEDED, context: STATUS_KEY.Layer }); + } catch (error) { + if (!controller.signal.aborted) { + changeStatus({ status: S_FAILED, context: STATUS_KEY.Layer }); + } + LOG && + /* eslint-disable-next-line no-console */ + console.log( + 'MapLayerRasterTimeseries %cAborted Mosaic', + 'color: red;', + id + ); + return; + } + }; + + load(); + + return () => { + controller.abort(); + changeStatus({ status: 'idle', context: STATUS_KEY.Layer }); + }; + }, []); + + const markerLayout = useCustomMarker(mapInstance); + + // + // Generate Mapbox GL layers and sources for raster timeseries + // + const haveSourceParamsChanged = useMemo( + () => JSON.stringify(sourceParams), + [sourceParams] + ); + useEffect( + () => { + let layers: AnyLayer[] = []; + let sources: Record = {}; + + if (tilesUrl) { + let tileParams = qs.stringify( + { + url: assetUrl, + ...sourceParams + } + ); + + const zarrSource: RasterSource = { + type: 'raster', + url: `${tilesUrl}?${tileParams}` + }; + + const zarrLayer: RasterLayer = { + id: id, + type: 'raster', + source: id, + layout: { + visibility: isHidden ? 'none' : 'visible' + }, + paint: { + 'raster-opacity': Number(!isHidden), + 'raster-opacity-transition': { + duration: 320 + } + }, + minzoom: minZoom, + metadata: { + layerOrderPosition: 'raster' + } + }; + + sources = { + ...sources, + [id]: zarrSource + }; + layers = [...layers, zarrLayer]; + } + + if (points && minZoom > 0) { + const pointsSourceId = `${id}-points`; + const pointsSource: GeoJSONSourceRaw = { + type: 'geojson', + data: featureCollection( + points.map((p) => point(p.center, { bounds: p.bounds })) + ) + }; + + const pointsLayer: SymbolLayer = { + type: 'symbol', + id: pointsSourceId, + source: pointsSourceId, + layout: { + ...(markerLayout as any), + visibility: isHidden ? 'none' : 'visible', + 'icon-allow-overlap': true + }, + paint: { + 'icon-color': theme.color?.primary, + 'icon-halo-color': theme.color?.base, + 'icon-halo-width': 1 + }, + maxzoom: minZoom, + metadata: { + layerOrderPosition: 'markers' + } + }; + sources = { + ...sources, + [pointsSourceId]: pointsSource as AnySourceImpl + }; + layers = [...layers, pointsLayer]; + } + + updateStyle({ + generatorId: 'raster-timeseries', + sources, + layers + }); + }, + // sourceParams not included, but using a stringified version of it to detect changes (haveSourceParamsChanged) + [ + updateStyle, + id, + tilesUrl, + minZoom, + points, + haveSourceParamsChanged, + isHidden + ] + ); + + // + // Cleanup layers on unmount. + // + useEffect(() => { + return () => { + updateStyle({ + generatorId: 'raster-timeseries', + sources: {}, + layers: [] + }); + }; + }, [updateStyle]); + + // + // Listen to mouse events on the markers layer + // + const onPointsClick = useCallback( + (features) => { + const bounds = JSON.parse(features[0].properties.bounds); + mapInstance.fitBounds(bounds, { padding: FIT_BOUNDS_PADDING }); + }, + [mapInstance] + ); + useLayerInteraction({ + layerId: `${id}-points`, + mapInstance, + onClick: onPointsClick + }); + + return null; +} diff --git a/app/scripts/components/common/mapbox/map-options/basemaps.ts b/app/scripts/components/common/mapbox/map-options/basemaps.ts index bcd94603b..9b207daa4 100644 --- a/app/scripts/components/common/mapbox/map-options/basemaps.ts +++ b/app/scripts/components/common/mapbox/map-options/basemaps.ts @@ -15,8 +15,8 @@ export const BASEMAP_STYLES = [ { id: 'satellite', label: 'Satellite', - mapboxId: 'cldu1cb8f00ds01p6gi583w1m', - thumbnailUrl: `https://api.mapbox.com/styles/v1/covid-nasa/cldac5c2c003k01oebmavw4q3/static/-9.14,38.7,10.5,0/480x320?access_token=${process.env.MAPBOX_TOKEN}` + mapboxId: 'ckb01h6f10bn81iqg98ne0i2y', + thumbnailUrl: `https://api.mapbox.com/styles/v1/covid-nasa/ckb01h6f10bn81iqg98ne0i2y/static/-9.14,38.7,10.5,0/480x320?access_token=${process.env.MAPBOX_TOKEN}` }, { id: 'dark', diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index f27475c4d..a27c5bfeb 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -14,6 +14,7 @@ import { DatasetLayerCompareNormalized, datasets } from 'veda'; +import staticStac from '$context/power-stac'; import { getCompareLayerData } from '$components/common/mapbox/layers/utils'; import { S_SUCCEEDED } from '$utils/status'; @@ -21,6 +22,7 @@ import { S_SUCCEEDED } from '$utils/status'; export type TimeDensity = 'day' | 'month' | 'year' | null; interface STACLayerData { + assetUrl?: string[]; timeseries: { isPeriodic: boolean; timeDensity: TimeDensity; @@ -33,10 +35,17 @@ const fetchLayerById = async ( ): Promise => { const { type, stacCol } = layer; - const { data } = await axios.get( - `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}` - ); + let data; + if (type === 'zarr') { + data = staticStac; + } else { + data = await axios.get(`${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) + }; + const commonTimeseriesParams = { + isPeriodic: data['dashboard:is_periodic'], + timeDensity: data['dashboard:time_density'] + } // TODO: Normalize API data structure // For the time being the vector and raster sources have different api // endpoints, and different properties to get data from. @@ -46,20 +55,27 @@ const fetchLayerById = async ( return { timeseries: { - isPeriodic: data['dashboard:is_periodic'], - timeDensity: data['dashboard:time_density'], + ...commonTimeseriesParams, domain: featuresApiData.extent.temporal.interval[0] } }; } - return { + const defaultData = { timeseries: { - isPeriodic: data['dashboard:is_periodic'], - timeDensity: data['dashboard:time_density'], + ...commonTimeseriesParams, domain: data.summaries.datetime } }; + + if (type === 'zarr') { + return { + ...defaultData, + assetUrl: data.assets.zarr.href + }; + } + + return defaultData; }; // Create a query object for react query. diff --git a/app/scripts/context/power-stac.js b/app/scripts/context/power-stac.js new file mode 100644 index 000000000..8b124d30e --- /dev/null +++ b/app/scripts/context/power-stac.js @@ -0,0 +1,115 @@ +module.exports = { + "id": "power-meteorology", + "type": "Collection", + "links": [ + ], + "title": "POWER Monthly Meteorological Dataset", + "assets": { + "zarr": { + "href": "s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr", + "type": "application/vnd+zarr", + "roles": [ + "data", + "zarr" + ], + "title": "power-meteorology Zarr root", + "description": "", + "xarray:open_kwargs": { + "chunks": {}, + "engine": "zarr", + "consolidated": true + } + } + }, + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -90, + 180, + 90 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ] + ] + } + }, + "license": "CC0-1.0", + "description": "", + "stac_version": "1.0.0", + "cube:variables": { + "TS_MAX_AVG": { + "type": "data", + "unit": "temperature", + "attrs": { + "units": "temperature", + "long_name": "ADD ME" + }, + "shape": [ + 492, + 361, + 576 + ], + "chunks": [ + 492, + 25, + 25 + ], + "dimensions": [ + "time", + "lat", + "lon" + ], + "description": "The average temperature at the earth's surface." + } + }, + "cube:dimensions": { + "lat": { + "axis": "y", + "type": "spatial", + "extent": [ + -90, + 90 + ], + "description": "latitude", + "reference_system": 4326 + }, + "lon": { + "axis": "x", + "type": "spatial", + "extent": [ + -180, + 179.4 + ], + "description": "longitude", + "reference_system": 4326 + }, + "time": { + "step": "P1DT0H0M0S", + "type": "temporal", + "extent": [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ], + "description": "time" + } + }, + "stac_extensions": [ + "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + ], + "dashboard:is_periodic": true, + "dashboard:time_density": "month", + "summaries": { + "datetime": [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ] + } +} diff --git a/mock/datasets/power.data.mdx b/mock/datasets/power.data.mdx new file mode 100644 index 000000000..142c80cf4 --- /dev/null +++ b/mock/datasets/power.data.mdx @@ -0,0 +1,128 @@ +--- +id: power-meteorology +name: 'POWER Meteorological Dataset' +featured: true +description: "Prediction of Worldwide Energy Resources (POWER)" +media: + src: ::file ./no2--dataset-cover.jpg + alt: Power plant shooting steam at the sky. + author: + name: Mick Truyts + url: https://unsplash.com/photos/x6WQeNYJC1w +thematics: + - air +layers: + - id: power-meteorology-temperature + stacCol: power-meteorology + name: Earth Skin Temperature + type: zarr + description: The average temperature at the earth's surface. + zoomExtent: + - 0 + - 20 + sourceParams: + url: s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr + resampling_method: bilinear + variable: TS + colormap_name: gnbu + rescale: 250,300 + legend: + unit: + label: + type: gradient + min: "Less" + max: "More" + stops: + - "#99c5e0" + - "#f9eaa9" + - "#f7765d" + - "#c13b72" + - "#461070" + - "#050308" +--- + + +
+ + + NO2 levels fell by as much as 30% over much of the Northeast U.S. + +
+ +Nitrogen dioxide (NO2) is a common air pollutant primarily emitted from the burning of fossil fuels in cars and power plants. Lower to the ground, nitrogen dioxide can directly irritate the lungs and contributes to the production of particulate pollution and smog when it reacts with sunlight. + +During the COVID-19 pandemic, scientists have observed considerable decreases in nitrogen dioxide levels around the world. These decreases are predominantly associated with changing human behavior in response to the spread of COVID-19. As communities worldwide have implemented lockdown restrictions in an attempt to stem the spread of the virus, the reduction in human transportation activity has resulted in less NO2 being emitted into the atmosphere. + +These changes are particularly apparent over large urban areas and economic corridors, which typically have high levels of automobile traffic, airline flights, and other related activity. + +NASA has observed subsequent rebounds in nitrogen dioxide levels as the lockdown restrictions ease. + +
+ + +## Scientific research +[Ongoing research](https://airquality.gsfc.nasa.gov/) by scientists in the Atmospheric Chemistry and Dynamics Laboratory at NASA’s Goddard Space Flight Center and [new research](https://science.nasa.gov/earth-science/rrnes-awards) funded by NASA's Rapid Response and Novel research in the Earth Sciences (RRNES) program element seek to better understand the atmospheric effects of the COVID-19 shutdowns. + +For nitrogen dioxide levels related to COVID-19, NASA uses data collected by the joint NASA-Royal Netherlands Meteorological Institute (KNMI) [Ozone Monitoring Instrument (OMI)](https://aura.gsfc.nasa.gov/omi.html) aboard the Aura satellite, as well as data collected by the Tropospheric Monitoring Instrument (TROPOMI) aboard the European Commission’s Copernicus Sentinel-5P satellite, built by the European Space Agency. + +OMI, which launched in 2004, preceded TROPOMI, which launched in 2017. While TROPOMI provides higher resolution information, the longer OMI data record provides context for the TROPOMI observations. + +Scientists will use these data to investigate how travel bans and lockdown orders related to the novel coronavirus are impacting regional air quality and chemistry, as well as why these restrictions may be having inconsistent effects on air quality around the world. + + + + +
+ NO2 levels over South America from the Ozone Monitoring Instrument + + NO2 levels over South America from the Ozone Monitoring Instrument. The dark green areas in the northwest indicate areas of no data, most likely associated with cloud cover or snow. + +
+ +## Interpreting the data +Nitrogen dioxide has a relatively short lifespan in the atmosphere. Once it is emitted, it lasts only a few hours before it dissipates, so it does not travel far from its source. + +Because nitrogen dioxide is primarily emitted from burning fossil fuels, changes in its atmospheric concentration can be related to changes in human activity, if the data are properly processed and interpreted. + +Interpreting satellite NO2 data must be done carefully, as the quantity observed by satellite is not exactly the same as the abundance at ground level, and natural variations in weather (e.g., temperature, wind speed, solar intensity) influence the amount of NO2 in the atmosphere. In addition, the OMI and TROPOMI instruments cannot observe the NO2 abundance underneath clouds. For more information on processing and cautions related to interpreting this data, please click [here](https://airquality.gsfc.nasa.gov/caution-interpretation). + +
+ + + +## Additional resources +### NASA Features +* [Airborne Nitrogen Dioxide Plummets Over China](https://earthobservatory.nasa.gov/images/146362/airborne-nitrogen-dioxide-plummets-over-china) +* [Airborne Nitrogen Dioxide Decreases Over Italy](https://earthobservatory.nasa.gov/blogs/earthmatters/2020/03/13/airborne-nitrogen-dioxide-decreases-over-italy/) +* [NASA Satellite Data Show 30 Percent Drop In Air Pollution Over Northeast U.S.](https://www.nasa.gov/feature/goddard/2020/drop-in-air-pollution-over-northeast) +* [Airborne Particle Levels Plummet in Northern India](https://earthobservatory.nasa.gov/images/146596/airborne-particle-levels-plummet-in-northern-india) +* [NASA Satellite Data Show Air Pollution Decreases over Southwest U.S. Cities](https://www.nasa.gov/feature/goddard/2020/nasa-satellite-data-show-air-pollution-decreases-over-southwest-us-cities) +* [Nitrogen Dioxide Levels Rebound in China](https://earthobservatory.nasa.gov/images/146741/nitrogen-dioxide-levels-rebound-in-china?utm_source=card_2&utm_campaign=home) + +### Explore the Data +* [How to Find and Visualize Nitrogen Dioxide Satellite Data](https://earthdata.nasa.gov/learn/articles/feature-articles/health-and-air-quality-articles/find-no2-data) +* [COVID-19 Data Pathfinder](https://earthdata.nasa.gov/learn/pathfinders/covid-19) +* [Reductions in Nitrogen Dioxide Associated with Decreased Fossil Fuel Use Resulting from COVID-19 Mitigation](https://svs.gsfc.nasa.gov/4810) + +### Explore the Missions +* [Ozone Monitoring Instrument (OMI)](https://aura.gsfc.nasa.gov/omi.html) +* [Tropospheric Emissions: Monitoring of Pollution (TEMPO)](http://tempo.si.edu/outreach.html) +* [Pandora Project](https://pandora.gsfc.nasa.gov/) + + diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index 87a74fa02..f19a88141 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -6,7 +6,7 @@ declare module 'veda' { // /////////////////////////////////////////////////////////////////////////// // Datasets // // /////////////////////////////////////////////////////////////////////////// - type DatasetLayerType = 'raster' | 'vector'; + type DatasetLayerType = 'raster' | 'vector' | 'zarr'; // // Dataset Layers @@ -54,6 +54,7 @@ declare module 'veda' { initialDatetime?: 'newest' | 'oldest' | string; projection?: ProjectionOptions; type: DatasetLayerType; + assetUrl?: string; compare: DatasetLayerCompareSTAC | DatasetLayerCompareInternal | null; legend?: LayerLegendCategorical | LayerLegendGradient; } From 1a7f07e039710c05fecf4cd3e856561ed9bf307c Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sat, 6 May 2023 20:15:22 -0700 Subject: [PATCH 02/29] Create stac override --- app/scripts/context/layer-data.tsx | 10 ++-------- mock/datasets/power.data.mdx | 1 + parcel-resolver-veda/index.d.ts | 1 + 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index a27c5bfeb..177b54518 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -14,7 +14,6 @@ import { DatasetLayerCompareNormalized, datasets } from 'veda'; -import staticStac from '$context/power-stac'; import { getCompareLayerData } from '$components/common/mapbox/layers/utils'; import { S_SUCCEEDED } from '$utils/status'; @@ -33,14 +32,9 @@ interface STACLayerData { const fetchLayerById = async ( layer: DatasetLayer | DatasetLayerCompareNormalized ): Promise => { - const { type, stacCol } = layer; + const { type, stacCol, stacApiOverride } = layer; - let data; - if (type === 'zarr') { - data = staticStac; - } else { - data = await axios.get(`${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) - }; + const data = await axios.get(stacApiOverride || `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) const commonTimeseriesParams = { isPeriodic: data['dashboard:is_periodic'], diff --git a/mock/datasets/power.data.mdx b/mock/datasets/power.data.mdx index 142c80cf4..d1a9382c4 100644 --- a/mock/datasets/power.data.mdx +++ b/mock/datasets/power.data.mdx @@ -13,6 +13,7 @@ thematics: - air layers: - id: power-meteorology-temperature + stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/datasets/power-stac.json stacCol: power-meteorology name: Earth Skin Temperature type: zarr diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index f19a88141..627f3bf17 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -55,6 +55,7 @@ declare module 'veda' { projection?: ProjectionOptions; type: DatasetLayerType; assetUrl?: string; + stacApiOverride?: string; compare: DatasetLayerCompareSTAC | DatasetLayerCompareInternal | null; legend?: LayerLegendCategorical | LayerLegendGradient; } From dc8157f03367e4a649d59ed9ef7305101fe3709c Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sat, 6 May 2023 20:15:54 -0700 Subject: [PATCH 03/29] Add static stac file --- data/power-stac.json | 115 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 data/power-stac.json diff --git a/data/power-stac.json b/data/power-stac.json new file mode 100644 index 000000000..fe2b6b665 --- /dev/null +++ b/data/power-stac.json @@ -0,0 +1,115 @@ +{ + "id": "power-meteorology", + "type": "Collection", + "links": [ + ], + "title": "POWER Monthly Meteorological Dataset", + "assets": { + "zarr": { + "href": "s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr", + "type": "application/vnd+zarr", + "roles": [ + "data", + "zarr" + ], + "title": "power-meteorology Zarr root", + "description": "", + "xarray:open_kwargs": { + "chunks": {}, + "engine": "zarr", + "consolidated": true + } + } + }, + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -90, + 180, + 90 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ] + ] + } + }, + "license": "CC0-1.0", + "description": "", + "stac_version": "1.0.0", + "cube:variables": { + "TS_MAX_AVG": { + "type": "data", + "unit": "temperature", + "attrs": { + "units": "temperature", + "long_name": "ADD ME" + }, + "shape": [ + 492, + 361, + 576 + ], + "chunks": [ + 492, + 25, + 25 + ], + "dimensions": [ + "time", + "lat", + "lon" + ], + "description": "The average temperature at the earth's surface." + } + }, + "cube:dimensions": { + "lat": { + "axis": "y", + "type": "spatial", + "extent": [ + -90, + 90 + ], + "description": "latitude", + "reference_system": 4326 + }, + "lon": { + "axis": "x", + "type": "spatial", + "extent": [ + -180, + 179.4 + ], + "description": "longitude", + "reference_system": 4326 + }, + "time": { + "step": "P1DT0H0M0S", + "type": "temporal", + "extent": [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ], + "description": "time" + } + }, + "stac_extensions": [ + "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + ], + "dashboard:is_periodic": true, + "dashboard:time_density": "month", + "summaries": { + "datetime": [ + "1981-01-31T12:00:00Z", + "2021-12-31T12:00:00Z" + ] + } +} From b7948227db19eeae929799feff7d3d2cdfd39fc4 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sat, 6 May 2023 20:19:23 -0700 Subject: [PATCH 04/29] Move static STAC --- {data => mock/datasets}/power-stac.json | 0 mock/datasets/power.data.mdx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {data => mock/datasets}/power-stac.json (100%) diff --git a/data/power-stac.json b/mock/datasets/power-stac.json similarity index 100% rename from data/power-stac.json rename to mock/datasets/power-stac.json diff --git a/mock/datasets/power.data.mdx b/mock/datasets/power.data.mdx index d1a9382c4..fe4f6968f 100644 --- a/mock/datasets/power.data.mdx +++ b/mock/datasets/power.data.mdx @@ -13,7 +13,7 @@ thematics: - air layers: - id: power-meteorology-temperature - stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/datasets/power-stac.json + stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/power-stac.json stacCol: power-meteorology name: Earth Skin Temperature type: zarr From f3f686d8beb9b5fd705e465f0942b0b3a2ed56b8 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sat, 6 May 2023 21:00:26 -0700 Subject: [PATCH 05/29] Remove useEffect for tilesUrl and fix data loading --- .../common/mapbox/layers/zarr-timeseries.tsx | 64 +------------------ app/scripts/context/layer-data.tsx | 2 +- 2 files changed, 3 insertions(+), 63 deletions(-) diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 14c2d88ff..35f33b1cb 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -150,68 +150,7 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { // // Tiles // - let [tilesUrl, setTilesUrl] = useState(null); - useEffect(() => { - if (!id || !sourceParams['variable'] || !date) return; - - const controller = new AbortController(); - - const load = async () => { - changeStatus({ status: S_LOADING, context: STATUS_KEY.Layer }); - try { - const payload = {}; - - /* eslint-disable no-console */ - LOG && - console.groupCollapsed( - 'MapLayerZarrTimeseries %cLoading Zarr', - 'color: orange;', - id - ); - LOG && console.log('Payload', payload); - LOG && console.groupEnd(); - /* eslint-enable no-console */ - - // TODO(aimee: fetch CATALOG) - - setTilesUrl('https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json'); - - /* eslint-disable no-console */ - LOG && - console.groupCollapsed( - 'MapLayerRasterTimeseries %cAdding Mosaic', - 'color: green;', - id - ); - // links[0] : metadata , links[1]: tile - // LOG && console.log('Url', responseData.links[1].href); - // LOG && console.log('STAC response', responseData); - LOG && console.groupEnd(); - /* eslint-enable no-console */ - changeStatus({ status: S_SUCCEEDED, context: STATUS_KEY.Layer }); - } catch (error) { - if (!controller.signal.aborted) { - changeStatus({ status: S_FAILED, context: STATUS_KEY.Layer }); - } - LOG && - /* eslint-disable-next-line no-console */ - console.log( - 'MapLayerRasterTimeseries %cAborted Mosaic', - 'color: red;', - id - ); - return; - } - }; - - load(); - - return () => { - controller.abort(); - changeStatus({ status: 'idle', context: STATUS_KEY.Layer }); - }; - }, []); - + const tilesUrl = 'http://localhost:8002/tilejson.json'; const markerLayout = useCustomMarker(mapInstance); // @@ -230,6 +169,7 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { let tileParams = qs.stringify( { url: assetUrl, + time_slice: date, ...sourceParams } ); diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index 177b54518..5cfeb772d 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -34,7 +34,7 @@ const fetchLayerById = async ( ): Promise => { const { type, stacCol, stacApiOverride } = layer; - const data = await axios.get(stacApiOverride || `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) + const { data } = await axios.get(stacApiOverride || `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) const commonTimeseriesParams = { isPeriodic: data['dashboard:is_periodic'], From 32e3249e90212d3fcbc4487c26c1c7000f2ee6ad Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sun, 7 May 2023 18:03:38 -0700 Subject: [PATCH 06/29] Fix gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 78eb27585..a045abd00 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,6 @@ nbproject temp tmp .tmp -distparcel-resolver-thematics/veda-thematic.out.js +dist parcel-resolver-thematics/veda-thematic.out.js dist/ From c969de25f352386a884e41ef304a2e06c5f4c97a Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Sun, 7 May 2023 18:44:45 -0700 Subject: [PATCH 07/29] haveSourceParamsChanged should depend on date --- .gitignore | 2 +- app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a045abd00..cd3245a71 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,4 @@ tmp .tmp dist parcel-resolver-thematics/veda-thematic.out.js -dist/ + diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 35f33b1cb..8f1c5551c 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -250,6 +250,7 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { [ updateStyle, id, + date, tilesUrl, minZoom, points, From 3d018631caf5168fd450288a4f6e3c51424ce10e Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 9 May 2023 08:09:37 -0700 Subject: [PATCH 08/29] Add cmip6 zarr and modify color maps --- mock/datasets/cmip6-tas-stac.json | 114 ++++++++++++++++++++++++++++++ mock/datasets/cmip6-tas.data.mdx | 44 ++++++++++++ mock/datasets/power-stac.json | 1 - mock/datasets/power.data.mdx | 105 +++------------------------ 4 files changed, 168 insertions(+), 96 deletions(-) create mode 100644 mock/datasets/cmip6-tas-stac.json create mode 100644 mock/datasets/cmip6-tas.data.mdx diff --git a/mock/datasets/cmip6-tas-stac.json b/mock/datasets/cmip6-tas-stac.json new file mode 100644 index 000000000..f8a8abba6 --- /dev/null +++ b/mock/datasets/cmip6-tas-stac.json @@ -0,0 +1,114 @@ +{ + "id": "cmip6-tas", + "type": "Collection", + "links": [ + ], + "title": "CMIP6 Near-Surface Air Temperature", + "assets": { + "zarr": { + "href": "s3://cmip6-pds/CMIP6/CMIP/NASA-GISS/GISS-E2-1-G/historical/r2i1p1f1/Amon/tas/gn/v20180827/", + "type": "application/vnd+zarr", + "roles": [ + "data", + "zarr" + ], + "title": "CMIP6 Near-Surface Air Temperature Zarr root", + "description": "", + "xarray:open_kwargs": { + "chunks": {}, + "engine": "zarr", + "consolidated": true + } + } + }, + "extent": { + "spatial": { + "bbox": [ + [ + -178, + -89, + 178, + 89 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1850-01-16T12:00:00Z", + "2014-12-16T12:00:00Z" + ] + ] + } + }, + "license": "CC0-1.0", + "description": "", + "stac_version": "1.0.0", + "cube:variables": { + "tas": { + "type": "data", + "unit": "K", + "attrs": { + "units": "K", + "long_name": "kelvin" + }, + "shape": [ + 1980, + 90, + 144 + ], + "chunks": [ + 600, + 90, + 144 + ], + "dimensions": [ + "time", + "lat", + "lon" + ], + "description": "The average temperature at the earth's surface." + } + }, + "cube:dimensions": { + "lat": { + "axis": "y", + "type": "spatial", + "extent": [ + -89, + 89 + ], + "description": "latitude", + "reference_system": 4326 + }, + "lon": { + "axis": "x", + "type": "spatial", + "extent": [ + -178.75, + 178 + ], + "description": "longitude", + "reference_system": 4326 + }, + "time": { + "type": "temporal", + "extent": [ + "1850-01-16T12:00:00Z", + "2014-12-16T12:00:00Z" + ], + "description": "time" + } + }, + "stac_extensions": [ + "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + ], + "dashboard:is_periodic": true, + "dashboard:time_density": "month", + "summaries": { + "datetime": [ + "1850-01-16T12:00:00Z", + "2014-12-16T12:00:00Z" + ] + } +} diff --git a/mock/datasets/cmip6-tas.data.mdx b/mock/datasets/cmip6-tas.data.mdx new file mode 100644 index 000000000..862077eab --- /dev/null +++ b/mock/datasets/cmip6-tas.data.mdx @@ -0,0 +1,44 @@ +--- +id: cmip6-historical-tas +name: 'CMIP6 Historical Temperature' +featured: true +description: "CMIP6 Historical Near-Surface Air Temperature" +media: + src: ::file ./no2--dataset-cover.jpg + alt: Power plant shooting steam at the sky. + author: + name: Mick Truyts + url: https://unsplash.com/photos/x6WQeNYJC1w +thematics: + - air +layers: + - id: cmip6-historical-tas + stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/power-stac.json + stacCol: cmip6-historical-tas + name: CMIP6 Near-Surface Air Temperature + type: zarr + description: CMIP6 Near-Surface Air Temperature + zoomExtent: + - 0 + - 20 + sourceParams: + url: s3://cmip6-pds/CMIP6/CMIP/NASA-GISS/GISS-E2-1-G/historical/r2i1p1f1/Amon/tas/gn/v20180827/ + resampling_method: bilinear + variable: tas + colormap_name: jet + rescale: 200,300 + legend: + unit: + label: + type: gradient + min: 200 + max: 300 + stops: + - '#000080' + - '#004cff' + - '#29ffce' + - '#ceff29' + - '#ff6800' + - '#800000' +--- + diff --git a/mock/datasets/power-stac.json b/mock/datasets/power-stac.json index fe2b6b665..bc00a2669 100644 --- a/mock/datasets/power-stac.json +++ b/mock/datasets/power-stac.json @@ -92,7 +92,6 @@ "reference_system": 4326 }, "time": { - "step": "P1DT0H0M0S", "type": "temporal", "extent": [ "1981-01-31T12:00:00Z", diff --git a/mock/datasets/power.data.mdx b/mock/datasets/power.data.mdx index fe4f6968f..b8ef8feb8 100644 --- a/mock/datasets/power.data.mdx +++ b/mock/datasets/power.data.mdx @@ -25,105 +25,20 @@ layers: url: s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr resampling_method: bilinear variable: TS - colormap_name: gnbu - rescale: 250,300 + colormap_name: jet + rescale: 200,300 legend: unit: label: type: gradient - min: "Less" - max: "More" + min: 200 + max: 300 stops: - - "#99c5e0" - - "#f9eaa9" - - "#f7765d" - - "#c13b72" - - "#461070" - - "#050308" + - '#000080' + - '#004cff' + - '#29ffce' + - '#ceff29' + - '#ff6800' + - '#800000' --- - -
- - - NO2 levels fell by as much as 30% over much of the Northeast U.S. - -
- -Nitrogen dioxide (NO2) is a common air pollutant primarily emitted from the burning of fossil fuels in cars and power plants. Lower to the ground, nitrogen dioxide can directly irritate the lungs and contributes to the production of particulate pollution and smog when it reacts with sunlight. - -During the COVID-19 pandemic, scientists have observed considerable decreases in nitrogen dioxide levels around the world. These decreases are predominantly associated with changing human behavior in response to the spread of COVID-19. As communities worldwide have implemented lockdown restrictions in an attempt to stem the spread of the virus, the reduction in human transportation activity has resulted in less NO2 being emitted into the atmosphere. - -These changes are particularly apparent over large urban areas and economic corridors, which typically have high levels of automobile traffic, airline flights, and other related activity. - -NASA has observed subsequent rebounds in nitrogen dioxide levels as the lockdown restrictions ease. - -
- - -## Scientific research -[Ongoing research](https://airquality.gsfc.nasa.gov/) by scientists in the Atmospheric Chemistry and Dynamics Laboratory at NASA’s Goddard Space Flight Center and [new research](https://science.nasa.gov/earth-science/rrnes-awards) funded by NASA's Rapid Response and Novel research in the Earth Sciences (RRNES) program element seek to better understand the atmospheric effects of the COVID-19 shutdowns. - -For nitrogen dioxide levels related to COVID-19, NASA uses data collected by the joint NASA-Royal Netherlands Meteorological Institute (KNMI) [Ozone Monitoring Instrument (OMI)](https://aura.gsfc.nasa.gov/omi.html) aboard the Aura satellite, as well as data collected by the Tropospheric Monitoring Instrument (TROPOMI) aboard the European Commission’s Copernicus Sentinel-5P satellite, built by the European Space Agency. - -OMI, which launched in 2004, preceded TROPOMI, which launched in 2017. While TROPOMI provides higher resolution information, the longer OMI data record provides context for the TROPOMI observations. - -Scientists will use these data to investigate how travel bans and lockdown orders related to the novel coronavirus are impacting regional air quality and chemistry, as well as why these restrictions may be having inconsistent effects on air quality around the world. - - - - -
- NO2 levels over South America from the Ozone Monitoring Instrument - - NO2 levels over South America from the Ozone Monitoring Instrument. The dark green areas in the northwest indicate areas of no data, most likely associated with cloud cover or snow. - -
- -## Interpreting the data -Nitrogen dioxide has a relatively short lifespan in the atmosphere. Once it is emitted, it lasts only a few hours before it dissipates, so it does not travel far from its source. - -Because nitrogen dioxide is primarily emitted from burning fossil fuels, changes in its atmospheric concentration can be related to changes in human activity, if the data are properly processed and interpreted. - -Interpreting satellite NO2 data must be done carefully, as the quantity observed by satellite is not exactly the same as the abundance at ground level, and natural variations in weather (e.g., temperature, wind speed, solar intensity) influence the amount of NO2 in the atmosphere. In addition, the OMI and TROPOMI instruments cannot observe the NO2 abundance underneath clouds. For more information on processing and cautions related to interpreting this data, please click [here](https://airquality.gsfc.nasa.gov/caution-interpretation). - -
- - - -## Additional resources -### NASA Features -* [Airborne Nitrogen Dioxide Plummets Over China](https://earthobservatory.nasa.gov/images/146362/airborne-nitrogen-dioxide-plummets-over-china) -* [Airborne Nitrogen Dioxide Decreases Over Italy](https://earthobservatory.nasa.gov/blogs/earthmatters/2020/03/13/airborne-nitrogen-dioxide-decreases-over-italy/) -* [NASA Satellite Data Show 30 Percent Drop In Air Pollution Over Northeast U.S.](https://www.nasa.gov/feature/goddard/2020/drop-in-air-pollution-over-northeast) -* [Airborne Particle Levels Plummet in Northern India](https://earthobservatory.nasa.gov/images/146596/airborne-particle-levels-plummet-in-northern-india) -* [NASA Satellite Data Show Air Pollution Decreases over Southwest U.S. Cities](https://www.nasa.gov/feature/goddard/2020/nasa-satellite-data-show-air-pollution-decreases-over-southwest-us-cities) -* [Nitrogen Dioxide Levels Rebound in China](https://earthobservatory.nasa.gov/images/146741/nitrogen-dioxide-levels-rebound-in-china?utm_source=card_2&utm_campaign=home) - -### Explore the Data -* [How to Find and Visualize Nitrogen Dioxide Satellite Data](https://earthdata.nasa.gov/learn/articles/feature-articles/health-and-air-quality-articles/find-no2-data) -* [COVID-19 Data Pathfinder](https://earthdata.nasa.gov/learn/pathfinders/covid-19) -* [Reductions in Nitrogen Dioxide Associated with Decreased Fossil Fuel Use Resulting from COVID-19 Mitigation](https://svs.gsfc.nasa.gov/4810) - -### Explore the Missions -* [Ozone Monitoring Instrument (OMI)](https://aura.gsfc.nasa.gov/omi.html) -* [Tropospheric Emissions: Monitoring of Pollution (TEMPO)](http://tempo.si.edu/outreach.html) -* [Pandora Project](https://pandora.gsfc.nasa.gov/) - - From 7b24654f602b4c1ae7e71edc294c88f8fe38cf56 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 10 May 2023 18:01:09 -0700 Subject: [PATCH 09/29] Add geos FWI data --- .../common/mapbox/layers/zarr-timeseries.tsx | 3 +- mock/datasets/cmip6-tas.data.mdx | 5 +- mock/datasets/geos-fwi-hourly-stac.json | 114 ++++++++++++++++++ mock/datasets/geos-fwi-hourly.data.mdx | 44 +++++++ 4 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 mock/datasets/geos-fwi-hourly-stac.json create mode 100644 mock/datasets/geos-fwi-hourly.data.mdx diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 8f1c5551c..b3e2585d4 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -150,7 +150,8 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { // // Tiles // - const tilesUrl = 'http://localhost:8002/tilejson.json'; + //const tilesUrl = 'http://localhost:8002/tilejson.json'; + const tilesUrl = 'https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json'; const markerLayout = useCustomMarker(mapInstance); // diff --git a/mock/datasets/cmip6-tas.data.mdx b/mock/datasets/cmip6-tas.data.mdx index 862077eab..ac36d37b5 100644 --- a/mock/datasets/cmip6-tas.data.mdx +++ b/mock/datasets/cmip6-tas.data.mdx @@ -13,11 +13,12 @@ thematics: - air layers: - id: cmip6-historical-tas - stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/power-stac.json + stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/cmip6-tas-stac.json stacCol: cmip6-historical-tas name: CMIP6 Near-Surface Air Temperature type: zarr - description: CMIP6 Near-Surface Air Temperature + description: + "CMIP6 Historical (1850-2014) Near-Surface Air Temperature from NASA-GISS: Goddard Institute for Space Studies. Zarr source is provided by the Pangeo / ESGF Cloud Data Working Group and the zarr store is accessed from it's S3 bucket location: s3://cmip6-pds/CMIP6/CMIP/NASA-GISS/GISS-E2-1-G/historical/r2i1p1f1/Amon/tas/gn/v20180827/." zoomExtent: - 0 - 20 diff --git a/mock/datasets/geos-fwi-hourly-stac.json b/mock/datasets/geos-fwi-hourly-stac.json new file mode 100644 index 000000000..49753c388 --- /dev/null +++ b/mock/datasets/geos-fwi-hourly-stac.json @@ -0,0 +1,114 @@ +{ + "id": "geos-fwi-hourly", + "type": "Collection", + "links": [ + ], + "title": "GEOS Hourly Fire Weather Index", + "assets": { + "zarr": { + "href": "s3://veda-data-store-staging/EIS/zarr/FWI-GEOS-5-Hourly", + "type": "application/vnd+zarr", + "roles": [ + "data", + "zarr" + ], + "title": "geos-fwi-hourly Zarr root", + "description": "", + "xarray:open_kwargs": { + "chunks": {}, + "engine": "zarr", + "consolidated": true + } + } + }, + "extent": { + "spatial": { + "bbox": [ + [ + -180, + -90, + 180, + 90 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2020-01-01T00:00:00Z", + "2023-01-24T23:00:00Z" + ] + ] + } + }, + "license": "CC0-1.0", + "description": "", + "stac_version": "1.0.0", + "cube:variables": { + "GEOS-5_FWI": { + "type": "data", + "unit": "fire_weather_index", + "attrs": { + "units": "n/a", + "long_name": "ADD ME" + }, + "shape": [ + 26880, + 533, + 1152 + ], + "chunks": [ + 26880, + 533, + 1152 + ], + "dimensions": [ + "time", + "lat", + "lon" + ], + "description": "The average temperature at the earth's surface." + } + }, + "cube:dimensions": { + "lat": { + "axis": "y", + "type": "spatial", + "extent": [ + -58, + 75 + ], + "description": "latitude", + "reference_system": 4326 + }, + "lon": { + "axis": "x", + "type": "spatial", + "extent": [ + -180, + 179.7 + ], + "description": "longitude", + "reference_system": 4326 + }, + "time": { + "type": "temporal", + "extent": [ + "2020-01-01T00:00:00Z", + "2023-01-24T23:00:00Z" + ], + "description": "time" + } + }, + "stac_extensions": [ + "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + ], + "dashboard:is_periodic": true, + "dashboard:time_density": "hour", + "summaries": { + "datetime": [ + "2020-01-01T00:00:00Z", + "2023-01-24T23:00:00Z" + ] + } +} diff --git a/mock/datasets/geos-fwi-hourly.data.mdx b/mock/datasets/geos-fwi-hourly.data.mdx new file mode 100644 index 000000000..9e5271d91 --- /dev/null +++ b/mock/datasets/geos-fwi-hourly.data.mdx @@ -0,0 +1,44 @@ +--- +id: geos-fwi-hourly +name: 'GEOS Hourly Fire Weather Index' +featured: true +description: "GEOS Hourly Fire Weather Index" +media: + src: ::file ./no2--dataset-cover.jpg + alt: Power plant shooting steam at the sky. + author: + name: Mick Truyts + url: https://unsplash.com/photos/x6WQeNYJC1w +thematics: + - air +layers: + - id: geos-fwi-hourly + stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/geos-fwi-hourly-stac.json + stacCol: geos-fwi-hourly + name: GEOS Hourly Fire Weather Index + type: zarr + description: + "Fire risk is determined by a combination of weather, climate and fuel conditions. One metric for determining fire risk is the Fire Weather Index (FWI), an index that is used in Alaska and internationally. FWI is a combination of many sub-metrics that look at different aspects of fire risk like fuel moisture, dryness, and wind. FWI helps scientists anticipate when fire will ignight or spread rapidly." + zoomExtent: + - 0 + - 20 + sourceParams: + url: s3://veda-data-store-staging/EIS/zarr/FWI-GEOS-5-Hourly + resampling_method: bilinear + variable: GEOS-5_FWI + colormap_name: rdbu_r + rescale: 0,40 + legend: + unit: + label: + type: gradient + min: 0 + max: 40 + stops: + - "#3A88BD" + - "#C9E0ED" + - "#E4EEF3" + - "#FDDCC9" + - "#DD7059" +--- + From a2a830c94956aad445874c5fa6e3432dab31b0e7 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 10 May 2023 18:05:53 -0700 Subject: [PATCH 10/29] Use day as time_density --- mock/datasets/geos-fwi-hourly-stac.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mock/datasets/geos-fwi-hourly-stac.json b/mock/datasets/geos-fwi-hourly-stac.json index 49753c388..053c21eaf 100644 --- a/mock/datasets/geos-fwi-hourly-stac.json +++ b/mock/datasets/geos-fwi-hourly-stac.json @@ -104,7 +104,7 @@ "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" ], "dashboard:is_periodic": true, - "dashboard:time_density": "hour", + "dashboard:time_density": "day", "summaries": { "datetime": [ "2020-01-01T00:00:00Z", From 15d5abfafeb9def6d2a52a0d96d415bda55d7762 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Fri, 12 May 2023 15:11:02 -0700 Subject: [PATCH 11/29] Refactoring --- .env | 3 +- .../common/mapbox/layers/zarr-timeseries.tsx | 177 +----------------- app/scripts/context/layer-data.tsx | 4 +- mock/datasets/cmip6-tas-stac.json | 114 ----------- mock/datasets/geos-fwi-hourly-stac.json | 114 ----------- mock/datasets/geos-fwi-hourly.data.mdx | 44 ----- ...s.data.mdx => oco2-geos-l3-daily.data.mdx} | 0 mock/datasets/power-stac.json | 114 ----------- mock/datasets/power.data.mdx | 44 ----- 9 files changed, 7 insertions(+), 607 deletions(-) delete mode 100644 mock/datasets/cmip6-tas-stac.json delete mode 100644 mock/datasets/geos-fwi-hourly-stac.json delete mode 100644 mock/datasets/geos-fwi-hourly.data.mdx rename mock/datasets/{cmip6-tas.data.mdx => oco2-geos-l3-daily.data.mdx} (100%) delete mode 100644 mock/datasets/power-stac.json delete mode 100644 mock/datasets/power.data.mdx diff --git a/.env b/.env index 9b9f3f8a0..871eb4199 100644 --- a/.env +++ b/.env @@ -3,7 +3,8 @@ APP_DESCRIPTION=User interface of module VEDA APP_CONTACT_EMAIL=email@example.org API_RASTER_ENDPOINT='https://staging-raster.delta-backend.com' -API_STAC_ENDPOINT='https://staging-stac.delta-backend.com' +API_STAC_ENDPOINT='https://dev-stac.delta-backend.com' +API_XARRAY_ENDPOINT='https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json' # If the app is being served in from a subfolder, the domain url must be set. # For example, if the app is served from /mysite: diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index b3e2585d4..c79448529 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -1,40 +1,14 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo } from 'react'; import qs from 'qs'; import { Map as MapboxMap, AnyLayer, AnySourceImpl, - GeoJSONSourceRaw, - LngLatBoundsLike, RasterLayer, RasterSource, - SymbolLayer } from 'mapbox-gl'; -import { useTheme } from 'styled-components'; -import { featureCollection, point } from '@turf/helpers'; -import { useMapStyle } from './styles'; -import { - checkFitBoundsFromLayer, - getFilterPayload, - getMergedBBox, - requestQuickCache, - useLayerInteraction -} from './utils'; -import { useCustomMarker } from './custom-marker'; - -import { - ActionStatus, - S_FAILED, - S_IDLE, - S_LOADING, - S_SUCCEEDED -} from '$utils/status'; - -// Whether or not to print the request logs. -const LOG = true; - -const FIT_BOUNDS_PADDING = 32; +import { useMapStyle } from './styles';; interface MapLayerZarrTimeseriesProps { id: string; @@ -44,116 +18,23 @@ interface MapLayerZarrTimeseriesProps { sourceParams: object; zoomExtent?: [number, number]; assetUrl?: string; - onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden: boolean; } -export interface StacFeature { - bbox: [number, number, number, number]; -} - -enum STATUS_KEY { - Global, - Layer, - StacSearch -} - -interface Statuses { - [STATUS_KEY.Global]: ActionStatus; - [STATUS_KEY.Layer]: ActionStatus; - [STATUS_KEY.StacSearch]: ActionStatus; -} - export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { const { id, - stacCol, date, - mapInstance, sourceParams, zoomExtent, assetUrl, - onStatusChange, isHidden } = props; - const theme = useTheme(); const { updateStyle } = useMapStyle(); const minZoom = zoomExtent?.[0] ?? 0; - - // Status tracking. - // A raster timeseries layer has a base layer and may have markers. - // The status is succeeded only if all requests succeed. - const statuses = useRef({ - [STATUS_KEY.Global]: S_IDLE, - [STATUS_KEY.Layer]: S_IDLE, - [STATUS_KEY.StacSearch]: S_IDLE - }); - - const changeStatus = useCallback( - ({ - status, - context - }: { - status: ActionStatus; - context: STATUS_KEY.StacSearch | STATUS_KEY.Layer; - }) => { - // Set the new status - statuses.current[context] = status; - - const layersToCheck = [ - statuses.current[STATUS_KEY.Layer] - ]; - - let newStatus = statuses.current[STATUS_KEY.Global]; - // All must succeed to be considered successful. - if (layersToCheck.every((s) => s === S_SUCCEEDED)) { - newStatus = S_SUCCEEDED; - - // One failed status is enough for all. - // Failed takes priority over loading. - } else if (layersToCheck.some((s) => s === S_FAILED)) { - newStatus = S_FAILED; - // One loading status is enough for all. - } else if (layersToCheck.some((s) => s === S_LOADING)) { - newStatus = S_LOADING; - } else if (layersToCheck.some((s) => s === S_IDLE)) { - newStatus = S_IDLE; - } - - // Only emit on status change. - if (newStatus !== statuses.current[STATUS_KEY.Global]) { - statuses.current[STATUS_KEY.Global] = newStatus; - onStatusChange?.({ status: newStatus, id }); - } - }, - [id, onStatusChange] - ); - - - // - // Markers - // - // TODO(aimee): Pull these from Zarr metadata - const points = useMemo(() => { - const [w, s, e, n] = [-180, -90, 180, 90]; - return [{ - bounds: [ - [w, s], - [e, n] - ] as LngLatBoundsLike, - center: [(w + e) / 2, (s + n) / 2] as [number, number] - }]; - }, []); - - // - // Tiles - // - //const tilesUrl = 'http://localhost:8002/tilejson.json'; - const tilesUrl = 'https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json'; - const markerLayout = useCustomMarker(mapInstance); - + const tilesUrl = process.env.API_XARRAY_URL; // // Generate Mapbox GL layers and sources for raster timeseries // @@ -206,41 +87,6 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { layers = [...layers, zarrLayer]; } - if (points && minZoom > 0) { - const pointsSourceId = `${id}-points`; - const pointsSource: GeoJSONSourceRaw = { - type: 'geojson', - data: featureCollection( - points.map((p) => point(p.center, { bounds: p.bounds })) - ) - }; - - const pointsLayer: SymbolLayer = { - type: 'symbol', - id: pointsSourceId, - source: pointsSourceId, - layout: { - ...(markerLayout as any), - visibility: isHidden ? 'none' : 'visible', - 'icon-allow-overlap': true - }, - paint: { - 'icon-color': theme.color?.primary, - 'icon-halo-color': theme.color?.base, - 'icon-halo-width': 1 - }, - maxzoom: minZoom, - metadata: { - layerOrderPosition: 'markers' - } - }; - sources = { - ...sources, - [pointsSourceId]: pointsSource as AnySourceImpl - }; - layers = [...layers, pointsLayer]; - } - updateStyle({ generatorId: 'raster-timeseries', sources, @@ -254,7 +100,6 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { date, tilesUrl, minZoom, - points, haveSourceParamsChanged, isHidden ] @@ -273,21 +118,5 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { }; }, [updateStyle]); - // - // Listen to mouse events on the markers layer - // - const onPointsClick = useCallback( - (features) => { - const bounds = JSON.parse(features[0].properties.bounds); - mapInstance.fitBounds(bounds, { padding: FIT_BOUNDS_PADDING }); - }, - [mapInstance] - ); - useLayerInteraction({ - layerId: `${id}-points`, - mapInstance, - onClick: onPointsClick - }); - return null; } diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index 5cfeb772d..72c03e370 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -32,9 +32,9 @@ interface STACLayerData { const fetchLayerById = async ( layer: DatasetLayer | DatasetLayerCompareNormalized ): Promise => { - const { type, stacCol, stacApiOverride } = layer; + const { type, stacCol } = layer; - const { data } = await axios.get(stacApiOverride || `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) + const { data } = await axios.get(`${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) const commonTimeseriesParams = { isPeriodic: data['dashboard:is_periodic'], diff --git a/mock/datasets/cmip6-tas-stac.json b/mock/datasets/cmip6-tas-stac.json deleted file mode 100644 index f8a8abba6..000000000 --- a/mock/datasets/cmip6-tas-stac.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "id": "cmip6-tas", - "type": "Collection", - "links": [ - ], - "title": "CMIP6 Near-Surface Air Temperature", - "assets": { - "zarr": { - "href": "s3://cmip6-pds/CMIP6/CMIP/NASA-GISS/GISS-E2-1-G/historical/r2i1p1f1/Amon/tas/gn/v20180827/", - "type": "application/vnd+zarr", - "roles": [ - "data", - "zarr" - ], - "title": "CMIP6 Near-Surface Air Temperature Zarr root", - "description": "", - "xarray:open_kwargs": { - "chunks": {}, - "engine": "zarr", - "consolidated": true - } - } - }, - "extent": { - "spatial": { - "bbox": [ - [ - -178, - -89, - 178, - 89 - ] - ] - }, - "temporal": { - "interval": [ - [ - "1850-01-16T12:00:00Z", - "2014-12-16T12:00:00Z" - ] - ] - } - }, - "license": "CC0-1.0", - "description": "", - "stac_version": "1.0.0", - "cube:variables": { - "tas": { - "type": "data", - "unit": "K", - "attrs": { - "units": "K", - "long_name": "kelvin" - }, - "shape": [ - 1980, - 90, - 144 - ], - "chunks": [ - 600, - 90, - 144 - ], - "dimensions": [ - "time", - "lat", - "lon" - ], - "description": "The average temperature at the earth's surface." - } - }, - "cube:dimensions": { - "lat": { - "axis": "y", - "type": "spatial", - "extent": [ - -89, - 89 - ], - "description": "latitude", - "reference_system": 4326 - }, - "lon": { - "axis": "x", - "type": "spatial", - "extent": [ - -178.75, - 178 - ], - "description": "longitude", - "reference_system": 4326 - }, - "time": { - "type": "temporal", - "extent": [ - "1850-01-16T12:00:00Z", - "2014-12-16T12:00:00Z" - ], - "description": "time" - } - }, - "stac_extensions": [ - "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" - ], - "dashboard:is_periodic": true, - "dashboard:time_density": "month", - "summaries": { - "datetime": [ - "1850-01-16T12:00:00Z", - "2014-12-16T12:00:00Z" - ] - } -} diff --git a/mock/datasets/geos-fwi-hourly-stac.json b/mock/datasets/geos-fwi-hourly-stac.json deleted file mode 100644 index 053c21eaf..000000000 --- a/mock/datasets/geos-fwi-hourly-stac.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "id": "geos-fwi-hourly", - "type": "Collection", - "links": [ - ], - "title": "GEOS Hourly Fire Weather Index", - "assets": { - "zarr": { - "href": "s3://veda-data-store-staging/EIS/zarr/FWI-GEOS-5-Hourly", - "type": "application/vnd+zarr", - "roles": [ - "data", - "zarr" - ], - "title": "geos-fwi-hourly Zarr root", - "description": "", - "xarray:open_kwargs": { - "chunks": {}, - "engine": "zarr", - "consolidated": true - } - } - }, - "extent": { - "spatial": { - "bbox": [ - [ - -180, - -90, - 180, - 90 - ] - ] - }, - "temporal": { - "interval": [ - [ - "2020-01-01T00:00:00Z", - "2023-01-24T23:00:00Z" - ] - ] - } - }, - "license": "CC0-1.0", - "description": "", - "stac_version": "1.0.0", - "cube:variables": { - "GEOS-5_FWI": { - "type": "data", - "unit": "fire_weather_index", - "attrs": { - "units": "n/a", - "long_name": "ADD ME" - }, - "shape": [ - 26880, - 533, - 1152 - ], - "chunks": [ - 26880, - 533, - 1152 - ], - "dimensions": [ - "time", - "lat", - "lon" - ], - "description": "The average temperature at the earth's surface." - } - }, - "cube:dimensions": { - "lat": { - "axis": "y", - "type": "spatial", - "extent": [ - -58, - 75 - ], - "description": "latitude", - "reference_system": 4326 - }, - "lon": { - "axis": "x", - "type": "spatial", - "extent": [ - -180, - 179.7 - ], - "description": "longitude", - "reference_system": 4326 - }, - "time": { - "type": "temporal", - "extent": [ - "2020-01-01T00:00:00Z", - "2023-01-24T23:00:00Z" - ], - "description": "time" - } - }, - "stac_extensions": [ - "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" - ], - "dashboard:is_periodic": true, - "dashboard:time_density": "day", - "summaries": { - "datetime": [ - "2020-01-01T00:00:00Z", - "2023-01-24T23:00:00Z" - ] - } -} diff --git a/mock/datasets/geos-fwi-hourly.data.mdx b/mock/datasets/geos-fwi-hourly.data.mdx deleted file mode 100644 index 9e5271d91..000000000 --- a/mock/datasets/geos-fwi-hourly.data.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: geos-fwi-hourly -name: 'GEOS Hourly Fire Weather Index' -featured: true -description: "GEOS Hourly Fire Weather Index" -media: - src: ::file ./no2--dataset-cover.jpg - alt: Power plant shooting steam at the sky. - author: - name: Mick Truyts - url: https://unsplash.com/photos/x6WQeNYJC1w -thematics: - - air -layers: - - id: geos-fwi-hourly - stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/geos-fwi-hourly-stac.json - stacCol: geos-fwi-hourly - name: GEOS Hourly Fire Weather Index - type: zarr - description: - "Fire risk is determined by a combination of weather, climate and fuel conditions. One metric for determining fire risk is the Fire Weather Index (FWI), an index that is used in Alaska and internationally. FWI is a combination of many sub-metrics that look at different aspects of fire risk like fuel moisture, dryness, and wind. FWI helps scientists anticipate when fire will ignight or spread rapidly." - zoomExtent: - - 0 - - 20 - sourceParams: - url: s3://veda-data-store-staging/EIS/zarr/FWI-GEOS-5-Hourly - resampling_method: bilinear - variable: GEOS-5_FWI - colormap_name: rdbu_r - rescale: 0,40 - legend: - unit: - label: - type: gradient - min: 0 - max: 40 - stops: - - "#3A88BD" - - "#C9E0ED" - - "#E4EEF3" - - "#FDDCC9" - - "#DD7059" ---- - diff --git a/mock/datasets/cmip6-tas.data.mdx b/mock/datasets/oco2-geos-l3-daily.data.mdx similarity index 100% rename from mock/datasets/cmip6-tas.data.mdx rename to mock/datasets/oco2-geos-l3-daily.data.mdx diff --git a/mock/datasets/power-stac.json b/mock/datasets/power-stac.json deleted file mode 100644 index bc00a2669..000000000 --- a/mock/datasets/power-stac.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "id": "power-meteorology", - "type": "Collection", - "links": [ - ], - "title": "POWER Monthly Meteorological Dataset", - "assets": { - "zarr": { - "href": "s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr", - "type": "application/vnd+zarr", - "roles": [ - "data", - "zarr" - ], - "title": "power-meteorology Zarr root", - "description": "", - "xarray:open_kwargs": { - "chunks": {}, - "engine": "zarr", - "consolidated": true - } - } - }, - "extent": { - "spatial": { - "bbox": [ - [ - -180, - -90, - 180, - 90 - ] - ] - }, - "temporal": { - "interval": [ - [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ] - ] - } - }, - "license": "CC0-1.0", - "description": "", - "stac_version": "1.0.0", - "cube:variables": { - "TS_MAX_AVG": { - "type": "data", - "unit": "temperature", - "attrs": { - "units": "temperature", - "long_name": "ADD ME" - }, - "shape": [ - 492, - 361, - 576 - ], - "chunks": [ - 492, - 25, - 25 - ], - "dimensions": [ - "time", - "lat", - "lon" - ], - "description": "The average temperature at the earth's surface." - } - }, - "cube:dimensions": { - "lat": { - "axis": "y", - "type": "spatial", - "extent": [ - -90, - 90 - ], - "description": "latitude", - "reference_system": 4326 - }, - "lon": { - "axis": "x", - "type": "spatial", - "extent": [ - -180, - 179.4 - ], - "description": "longitude", - "reference_system": 4326 - }, - "time": { - "type": "temporal", - "extent": [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ], - "description": "time" - } - }, - "stac_extensions": [ - "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" - ], - "dashboard:is_periodic": true, - "dashboard:time_density": "month", - "summaries": { - "datetime": [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ] - } -} diff --git a/mock/datasets/power.data.mdx b/mock/datasets/power.data.mdx deleted file mode 100644 index b8ef8feb8..000000000 --- a/mock/datasets/power.data.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: power-meteorology -name: 'POWER Meteorological Dataset' -featured: true -description: "Prediction of Worldwide Energy Resources (POWER)" -media: - src: ::file ./no2--dataset-cover.jpg - alt: Power plant shooting steam at the sky. - author: - name: Mick Truyts - url: https://unsplash.com/photos/x6WQeNYJC1w -thematics: - - air -layers: - - id: power-meteorology-temperature - stacApiOverride: https://raw.githubusercontent.com/nasa-impact/veda-ui/ab/zarr-layers/mock/datasets/power-stac.json - stacCol: power-meteorology - name: Earth Skin Temperature - type: zarr - description: The average temperature at the earth's surface. - zoomExtent: - - 0 - - 20 - sourceParams: - url: s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr - resampling_method: bilinear - variable: TS - colormap_name: jet - rescale: 200,300 - legend: - unit: - label: - type: gradient - min: 200 - max: 300 - stops: - - '#000080' - - '#004cff' - - '#29ffce' - - '#ceff29' - - '#ff6800' - - '#800000' ---- - From 5e2b7841f65b8f9f7cf523a35f2835e61fe47a6f Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Fri, 12 May 2023 16:03:17 -0700 Subject: [PATCH 12/29] Add assetUrl to component --- .../components/common/mapbox/index.tsx | 1 + .../components/common/mapbox/layers/utils.ts | 1 + .../common/mapbox/layers/zarr-timeseries.tsx | 3 +- app/scripts/context/layer-data.tsx | 4 +- mock/datasets/oco2-geos-l3-daily.data.mdx | 39 +++++++++---------- parcel-resolver-veda/index.d.ts | 1 + 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 58a3779ea..87124d8e1 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -399,6 +399,7 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { 403 ppm" stops: - - '#000080' - - '#004cff' - - '#29ffce' - - '#ceff29' - - '#ff6800' - - '#800000' + - "#4575b4" + - "#91bfdb" + - "#e0f3f8" + - "#ffffbf" + - "#fee090" + - "#fc8d59" + - "#d73027" --- diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index 627f3bf17..81d60ae0d 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -68,6 +68,7 @@ declare module 'veda' { extends DatasetLayerCommonCompareProps { id: string; stacCol: string; + assetUrl?: string; type: DatasetLayerType; } From 6b305d2598522b832e47692ce4073b6d2c5e7c49 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Mon, 15 May 2023 14:12:06 -0700 Subject: [PATCH 13/29] Add titiler xarray host --- .env | 2 +- app/scripts/context/power-stac.js | 115 ------------------------------ 2 files changed, 1 insertion(+), 116 deletions(-) delete mode 100644 app/scripts/context/power-stac.js diff --git a/.env b/.env index 871eb4199..6c19f0ce9 100644 --- a/.env +++ b/.env @@ -4,7 +4,7 @@ APP_CONTACT_EMAIL=email@example.org API_RASTER_ENDPOINT='https://staging-raster.delta-backend.com' API_STAC_ENDPOINT='https://dev-stac.delta-backend.com' -API_XARRAY_ENDPOINT='https://enjmncj3p2.execute-api.us-west-2.amazonaws.com/tilejson.json' +API_XARRAY_ENDPOINT='https://dev-titiler-xarray.delta-backend.com' # If the app is being served in from a subfolder, the domain url must be set. # For example, if the app is served from /mysite: diff --git a/app/scripts/context/power-stac.js b/app/scripts/context/power-stac.js deleted file mode 100644 index 8b124d30e..000000000 --- a/app/scripts/context/power-stac.js +++ /dev/null @@ -1,115 +0,0 @@ -module.exports = { - "id": "power-meteorology", - "type": "Collection", - "links": [ - ], - "title": "POWER Monthly Meteorological Dataset", - "assets": { - "zarr": { - "href": "s3://power-analysis-ready-datastore/power_901_monthly_meteorology_utc.zarr", - "type": "application/vnd+zarr", - "roles": [ - "data", - "zarr" - ], - "title": "power-meteorology Zarr root", - "description": "", - "xarray:open_kwargs": { - "chunks": {}, - "engine": "zarr", - "consolidated": true - } - } - }, - "extent": { - "spatial": { - "bbox": [ - [ - -180, - -90, - 180, - 90 - ] - ] - }, - "temporal": { - "interval": [ - [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ] - ] - } - }, - "license": "CC0-1.0", - "description": "", - "stac_version": "1.0.0", - "cube:variables": { - "TS_MAX_AVG": { - "type": "data", - "unit": "temperature", - "attrs": { - "units": "temperature", - "long_name": "ADD ME" - }, - "shape": [ - 492, - 361, - 576 - ], - "chunks": [ - 492, - 25, - 25 - ], - "dimensions": [ - "time", - "lat", - "lon" - ], - "description": "The average temperature at the earth's surface." - } - }, - "cube:dimensions": { - "lat": { - "axis": "y", - "type": "spatial", - "extent": [ - -90, - 90 - ], - "description": "latitude", - "reference_system": 4326 - }, - "lon": { - "axis": "x", - "type": "spatial", - "extent": [ - -180, - 179.4 - ], - "description": "longitude", - "reference_system": 4326 - }, - "time": { - "step": "P1DT0H0M0S", - "type": "temporal", - "extent": [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ], - "description": "time" - } - }, - "stac_extensions": [ - "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" - ], - "dashboard:is_periodic": true, - "dashboard:time_density": "month", - "summaries": { - "datetime": [ - "1981-01-31T12:00:00Z", - "2021-12-31T12:00:00Z" - ] - } -} From 057aed62dfc45ef190c02ac6d21bd6273ebf9a44 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Mon, 15 May 2023 14:13:26 -0700 Subject: [PATCH 14/29] Revert change to gitignore --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index cd3245a71..12fd79316 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,4 @@ nbproject temp tmp .tmp -dist -parcel-resolver-thematics/veda-thematic.out.js - +dist \ No newline at end of file From d297992014ec59665306bdaa53013a816a4dffc2 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 08:50:54 -0700 Subject: [PATCH 15/29] Update .env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 6c19f0ce9..b4a9c409d 100644 --- a/.env +++ b/.env @@ -4,7 +4,7 @@ APP_CONTACT_EMAIL=email@example.org API_RASTER_ENDPOINT='https://staging-raster.delta-backend.com' API_STAC_ENDPOINT='https://dev-stac.delta-backend.com' -API_XARRAY_ENDPOINT='https://dev-titiler-xarray.delta-backend.com' +API_XARRAY_ENDPOINT='https://dev-titiler-xarray.delta-backend.com/tilejson.json' # If the app is being served in from a subfolder, the domain url must be set. # For example, if the app is served from /mysite: From a7ee6099b11b14863968fdf0e57dc5b4d87125d5 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 08:58:31 -0700 Subject: [PATCH 16/29] Add parcel resolver thematics to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 12fd79316..fc0e22e41 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,5 @@ nbproject temp tmp .tmp -dist \ No newline at end of file +dist/ +parcel-resolver-thematics/ From b2422ac2ecfa8e1f79ad48015c52d31396bc4d5d Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 09:00:27 -0700 Subject: [PATCH 17/29] Fix lint errors --- .../components/common/mapbox/layers/zarr-timeseries.tsx | 7 ++----- app/scripts/context/layer-data.tsx | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 882227d15..1524b3776 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -8,13 +8,11 @@ import { RasterSource, } from 'mapbox-gl'; -import { useMapStyle } from './styles';; +import { useMapStyle } from './styles'; interface MapLayerZarrTimeseriesProps { id: string; - stacCol: string; date?: Date; - mapInstance: MapboxMap; sourceParams: object; zoomExtent?: [number, number]; assetUrl?: string; @@ -24,7 +22,6 @@ interface MapLayerZarrTimeseriesProps { export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { const { id, - stacCol, date, sourceParams, zoomExtent, @@ -49,7 +46,7 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { let sources: Record = {}; if (tilesUrl) { - let tileParams = qs.stringify( + const tileParams = qs.stringify( { url: assetUrl, time_slice: date, diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index e7d220786..6e0363872 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -34,12 +34,14 @@ const fetchLayerById = async ( ): Promise => { const { type, stacCol } = layer; - const { data } = await axios.get(`${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`) + const { data } = await axios.get( + `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}` + ); const commonTimeseriesParams = { isPeriodic: data['dashboard:is_periodic'], timeDensity: data['dashboard:time_density'] - } + }; // TODO: Normalize API data structure // For the time being the vector and raster sources have different api // endpoints, and different properties to get data from. From 54f5eb0a5ac293ccff12c73c111f74e21f783c24 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 09:05:33 -0700 Subject: [PATCH 18/29] Remove unused instance of MapboxMap --- app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 1524b3776..1339012d4 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -1,7 +1,6 @@ import { useEffect, useMemo } from 'react'; import qs from 'qs'; import { - Map as MapboxMap, AnyLayer, AnySourceImpl, RasterLayer, From eaf2ae7879e892654fa8ab89e17a6a6c2f4c94f0 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 09:07:26 -0700 Subject: [PATCH 19/29] Make description shorter --- mock/datasets/oco2-geos-l3-daily.data.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mock/datasets/oco2-geos-l3-daily.data.mdx b/mock/datasets/oco2-geos-l3-daily.data.mdx index 32e4e7498..561ad8769 100644 --- a/mock/datasets/oco2-geos-l3-daily.data.mdx +++ b/mock/datasets/oco2-geos-l3-daily.data.mdx @@ -2,7 +2,7 @@ id: oco2-geos-l3-daily name: 'Gridded Daily OCO-2 Carbon Dioxide assimilated dataset' featured: true -description: "The OCO-2 mission provides the highest quality space-based XCO2 retrievals to date. However, the instrument data are characterized by large gaps in coverage due to OCO-2’s narrow 10-km ground track and an inability to see through clouds and thick aerosols. This global gridded dataset is produced using a data assimilation technique commonly referred to as state estimation within the geophysical literature. Data assimilation synthesizes simulations and observations, adjusting the state of atmospheric constituents like CO2 to reflect observed values, thus gap-filling observations when and where they are unavailable based on previous observations and short transport simulations by GEOS. Compared to other methods, data assimilation has the advantage that it makes estimates based on our collective scientific understanding, notably of the Earth’s carbon cycle and atmospheric transport. OCO-2 GEOS (Goddard Earth Observing System) Level 3 data are produced by ingesting OCO-2 L2 retrievals every 6 hours with GEOS CoDAS, a modeling and data assimilation system maintained by NASA’s Global Modeling and Assimilation Office (GMAO). GEOS CoDAS uses a high-performance computing implementation of the Gridpoint Statistical Interpolation approach for solving the state estimation problem. GSI finds the analyzed state that minimizes the three-dimensional variational (3D-Var) cost function formulation of the state estimation problem." +description: "NASA’s Orbiting Carbon Observatory, 2 (OCO-2) provides the most complete dataset tracking the concentration of atmospheric carbon dioxide (CO₂), the main driver of climate change. Since its launch (July 2014), OCO-2 measures sunlight reflected from Earth’s surface to infer the dry-air column-averaged CO2 mixing ratio and provides around 100,000 cloud-free observations." media: src: ::file ./no2--dataset-cover.jpg alt: Power plant shooting steam at the sky. From 5eff10d0d52a3a8b2c7242b21833b511499ce340 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Tue, 16 May 2023 09:17:50 -0700 Subject: [PATCH 20/29] Change co2 range --- mock/datasets/oco2-geos-l3-daily.data.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mock/datasets/oco2-geos-l3-daily.data.mdx b/mock/datasets/oco2-geos-l3-daily.data.mdx index 561ad8769..f42e23b68 100644 --- a/mock/datasets/oco2-geos-l3-daily.data.mdx +++ b/mock/datasets/oco2-geos-l3-daily.data.mdx @@ -25,13 +25,13 @@ layers: resampling_method: bilinear variable: XCO2 colormap_name: rdylbu_r - rescale: 0.00039394,0.00040339 + rescale: 0.00039394,0.000420 legend: unit: label: type: gradient min: "< 394 ppm" - max: "> 403 ppm" + max: "> 420 ppm" stops: - "#4575b4" - "#91bfdb" From 4cd8a80686f3e50b560fba802db752830254b61b Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Wed, 17 May 2023 18:07:07 +0100 Subject: [PATCH 21/29] Revert mapbox style id --- app/scripts/components/common/mapbox/map-options/basemaps.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/components/common/mapbox/map-options/basemaps.ts b/app/scripts/components/common/mapbox/map-options/basemaps.ts index 9b207daa4..dff4a92b1 100644 --- a/app/scripts/components/common/mapbox/map-options/basemaps.ts +++ b/app/scripts/components/common/mapbox/map-options/basemaps.ts @@ -15,8 +15,8 @@ export const BASEMAP_STYLES = [ { id: 'satellite', label: 'Satellite', - mapboxId: 'ckb01h6f10bn81iqg98ne0i2y', - thumbnailUrl: `https://api.mapbox.com/styles/v1/covid-nasa/ckb01h6f10bn81iqg98ne0i2y/static/-9.14,38.7,10.5,0/480x320?access_token=${process.env.MAPBOX_TOKEN}` + mapboxId: 'cldu1cb8f00ds01p6gi583w1m', + thumbnailUrl: `https://api.mapbox.com/styles/v1/covid-nasa/cldu1cb8f00ds01p6gi583w1m/static/-9.14,38.7,10.5,0/480x320?access_token=${process.env.MAPBOX_TOKEN}` }, { id: 'dark', From f8b15c1f210b5ca32a0ab14eaee49ce3883c07e7 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Wed, 17 May 2023 18:07:38 +0100 Subject: [PATCH 22/29] Pass layer data to layer components instead of specifying --- .../common/blocks/scrollytelling/index.tsx | 16 ++---- .../components/common/mapbox/index.tsx | 15 ++--- .../mapbox/layers/raster-timeseries.tsx | 24 +++----- .../components/common/mapbox/layers/utils.ts | 23 ++++++-- .../mapbox/layers/vector-timeseries.tsx | 27 ++++----- .../common/mapbox/layers/zarr-timeseries.tsx | 55 ++++++++----------- app/scripts/context/layer-data.tsx | 34 ++++++------ parcel-resolver-veda/index.d.ts | 3 - 8 files changed, 87 insertions(+), 110 deletions(-) diff --git a/app/scripts/components/common/blocks/scrollytelling/index.tsx b/app/scripts/components/common/blocks/scrollytelling/index.tsx index 79051a32c..e81793484 100644 --- a/app/scripts/components/common/blocks/scrollytelling/index.tsx +++ b/app/scripts/components/common/blocks/scrollytelling/index.tsx @@ -378,16 +378,14 @@ function Scrollytelling(props) { ); @@ -468,16 +466,14 @@ function Scrollytelling(props) { ); diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 87124d8e1..9a35241bb 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -398,12 +398,9 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { {isMapLoaded && baseLayerResolvedData && BaseLayerComponent && ( )} @@ -448,11 +445,9 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { CompareLayerComponent && ( )} diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index 1da88e46b..dff4795af 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -36,15 +36,17 @@ const LOG = true; const FIT_BOUNDS_PADDING = 32; -interface MapLayerRasterTimeseriesProps { +export interface MapLayerRasterTimeseriesProps { id: string; - stacCol: string; date?: Date; mapInstance: MapboxMap; - sourceParams: object; - zoomExtent?: [number, number]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; - isHidden: boolean; + isHidden?: boolean; + layerData: { + sourceParams?: object; + zoomExtent?: number[]; + stacCol: string; + }; } export interface StacFeature { @@ -64,16 +66,8 @@ interface Statuses { } export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { - const { - id, - stacCol, - date, - mapInstance, - sourceParams, - zoomExtent, - onStatusChange, - isHidden - } = props; + const { id, date, mapInstance, onStatusChange, isHidden } = props; + const { sourceParams, zoomExtent, stacCol } = props.layerData; const theme = useTheme(); const { updateStyle } = useMapStyle(); diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index 651f546fa..4656890fc 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -19,9 +19,19 @@ import { DatasetDatumReturnType, DatasetLayerCompareNormalized } from 'veda'; -import { MapLayerRasterTimeseries, StacFeature } from './raster-timeseries'; -import { MapLayerVectorTimeseries } from './vector-timeseries'; -import { MapLayerZarrTimeseries } from './zarr-timeseries'; +import { + MapLayerRasterTimeseries, + MapLayerRasterTimeseriesProps, + StacFeature +} from './raster-timeseries'; +import { + MapLayerVectorTimeseries, + MapLayerVectorTimeseriesProps +} from './vector-timeseries'; +import { + MapLayerZarrTimeseries, + MapLayerZarrTimeseriesProps +} from './zarr-timeseries'; import { userTzDate2utcString, utcString2userTzDate } from '$utils/date'; import { AsyncDatasetLayer } from '$context/layer-data'; @@ -31,7 +41,11 @@ import { HintedError } from '$utils/hinted-error'; export const getLayerComponent = ( isTimeseries: boolean, layerType: 'raster' | 'vector' | 'zarr' -): FunctionComponent | null => { +): FunctionComponent< + | MapLayerRasterTimeseriesProps + | MapLayerVectorTimeseriesProps + | MapLayerZarrTimeseriesProps +> | null => { if (isTimeseries) { if (layerType === 'raster') return MapLayerRasterTimeseries; if (layerType === 'vector') return MapLayerVectorTimeseries; @@ -71,7 +85,6 @@ export const getCompareLayerData = ( id: stacCol, stacCol, type: type || layerData.type, - assetUrl: layerData.assetUrl, zoomExtent: zoomExtent || layerData.zoomExtent, sourceParams: defaultsDeep({}, sourceParams, layerData.sourceParams), ...passThroughProps diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 3763a4bec..766fca30c 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -19,28 +19,22 @@ import { useCustomMarker } from './custom-marker'; import { ActionStatus, S_FAILED, S_LOADING, S_SUCCEEDED } from '$utils/status'; import { userTzDate2utcString } from '$utils/date'; -interface MapLayerVectorTimeseriesProps { +export interface MapLayerVectorTimeseriesProps { id: string; - stacCol: string; date?: Date; mapInstance: MapboxMap; - sourceParams: object; - zoomExtent?: [number, number]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; - isHidden: boolean; + isHidden?: boolean; + layerData: { + sourceParams?: object; + zoomExtent?: number[]; + stacCol: string; + }; } export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { - const { - id, - stacCol, - date, - mapInstance, - sourceParams, - zoomExtent, - onStatusChange, - isHidden - } = props; + const { id, date, mapInstance, onStatusChange, isHidden } = props; + const { sourceParams, zoomExtent, stacCol } = props.layerData; const theme = useTheme(); const { updateStyle } = useMapStyle(); @@ -81,7 +75,6 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { }; }, [mapInstance, id, stacCol, date, onStatusChange]); - const markerLayout = useCustomMarker(mapInstance); // @@ -188,7 +181,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { 'source-layer': 'default', layout: { ...(markerLayout as any), - visibility: isHidden ? 'none' : 'visible', + visibility: isHidden ? 'none' : 'visible' }, paint: { 'icon-color': theme.color?.infographicB, diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 1339012d4..9a56df5eb 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -1,37 +1,29 @@ import { useEffect, useMemo } from 'react'; import qs from 'qs'; -import { - AnyLayer, - AnySourceImpl, - RasterLayer, - RasterSource, -} from 'mapbox-gl'; +import { AnyLayer, AnySourceImpl, RasterLayer, RasterSource } from 'mapbox-gl'; import { useMapStyle } from './styles'; -interface MapLayerZarrTimeseriesProps { +export interface MapLayerZarrTimeseriesProps { id: string; date?: Date; - sourceParams: object; - zoomExtent?: [number, number]; - assetUrl?: string; - isHidden: boolean; + isHidden?: boolean; + layerData: { + sourceParams?: object; + zoomExtent?: number[]; + assetUrl: string; + }; } export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { - const { - id, - date, - sourceParams, - zoomExtent, - assetUrl, - isHidden - } = props; + const { id, date, isHidden } = props; + const { sourceParams = {}, zoomExtent, assetUrl } = props.layerData; const { updateStyle } = useMapStyle(); const minZoom = zoomExtent?.[0] ?? 0; - const tilesUrl = process.env.API_XARRAY_ENDPOINT; + const tilerUrl = process.env.API_XARRAY_ENDPOINT; + // // Generate Mapbox GL layers and sources for raster timeseries // @@ -39,23 +31,22 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { () => JSON.stringify(sourceParams), [sourceParams] ); + useEffect( () => { let layers: AnyLayer[] = []; let sources: Record = {}; - if (tilesUrl) { - const tileParams = qs.stringify( - { - url: assetUrl, - time_slice: date, - ...sourceParams - } - ); + if (tilerUrl) { + const tileParams = qs.stringify({ + url: assetUrl, + time_slice: date, + ...sourceParams + }); const zarrSource: RasterSource = { type: 'raster', - url: `${tilesUrl}?${tileParams}` + url: `${tilerUrl}?${tileParams}` }; const zarrLayer: RasterLayer = { @@ -78,10 +69,9 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { }; sources = { - ...sources, [id]: zarrSource }; - layers = [...layers, zarrLayer]; + layers = [zarrLayer]; } updateStyle({ @@ -95,7 +85,8 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { updateStyle, id, date, - tilesUrl, + assetUrl, + tilerUrl, minZoom, haveSourceParamsChanged, isHidden diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index 6e0363872..1ae0d111c 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -42,9 +42,7 @@ const fetchLayerById = async ( isPeriodic: data['dashboard:is_periodic'], timeDensity: data['dashboard:time_density'] }; - // TODO: Normalize API data structure - // For the time being the vector and raster sources have different api - // endpoints, and different properties to get data from. + if (type === 'vector') { const featuresApiEndpoint = data.links.find((l) => l.rel === 'child').href; const { data: featuresApiData } = await axios.get(featuresApiEndpoint); @@ -55,23 +53,23 @@ const fetchLayerById = async ( domain: featuresApiData.extent.temporal.interval[0] } }; - } - - const defaultData = { - timeseries: { - ...commonTimeseriesParams, - domain: data.summaries ? data.summaries.datetime : data.extent.temporal.interval[0] - } - }; - - if (type === 'zarr') { - return { - ...defaultData, - assetUrl: data.assets.zarr.href + } else { + const defaultData = { + timeseries: { + ...commonTimeseriesParams, + domain: data.summaries ? data.summaries.datetime : data.extent.temporal.interval[0] + } }; + + if (type === 'zarr') { + return { + ...defaultData, + assetUrl: data.assets.zarr.href + }; + } else { + return defaultData; + } } - - return defaultData; }; // Create a query object for react query. diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index 81d60ae0d..261ebf05f 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -54,8 +54,6 @@ declare module 'veda' { initialDatetime?: 'newest' | 'oldest' | string; projection?: ProjectionOptions; type: DatasetLayerType; - assetUrl?: string; - stacApiOverride?: string; compare: DatasetLayerCompareSTAC | DatasetLayerCompareInternal | null; legend?: LayerLegendCategorical | LayerLegendGradient; } @@ -68,7 +66,6 @@ declare module 'veda' { extends DatasetLayerCommonCompareProps { id: string; stacCol: string; - assetUrl?: string; type: DatasetLayerType; } From f366f13f81b6330149ea62f44c44d60976dbbae8 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Thu, 8 Jun 2023 10:13:27 -0700 Subject: [PATCH 23/29] Fix STAC endpoint --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index b4a9c409d..89545aef4 100644 --- a/.env +++ b/.env @@ -3,7 +3,7 @@ APP_DESCRIPTION=User interface of module VEDA APP_CONTACT_EMAIL=email@example.org API_RASTER_ENDPOINT='https://staging-raster.delta-backend.com' -API_STAC_ENDPOINT='https://dev-stac.delta-backend.com' +API_STAC_ENDPOINT='https://staging-stac.delta-backend.com' API_XARRAY_ENDPOINT='https://dev-titiler-xarray.delta-backend.com/tilejson.json' # If the app is being served in from a subfolder, the domain url must be set. From 0e3ca145eb36a59db92871197442f04abfd8f682 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 5 Jul 2023 11:40:06 -0700 Subject: [PATCH 24/29] Revert change to vector-timeseries --- .../components/common/mapbox/layers/vector-timeseries.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 60f1433d8..800be3836 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -19,10 +19,13 @@ import { useCustomMarker } from './custom-marker'; import { ActionStatus, S_FAILED, S_LOADING, S_SUCCEEDED } from '$utils/status'; import { userTzDate2utcString } from '$utils/date'; -export interface MapLayerVectorTimeseriesProps { +interface MapLayerVectorTimeseriesProps { id: string; + stacCol: string; date?: Date; mapInstance: MapboxMap; + sourceParams: object; + zoomExtent?: [number, number]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden: boolean; idSuffix?: string; @@ -82,6 +85,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { }; }, [mapInstance, id, stacCol, date, onStatusChange]); + const markerLayout = useCustomMarker(mapInstance); // @@ -188,7 +192,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { 'source-layer': 'default', layout: { ...(markerLayout as any), - visibility: isHidden ? 'none' : 'visible' + visibility: isHidden ? 'none' : 'visible', }, paint: { 'icon-color': theme.color?.infographicB, From f194734588b3838bf717f28286cab9594d61f7c8 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 5 Jul 2023 11:45:36 -0700 Subject: [PATCH 25/29] Match updates to interface in main --- .../common/blocks/scrollytelling/index.tsx | 47 ++----------------- .../components/common/mapbox/index.tsx | 14 ++++-- .../mapbox/layers/raster-timeseries.tsx | 10 ++-- .../common/mapbox/layers/zarr-timeseries.tsx | 11 ++--- 4 files changed, 23 insertions(+), 59 deletions(-) diff --git a/app/scripts/components/common/blocks/scrollytelling/index.tsx b/app/scripts/components/common/blocks/scrollytelling/index.tsx index 850463f6c..695a229ae 100644 --- a/app/scripts/components/common/blocks/scrollytelling/index.tsx +++ b/app/scripts/components/common/blocks/scrollytelling/index.tsx @@ -364,40 +364,6 @@ function Scrollytelling(props) { return ( - - - {isMapLoaded && - resolvedLayers.map((resolvedLayer) => { - if (!resolvedLayer) return null; - - const { runtimeData, Component: LayerCmp, layer } = resolvedLayer; - - if (!LayerCmp) return null; - - // Each layer type is added to the map through a component. This - // component has all the logic needed to add/update/remove the - // layer. Which component to use will depend on the characteristics - // of the layer and dataset. - // The function getLayerComponent() should be used to get the - // correct component. - return ( - - ); - })} - - {areLayersLoading && } {/* @@ -488,17 +454,14 @@ function Scrollytelling(props) { ); })} diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 1ee22f692..45a7392ba 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -412,9 +412,11 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { {isMapLoaded && baseLayerResolvedData && BaseLayerComponent && ( )} @@ -458,9 +460,11 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { CompareLayerComponent && ( )} diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index 8d6aad4fa..1d3384e48 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -36,16 +36,14 @@ const LOG = true; const FIT_BOUNDS_PADDING = 32; -export interface MapLayerRasterTimeseriesProps { +interface MapLayerRasterTimeseriesProps { id: string; + stacCol: string; date?: Date; mapInstance: MapboxMap; + sourceParams: object; + zoomExtent?: [number, number]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; - layerData: { - sourceParams?: object; - zoomExtent?: number[]; - stacCol: string; - }; isHidden: boolean; idSuffix?: string; } diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 9a56df5eb..6d6e34d3b 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -6,18 +6,17 @@ import { useMapStyle } from './styles'; export interface MapLayerZarrTimeseriesProps { id: string; + stacCol: string; date?: Date; + sourceParams?: object; + zoomExtent?: number[]; + assetUrl: string; isHidden?: boolean; - layerData: { - sourceParams?: object; - zoomExtent?: number[]; - assetUrl: string; - }; } export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { const { id, date, isHidden } = props; - const { sourceParams = {}, zoomExtent, assetUrl } = props.layerData; + const { sourceParams = {}, zoomExtent, assetUrl } = props; const { updateStyle } = useMapStyle(); From a9e15594d292da06d85c9aeac64546fc35508111 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 5 Jul 2023 12:48:18 -0700 Subject: [PATCH 26/29] Add assetUrl --- app/scripts/components/common/mapbox/index.tsx | 1 + app/scripts/components/common/mapbox/layers/utils.ts | 1 + parcel-resolver-veda/index.d.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 45a7392ba..22a2ebf24 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -413,6 +413,7 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { Date: Wed, 5 Jul 2023 12:49:16 -0700 Subject: [PATCH 27/29] Revert change to gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fc0e22e41..12fd79316 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,4 @@ nbproject temp tmp .tmp -dist/ -parcel-resolver-thematics/ +dist \ No newline at end of file From 9f161696e85cb00afc5488137fa6cd0d1781d312 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Wed, 5 Jul 2023 13:07:57 -0700 Subject: [PATCH 28/29] Fix lint errors --- .../components/common/mapbox/layers/raster-timeseries.tsx | 2 +- .../components/common/mapbox/layers/vector-timeseries.tsx | 2 +- app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx | 1 - parcel-resolver-veda/index.d.ts | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index 1d3384e48..a624a1e7e 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -36,7 +36,7 @@ const LOG = true; const FIT_BOUNDS_PADDING = 32; -interface MapLayerRasterTimeseriesProps { +export interface MapLayerRasterTimeseriesProps { id: string; stacCol: string; date?: Date; diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 800be3836..719980353 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -19,7 +19,7 @@ import { useCustomMarker } from './custom-marker'; import { ActionStatus, S_FAILED, S_LOADING, S_SUCCEEDED } from '$utils/status'; import { userTzDate2utcString } from '$utils/date'; -interface MapLayerVectorTimeseriesProps { +export interface MapLayerVectorTimeseriesProps { id: string; stacCol: string; date?: Date; diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 6d6e34d3b..57fe023b0 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -6,7 +6,6 @@ import { useMapStyle } from './styles'; export interface MapLayerZarrTimeseriesProps { id: string; - stacCol: string; date?: Date; sourceParams?: object; zoomExtent?: number[]; diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index f2d640cdf..989de20ed 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -79,6 +79,7 @@ declare module 'veda' { name: string; description: string; stacCol: string; + assetUrl?: string; type: DatasetLayerType; legend?: LayerLegendCategorical | LayerLegendGradient; } From f71364abb7aa85b1536699ccb98271d3f3262e1f Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Mon, 10 Jul 2023 18:40:33 +0100 Subject: [PATCH 29/29] Simplify zarr layer handling --- .../common/blocks/scrollytelling/index.tsx | 2 +- .../components/common/mapbox/index.tsx | 61 ++++---- .../mapbox/layers/raster-timeseries.tsx | 6 +- .../components/common/mapbox/layers/utils.ts | 6 +- .../mapbox/layers/vector-timeseries.tsx | 6 +- .../common/mapbox/layers/zarr-timeseries.tsx | 147 ++++++++++++------ app/scripts/context/layer-data.tsx | 18 +-- parcel-resolver-veda/index.d.ts | 2 - 8 files changed, 147 insertions(+), 101 deletions(-) diff --git a/app/scripts/components/common/blocks/scrollytelling/index.tsx b/app/scripts/components/common/blocks/scrollytelling/index.tsx index 695a229ae..e56873285 100644 --- a/app/scripts/components/common/blocks/scrollytelling/index.tsx +++ b/app/scripts/components/common/blocks/scrollytelling/index.tsx @@ -434,7 +434,7 @@ function Scrollytelling(props) { {isMapLoaded && resolvedLayers.map((resolvedLayer, lIdx) => { - if (!resolvedLayer) return null; + if (!resolvedLayer || !mapRef.current) return null; const { runtimeData, Component: LayerCmp, layer } = resolvedLayer; const isHidden = diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 22a2ebf24..eaf22a64d 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -1,4 +1,5 @@ import React, { + MutableRefObject, ReactNode, forwardRef, useCallback, @@ -105,7 +106,10 @@ const getMapPositionOptions = (position) => { return opts; }; -function MapboxMapComponent(props: MapboxMapProps, ref) { +function MapboxMapComponent( + props: MapboxMapProps, + ref: MutableRefObject +) { /* eslint-disable react/prop-types */ const { className, @@ -371,21 +375,19 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { title={baseLayerResolvedData.name} description={baseLayerResolvedData.description} {...baseLayerResolvedData.legend} - /> + /> {compareLayerResolvedData?.legend && - isComparing && - (baseLayerResolvedData.id !== compareLayerResolvedData.id) && - } + isComparing && + baseLayerResolvedData.id !== compareLayerResolvedData.id && ( + + )} - )} - - - + )} {/* Maps container @@ -409,18 +411,20 @@ function MapboxMapComponent(props: MapboxMapProps, ref) { labelsOption={labelsOption} boundariesOption={boundariesOption} /> - {isMapLoaded && baseLayerResolvedData && BaseLayerComponent && ( - - )} + {mapRef.current && + isMapLoaded && + baseLayerResolvedData && + BaseLayerComponent && ( + + )} - {isMapCompareLoaded && + {mapCompareRef.current && + isMapCompareLoaded && compareLayerResolvedData && CompareLayerComponent && ( ; + zoomExtent?: number[]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; - isHidden: boolean; + isHidden?: boolean; idSuffix?: string; } diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index ba269df44..91f46f731 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -17,7 +17,8 @@ import { DatasetDatumFn, DatasetDatumFnResolverBag, DatasetDatumReturnType, - DatasetLayerCompareNormalized + DatasetLayerCompareNormalized, + DatasetLayerType } from 'veda'; import { MapLayerRasterTimeseries, @@ -40,7 +41,7 @@ import { HintedError } from '$utils/hinted-error'; export const getLayerComponent = ( isTimeseries: boolean, - layerType: 'raster' | 'vector' | 'zarr' + layerType: DatasetLayerType ): FunctionComponent< | MapLayerRasterTimeseriesProps | MapLayerVectorTimeseriesProps @@ -85,7 +86,6 @@ export const getCompareLayerData = ( id: stacCol, stacCol, type: type || layerData.type, - assetUrl: layerData.assetUrl, zoomExtent: zoomExtent ?? layerData.zoomExtent, sourceParams: defaultsDeep({}, sourceParams, layerData.sourceParams), ...passThroughProps diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 719980353..e790322d8 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -24,10 +24,10 @@ export interface MapLayerVectorTimeseriesProps { stacCol: string; date?: Date; mapInstance: MapboxMap; - sourceParams: object; - zoomExtent?: [number, number]; + sourceParams?: Record; + zoomExtent?: number[]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; - isHidden: boolean; + isHidden?: boolean; idSuffix?: string; } diff --git a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx index 57fe023b0..74c60799c 100644 --- a/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/zarr-timeseries.tsx @@ -1,26 +1,78 @@ -import { useEffect, useMemo } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import qs from 'qs'; -import { AnyLayer, AnySourceImpl, RasterLayer, RasterSource } from 'mapbox-gl'; +import { Map as MapboxMap, RasterSource, RasterLayer } from 'mapbox-gl'; +import { requestQuickCache } from './utils'; import { useMapStyle } from './styles'; +import { ActionStatus, S_FAILED, S_LOADING, S_SUCCEEDED } from '$utils/status'; + +const tilerUrl = process.env.API_XARRAY_ENDPOINT; + export interface MapLayerZarrTimeseriesProps { id: string; + stacCol: string; date?: Date; - sourceParams?: object; + mapInstance: MapboxMap; + sourceParams?: Record; zoomExtent?: number[]; - assetUrl: string; + onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden?: boolean; + idSuffix?: string; } export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { - const { id, date, isHidden } = props; - const { sourceParams = {}, zoomExtent, assetUrl } = props; + const { + id, + stacCol, + date, + mapInstance, + sourceParams, + zoomExtent, + onStatusChange, + isHidden, + idSuffix = '' + } = props; const { updateStyle } = useMapStyle(); + const [assetUrl, setAssetUrl] = useState(''); + + const [minZoom] = zoomExtent ?? [0, 20]; + + const generatorId = 'zarr-timeseries' + idSuffix; + + // + // Get the asset url + // + useEffect(() => { + const controller = new AbortController(); + + async function load() { + try { + onStatusChange?.({ status: S_LOADING, id }); + const data = await requestQuickCache({ + url: `${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`, + method: 'GET', + controller + }); - const minZoom = zoomExtent?.[0] ?? 0; - const tilerUrl = process.env.API_XARRAY_ENDPOINT; + setAssetUrl(data.assets.zarr.href); + onStatusChange?.({ status: S_SUCCEEDED, id }); + } catch (error) { + if (!controller.signal.aborted) { + setAssetUrl(''); + onStatusChange?.({ status: S_FAILED, id }); + } + return; + } + } + + load(); + + return () => { + controller.abort(); + }; + }, [mapInstance, id, stacCol, date, onStatusChange]); // // Generate Mapbox GL layers and sources for raster timeseries @@ -32,48 +84,45 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { useEffect( () => { - let layers: AnyLayer[] = []; - let sources: Record = {}; - - if (tilerUrl) { - const tileParams = qs.stringify({ - url: assetUrl, - time_slice: date, - ...sourceParams - }); + if (!tilerUrl) return; + + const tileParams = qs.stringify({ + url: assetUrl, + time_slice: date, + ...sourceParams + }); - const zarrSource: RasterSource = { - type: 'raster', - url: `${tilerUrl}?${tileParams}` - }; - - const zarrLayer: RasterLayer = { - id: id, - type: 'raster', - source: id, - layout: { - visibility: isHidden ? 'none' : 'visible' - }, - paint: { - 'raster-opacity': Number(!isHidden), - 'raster-opacity-transition': { - duration: 320 - } - }, - minzoom: minZoom, - metadata: { - layerOrderPosition: 'raster' + const zarrSource: RasterSource = { + type: 'raster', + url: `${tilerUrl}?${tileParams}` + }; + + const zarrLayer: RasterLayer = { + id: id, + type: 'raster', + source: id, + layout: { + visibility: isHidden ? 'none' : 'visible' + }, + paint: { + 'raster-opacity': Number(!isHidden), + 'raster-opacity-transition': { + duration: 320 } - }; + }, + minzoom: minZoom, + metadata: { + layerOrderPosition: 'raster' + } + }; - sources = { - [id]: zarrSource - }; - layers = [zarrLayer]; - } + const sources = { + [id]: zarrSource + }; + const layers = [zarrLayer]; updateStyle({ - generatorId: 'raster-timeseries', + generatorId, sources, layers }); @@ -84,10 +133,10 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { id, date, assetUrl, - tilerUrl, minZoom, haveSourceParamsChanged, - isHidden + isHidden, + generatorId ] ); @@ -97,12 +146,12 @@ export function MapLayerZarrTimeseries(props: MapLayerZarrTimeseriesProps) { useEffect(() => { return () => { updateStyle({ - generatorId: 'raster-timeseries', + generatorId, sources: {}, layers: [] }); }; - }, [updateStyle]); + }, [updateStyle, generatorId]); return null; } diff --git a/app/scripts/context/layer-data.tsx b/app/scripts/context/layer-data.tsx index 46e50c2ee..20fa952aa 100644 --- a/app/scripts/context/layer-data.tsx +++ b/app/scripts/context/layer-data.tsx @@ -17,7 +17,6 @@ import { S_SUCCEEDED } from '$utils/status'; export type TimeDensity = 'day' | 'month' | 'year' | null; interface STACLayerData { - assetUrl?: string; timeseries: { isPeriodic: boolean; timeDensity: TimeDensity; @@ -52,21 +51,16 @@ const fetchLayerById = async ( } }; } else { - const defaultData = { + const domain = data.summaries + ? data.summaries.datetime + : data.extent.temporal.interval[0]; + + return { timeseries: { ...commonTimeseriesParams, - domain: data.summaries ? data.summaries.datetime : data.extent.temporal.interval[0] + domain } }; - - if (type === 'zarr') { - return { - ...defaultData, - assetUrl: data.assets.zarr.href - }; - } else { - return defaultData; - } } }; diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index 989de20ed..82be3ac03 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -57,7 +57,6 @@ declare module 'veda' { description: string; initialDatetime?: 'newest' | 'oldest' | string; projection?: ProjectionOptions; - assetUrl?: string; type: DatasetLayerType; compare: DatasetLayerCompareSTAC | DatasetLayerCompareInternal | null; legend?: LayerLegendCategorical | LayerLegendGradient; @@ -79,7 +78,6 @@ declare module 'veda' { name: string; description: string; stacCol: string; - assetUrl?: string; type: DatasetLayerType; legend?: LayerLegendCategorical | LayerLegendGradient; }