From 806cda41e133fe6398d820022af7a1c0ebf1ba35 Mon Sep 17 00:00:00 2001 From: Dan Moran MBA15 Date: Sun, 24 Nov 2024 14:07:30 +0100 Subject: [PATCH] color work --- src/components/COGURL.astro | 2 +- src/components/ChoosePalette.astro | 39 +++++++++- src/components/Colorbar.astro | 30 +++++--- src/components/Map.astro | 120 +++++++++++------------------ src/components/STACTreeView.jsx | 20 +++-- src/components/Toaster.astro | 7 +- src/scripts/buildMapForSTACItem.ts | 64 +++++++++++++++ src/scripts/getAllSTACItems.ts | 5 +- src/scripts/palette.js | 92 ++++++++++------------ src/scripts/parseQGISColorfile.js | 92 ++++++++++++++++++++++ src/style.css | 16 ++-- 11 files changed, 330 insertions(+), 157 deletions(-) create mode 100644 src/scripts/buildMapForSTACItem.ts create mode 100644 src/scripts/parseQGISColorfile.js diff --git a/src/components/COGURL.astro b/src/components/COGURL.astro index 4c94da4..47c2e96 100644 --- a/src/components/COGURL.astro +++ b/src/components/COGURL.astro @@ -1,7 +1,7 @@
diff --git a/src/components/ChoosePalette.astro b/src/components/ChoosePalette.astro index 758c409..b684aba 100644 --- a/src/components/ChoosePalette.astro +++ b/src/components/ChoosePalette.astro @@ -5,7 +5,8 @@ - + +
@@ -20,17 +21,35 @@ document.querySelector('input[name="invertpalette"]')?.addEventListener('change' document.dispatchEvent(new Event('newpalette')); }); + // Add an event listener for the "newpalette" event. If localStorage.palette=rgb hide the Invert checkbox document.addEventListener('newpalette', () => { const palette = localStorage.getItem('palette'); const invertCheckbox = document.querySelector('label.invertpalette') as HTMLLabelElement; - invertCheckbox.style.opacity = palette==='rgb' ? '0' : '1'; + invertCheckbox.style.opacity = (palette==='rgb'||palette==='qgis') ? '0' : '1'; + + const qg = localStorage.getItem('QGISColorfile') || ''; + const qgisLabel = document.querySelector('label.qgis') as HTMLLabelElement; + if (qg.length>0) qgisLabel.style.opacity = '1'; + else qgisLabel.style.opacity = '0'; + + // Ensure the correct radio button is checked + const paletteRadio = document.querySelector(`input[name="palette"][value="${palette}"]`) as HTMLInputElement; + if (paletteRadio) paletteRadio.checked = true; +}); + +// Add an event listener for the "newbandcount" event. Show the True Color option if bandcount>1 +document.addEventListener('newbandcount', () => { + const bandcount = localStorage.getItem('bandcount') ?? '1'; + const rgbLabel = document.querySelector('.choosepalette .truecolor') as HTMLLabelElement; + if (rgbLabel) rgbLabel.style.display = bandcount==='1' ? 'none' : 'initial'; }); // Add click handlers for the palette radio buttons document.querySelectorAll('input[name="palette"]').forEach( (radio : Element) => { // Trigger a "palette" event when the palette is changed (radio as HTMLInputElement).onchange = (e) => { + console.log('palette changed to', (e.target as HTMLInputElement).value); localStorage.setItem('palette', (e.target as HTMLInputElement).value); document.dispatchEvent(new Event('newpalette')); }; @@ -46,6 +65,17 @@ document.addEventListener('newsource', () => { if (rgbLabel) rgbLabel.style.display = isTiff ? 'initial' : 'none'; }); + +// On page load, if localStorage.palette is set check the radio button +document.addEventListener('DOMContentLoaded', () => { + + const palette = localStorage.getItem('palette'); + if (!palette) return; + const radio = document.querySelector(`input[name="palette"][value="${palette}"]`); + if (!radio) return; + (radio as HTMLInputElement).checked = true; +}); + \ No newline at end of file diff --git a/src/components/Map.astro b/src/components/Map.astro index c879923..271ef7c 100644 --- a/src/components/Map.astro +++ b/src/components/Map.astro @@ -6,6 +6,10 @@ import 'ol/ol.css'; import * as ol from 'ol'; import TileLayer from 'ol/layer/WebGLTile'; +import Map from 'ol/Map.js'; +import Source from 'ol/source/ImageTile.js'; +import View from 'ol/View.js'; + import {getGeotiffMinmaxFromAuxXML} from '../scripts/getGeotiffMinmaxFromAuxXML.js'; import type {Extent} from 'ol/extent'; @@ -17,7 +21,9 @@ import { transformExtent, Projection, get as getProjection, fromLonLat } from 'o import {register,fromEPSGCode} from 'ol/proj/proj4'; import STAC from 'ol-stac'; import proj4 from 'proj4' -import * as d3 from 'd3'; +import {getQGISColorfile} from '../scripts/parseQGISColorfile.js'; +import {useGeographic} from 'ol/proj.js'; + let source : any = null; let layer : TileLayer | null = null; @@ -26,10 +32,17 @@ let flag_styleIsSet = false; // This is used to prevent setting the style multip // Register EPSG:3035 //proj4.defs("EPSG:3035","+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs"); proj4.defs("EPSG:31256","+proj=tmerc +lat_0=0 +lon_0=16.3333333333333 +k=1 +x_0=0 +y_0=-5000000 +ellps=bessel +towgs84=551.7,162.9,467.9,6.04,1.96,-11.38,-4.82 +units=m +no_defs +type=crs"); - register(proj4); // Make projections defined in proj4 (with proj4.defs()) available in OpenLayers. Req -const baselayer = new OldTileLayer({source:new OSM()}); +if (!localStorage.getItem('palette')) { + console.log('No palette set. Setting to d3.interpolateBlues'); + localStorage.setItem('palette', 'd3.interpolateBlues'); +} + +//useGeographic(); + + +//const baselayer = new OldTileLayer({source:new OSM()}); const googleview = new ol.View({ projection: 'EPSG:3857', //center: [16.2,48.2],zoom:10 // Vienna @@ -40,6 +53,17 @@ googleview.setCenter( fromLonLat([-77.1778,26.4858], googleview.getProjection()) googleview.setCenter( fromLonLat([-10,55.2], googleview.getProjection())); googleview.setZoom(6); // Copenhagen googleview.setCenter( fromLonLat([16.2,48.2], googleview.getProjection())); googleview.setZoom(10); // Vienna +const baselayer = new TileLayer({ + source: new Source({ + attributions: + 'Tiles © ArcGIS', + url: + 'https://server.arcgisonline.com/ArcGIS/rest/services/' + + 'World_Topo_Map/MapServer/tile/{z}/{y}/{x}', + }) + }); + const map = new ol.Map({ target: 'map', @@ -98,25 +122,35 @@ async function buildMapForGeotiff() { } }); + getQGISColorfile(); // Create the source and layer - source = new GeoTIFF({sources: [geotiffSource], normalize: true}); + source = new GeoTIFF({sources: [geotiffSource], normalize: false}); layer = new TileLayer({source}); // Set timeout for 5 seconds. If flag_styleIsSet false at that time, print a warning to console that we haven't loaded any tiles. setTimeout(() => { - if (!flag_styleIsSet) window.newToast({title: 'Error', message: `After 5s, no tiles have been loaded...`}); + + if (!flag_styleIsSet) { + window.newToast({ok:false, message: `Could nor load dataset.`}); + } }, 5000); + localStorage.setItem('bandcount', '1'); + document.dispatchEvent(new Event('newbandcount')); + // Callback on tile load. This is used to set the style source.on('tileloadend', (event) => { if (flag_styleIsSet) return; // If the style has already been set, do nothing flag_styleIsSet = true; // Set the flag - window.newToast(`Got image with ${event.target.bandCount} bands`); + //window.newToast(`Got image with ${event.target.bandCount} bands`); + localStorage.setItem('bandcount', event.target.bandCount.toString()); + document.dispatchEvent(new Event('newbandcount')); + if (layer) (layer as TileLayer).setStyle(getInterpolateBand1AsColor()); // Set the style }); @@ -155,79 +189,13 @@ async function buildMapForGeotiff() { // Add a listener for the "newpalette" event -document.addEventListener('newpalette', updateGeotiffColors); -function updateGeotiffColors(){ - if (!layer) { - console.error('No layer to update colors for'); - return; - } +document.addEventListener('newpalette', () => { + if (!layer) return; (layer as TileLayer).setStyle(getInterpolateBand1AsColor()); // Set the style -} - - - - -async function buildMapForSTACItem() { - -// Which STAC item to load? -let stac_json = localStorage.getItem('stac'); - - -if (!stac_json) return buildMapForGeotiff(); - - -const layer = new STAC({url: stac_json}); - - -// Try to fetch and parse an {url}.aux.xml file. The file is an XML file with metadata about the GeoTIFF -/*const minmax = await getGeotiffMinmaxFromAuxXML(url); -if (minmax) { - geotiffSource.min = minmax[0]; - geotiffSource.max = minmax[1]; - console.log(`Used .tif.aux.xml to find min=${geotiffSource.min}, max=${geotiffSource.max}`); -} -*/ - - - -layer.on('sourceready', () => { - const view = map.getView(); - view.fit(layer.getExtent() as Extent); - -}); - -layer.on('assetsready', () => { - // Assign titles for e.g. a layerswitcher - for (const sublayer of layer.getLayersArray()) { - const stac = sublayer.get('stac'); - let title; - if (stac.isAsset() || stac.isLink()) { - title = stac.getMetadata('title') || stac.getKey(); - } else { - - const firstDataAsset = stac.getAssets().find(d=>d.roles.includes("data")); - if (firstDataAsset) { - localStorage.setItem('url', firstDataAsset.href); - buildMapForGeotiff(); - } else { - console.error(`No asset with role "data" found in ${stac_json}`); - } - - } - - sublayer.set('title', title); - } }); -// Create the map -map.setLayers([baselayer,layer]); -map.render(); - - -}; // function buildMapForSTACItem - @@ -236,10 +204,10 @@ map.render(); -buildMapForSTACItem(); // Execute this on page load +buildMapForGeotiff(); // Execute this on page load // Add a listener for the "newsource" event -document.addEventListener('newsource', buildMapForSTACItem); +document.addEventListener('newsource', buildMapForGeotiff); diff --git a/src/components/STACTreeView.jsx b/src/components/STACTreeView.jsx index 53f71a0..8de6e26 100644 --- a/src/components/STACTreeView.jsx +++ b/src/components/STACTreeView.jsx @@ -29,10 +29,12 @@ export default function STACTreeView(props) { function ViewCollection({collection}) { + // This should (but doesn't) show the whole collection as outlines in the map. + // - return
{collection.title}: + if (collection.items.length===0) return null; - + return
{collection.title}:
    @@ -41,13 +43,14 @@ function ViewCollection({collection}) { const selfLink = item.links.find(link => link.rel == "self"); const href = selfLink ? selfLink.href : null; - const handleClickOnSTACItem = (e) => { + /*const handleClickOnSTACItem = (e) => { e.preventDefault(); localStorage.setItem('stac', e.target.href); document.dispatchEvent(new Event('newsource')); - } + }*/ - return
  • {item.id} + //return
  • {item.id} + return
  • {item.id} {(entry) => } @@ -63,12 +66,13 @@ function STACItem({entry}) { const [key, val] = entry; + /* // If val.rolesdoes not contain "data", skip - if (!val.roles.includes("data")) { + if (!(val.roles.includes("data") || val.roles.includes("overview"))) { return null; - return <>No asset with "data" role (only {val.roles.join(", ")}) + return <>No asset with "data" or "overview" role (only {val.roles.join(", ")}) } - +*/ const urlInput = document.getElementById('url-input'); if (urlInput) urlInput.onchange = () => document.dispatchEvent(new Event('newsource')); diff --git a/src/components/Toaster.astro b/src/components/Toaster.astro index 4ff9ced..581287a 100644 --- a/src/components/Toaster.astro +++ b/src/components/Toaster.astro @@ -3,7 +3,7 @@