[general]
name=HDF-EOS Explorer
qgisMinimumVersion=3.16
description=Opens hyperspectral HDF-EOS5 cubes (Planet Tanager and similar) and explores them: click a pixel and see its spectrum, sweep a sampling line, navigate the data cube. Also extracts to ENVI's native format with wavelengths, FWHM and bad band list. / Abre cubos hiperespectrales HDF-EOS5 y los explora; tambien los extrae al formato nativo de ENVI.
version=2.16
author=Carlo Soto Castro
email=carlo.soto.castro@gmail.com

about=A hyperspectral cube is three things at once - a place, a picture and a spectrum - and most tools show only one of them. This plugin links the three, and it does it on the original HDF-EOS5 container: no need to convert anything first. Click a pixel on the map or on the cube and its full spectral signature is plotted immediately; drag a sampling line across the scene and the plot follows, summarising every pixel the line crosses; drag a rectangle and get the mean signature of the area with its per-band standard deviation. The classic hyperspectral cube is drawn too - the image at the front, the spectral transects as the two side faces - but unlike a static cube the faces are the slice that passes through the crosshair, so dragging it actually travels through the cube, and clicking a side face brings that band to the front. RGB composition is chosen by wavelength and not by band number, so the presets mean the same thing on any sensor. Bad bands are handled properly: the file's own bbl, the water vapour absorption windows, the noisy ends of the range and any hand-written ranges are excluded from the analysis - not just hidden from the plot, but kept out of the mean, the standard deviation and the spectral angle, where a bad band does its real damage unseen. Signatures can be named, annotated, compared by spectral angle, saved to a JSON library and exported to CSV. The plugin also keeps the Processing algorithm "HDF-EOS5 to ENVI", which unpacks the container into ENVI's native format - raw binary plus text header, read by every hyperspectral package - with the centre wavelengths, the FWHM, the fill value and a bad band list that already flags the water vapour absorption windows, so MNF, PPI and spectral unmixing do not drag those bands along. Unorthorectified products come in sensor geometry with latitude and longitude stored per pixel; those can be written as an IGM file for ENVI's "Georeference from Input Geometry", and the 2D ancillary layers (cloud and cirrus masks, nodata, aerosol optical depth, column water vapour, sun and sensor angles) stacked into one multiband file. Because it is a Processing algorithm it also works in batch over a folder of scenes and inside the graphical modeller. NO MANDATORY EXTERNAL DEPENDENCY: it needs only numpy and GDAL, both bundled with QGIS. h5py is used when present because it gives fuller access to HDF5 attributes and faster random reads; pyqtgraph is used for the spectral plot when installed and a built-in canvas takes over when it is not. The analysis core imports neither QGIS nor Qt, so the same code runs from Jupyter or a plain script. Nothing is downloaded and no remote service is contacted. ESPANOL: abre el cubo HDF-EOS5 y lo explora enlazando en tiempo real la posicion del pixel, la imagen y su firma espectral, sin convertir nada antes. Dibuja el cubo con los transectos espectrales en las caras, y esas caras siguen a la cruz. Conserva el algoritmo de extraccion a ENVI, por lotes y desde el modelador. Sin dependencias externas obligatorias.

icon=icon.png
tags=hyperspectral,hdf5,hdf-eos,envi,tanager,planet,remote sensing,spectral,spectra,signature,reflectance,raster,imagery,processing,analysis,spectral library,datacube

