From b5d273c8d48acbe97861e885d3a01740a12db269 Mon Sep 17 00:00:00 2001 From: morandd Date: Fri, 22 Nov 2024 16:53:03 +0100 Subject: [PATCH] move to Astro --- .../{npm_run_build => npm_run_build.yml} | 0 astro.config.mjs | 10 + index.html | 5 +- package.json | 10 +- {src => src-bad}/App.css | 0 {src => src-bad}/App.tsx | 0 {src => src-bad}/components/App.module.css | 0 {src => src-bad}/components/ChoosePalette.jsx | 0 .../components/ChooseRasterLayer.jsx | 0 .../components/ChooseVectorLayer.jsx | 0 {src => src-bad}/components/Colorbar.jsx | 0 .../components/Colorbar.module.css | 0 {src => src-bad}/components/LogoNILU.astro | 2 +- {src => src-bad}/components/LogoProject.astro | 2 +- {src => src-bad}/components/Map.js | 0 src-bad/components/STACTreeView.jsx | 49 ++++ .../components/utils/ShowWholeCollection.js | 48 ++++ .../components/utils/createBasemapLayer.js | 0 .../components/utils/createVectorLayer.js | 0 .../utils/getGeotiffMinmaxFromAuxXML.js | 17 ++ .../components/utils/vectorFeatureStyle.js | 0 .../components/utils/zoomToFitData.js | 0 src-bad/env.d.ts | 1 + {src => src-bad}/index.css | 0 src/components/App.jsx => src-bad/main.ts | 8 +- {src => src-bad}/scripts/getAllItems.ts | 0 src-bad/scripts/palette.js | 74 ++++++ {src => src-bad}/vite-env.d.ts | 0 src/components/ChoosePalette.astro | 65 +++++ src/components/Colorbar.astro | 53 ++++ src/components/Dataviewer.astro | 52 ++++ src/components/Logos.astro | 38 +++ src/components/Map.astro | 243 ++++++++++++++++++ src/components/STACTreeView.jsx | 71 +++-- src/components/Toaster.astro | 56 ++++ src/components/utils/ShowWholeCollection.js | 9 +- src/env.d.ts | 1 + src/index.tsx | 9 - src/pages/index.astro | 51 ++++ src/scripts/getAllSTACItems.ts | 96 +++++++ src/scripts/getGeotiffMinmaxFromAuxXML.js | 17 ++ src/scripts/palette.js | 17 +- src/style.css | 46 ++++ static/NILU_Logo_Seagreen_RGB_small.svg | 26 ++ static/fairicube_logo_400x297.jpg | Bin 0 -> 51695 bytes tsconfig.app.json | 2 +- 46 files changed, 1033 insertions(+), 45 deletions(-) rename .github/workflows/{npm_run_build => npm_run_build.yml} (100%) create mode 100644 astro.config.mjs rename {src => src-bad}/App.css (100%) rename {src => src-bad}/App.tsx (100%) rename {src => src-bad}/components/App.module.css (100%) rename {src => src-bad}/components/ChoosePalette.jsx (100%) rename {src => src-bad}/components/ChooseRasterLayer.jsx (100%) rename {src => src-bad}/components/ChooseVectorLayer.jsx (100%) rename {src => src-bad}/components/Colorbar.jsx (100%) rename {src => src-bad}/components/Colorbar.module.css (100%) rename {src => src-bad}/components/LogoNILU.astro (74%) rename {src => src-bad}/components/LogoProject.astro (84%) rename {src => src-bad}/components/Map.js (100%) create mode 100644 src-bad/components/STACTreeView.jsx create mode 100644 src-bad/components/utils/ShowWholeCollection.js rename {src => src-bad}/components/utils/createBasemapLayer.js (100%) rename {src => src-bad}/components/utils/createVectorLayer.js (100%) create mode 100644 src-bad/components/utils/getGeotiffMinmaxFromAuxXML.js rename {src => src-bad}/components/utils/vectorFeatureStyle.js (100%) rename {src => src-bad}/components/utils/zoomToFitData.js (100%) create mode 100644 src-bad/env.d.ts rename {src => src-bad}/index.css (100%) rename src/components/App.jsx => src-bad/main.ts (90%) rename {src => src-bad}/scripts/getAllItems.ts (100%) create mode 100644 src-bad/scripts/palette.js rename {src => src-bad}/vite-env.d.ts (100%) create mode 100644 src/components/ChoosePalette.astro create mode 100644 src/components/Colorbar.astro create mode 100644 src/components/Dataviewer.astro create mode 100644 src/components/Logos.astro create mode 100644 src/components/Map.astro create mode 100644 src/components/Toaster.astro create mode 100644 src/env.d.ts delete mode 100644 src/index.tsx create mode 100644 src/pages/index.astro create mode 100644 src/scripts/getAllSTACItems.ts create mode 100644 src/scripts/getGeotiffMinmaxFromAuxXML.js create mode 100644 src/style.css create mode 100644 static/NILU_Logo_Seagreen_RGB_small.svg create mode 100644 static/fairicube_logo_400x297.jpg diff --git a/.github/workflows/npm_run_build b/.github/workflows/npm_run_build.yml similarity index 100% rename from .github/workflows/npm_run_build rename to .github/workflows/npm_run_build.yml diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..ca6257a --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,10 @@ +import { defineConfig } from 'astro/config' +import solid from '@astrojs/solid-js'; + +export default defineConfig({ + site: "https://vis.fairicube.eu", + integrations: [solid()], + output: "static", + publicDir: "static", + outDir: "public", +}) diff --git a/index.html b/index.html index 7021737..3d388f7 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,11 @@ - Vite + Solid + TS + + FAIRiCUBE Visualization Service
- + diff --git a/package.json b/package.json index 4eae6c1..cfa66a1 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,15 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "preview": "vite preview" + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview" }, "dependencies": { + "@astrojs/check": "^0.9.4", + "@astrojs/solid-js": "^4.4.4", + "astro": "^4.16.14", "d3": "^7.9.0", "geotiff": "^2.1.3", "ol": "^10.2.1", diff --git a/src/App.css b/src-bad/App.css similarity index 100% rename from src/App.css rename to src-bad/App.css diff --git a/src/App.tsx b/src-bad/App.tsx similarity index 100% rename from src/App.tsx rename to src-bad/App.tsx diff --git a/src/components/App.module.css b/src-bad/components/App.module.css similarity index 100% rename from src/components/App.module.css rename to src-bad/components/App.module.css diff --git a/src/components/ChoosePalette.jsx b/src-bad/components/ChoosePalette.jsx similarity index 100% rename from src/components/ChoosePalette.jsx rename to src-bad/components/ChoosePalette.jsx diff --git a/src/components/ChooseRasterLayer.jsx b/src-bad/components/ChooseRasterLayer.jsx similarity index 100% rename from src/components/ChooseRasterLayer.jsx rename to src-bad/components/ChooseRasterLayer.jsx diff --git a/src/components/ChooseVectorLayer.jsx b/src-bad/components/ChooseVectorLayer.jsx similarity index 100% rename from src/components/ChooseVectorLayer.jsx rename to src-bad/components/ChooseVectorLayer.jsx diff --git a/src/components/Colorbar.jsx b/src-bad/components/Colorbar.jsx similarity index 100% rename from src/components/Colorbar.jsx rename to src-bad/components/Colorbar.jsx diff --git a/src/components/Colorbar.module.css b/src-bad/components/Colorbar.module.css similarity index 100% rename from src/components/Colorbar.module.css rename to src-bad/components/Colorbar.module.css diff --git a/src/components/LogoNILU.astro b/src-bad/components/LogoNILU.astro similarity index 74% rename from src/components/LogoNILU.astro rename to src-bad/components/LogoNILU.astro index 39c8f59..9254011 100644 --- a/src/components/LogoNILU.astro +++ b/src-bad/components/LogoNILU.astro @@ -19,4 +19,4 @@ - + diff --git a/src/components/LogoProject.astro b/src-bad/components/LogoProject.astro similarity index 84% rename from src/components/LogoProject.astro rename to src-bad/components/LogoProject.astro index 25a2f6e..30e93af 100644 --- a/src/components/LogoProject.astro +++ b/src-bad/components/LogoProject.astro @@ -11,7 +11,7 @@ z-index: 1000; width: 100px; height: 100px; - background-image: url('../images/fairicube_logo_400x297.jpg'); + background-image: url('/fairicube_logo_400x297.jpg'); background-size: contain; background-repeat: no-repeat; background-position: center; diff --git a/src/components/Map.js b/src-bad/components/Map.js similarity index 100% rename from src/components/Map.js rename to src-bad/components/Map.js diff --git a/src-bad/components/STACTreeView.jsx b/src-bad/components/STACTreeView.jsx new file mode 100644 index 0000000..70acb64 --- /dev/null +++ b/src-bad/components/STACTreeView.jsx @@ -0,0 +1,49 @@ +import { createResource, createSignal } from "solid-js"; +import { getAllSTACItems } from "../scripts/getAllItems"; +import { showWholeCollection } from "./utils/ShowWholeCollection"; + +export default function STACTreeView(props) { + + + //const [STACCatalogs, setSTACCatalogs] = createSignal(); + const [dummySignal, setDummySignal] = createSignal(); + + const [STACCatalogs] = createResource(dummySignal, getAllSTACItems); + + + setDummySignal(true); + + return Loading...}> + + {(collection, collectionIdx) => { + return + }} + +} + +function ViewCollection({collection}) { + + + return
{collection.title}: + + + + +
+} \ No newline at end of file diff --git a/src-bad/components/utils/ShowWholeCollection.js b/src-bad/components/utils/ShowWholeCollection.js new file mode 100644 index 0000000..583f45e --- /dev/null +++ b/src-bad/components/utils/ShowWholeCollection.js @@ -0,0 +1,48 @@ +import Map from 'ol/Map.js'; +import OSM from 'ol/source/OSM.js'; +import STAC from '../../ol/layer/STAC'; +import TileLayer from 'ol/layer/WebGLTile.js'; +import View from 'ol/View.js'; +import proj4 from 'proj4'; +import {getStacObjectsForEvent} from '../../ol/util.js'; +import {register} from 'ol/proj/proj4.js'; + +register(proj4); // required to support source reprojection + +export function showWholeCollection(collectionUrl){ + + const layer = new STAC({ + url: collectionUrl, + displayPreview: true, + collectionStyle: { + color: 'red', + }, + assets: ['data'], + + }); + + const background = new TileLayer({source: new OSM()}); + + const map = new Map({ + target: 'map-container', + layers: [background, layer], + view: new View({ + center: [0, 0], + zoom: 0, + }), + }); + map.on('singleclick', async (event) => { + const objects = await getStacObjectsForEvent(event); + if (objects.length > 0) { + objects.forEach((obj) => console.log(obj)); + } + }); + + layer.on('sourceready', () => { + const view = map.getView(); + view.fit(layer.getExtent()); + }); + + return false; + +} \ No newline at end of file diff --git a/src/components/utils/createBasemapLayer.js b/src-bad/components/utils/createBasemapLayer.js similarity index 100% rename from src/components/utils/createBasemapLayer.js rename to src-bad/components/utils/createBasemapLayer.js diff --git a/src/components/utils/createVectorLayer.js b/src-bad/components/utils/createVectorLayer.js similarity index 100% rename from src/components/utils/createVectorLayer.js rename to src-bad/components/utils/createVectorLayer.js diff --git a/src-bad/components/utils/getGeotiffMinmaxFromAuxXML.js b/src-bad/components/utils/getGeotiffMinmaxFromAuxXML.js new file mode 100644 index 0000000..3381335 --- /dev/null +++ b/src-bad/components/utils/getGeotiffMinmaxFromAuxXML.js @@ -0,0 +1,17 @@ +async function getGeotiffMinmaxFromAuxXML(geotiffSourceURL){ + try { + + const auxurl = geotiffSourceURL.replace('.tif','.tif.aux.xml'); + const aux = await fetch(auxurl); + const auxxml = await aux.text(); + const auxdoc = new DOMParser().parseFromString(auxxml, 'text/xml'); + const min = auxdoc.querySelector('[key="STATISTICS_MINIMUM"]').textContent; + const max = auxdoc.querySelector('[key="STATISTICS_MAXIMUM"]').textContent; + return [parseFloat(min), parseFloat(max)]; + } catch { + return null; + } +} + +export default getGeotiffMinmaxFromAuxXML; + diff --git a/src/components/utils/vectorFeatureStyle.js b/src-bad/components/utils/vectorFeatureStyle.js similarity index 100% rename from src/components/utils/vectorFeatureStyle.js rename to src-bad/components/utils/vectorFeatureStyle.js diff --git a/src/components/utils/zoomToFitData.js b/src-bad/components/utils/zoomToFitData.js similarity index 100% rename from src/components/utils/zoomToFitData.js rename to src-bad/components/utils/zoomToFitData.js diff --git a/src-bad/env.d.ts b/src-bad/env.d.ts new file mode 100644 index 0000000..9bc5cb4 --- /dev/null +++ b/src-bad/env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/src/index.css b/src-bad/index.css similarity index 100% rename from src/index.css rename to src-bad/index.css diff --git a/src/components/App.jsx b/src-bad/main.ts similarity index 90% rename from src/components/App.jsx rename to src-bad/main.ts index b675c21..83b72a9 100644 --- a/src/components/App.jsx +++ b/src-bad/main.ts @@ -1,10 +1,10 @@ import styles from "./App.module.css"; import { createSignal } from "solid-js"; -import Colorbar from "./Colorbar.jsx"; -import ChoosePalette from "./ChoosePalette.jsx"; -import ChooseVectorLayer from "./ChooseVectorLayer.jsx"; -import ChooseRasterLayer from "./ChooseRasterLayer.jsx"; +import Colorbar from "./components/Colorbar.jsx"; +import ChoosePalette from "./components/ChoosePalette.jsx"; +import ChooseVectorLayer from "./components/ChooseVectorLayer.jsx"; +import ChooseRasterLayer from "./components/ChooseRasterLayer.jsx"; import proj4 from 'proj4'; import {register} from 'ol/proj/proj4'; diff --git a/src/scripts/getAllItems.ts b/src-bad/scripts/getAllItems.ts similarity index 100% rename from src/scripts/getAllItems.ts rename to src-bad/scripts/getAllItems.ts diff --git a/src-bad/scripts/palette.js b/src-bad/scripts/palette.js new file mode 100644 index 0000000..45ee774 --- /dev/null +++ b/src-bad/scripts/palette.js @@ -0,0 +1,74 @@ +// import d3 scalechromatic +import * as d3 from 'd3'; +/* +import { palette } from '../components/App.js'; + + +export function getInterpolateBand1AsColor() { + + const pal = palette(); + + + if (pal.toLowerCase()=='rgb')return { color: ['array', ['band', 1], ['band', 2], ['band', 3], 1] }; + + let color = null; + if (pal=='A') color = d3.scaleSequential(d3.interpolateRainbow); + if (pal=='B') color = d3.scaleSequential(d3.interpolateViridis); + if (pal=='C') color = d3.scaleSequential(d3.interpolateWarm); + if (pal=='D') color = d3.scaleSequential(d3.interpolateGnBu); + + + const clr_arr = [ + 'interpolate', + ['linear'], + ['band', 1], + 0, + [255,0,0,0], + 0.001, + color(0.001), + 0.1, + color(0.1), + 0.1, + color(0.1), + 0.2, + color(0.2), + 0.4, + color(0.4), + 0.6, + color(0.6), + 1, + color(1) + ]; + interpolation_to_colorbar(clr_arr.slice(3)); + return {color: clr_arr}; + +} + + +// Given a MapLibre color interpolation array like [value, color, value, color, ...], create a +// colorbar elelement with a CSS background gradient +// that matches the color ramp +function interpolation_to_colorbar(interp){ + const colorbar = document.getElementById('colorbar'); + colorbar.innerHTML = ''; + for (let i = 0; i < interp.length; i+=2){ + let div = document.createElement('div'); + div.innerHTML = interp[i]; // Value + colorbar.appendChild(div); + } + const maxValue = interp[interp.length-2]; + let gradient = ''; + for (let i = 0; i < interp.length; i+=2){ + const value = interp[i]; + const percent = (value/maxValue)*100; + let clr = interp[i+1]; + // If the color is an array, convert it to a string + if (Array.isArray(clr)) clr = `rgba(${clr.join(',')})`; + gradient += clr + ' ' + percent + '%, '; + } + gradient = gradient.slice(0, -2); + colorbar.style.background = 'linear-gradient(to right, ' + gradient + ')'; + +} + +*/ \ No newline at end of file diff --git a/src/vite-env.d.ts b/src-bad/vite-env.d.ts similarity index 100% rename from src/vite-env.d.ts rename to src-bad/vite-env.d.ts diff --git a/src/components/ChoosePalette.astro b/src/components/ChoosePalette.astro new file mode 100644 index 0000000..59d5bb1 --- /dev/null +++ b/src/components/ChoosePalette.astro @@ -0,0 +1,65 @@ + +
+
+ + + + + +
+
+ + + + + + \ No newline at end of file diff --git a/src/components/Colorbar.astro b/src/components/Colorbar.astro new file mode 100644 index 0000000..4b33478 --- /dev/null +++ b/src/components/Colorbar.astro @@ -0,0 +1,53 @@ +
+ + + \ No newline at end of file diff --git a/src/components/Dataviewer.astro b/src/components/Dataviewer.astro new file mode 100644 index 0000000..96ffbc9 --- /dev/null +++ b/src/components/Dataviewer.astro @@ -0,0 +1,52 @@ + +
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/components/Logos.astro b/src/components/Logos.astro new file mode 100644 index 0000000..f20031f --- /dev/null +++ b/src/components/Logos.astro @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/components/Map.astro b/src/components/Map.astro new file mode 100644 index 0000000..3bf5658 --- /dev/null +++ b/src/components/Map.astro @@ -0,0 +1,243 @@ + + +
+ + + + \ No newline at end of file diff --git a/src/components/STACTreeView.jsx b/src/components/STACTreeView.jsx index 70acb64..53f71a0 100644 --- a/src/components/STACTreeView.jsx +++ b/src/components/STACTreeView.jsx @@ -1,6 +1,7 @@ import { createResource, createSignal } from "solid-js"; -import { getAllSTACItems } from "../scripts/getAllItems"; +import { getAllSTACItems } from "../scripts/getAllSTACItems"; import { showWholeCollection } from "./utils/ShowWholeCollection"; +import STAC from 'ol-stac'; export default function STACTreeView(props) { @@ -13,14 +14,19 @@ export default function STACTreeView(props) { setDummySignal(true); - return Loading...}> - - {(collection, collectionIdx) => { - return - }} - + return
+ Loading...
}> + + {(collection, collectionIdx) => { + return + }} + + } + + + function ViewCollection({collection}) { @@ -31,14 +37,19 @@ function ViewCollection({collection}) {
    {(item, itemIdx) => { - return
  • {item.id}
    + // find the item in item.links[] array named "rel"="self" and get the "href" attribute of that item + const selfLink = item.links.find(link => link.rel == "self"); + const href = selfLink ? selfLink.href : null; + + const handleClickOnSTACItem = (e) => { + e.preventDefault(); + localStorage.setItem('stac', e.target.href); + document.dispatchEvent(new Event('newsource')); + } + + return
  • {item.id} - {(entry) => { - const [key, val] = entry; - // If val.rolesdoes not contain "data", skip - if (!val.roles.includes("data")) return <>🔗 - return
    🔎 {val?.description ?? val.id ?? key}: {val.href}
    - }} + {(entry) => }
  • @@ -46,4 +57,34 @@ function ViewCollection({collection}) {
-} \ No newline at end of file +} + +function STACItem({entry}) { + + const [key, val] = entry; + + // If val.rolesdoes not contain "data", skip + if (!val.roles.includes("data")) { + return null; + return <>No asset with "data" role (only {val.roles.join(", ")}) + } + + + const urlInput = document.getElementById('url-input'); + if (urlInput) urlInput.onchange = () => document.dispatchEvent(new Event('newsource')); + + const urlInputButton = document.getElementById('url-input-button'); + if (urlInputButton) urlInputButton.onclick = () => document.dispatchEvent(new Event('newsource')); + + function handleClick(e) { + e.preventDefault(); + + localStorage.setItem('url', e.target.href); + + document.dispatchEvent(new Event('newsource')); + } + + return {val?.description ?? val.id ?? key} + +} + diff --git a/src/components/Toaster.astro b/src/components/Toaster.astro new file mode 100644 index 0000000..37f394d --- /dev/null +++ b/src/components/Toaster.astro @@ -0,0 +1,56 @@ +
+ + + + \ No newline at end of file diff --git a/src/components/utils/ShowWholeCollection.js b/src/components/utils/ShowWholeCollection.js index 3fe8f20..c5c38a8 100644 --- a/src/components/utils/ShowWholeCollection.js +++ b/src/components/utils/ShowWholeCollection.js @@ -1,10 +1,11 @@ -import Map from 'ol/Map.js'; -import OSM from 'ol/source/OSM.js'; +import Map from 'ol/Map'; +import OSM from 'ol/source/OSM'; import STAC from 'ol-stac'; import TileLayer from 'ol/layer/WebGLTile.js'; import View from 'ol/View.js'; import proj4 from 'proj4'; -import {getStacObjectsForEvent} from 'ol-stac/util.js'; +import * as olSTACUtil from 'ol-stac/util'; +//import {getStacObjectsForEvent} from 'ol/util'; import {register} from 'ol/proj/proj4.js'; register(proj4); // required to support source reprojection @@ -32,7 +33,7 @@ export function showWholeCollection(collectionUrl){ }), }); map.on('singleclick', async (event) => { - const objects = await getStacObjectsForEvent(event); + const objects = await olSTACUtil.getStacObjectsForEvent(event); if (objects.length > 0) { objects.forEach((obj) => console.log(obj)); } diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..9bc5cb4 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 425abdb..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/* @refresh reload */ -import { render } from 'solid-js/web'; -import './index.css'; -import App from './App.tsx'; - - -const root = document.getElementById('root') - -render(() => , root!) diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..4444b33 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,51 @@ +--- +import Colorbar from "../components/Colorbar.astro"; +import Logos from "../components/Logos.astro"; +import Map from "../components/Map.astro"; +import Dataviewer from "../components/Dataviewer.astro"; +import STACTreeView from "../components/STACTreeView.jsx"; +import Toaster from "../components/Toaster.astro"; +import ChoosePalette from "../components/ChoosePalette.astro"; + +export const prerender = true; +--- + + + + + + + + FAIRiCUBE Visualization + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/scripts/getAllSTACItems.ts b/src/scripts/getAllSTACItems.ts new file mode 100644 index 0000000..a7903cf --- /dev/null +++ b/src/scripts/getAllSTACItems.ts @@ -0,0 +1,96 @@ + + +const baseUrl = 'https://stacapi.eoxhub.fairicube.eu'; + +export type Collections = Collection[]; +export type Collection = any; +export type Item = any; +export type Items = Item[]; + +/** + * This function will get all the STAC items from the STAC API for the catalog at: + * https://stacapi.eoxhub.fairicube.eu + * + * The function queries all collections, then queries each collection to get all items in the collection. It returns a JSON object. + * + */ +export async function getAllSTACItems(){ + console.log("Getting all STAC items"); + return await getCollections(); + +} + + +async function getCollections(){ + + const response = await fetch(`${baseUrl}/collections`); + const data = await response.json(); + const collections = data.collections as Collections; + // If there is a link with rel="next" in the collections, + // follow that to fetch the next page of collections + let nextLink = data.links.find((link: any) => link.rel === 'next'); + if (nextLink){ + const response = await fetch(nextLink.href); + const data = await response.json(); + collections.push(...data.collections as Collections); + nextLink = data.links.find((link: any) => link.rel === 'next'); + } + + for (const [i, collection] of collections.entries()){ + collections[i].items = await getItems(collection); + } + + return collections; +} + +/** + * This function will get all the items for each collection + * It contintually calls the links:next + * to get another page of items + * until there are no more links:next + * + * @param {*} collections + * @returns + */ +async function getItems(collection: Collection): Promise{ + let items : Items = []; + const response = await fetch(`${baseUrl}/collections/${collection.id}/items`); + const data = await response.json(); + items.push(...data.features); + + // get the next link + let nextLink = data.links.find((link: any) => link.rel === 'next'); + while (nextLink){ + const response = await fetch(nextLink.href); + const data = await response.json(); + items.push(...data.features); + nextLink = data.links.find((link: any) => link.rel === 'next'); + } + + const count = items.length; + items.forEach((item) => { + // convert item.assets to an array, and add the key as ".id"to each item + //@ts-ignore + item.assets = Object.entries(item.assets).map(([key, val]) => ({...val, id: key})); + + // Remove assets that do not contain "data" or "thumbnail" in the .roles array + item.assets = item.assets.filter((asset: any) => asset.roles.includes("data") || asset.roles.includes("thumbnail")); + + // Remove assets that do not have a .href + item.assets = item.assets.filter((asset: any) => asset.href); + + // Remove assets where href does not end with .tif,.png,.jpg,.jpeg,.gif,.bmp,.webp, or .geojson + item.assets = item.assets.filter((asset: any) => asset.href.endsWith(".tif") || asset.href.endsWith(".png") || asset.href.endsWith(".jpg") || asset.href.endsWith(".jpeg") || asset.href.endsWith(".gif") || asset.href.endsWith(".bmp") || asset.href.endsWith(".webp") || asset.href.endsWith(".geojson")); + }); + + + + // Remove items that do not contain any assets + items = items.filter((item) => item.assets.length > 0); + + if (count != items.length){ + console.log(`Removed ${count - items.length} items from collection ${collection.id} (kept only items with a 'data' or 'thumbnail' role, and where the .href ends with .tif,.png,.jpg,.jpeg,.gif,.bmp,.webp, or .geojson)`); + } + + return items; +} \ No newline at end of file diff --git a/src/scripts/getGeotiffMinmaxFromAuxXML.js b/src/scripts/getGeotiffMinmaxFromAuxXML.js new file mode 100644 index 0000000..bf1f9ff --- /dev/null +++ b/src/scripts/getGeotiffMinmaxFromAuxXML.js @@ -0,0 +1,17 @@ +export async function getGeotiffMinmaxFromAuxXML(geotiffSourceURL){ + try { + + const auxurl = geotiffSourceURL.replace('.tif','.tif.aux.xml'); + const aux = await fetch(auxurl); + const auxxml = await aux.text(); + const auxdoc = new DOMParser().parseFromString(auxxml, 'text/xml'); + const min = auxdoc.querySelector('[key="STATISTICS_MINIMUM"]').textContent; + const max = auxdoc.querySelector('[key="STATISTICS_MAXIMUM"]').textContent; + return [parseFloat(min), parseFloat(max)]; + } catch { + return null; + } +} + +export default getGeotiffMinmaxFromAuxXML; + diff --git a/src/scripts/palette.js b/src/scripts/palette.js index 45ee774..2217d8a 100644 --- a/src/scripts/palette.js +++ b/src/scripts/palette.js @@ -1,16 +1,23 @@ // import d3 scalechromatic import * as d3 from 'd3'; -/* -import { palette } from '../components/App.js'; -export function getInterpolateBand1AsColor() { - const pal = palette(); +export function getInterpolateBand1AsColor(pal) { + // If pal is not specified, use 'rgb' if palette-rgb is checked, otherwise use 'A' + if (!pal) { + if (document.getElementById('palette-rgb').checked) pal='rgb'; + else pal = document.querySelector('input[name="palette"]:checked').value; + } + + // Hide #colorbar element + document.getElementById('colorbar').style.display = 'none'; if (pal.toLowerCase()=='rgb')return { color: ['array', ['band', 1], ['band', 2], ['band', 3], 1] }; + document.getElementById('colorbar').style.display = 'block'; + let color = null; if (pal=='A') color = d3.scaleSequential(d3.interpolateRainbow); if (pal=='B') color = d3.scaleSequential(d3.interpolateViridis); @@ -42,6 +49,7 @@ export function getInterpolateBand1AsColor() { interpolation_to_colorbar(clr_arr.slice(3)); return {color: clr_arr}; + } @@ -71,4 +79,3 @@ function interpolation_to_colorbar(interp){ } -*/ \ No newline at end of file diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..9fd909c --- /dev/null +++ b/src/style.css @@ -0,0 +1,46 @@ +:root { + --nilu-dark-blue: rgb(5, 63, 83); + --nilu-med-blue: rgb(159, 210, 254); + --nilu-light-blue: rgb(233, 254, 255); + --nilu-soft-accent: rgb(84, 254, 219); + --nilu-soft-accent-dark: rgb(58, 177, 153); + --nilu-bright-accent: rgb(233, 60, 172); + --nilu-gray: rgb(176, 184, 194); + --nilu-dark-gray: rgba(6, 58, 75, 0.715); + --iconSize: 1.5rem; + } + +.stactreeview { + background-color: var(--nilu-light-blue); + + max-height: 80vh; + width: 20vw; + padding: 1rem; + overflow: auto; + position: absolute; + top: 0.5vh; + left: 0.5vw; + + ul { + list-style-type: none; + padding: 0; + margin: 0; + li { + list-style-type: none; + padding: 0; + margin: 0; + a.stacitemasset { + color: var(--nilu-dark-blue); + &::before { + content: 'Data Asset:'; + margin-right: 0.5rem; + } + margin-left: 1rem; + } + } + } + a { + display: block; + } +} + \ No newline at end of file diff --git a/static/NILU_Logo_Seagreen_RGB_small.svg b/static/NILU_Logo_Seagreen_RGB_small.svg new file mode 100644 index 0000000..4f1219f --- /dev/null +++ b/static/NILU_Logo_Seagreen_RGB_small.svg @@ -0,0 +1,26 @@ + + + + + + diff --git a/static/fairicube_logo_400x297.jpg b/static/fairicube_logo_400x297.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46274f3f7481ab09809ff5f82cfa16f09ceb848b GIT binary patch literal 51695 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� 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

5InTtlVdIpF-#=KvWU!+J#?`^{m#_*QN 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 kxit)>LltEjeCDH?jc!