Извлечение значений на определенной широте, долготе из данных полосы MODIS

9

Я пытаюсь определить количество осаждаемого водяного пара (PWV), озона и аэрозолей в зависимости от времени в определенных точках Земли, а именно в наших астрономических обсерваториях. Для этого у меня уже есть код Python, modapsclientкоторый используется для загрузки продуктов MODIS Aqua и Terra MYDATML2 и MODATML2 два раза в день, которые охватывают интересующую меня широту и долготу.

В чем я не уверен, так это в том, как извлечь конкретные величины, которые мне нужны, такие как время, когда были взяты данные MODIS, и PWV для конкретной широты и долготы моей обсерватории, чтобы превратить их во временные ряды значений. Продукты MYDATML2, кажется, содержат двумерные сетки широты и долготы, Cell_Along_Swath_5kmи Cell_Across_Swath_5kmпоэтому я полагаю, что это делает данные в отдельности в отличие от данных плитки или сетки? Величины, которые я хочу, такие как, Precipitable_Water_Infrared_ClearSkyкажется, также против Cell_Along_Swath_5kmи, Cell_Across_Swath_5kmно я не уверен, как получить это значение PWV в конкретном lat, долго меня интересует. Помогите, пожалуйста?

astrosnapper
источник
Можете ли вы предоставить ссылку на изображение или его образец?
Андреа Массетти
Конечно, вот пример файла в архиве MODIS: ladsweb.modaps.eosdis.nasa.gov/archive/allData/61/MODATML2/2018/…
astrosnapper
Привет, ты получил шанс попробовать мой ответ?
Андреа Массетти
1
Извините, я был на конференции и представлял работу, основанную на аналогичных определениях PWV из спутниковых данных ... Ваш обновленный код дает мне те же значения, которые я вижу в PanoplyJ для одной и той же ячейки (принимая во внимание другой порядок индекса массива и разница в индексе массива 'off by 1' начинается)
astrosnapper

Ответы:

1

[РЕДАКТИРОВАТЬ 1 - Я изменил поиск координат пикселей]

Используя этот образец MODATML, который вы предоставили, и используя библиотеку gdal. Давайте откроем hdf с помощью gdal:

import gdal
dataset = gdal.Open(r"E:\modis\MODATML2.A2018182.0800.061.2018182195418.hdf")

Затем мы хотим посмотреть, как называются поднаборы, чтобы правильно импортировать те, которые нам нужны:

datasets_meta = dataset.GetMetadata("SUBDATASETS")

Это возвращает словарь:

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)',
[....]}

Допустим, мы хотим получить первую переменную, оптическую толщину облака, мы можем получить доступ к ее названию:

datasets_meta['SUBDATASET_1_NAME']
>>>'HDF4_EOS:EOS_SWATH:"E:\\modis\\MODATML2.A2018182.0800.061.2018182195418.hdf":atml2:Cloud_Optical_Thickness'

Теперь мы можем загрузить переменную в память, снова вызывая метод .Open ():

Cloud_opt_th = gdal.Open(datasets_meta['SUBDATASET_1_NAME'])

Например, вы можете получить доступ к Precipitable_Water_Infrared_ClearSky, в котором вы заинтересованы, указав SUBDATASET_20_NAME. Просто взгляните на словарь datasets_meta.

Однако извлеченная переменная не имеет геопроекции (var.GetGeoprojection ()), как можно было бы ожидать от других типов файлов, таких как GeoTiff. Вы можете загрузить переменную как массив numpy и построить 2d переменную без проекции:

Cloud_opt_th_array = Cloud_opt_th.ReadAsArray()
import matplotlib.pyplot as plt
plt.imshow(Cloud_opt_th_array)

Теперь, поскольку нет геопроекции, мы рассмотрим метаданные переменной:

Cloud_opt_th_meta = Cloud_opt_th.GetMetadata()

Это еще один словарь, который включает в себя всю необходимую вам информацию, включая подробное описание подвыборки (я заметил, что это предоставляется только с первым набором подданных), которое включает в себя объяснение этих 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.'

Я думаю, это означает, что на основе этих 1км пикселей было построено 5км с учетом точных значений пикселей в определенной позиции в массиве датчиков 5х5 (позиция указана в метаданных, я думаю, что это инструментальная вещь для уменьшения ошибок).

Во всяком случае, на данный момент у нас есть массив ячеек 1x1 км (см. Описание подвыборки выше, не уверен насчет науки, стоящей за этим). Чтобы получить координаты центроида каждого пикселя, нам нужно загрузить наборы данных широты и долготы.

Latitude = gdal.Open(datasets_meta['SUBDATASET_66_NAME']).ReadAsArray()
Longitude = gdal.Open(datasets_meta['SUBDATASET_67_NAME']).ReadAsArray()

Например,

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)        

Вы можете заметить, что координаты широты и долготы различны для каждого пикселя.

Скажем, ваша обсерватория расположена в координатах lat_obs, long_obs, чем вы минимизируете разницу координат x, y:

coordinates = np.unravel_index((np.abs(Latitude - lat_obs) + np.abs(Longitude - long_obs)).argmin(), Latitude.shape)

и извлечь свою ценность

Cloud_opt_th_array[coordinates]
Андреа Массетти
источник
Спасибо за информацию, но у меня возникли проблемы с преобразованием координат; Longitude_pxи Latitude_pxоба нулевой длины массивов. Также есть способ обработать преобразование, используя gdalсебя? (вместо того, чтобы полагаться на приближение в 1 градус - это X число миль, а затем пересчитать это значение в км)
астроснаппер
Широта и долгота представлены в виде поднаборов, а именно 66 и 67. Я обновлю вторую часть.
Андреа Массетти
@astrosnapper теперь ответ должен полностью ответить на ваш вопрос.
Андреа Массетти