Saltar al contenido

Convierta datos HDF5 LiDAR (de GEDI) a LAS o LAZ

Ya no busques más por otras webs porque estás al espacio necesario, poseemos la solución que necesitas encontrar sin complicarte.

Solución:

No es posible convertir GEDI .h5 archivo a archivo LAS incluyendo todos los datos. Porque .h5 El archivo incluye mucha información sobre un punto (en realidad es una ventana en GEDI .h5 formato, no un punto). Además, dado que el archivo LAS tiene ciertos atributos para un punto que no coincide con los atributos / valores en .h5 archivo, no puede agregar toda la información al archivo LAS. Por ejemplo, qué valor en .h5 el archivo coincide Z valor en archivo LAS, elevation_bin0 o elevation_lastbin? Etc.

No hay una sola forma / método para convertir .h5 archivo a otro formato. Depende de la estructura de datos incluida en .h5 expediente. Varía de .h5 para .h5. Por lo tanto, debe decidir los atributos que le gustaría utilizar.

Utilizando h5py paquete, puede leer fácilmente h5 expediente. En primer lugar, examinemos la estructura del archivo GEDI .h5.

import h5py
import pandas as pd
import numpy as np

file_path = "path/to/GEDI01_B_2019108002011_O01959_T03909_02_003_01.h5"
f = h5py.File(file_path, 'r')
print(list(f.keys()))

# OUT
# ['BEAM0000', 'BEAM0001', 'BEAM0010', 'BEAM0011', 'BEAM0101', 'BEAM0110', 'BEAM1000', 'BEAM1011', 'METADATA']

Hay 8 BEAMXXXX grupos y 1 METADATA grupo. Ahora, veamos todos los conjuntos de datos en todos los grupos.

def get_h5_structure(f, level=0):
    """    prints structure of hdf5 file    """
    for key in f.keys():
        if isinstance(f[key], h5py._hl.dataset.Dataset):
            print(f"'  '*level DATASET: f[key].name")
        elif isinstance(f[key], h5py._hl.group.Group):
            print(f"'  '*level GROUP: key, f[key].name")
            level += 1
            get_h5_structure(f[key], level)
            level -= 1

        if f[key].parent.name == "/":
            print("n"*2)

get_h5_structure(f)

### OUTPUT: (removed some lines) ###
# GROUP: ('BEAM0000', '/BEAM0000')
#   DATASET: /BEAM0000/all_samples_sum
#   GROUP: ('ancillary', '/BEAM0000/ancillary')
#     DATASET: /BEAM0000/ancillary/master_time_epoch
#     DATASET: /BEAM0000/ancillary/mean_samples
#     DATASET: /BEAM0000/ancillary/smoothing_width
#   DATASET: /BEAM0000/beam
#   DATASET: /BEAM0000/channel
#   DATASET: /BEAM0000/delta_time
#   GROUP: ('geolocation', '/BEAM0000/geolocation')
#     DATASET: /BEAM0000/geolocation/altitude_instrument
#     DATASET: /BEAM0000/geolocation/altitude_instrument_error
#     DATASET: /BEAM0000/geolocation/bounce_time_offset_bin0
#     ...
#
# GROUP: ('BEAM0001', '/BEAM0001')
# ...
#  
# GROUP: ('METADATA', '/METADATA')
#   GROUP: ('DatasetIdentification', '/METADATA/DatasetIdentification')

NOTA: Usaré conjuntos de datos en ‘BEAM0000’ como ejemplo. Para otro grupo BEAMXXXX, debe cambiar group variable.

group = "BEAM0000"

# number_of records
n = f[group]["all_samples_sum"].shape[0]
print(n)
# OUT: 249810

Busquemos las claves que tienen 249810 (n) registros. Formaremos un DataFrame usando estas claves. Dado que hay dos niveles anidados, dos for los bucles son suficientes.

df = pd.DataFrame()

for k, v in f[group].items():
    if isinstance(v, h5py._hl.dataset.Dataset):
        if v.shape[0] == n:
            df[k] = v
    else: # if not dataset, it's group
        # iterate on datasets of the group
        for k2, v2 in v.items():
            if v2.shape[0] == n:
                df[k2] = v2

print(df.head())

### OUTPUT
#    all_samples_sum   beam  channel    delta_time   altitude_instrument  ...  tx_gloc  tx_gloc_error  tx_pulseflag  tx_sample_count  tx_sample_start_index  
# 0         16167838     0        0   4.078333e+07        411250.214378  ...      0.0            0.0             0              128                      1
# 1         16165121     0        0   4.078333e+07        411250.181709  ...      0.0            0.0             0              128                    129
# 2         16180451     0        0   4.078333e+07        411250.149040  ...      0.0            0.0             0              128                    257
# 3         16181775     0        0   4.078333e+07        411250.116372  ...      0.0            0.0             0              128                    385
# 4         16159591     0        0   4.078333e+07        411250.083705  ...      0.0            0.0             0              128                    513
# [5 rows x 77 columns]

surface_type, rxwaveform y txwaveform están perdidos. Hasta donde yo entiendo, rxwaveform y txwaveform son las claves más importantes de los datos.

Agreguemos surface_type, rxwaveform y txwaveform para df. Tenga en cuenta que cada uno no es un valor único, sino una lista de un punto. (Ver las últimas 3 columnas)

df["surface_type"] = [s_type for s_type in zip(*f[group]["geolocation"]["surface_type"][:,:])]