changelog=2.16 - QUE UN CUBO PESADO NO SE SIENTA LENTO. Medido sobre un cubo del tamano real de un Tanager -426 bandas, 655x785, HDF5 con gzip y chunks de un plano por banda, que es como circulan estos productos-, mover la cruz un pixel costaba 7,2 segundos y no mejoraba al repetirlo. El motivo esta en el archivo: con chunks de un plano por banda, leer el espectro de UN pixel obliga a descomprimir los 426 planos enteros, 400 MB de inflado para devolver 426 numeros, y cada pixel que la cruz recorre pide tres de esas lecturas -las dos caras del cubo son transectos, mas la firma-. Tres cambios, ninguno con dependencias nuevas. UNO, cache acotado por BYTES y no por piezas, con presupuesto propio para bandas, transectos y espectros: doce bandas de una escena chica son tres megabytes y doce de un Tanager son veinticinco, asi que contar piezas dejaba el consumo de memoria a merced del tamano de la escena. Se guarda sin enmascarar y la mascara se aplica al salir, asi que cambiar el filtro de vapor de agua no devuelve dato viejo. DOS, el espectro sale gratis del transecto: la cara de arriba del cubo ES el transecto en Y de la fila de la cruz y el espectro del pixel es una de sus columnas, asi que volver al disco por el era pagar dos veces el mismo dato; al reves no, un clic suelto no lee la fila entera porque sobre un ENVI mapeado en memoria seria traer cientos de veces mas dato del necesario. TRES, el arrastre se atiende al ritmo que el dato deje y no al del raton: sin freno, arrastrar medio segundo encolaba cien lecturas de cinco segundos cada una y QGIS quedaba inservible varios minutos por algo que el usuario ya habia dejado de pedir. El freno se mide solo -no empieza otro hasta que haya pasado lo que tardo el anterior- asi que con dato rapido no frena nada, y lo que se salta son los movimientos intermedios: el ultimo se guarda y se atiende al soltar, de modo que la cruz siempre acaba donde el usuario la llevo. RESULTADO MEDIDO: volver a un pixel ya visitado pasa de 7,0 segundos a 0,00, y arrastrar sesenta pixeles de 277 segundos a 14,4. Los cinco segundos que quedan en un pixel nuevo son el archivo y no el complemento: es lo que cuesta descomprimirlo. Sobre un ENVI extraido los mismos gestos cuestan milisegundos, y por eso Extraer sigue siendo la mejor inversion para trabajar mucho rato sobre una misma escena.
    2.15 - PLEGAR LO QUE AHORA MISMO NO SE ESTA MIRANDO. El panel tiene cuatro bloques y una sola columna de alto para repartir entre ellos. Segun lo que se este haciendo uno de ellos es el trabajo y los otros son contexto: al comparar firmas manda el grafico, al recorrer el cubo manda el cubo, y en ese momento la lista de firmas y el perfil son dos franjas que no se estan mirando y que le estan quitando alto a lo que si. Ahora el NOMBRE de la seccion es el boton: pulsar "Perfil espectral" o "Firmas guardadas" pliega esa seccion a su titulo y volver a pulsarlo la trae de vuelta. Es el mismo mecanismo que ya tenian los deslizadores RGB y las bandas malas, subido al titulo, y la flecha del nombre dice en que estado esta -abierta o plegada-, que es lo que distingue una seccion plegada de una seccion vacia. Plegar no es cerrar: no se reconstruye nada, las firmas siguen guardadas y sus curvas siguen en el grafico. Al plegar el perfil el divisor reparte de nuevo y ese alto se lo queda el cubo -los tamanos de un QSplitter son pegajosos y sin repartir, esconder una mitad dejaria un agujero en vez de agrandar la otra-, y al desplegarlo vuelve el reparto que el usuario tenia, no el de fabrica. NO se uso el QGroupBox marcable que Qt trae de fabrica, que seria lo obvio, porque marcarlo APAGA Y ENCIENDE a todos sus hijos: desplegar una seccion volveria a encender los botones que el panel habia apagado por no haber ninguna escena abierta, y el usuario podria pulsarlos. Dos pruebas lo fijan, una en el bloque y otra en el panel.
    2.14 - PREVENIR EL CUELGUE. Llego un informe de fallo de macOS: QGIS se cerraba de golpe -EXC_BAD_ACCESS- y la pila decia donde. El ultimo cuadro es QWidget::create, llamado desde QMainWindowLayout::animationFinished: QGIS termina la animacion de acople del panel, lo muestra, y al ir mostrando sus hijos uno a uno se topa con uno que ya no es lo que Qt creia. Ese hijo era el bloque del cubo. Para soltarlo a una ventana aparte se le ponia la bandera de ventana, y para empotrarlo de vuelta se le quitaba; eso obliga a Qt a destruir y rehacer su ventana nativa con la interfaz ya en marcha, que es justo el camino que reventaba. Ahora el cubo NUNCA se convierte en ventana: se muda a una ventana que ya existe, vacia hasta ese momento, y soltar y empotrar es solo cambiar de padre -lo que Qt hace todo el rato con un divisor o una pestana, y lo unico pensado para hacerse en caliente-. Nadie le toca las banderas nunca, y las pruebas lo comprueban literalmente asi. SEGUNDO CAMBIO EN EL MISMO CAMINO: el panel ya no hace trabajo dentro de su showEvent -que corre en mitad de esa animacion-; lo que hay que rehacer al volver a mostrarlo se aplaza un giro del bucle de eventos y sale de esa pila. TERCERO: cerrar el panel con el cubo suelto ya no cierra esa ventana ni reacomoda el arbol de widgets mientras Qt lo esta escondiendo; las esconde las dos, y al reabrir el panel el arreglo de dos ventanas vuelve tal cual, que ademas es lo que el usuario esperaba. Y al descargar el complemento la ventana de al lado se va con el, en vez de quedar flotando con un cubo ya cerrado dentro. DE PROPINA, LA MISMA LECCION EN LA SUITE: cada prueba dejaba un panel entero vivo del lado de C++, sujeto solo por Python, y el objeto de C++ moria cuando al recolector le parecia -a veces en medio de la prueba siguiente, mientras Qt construia otro arbol-. La suite se caia por segmentacion una de cada ocho corridas. Ahora cada prueba deshace su panel cuando termina: 35 corridas seguidas sin un solo fallo. Los widgets se destruyen cuando uno decide, no cuando toca.
    2.13 - ABRIR Y CERRAR SIN PERDER EL TRABAJO. Dos fallos distintos, los dos con el mismo sintoma: el cubo desaparecia y no habia forma de recuperarlo. UNO: cerrar el panel hacia el desmontaje completo -cerraba la escena, soltaba las senales del proyecto- cuando la X de un panel acoplado de QGIS solo lo esconde. Al volver a abrirlo el usuario encontraba el panel vacio: habia perdido el trabajo por pulsar una X que en ningun otro sitio de QGIS significa eso. Ahora cerrar esconde y punto; la escena, la biblioteca y el encuadre siguen ahi, y la herramienta de mapa y el vinculo vuelven solos al mostrarlo. El desmontaje de verdad corre al descargar el complemento, que es cuando de verdad se termina. DOS: la ventana suelta del cubo no volvia. Se reinsertaba en el divisor durante su propio evento de cierre, y Qt terminaba de cerrar despues y la escondia otra vez: quedaba empotrada pero invisible, sin ningun boton que dijera "traela de vuelta". Ahora el empotrado se aplaza al siguiente giro del bucle de eventos, con un temporizador hijo del panel para que no pueda dispararse sobre un panel ya borrado.
    2.12 - VINCULAR EL CUBO CON EL MAPA. En el cubo se ve el detalle espectral pero no el contexto; en el mapa esta el contexto -una ortofoto de alta resolucion, el catastro- pero no el espectro. El boton "Vincular con el mapa" hace que los dos miren lo mismo: al acercarse o desplazarse en uno, el otro va detras, y lo que se esta midiendo deja de ser un parche de colores para ser un sitio reconocible. Respeta el SRC de cada lado y la rotacion de la escena -con una escena inclinada, el rectangulo de pixeles es un rombo en el terreno y se usa su caja envolvente-. Necesita georreferencia: sin ella el boton se queda apagado y se dice por que, y con una escena en geometria de sensor avisa de que la correspondencia es aproximada. Lo dificil no era la geometria sino el bucle de realimentacion -cada lado avisa y el aviso mueve al otro, que avisa a su vez-, que no falla con una excepcion sino colgando QGIS; se corta por dos vias a la vez y la prueba que lo cubre revienta si se quita cualquiera de las dos. El vinculo se apaga solo al cambiar de escena o cerrar el panel.
    2.11 - DESBLOQUEA LA PUBLICACION. El repositorio de complementos bloqueaba la version por cuatro hallazgos de bandit -un except amplio seguido de pass o continue, cuatro veces- y por dos importaciones directas de PyQt5. Los cuatro except se arreglaron nombrando lo que de verdad puede fallar en cada sitio, no callando el aviso: la reproyeccion de un punto atrapa ahora QgsCsException, el respaldo de GDAL atrapa lo que GDAL lanza, y la apertura de un archivo GUARDA la razon de cada lector que falla en vez de tirarla -antes, si fallaban los tres, el usuario recibia solo la queja del ultimo sobre un archivo que a lo mejor era un HDF5 con un cubo que no se encontro; ahora salen las tres razones-. Las importaciones de PyQt del respaldo se resuelven con importlib: dentro de QGIS esa rama no se ejecuta nunca -manda qgis.PyQt- y existe solo para poder importar la vista sin QGIS abierto, que es lo unico que permite probarla. Las dos comprobaciones entran en el ciclo de siempre: bandit en verificar.sh y en CI, y una prueba que rechaza cualquier importacion directa de PyQt en el complemento. Sin cambios en el funcionamiento.
    2.10 - CORRIGE UN FALLO DE CARGA DE LA 2.9: el ZIP salio sin compat.py y el complemento no arrancaba, con "No module named 'hdfeos_extractor.compat'" al llamar a classFactory. El empaquetador tenia la lista de modulos escrita a mano, se agrego un modulo nuevo y nadie la actualizo. Ahora los modulos van por comodin, y dos comprobaciones nuevas lo vigilan: una compara el contenido del ZIP contra los archivos en disco -una lista no puede comprobar a otra lista- y otra lee todos los imports relativos del ZIP y exige que resuelvan dentro, que es hablar el mismo idioma que el error. Las dos se probaron contra el ZIP roto de la 2.9 antes de darlas por buenas. Sin cambios en el funcionamiento respecto a la 2.9.
    2.9 - COMPATIBLE CON Qt6. El verificador del repositorio de complementos marcaba tres enums en algoritmo.py. Eran las ramas de respaldo de un try/except que en Qt6 no se ejecutan, asi que el comportamiento ya era correcto; pero el verificador lee el codigo sin ejecutarlo y no puede saberlo, y con razon: un nombre plano en el fuente es un nombre plano. Se resuelven ahora con un ayudante que recibe el grupo y el miembro como cadenas, sin nada plano que ver. Al revisar el resto aparecieron 46 mas que el verificador NO mira, porque solo revisa los enums de QGIS: son de PyQt -Qt.Horizontal, QSizePolicy.Expanding, QFrame.NoFrame, Qt.Checked y companía- y en Qt6 tampoco existen. Ninguno falla al importar: fallan al abrir el panel, a media construccion de la interfaz. Los 49 pasan por el mismo resolutor, que prueba la forma calificada primero y deja la plana de respaldo, asi que se sigue corriendo en QGIS 3.16. QAction se importa de QtGui, que es donde Qt6 lo puso. Y para que no vuelva a colarse, dos comprobaciones nuevas en el ciclo de siempre: una lee el arbol sintactico de todo el complemento y falla si encuentra un solo enum sin calificar, y otra construye la interfaz con PyQt6 de verdad y la dibuja -lo que el detector estatico no vea, se cae al pintar-. Las dos corren en CI, en un trabajo aparte, porque la vinculacion de Qt se elige una vez por proceso y PyQt5 y PyQt6 no conviven en la misma sesion.
    2.8 - LA ESCENA SE IBA A GALAPAGOS, Y ERAN DOS FALLOS EN EL MISMO CAMINO, LOS DOS DEL RESPALDO DE GDAL. Uno: el StructMetadata de HDF-EOS no es un atributo sino un dataset de texto, asi que no aparece entre los metadatos de la raiz y el lector por GDAL devolvia None. El producto traia su georreferencia y el explorador no la veia por el unico motivo de que h5py no estuviera instalado, que es justo el caso de QGIS en macOS. Ahora se lee con la API multidimensional de GDAL, que si alcanza los datasets de texto. Dos: cuando la geotransformacion venia del driver de GDAL, su descripcion de la proyeccion es PROJCS["unnamed"] con el datum "Not specified" y sin codigo de autoridad. Es geometricamente correcta -es UTM 16N- pero QGIS no la casa con ningun sistema conocido, le aplica el SRC del proyecto, y ahi los metros pasan a leerse como grados. Ahora el codigo EPSG que declara el productor -Tanager lo pone en epsg_code, al lado del grid- rellena cualquier SRC que no se identifique, y al escribir la capa un WKT completo gana al codigo, el codigo gana a un WKT anonimo, y un WKT anonimo gana a no poner nada. Comprobado contra el archivo real por los dos caminos, con h5py y sin el: la misma geotransformacion y EPSG:32616 identificable en los dos.
    2.7 - VERIFICADO CONTRA EL PRODUCTO REAL. Con el archivo delante -20250223_165546_32_4001_ortho_sr_hdf5.h5, un Tanager ortho de 785x655 y 426 bandas- se comprobo la cadena entera: el grid del StructMetadata da (330480, 30, 0, 1472610, 0, -30) y EPSG:32616, la capa enviada al mapa coincide esquina a esquina con lo que declara el archivo, y la escena cae donde tiene que caer, en UTM 16N sobre la bahia de Jiquilisco. Ese encabezado real queda como prueba de regresion: una cabecera sintetica comprueba mi idea del formato, y esta comprueba el formato. Ademas, el codigo EPSG se busca tambien en los atributos aplanados por GDAL -Tanager lo declara aparte, como epsg_code del grupo del grid-, que es el nombre con el que llega por el camino de GDAL y con el que antes no se encontraba. Y se reconoce el bloque JSON de encuadre que algunos productos traen (la incidencia 12774 de GDAL): se busca por su contenido y no por el nombre del atributo, y se descarta si describe una imagen de otro tamano que el cubo, porque un encuadre ajeno aterriza cerca, que es la peor clase de error porque parece bien.
    2.6 - Cuarto camino para encontrar la georreferencia: si ninguno de los anteriores da con ella, se le pregunta al driver de GDAL, que lleva anos leyendo dialectos de HDF5 georreferenciado y a veces reconoce uno que este plugin no. Va el ultimo a proposito: cuando los caminos anteriores dan algo, ese algo viene del producto y no de la interpretacion de un driver.
    2.5 - EL PRODUCTO ORTORECTIFICADO SE UBICA SOLO. Faltaba el caso mas comun de todos. Un producto ortho se guarda como GRID de HDF-EOS: ya esta puesto sobre una proyeccion, trae una geotransformacion exacta en el StructMetadata y NO trae capas de latitud y longitud, porque no le hacen falta. El explorador solo sabia buscar esas capas, asi que no encontraba nada y una escena perfectamente ubicada en el archivo aterrizaba en coordenadas de pixel. Ahora se lee el StructMetadata -esquinas, tamano, proyeccion, zona y esferoide- y se prueba ANTES que las capas de lat/lon: cuando hay afin exacta no se remuestrea nada. Se traducen UTM norte y sur, y geograficas, incluido el detalle de que GCTP guarda las geograficas en microgrados -tomarlas como grados manda la escena a una longitud de setenta millones-. Una proyeccion que no se sepa traducir conserva igual las coordenadas y se avisa de que hay que asignar el SRC a mano. Comprobado de punta a punta: un cuadrado en un pixel conocido cae a 0,00 m de su coordenada UTM, entero y con zoom. Y porque un ortho puede llevar su afin de tres formas y ninguna incluye lat/lon, se reconocen las tres: el StructMetadata de HDF-EOS, una geotransformacion escrita como atributo -lo que hace rioxarray- y dos vectores con la coordenada del centro de cada fila y columna -la convencion CF-. El sistema de referencia se busca por todo el vocabulario en uso, porque el mismo dato aparece como crs_wkt, spatial_ref, EPSG o esri_pe_string segun quien escribiera el archivo, y exigir un nombre concreto es quedarse sin georreferencia por una cuestion de vocabulario. EXTRAER A ENVI YA NO PIERDE LA UBICACION: el cubo extraido de un grid sale con su map info, que antes no se escribia. LA GEOLOCALIZACION PUEDE VENIR EN OTRA RESOLUCION: hay productos que guardan lat/lon en una rejilla mas gruesa que la escena; el punto (f, c) de esa rejilla no es el pixel (f, c) del cubo, y tomarlo como si lo fuera dejaba la escena del tamano de la rejilla. Y cuando no se encuentra georreferencia, el aviso dice ahora QUE se busco y QUE habia -si hay StructMetadata, que capas 2D trae el archivo-, porque "no hay georreferencia" no se puede depurar.
    2.4 - PROYECCION. El envio de la vista a QGIS aterrizaba corrido, y la causa estaba antes del envio: la unica fuente de coordenadas era la extension de la capa de QGIS dividida por su tamano en pixeles, y eso solo acierta con una escena al norte y sin rotacion. Una escena hiperespectral rara vez lo esta. Ahora la georreferencia se lee del propio archivo y se distinguen tres casos: con geotransformacion afin -map info de ENVI, o la que traiga GDAL- se escribe tal cual, con sus terminos de rotacion, corrida al recorte y multiplicada por el submuestreo, sin tocar ningun pixel; en geometria de sensor no hay afin que la describa, asi que se sacan puntos de control de las capas de latitud y longitud del HDF-EOS5 y se REPROYECTA de verdad, por placa delgada, hacia una rejilla al norte; y sin georreferencia se dice y no se inventa un sistema de referencia, porque una capa con un SRC falso se ve bien puesta y esta mal. La vista exportada sale ademas con banda de transparencia: el relleno salia negro y tapaba el mapa de fondo. Comprobado de punta a punta: un cuadrado en un pixel conocido de una franja inclinada 20 grados cae a 3,5 m de su coordenada real, sobre un pixel de 56 m. Corregido tambien el desfase de hasta un paso entero al exportar con submuestreo, y la herramienta de mapa, que reproyectaba contra el SRC de la capa: con un HDF-EOS5 abierto directo no hay capa y las coordenadas son geograficas, asi que la marca del pixel se iba a miles de kilometros en un proyecto en UTM. INTERFAZ: el cubo vuelve al panel, junto al perfil espectral, separados por un divisor que el usuario reparte; son las dos caras del mismo dato y mirarlas a la vez es el trabajo. El reparto se acuesta o se apila segun donde este acoplado el panel, y el cubo se puede soltar a una ventana aparte cuando hay dos pantallas -cerrarla lo devuelve-. El panel entero baja de 642 a 399 pixeles de ancho minimo, menos que antes de traer el cubo. La barra de color ya no corta sus numeros: se ensancha segun lo que midan, y el exponente va compacto.
    2.3 - El cubo pasa a tener ventana propia, con las herramientas en una columna al costado: dentro del panel acoplado competia por el alto con los controles, el grafico y la lista, y eso le dejaba doscientos pixeles a lo que es el centro del trabajo. La ventana se mueve, se agranda y se manda a otra pantalla. Navegacion normal: herramientas Desplazar y Acercar, arrastre con el boton central en cualquier herramienta, rueda para acercar y alejar, botones de mas y menos, ver todo y ajustar a los datos. El boton derecho vuelve a ver todo -antes arrastraba un rectangulo de zoom, un gesto que no existe en ninguna otra parte de QGIS-. NUEVO "Enviar vista a QGIS": agrega al mapa solo el RGB visible, recortado a la vista y con el realce puesto, como GeoTIFF de tres bandas y 8 bits; no carga el cubo, asi que se puede digitalizar encima o componer un mapa mientras se sigue midiendo espectros en la ventana del cubo. El panel acoplado queda con lo que conviene tener junto al mapa: la escena, el perfil espectral con las bandas malas, y la biblioteca de firmas.
    2.2 - La ventana del cubo pasa a ser la interfaz principal. Barra de color con la escala del dato: sin ella el arcoiris de las caras era decorativo, se veia que una zona era distinta de otra pero no cuanto. Zoom: boton derecho arrastrando acerca a ese rectangulo, sin arrastrar o con la rueda aleja, y "Ajustar a los datos" encuadra lo que tiene dato dejando fuera el relleno y los ceros; al acercarse se recalculan el realce y la escala de color sobre lo visible, que con una franja de sensor rodeada de ceros es la diferencia entre ver el terreno y verlo aplastado en una banda de grises. Los cuatro modos de navegacion ahora funcionan tambien dentro del cubo, y ahi se nota la diferencia entre ellos: antes solo actuaban sobre el lienzo de QGIS y con un HDF-EOS5 abierto no hay capa ahi, asi que ninguno hacia nada. Modo "Pixeles" nuevo: cada clic suma un pixel y la firma sale del promedio del conjunto, con su variabilidad. La composicion pasa de 176 pixeles de alto permanentes a una sola fila -los deslizadores quedan detras de un boton- y la biblioteca pierde su segunda fila de botones, asi las dos vistas se quedan con el alto. CORRIGE un fallo al cerrar o cambiar de capa: close_cube cerraba el cubo y despues emitia la senal de composicion, y la vista del cubo, que todavia apuntaba al cubo cerrado, reventaba con "AttributeError: 'NoneType' object has no attribute 'read_band'". Ademas cambiar de escena ahora cierra la anterior, que antes dejaba su memoria mapeada viva y su archivo bloqueado en Windows.
    2.1 - Bandas malas. Un cubo hiperespectral siempre trae bandas que no sirven, y arrastrarlas no solo ensucia el grafico: entran en la media, en la desviacion y en el angulo espectral como si fueran mediciones. Ahora se descartan por cuatro criterios que se combinan y se encienden por separado -la lista bbl que trae el archivo, las ventanas de absorcion de vapor de agua (1340-1460 y 1790-1960 nm), los extremos del rango, y rangos escritos a mano-. La banda descartada sale como NaN y todo el resto la ignora solo: el grafico corta la curva y sombrea el tramo para que se vea que ahi falta el dato, nanmean la saltea, el angulo espectral la excluye y el realce del cubo deja de estar sesgado por el ruido. get_band NO se enmascara, a proposito: la mascara limpia el analisis, no impide mirar una banda. El compositor RGB evita caer en una banda descartada, que devolveria ruido puro y una imagen con textura que no existe en el terreno. Sin eje espectral en nanometros los criterios por longitud de onda se apagan solos.
    2.0 - El extractor y el explorador son un solo plugin, porque son un solo flujo: quien extrae un cubo lo hace para mirarlo. Panel acoplable nuevo que abre el HDF-EOS5 DIRECTAMENTE, sin extraer nada antes, y enlaza posicion, imagen y espectro: clic en un pixel y aparece su firma, linea de muestreo en X o en Y con media, minimo y maximo del transecto, firma media de area con envolvente de desviacion. Vista del cubo hiperespectral en proyeccion oblicua, con las dos caras laterales mostrando los transectos que pasan por la cruz -no el borde de la escena-, las bandas del RGB marcadas dentro de las caras y clic en un costado para llevar esa banda al frente. Composicion RGB por longitud de onda con presets filtrados segun el rango del sensor y cuatro modos de realce. Biblioteca espectral con nombre, notas, procedencia, comparacion por angulo espectral, guardado en JSON y exportacion a CSV. Boton para extraer a ENVI la escena abierta, que abre el algoritmo de siempre con el archivo ya puesto. CORRECCION: la cabecera ENVI anunciaba el valor de relleno crudo junto a datos ya escalados, asi que con scale != 1 esos pixeles no se enmascaraban nunca y entraban en el realce y en las estadisticas como si fueran medidas. El nucleo de analisis no importa QGIS ni Qt y se prueba aparte: 267 pruebas.
    1.0 - Version inicial. Algoritmo "HDF-EOS5 to ENVI" en la Caja de herramientas: extrae el cubo a ENVI BIL con wavelength, fwhm, data ignore value y bbl (ventanas de absorcion de vapor de agua marcadas); escribe el IGM de geolocalizacion para productos basic en geometria de sensor; apila las capas auxiliares 2D en un multibanda con nombres. Lectura por bloques para no cargar el cubo entero en memoria, con avance y cancelacion. Backend h5py con respaldo automatico en GDAL.

tracker=https://github.com/csoto666/qgis-hdfeos-extractor/issues
repository=https://github.com/csoto666/qgis-hdfeos-extractor
homepage=https://github.com/csoto666/qgis-hdfeos-extractor

category=Raster
hasProcessingProvider=yes
experimental=False
deprecated=False
