[EDIT 1 - Ho cambiato la ricerca delle coordinate pixel]
Utilizzando questo esempio di MODATML fornito e utilizzando la libreria gdal. Apriamo l'hdf con gdal:
import gdal
dataset = gdal.Open(r"E:\modis\MODATML2.A2018182.0800.061.2018182195418.hdf")
Quindi vogliamo vedere come vengono denominati i sotto-dataset per importare correttamente quelli di cui abbiamo bisogno:
datasets_meta = dataset.GetMetadata("SUBDATASETS")
Questo restituisce un dizionario:
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)',
[....]}
Diciamo che vogliamo ottenere la prima variabile, lo spessore ottico della nuvola, possiamo accedere al suo nome tramite:
datasets_meta['SUBDATASET_1_NAME']
>>>'HDF4_EOS:EOS_SWATH:"E:\\modis\\MODATML2.A2018182.0800.061.2018182195418.hdf":atml2:Cloud_Optical_Thickness'
Ora possiamo caricare nuovamente la variabile in memoria chiamando il metodo .Open ():
Cloud_opt_th = gdal.Open(datasets_meta['SUBDATASET_1_NAME'])
Ad esempio, puoi accedere a Precipitable_Water_Infrared_ClearSky a cui sei interessato fornendo "SUBDATASET_20_NAME". Dai un'occhiata al dizionario datasets_meta.
Tuttavia, la variabile estratta non ha una geoproiezione (var.GetGeoprojection ()) come ci si aspetterebbe da altri tipi di file come GeoTiff. È possibile caricare la variabile come una matrice numpy e tracciare la variabile 2d senza proiezione:
Cloud_opt_th_array = Cloud_opt_th.ReadAsArray()
import matplotlib.pyplot as plt
plt.imshow(Cloud_opt_th_array)
Ora, poiché non esiste alcuna geoproiezione, esamineremo i metadati della variabile:
Cloud_opt_th_meta = Cloud_opt_th.GetMetadata()
Questo è un altro dizionario che include tutte le informazioni necessarie, inclusa una lunga descrizione del sottocampionamento (ho notato che questo è fornito solo con il primo sottodataset), che include la spiegazione di questi 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.'
Penso che ciò significhi che sulla base di questi pixel da 1km i 5km sono stati costruiti prendendo esattamente i valori dei pixel in una certa posizione nell'array di rilevamento 5x5 (la posizione è indicata nei metadati, penso che questa sia una cosa strumentale per ridurre i guasti).
Ad ogni modo, a questo punto abbiamo una matrice di celle 1x1 km (vedi la descrizione del sottocampionamento sopra, non sono sicuro della scienza dietro di essa). Per ottenere le coordinate di ciascun centroide pixel, è necessario caricare i sottodataset di latitudine e longitudine.
Latitude = gdal.Open(datasets_meta['SUBDATASET_66_NAME']).ReadAsArray()
Longitude = gdal.Open(datasets_meta['SUBDATASET_67_NAME']).ReadAsArray()
Per esempio,
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)
È possibile notare che le coordinate di latitudine e longitudine sono diverse per ciascun pixel.
Supponiamo che il tuo osservatorio si trovi alle coordinate lat_obs, long_obs, quindi minimizzi la differenza di coordinate x, y:
coordinates = np.unravel_index((np.abs(Latitude - lat_obs) + np.abs(Longitude - long_obs)).argmin(), Latitude.shape)
ed estrai il tuo valore
Cloud_opt_th_array[coordinates]