rxwaveform = f[group]["rxwaveform"][:]
rx_sample_count = f[group]["rx_sample_count"][:]
rx_split_index = f[group]["rx_sample_start_index"][:]-1
df["rxwaveform"] = [ rxwaveform[x:x+i] for x, i in zip(rx_split_index, rx_sample_count) ]

txwaveform = f[group]["txwaveform"][:]
tx_sample_count = f[group]["tx_sample_count"][:]
tx_split_index = f[group]["tx_sample_start_index"][:]-1
df["txwaveform"] = [ txwaveform[x:x+i] for x, i in zip(tx_split_index, tx_sample_count) ]

print(df)

# OUTPUT
#          all_samples_sum  beam  channel    delta_time  altitude_instrument   altitude_instrument_error  .....  tx_pulseflag  tx_sample_count   tx_sample_start_index                  rxwaveform                   txwaveform      surface_type  
#  0              16167838     0        0  4.078333e+07        411250.214378                    0.223205  .....             0              128                       1   [245.59883, 245.52516,...    [246.21742, 246.26566,...   (0, 1, 0, 0, 0)  
#  1              16165121     0        0  4.078333e+07        411250.181709                    0.223205  .....             0              128                     129   [246.6961, 247.62282, ...    [246.30019, 245.81613,...   (0, 1, 0, 0, 0)  
#  ...                 ...   ...      ...           ...                  ...                         ...  .....           ...              ...                     ...                         ...                          ...               ...  
#  249808         16103852     0        0  4.078712e+07        423272.175929                    0.213935  .....             0              128                31975425   [245.15685, 245.5818, ...    [247.31981, 247.07133,...   (0, 1, 0, 0, 0)  
#  249809         16123677     0        0  4.078712e+07        423272.235064                    0.213935  .....             0              128                31975553   [245.64854, 244.94704,...    [247.12299, 247.5319, ...   (0, 1, 0, 0, 0)  
#  
#  [249810 rows x 80 columns]

No sé qué significan estos valores, por lo tanto, cómo usar df depende de ti.


Todo el guión necesario:

import h5py
import pandas as pd
import numpy as np

file_path = "path/to/GEDI01_B_2019108002011_O01959_T03909_02_003_01.h5"
f = h5py.File(file_path, 'r')

group = "BEAM0000"
n = f[group]["all_samples_sum"].shape[0]

df = pd.DataFrame()    
for k, v in f[group].items():
    if isinstance(v, h5py._hl.dataset.Dataset):
        if v.shape[0] == n:
            df[k] = v
    else: # if not dataset, it's group
        # iterate on datasets of the group
        for k2, v2 in v.items():
            if v2.shape[0] == n:
                df[k2] = v2

rxwaveform = f[group]["rxwaveform"][:]
rx_sample_count = f[group]["rx_sample_count"][:]
rx_split_index = f[group]["rx_sample_start_index"][:]-1
df["rxwaveform"] = [ rxwaveform[x:x+i] for x, i in zip(rx_split_index, rx_sample_count)]

txwaveform = f[group]["txwaveform"][:]
tx_sample_count = f[group]["tx_sample_count"][:]
tx_split_index = f[group]["tx_sample_start_index"][:]-1
df["txwaveform"] = [ txwaveform[x:x+i] for x, i in zip(tx_split_index, tx_sample_count)]

df["surface_type"] = [s_type for s_type in zip(*f[group]["geolocation"]["surface_type"][:,:])]

Si lo prefiere, puede guardar df como shapefile.

import geopandas as gpd

# 2000 sample records
df2 = df[-6000:-4000]

# convert lists to string not to get error
df2['rxwaveform'] = df2['rxwaveform'].apply(str)
df2['txwaveform'] = df2['txwaveform'].apply(str)
df2['surface_type'] = df2['surface_type'].apply(str)

geometries = gpd.points_from_xy(df2.longitude_bin0, df2.latitude_bin0)
gdf = gpd.GeoDataFrame(df2, geometry=geometries)
gdf.crs = '+init=epsg:4326' # WGS84
gdf.to_file("c:/path/to/output.shp")

ingrese la descripción de la imagen aquí

Yo usaría el rhdf5 biblioteca dentro de R para abrir la .h5 archivo y luego vincule cada atributo que le interese a las coordenadas colocadas. Luego, genere los datos en ASCII o .csv. Esto le permitirá importar los datos relevantes como una nube de puntos en QGIS. También recomendaría buscar en el software CloudCompare para cualquier análisis de nube de puntos. A continuación se muestra una muestra del código que escribí para manejar esto:

library(rhdf5)

#You can view the attribute information by using this method
h5ls(file.choose())

#Once you find the attribute you are looking for, use the path to
#direct the h5read function by setting it to the name variable
h5ImageAttribute <- h5read(file = file.choose(), name = "attributeName")
h5ImageY <- h5read(file = file.choose(), name = "attributeLatY")
h5ImageX <- h5read(file = file.choose(), name = "attributeLonX")
h5ImageZ <- h5read(file = file.choose(), name = "attributeHeightZ")

df <- data.frame(h5ImageAttribute, h5ImageY, h5ImageX, h5ImageZ)

write.table(df, file = fileName.xyz, append = T, row.names = F)

Necesitará información sobre los datos de los atributos para dirigir la h5read función, pero todo esto se incluirá dentro de la .xml datos. Si estás bien con que esté en .xyz formato, esto debería servirle bien.

Editar: código actualizado y referencia a .las y .xyz formatos de archivo

Comentarios y calificaciones del tutorial

Puedes añadir valor a nuestro contenido contribuyendo tu veteranía en los comentarios.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *