[EDITAR 1 - Cambié la búsqueda de coordenadas de píxeles]
Usando esta muestra de MODATML que proporcionó y usando la biblioteca gdal. Abramos el hdf con gdal:
import gdal
dataset = gdal.Open(r"E:\modis\MODATML2.A2018182.0800.061.2018182195418.hdf")
Luego, queremos ver cómo se nombran los subdatasets para importar correctamente los que necesitamos:
datasets_meta = dataset.GetMetadata("SUBDATASETS")
Esto devuelve un diccionario:
datasets_meta
>>>{'SUBDATASET_1_NAME': 'HDF4_EOS:EOS_SWATH:"E:\\modis\\MODATML2.A2018182.0800.061.2018182195418.hdf":atml2:Cloud_Optical_Thickness',
'SUBDATASET_1_DESC': '[406x271] Cloud_Optical_Thickness atml2 (16-bit integer)',
'SUBDATASET_2_NAME':'HDF4_EOS:EOS_SWATH:"E:\\modis\\MODATML2.A2018182.0800.061.2018182195418.hdf":atml2:Cloud_Effective_Radius',
'SUBDATASET_2_DESC': '[406x271] Cloud_Effective_Radius atml2 (16-bit integer)',
[....]}
Digamos que queremos obtener la primera variable, el grosor óptico de la nube, podemos acceder a su nombre de la siguiente manera:
datasets_meta['SUBDATASET_1_NAME']
>>>'HDF4_EOS:EOS_SWATH:"E:\\modis\\MODATML2.A2018182.0800.061.2018182195418.hdf":atml2:Cloud_Optical_Thickness'
Ahora podemos cargar la variable en la memoria llamando nuevamente. Método Open ():
Cloud_opt_th = gdal.Open(datasets_meta['SUBDATASET_1_NAME'])
Por ejemplo, puede acceder a Precipitable_Water_Infrared_ClearSky que le interesa proporcionando 'SUBDATASET_20_NAME'. Solo eche un vistazo al diccionario datasets_meta.
Sin embargo, la variable extraída no tiene una geoproyección (var.GetGeoprojection ()) como cabría esperar de otros tipos de archivos como GeoTiff. Puede cargar la variable como una matriz numpy y trazar la variable 2d sin proyección:
Cloud_opt_th_array = Cloud_opt_th.ReadAsArray()
import matplotlib.pyplot as plt
plt.imshow(Cloud_opt_th_array)
Ahora, como no hay geoproyección, analizaremos los metadatos de la variable:
Cloud_opt_th_meta = Cloud_opt_th.GetMetadata()
Este es otro diccionario que incluye toda la información que necesita, incluida una descripción larga del submuestreo (noté que solo se proporciona con el primer subdataset), que incluye la explicación de estos Cell_Along_Swath:
Cloud_opt_th_meta['1_km_to_5_km_subsampling_description']
>>>'Each value in this dataset does not represent an average of properties over a 5 x 5 km grid box, but rather a single sample from within each 5 km box. Normally, pixels in across-track rows 4 and 9 (counting in the direction of increasing scan number) out of every set of 10 rows are used for subsampling the 1 km retrievals to a 5 km resolution. If the array contents are determined to be all fill values after selecting the default pixel subset (e.g., from failed detectors), a different pair of pixel rows is used to perform the subsampling. Note that 5 km data sets are centered on rows 3 and 8; the default sampling choice of 4 and 9 is for better data quality and avoidance of dead detectors on Aqua. The row pair used for the 1 km sample is always given by the first number and last digit of the second number of the attribute Cell_Along_Swath_Sampling. The attribute Cell_Across_Swath_Sampling indicates that columns 3 and 8 are used, as they always are, for across-track sampling. Again these values are to be interpreted counting in the direction of the scan, from 1 through 10 inclusively. For example, if the value of attribute Cell_Along_Swath_Sampling is 3, 2028, 5, then the third and eighth pixel rows were used for subsampling. A value of 4, 2029, 5 indicates that the default fourth and ninth rows pair was used.'
Creo que esto significa que, en base a estos píxeles de 1 km, los 5 km se construyeron tomando exactamente los valores de píxeles en una determinada posición en la matriz de detección de 5x5 (la posición se indica en los metadatos, creo que esto es un instrumento para reducir las fallas).
De todos modos, en este punto tenemos una matriz de celdas de 1x1 km (ver descripción de submuestreo arriba, no estoy seguro de la ciencia detrás de esto). Para obtener las coordenadas de cada centroide de píxeles, necesitamos cargar los subdatasets de latitud y longitud.
Latitude = gdal.Open(datasets_meta['SUBDATASET_66_NAME']).ReadAsArray()
Longitude = gdal.Open(datasets_meta['SUBDATASET_67_NAME']).ReadAsArray()
Por ejemplo,
Longitude
>>> array([[-133.92064, -134.1386 , -134.3485 , ..., -154.79303, -154.9963 ,
-155.20723],
[-133.9295 , -134.14743, -134.3573 , ..., -154.8107 , -155.01431,
-155.2256 ],
[-133.93665, -134.1547 , -134.36465, ..., -154.81773, -155.02109,
-155.23212],
...,
[-136.54477, -136.80055, -137.04684, ..., -160.59378, -160.82101,
-161.05663],
[-136.54944, -136.80536, -137.05179, ..., -160.59897, -160.8257 ,
-161.06076],
[-136.55438, -136.81052, -137.05714, ..., -160.6279 , -160.85527,
-161.09099]], dtype=float32)
Puede notar que las coordenadas de latitud y longitud son diferentes para cada píxel.
Digamos que su observatorio está ubicado en las coordenadas lat_obs, long_obs, de modo que minimice la diferencia de coordenadas x, y:
coordinates = np.unravel_index((np.abs(Latitude - lat_obs) + np.abs(Longitude - long_obs)).argmin(), Latitude.shape)
y extrae tu valor
Cloud_opt_th_array[coordinates]