Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Implementation of HDF5 HDFEOS parser
5 : * Author: Even Rouault
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_error.h"
14 : #include "nasakeywordhandler.h"
15 :
16 : #include "hdf5eosparser.h"
17 :
18 : #include <cstring>
19 : #include <utility>
20 :
21 : /************************************************************************/
22 : /* HasHDFEOS() */
23 : /************************************************************************/
24 :
25 479 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
26 : {
27 479 : hsize_t numObjs = 0;
28 479 : H5Gget_num_objs(hRoot, &numObjs);
29 479 : bool bFound = false;
30 1504 : for (hsize_t i = 0; i < numObjs; ++i)
31 : {
32 : char szName[128];
33 : ssize_t nLen =
34 1056 : H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
35 1056 : if (nLen > 0)
36 : {
37 1056 : szName[nLen] = 0;
38 1056 : if (strcmp(szName, "HDFEOS INFORMATION") == 0)
39 : {
40 31 : bFound = true;
41 31 : break;
42 : }
43 : }
44 : }
45 479 : if (!bFound)
46 448 : return false;
47 :
48 : H5G_stat_t oStatbuf;
49 31 : if (H5Gget_objinfo(hRoot, "HDFEOS INFORMATION", false, &oStatbuf) < 0)
50 0 : return false;
51 :
52 31 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
53 31 : if (hHDFEOSInformation < 0)
54 : {
55 0 : return false;
56 : }
57 31 : H5Gclose(hHDFEOSInformation);
58 31 : return true;
59 : }
60 :
61 : /************************************************************************/
62 : /* Parse() */
63 : /************************************************************************/
64 :
65 31 : bool HDF5EOSParser::Parse(hid_t hRoot)
66 : {
67 31 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
68 31 : if (hHDFEOSInformation < 0)
69 : {
70 0 : return false;
71 : }
72 :
73 31 : const hid_t hArrayId = H5Dopen(hHDFEOSInformation, "StructMetadata.0");
74 31 : if (hArrayId < 0)
75 : {
76 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find StructMetadata.0");
77 0 : H5Gclose(hHDFEOSInformation);
78 0 : return false;
79 : }
80 :
81 31 : const hid_t hAttrSpace = H5Dget_space(hArrayId);
82 31 : const hid_t hAttrTypeID = H5Dget_type(hArrayId);
83 : const hid_t hAttrNativeType =
84 31 : H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
85 :
86 : // Fetch StructMetadata.0 content in a std::string
87 62 : std::string osResult;
88 31 : if (H5Tget_class(hAttrNativeType) == H5T_STRING &&
89 62 : !H5Tis_variable_str(hAttrNativeType) &&
90 31 : H5Sget_simple_extent_ndims(hAttrSpace) == 0)
91 : {
92 31 : const auto nSize = H5Tget_size(hAttrNativeType);
93 31 : if (nSize > 10 * 1024 * 1024)
94 : {
95 0 : CPLError(CE_Failure, CPLE_AppDefined,
96 : "Too large HDFEOS INFORMATION.StructMetadata.0");
97 : }
98 : else
99 : {
100 31 : osResult.resize(nSize);
101 31 : H5Dread(hArrayId, hAttrNativeType, H5S_ALL, hAttrSpace, H5P_DEFAULT,
102 31 : &osResult[0]);
103 : }
104 : }
105 : else
106 : {
107 0 : CPLError(CE_Failure, CPLE_AppDefined,
108 : "HDFEOS INFORMATION.StructMetadata.0 not of type string");
109 : }
110 31 : H5Sclose(hAttrSpace);
111 31 : H5Tclose(hAttrNativeType);
112 31 : H5Tclose(hAttrTypeID);
113 :
114 31 : H5Dclose(hArrayId);
115 31 : H5Gclose(hHDFEOSInformation);
116 :
117 31 : if (osResult.empty())
118 0 : return false;
119 :
120 : // Parse StructMetadata.0 with NASAKeywordHandler
121 62 : NASAKeywordHandler oKWHandler;
122 : #ifdef DEBUG
123 31 : CPLDebug("HDF5EOS", "%s", osResult.c_str());
124 : #endif
125 31 : if (!oKWHandler.Parse(osResult.c_str()))
126 : {
127 0 : CPLError(CE_Failure, CPLE_AppDefined,
128 : "Cannot parse HDFEOS INFORMATION.StructMetadata.0 with "
129 : "NASAKeywordHandler");
130 0 : return false;
131 : }
132 :
133 62 : auto oJsonRoot = oKWHandler.GetJsonObject();
134 93 : auto oGridStructure = oJsonRoot.GetObj("GridStructure");
135 62 : auto oSwathStructure = oJsonRoot.GetObj("SwathStructure");
136 31 : bool bOK = false;
137 : // An empty
138 : // GROUP=GridStructure
139 : // END_GROUP=GridStructure
140 : // will generate 2 keys (_type and END_GROUP)
141 31 : if (oGridStructure.IsValid() && oGridStructure.GetChildren().size() > 2)
142 : {
143 15 : bOK = true;
144 15 : m_eDataModel = DataModel::GRID;
145 15 : ParseGridStructure(oGridStructure);
146 : }
147 32 : else if (oSwathStructure.IsValid() &&
148 32 : oSwathStructure.GetChildren().size() > 2)
149 : {
150 16 : bOK = true;
151 16 : m_eDataModel = DataModel::SWATH;
152 16 : ParseSwathStructure(oSwathStructure);
153 : }
154 :
155 31 : return bOK;
156 : }
157 :
158 : /************************************************************************/
159 : /* GetGTCPProjectionCode() */
160 : /************************************************************************/
161 :
162 15 : static int GetGTCPProjectionCode(const std::string &osProjection)
163 : {
164 15 : const char *const apszGCTPProjections[] = {
165 : "HE5_GCTP_GEO", "HE5_GCTP_UTM", "HE5_GCTP_SPCS",
166 : "HE5_GCTP_ALBERS", "HE5_GCTP_LAMCC", "HE5_GCTP_MERCAT",
167 : "HE5_GCTP_PS", "HE5_GCTP_POLYC", "HE5_GCTP_EQUIDC",
168 : "HE5_GCTP_TM", "HE5_GCTP_STEREO", "HE5_GCTP_LAMAZ",
169 : "HE5_GCTP_AZMEQD", "HE5_GCTP_GNOMON", "HE5_GCTP_ORTHO",
170 : "HE5_GCTP_GVNSP", "HE5_GCTP_SNSOID", "HE5_GCTP_EQRECT",
171 : "HE5_GCTP_MILLER", "HE5_GCTP_VGRINT", "HE5_GCTP_HOM",
172 : "HE5_GCTP_ROBIN", "HE5_GCTP_SOM", "HE5_GCTP_ALASKA",
173 : "HE5_GCTP_GOOD", "HE5_GCTP_MOLL", "HE5_GCTP_IMOLL",
174 : "HE5_GCTP_HAMMER", "HE5_GCTP_WAGIV", "HE5_GCTP_WAGVII",
175 : "HE5_GCTP_OBLEQA"};
176 : // HE5_GCTP_CEA, HE5_GCTP_BCEA, HE5_GCTP_ISINUS not taken
177 : // into account.
178 132 : for (int i = 0; i < static_cast<int>(CPL_ARRAYSIZE(apszGCTPProjections));
179 : ++i)
180 : {
181 132 : if (osProjection == apszGCTPProjections[i])
182 : {
183 15 : return i;
184 : }
185 : }
186 0 : return -1;
187 : }
188 :
189 : /************************************************************************/
190 : /* ParseGridStructure() */
191 : /************************************************************************/
192 :
193 15 : void HDF5EOSParser::ParseGridStructure(const CPLJSONObject &oGridStructure)
194 : {
195 60 : for (const auto &oGrid : oGridStructure.GetChildren())
196 : {
197 45 : if (oGrid.GetType() == CPLJSONObject::Type::Object)
198 : {
199 45 : const auto osGridName = oGrid.GetString("GridName");
200 45 : const auto oDataFields = oGrid.GetObj("DataField");
201 45 : const auto oDimensions = oGrid.GetObj("Dimension");
202 30 : std::map<std::string, int> oMapDimensionNameToSize;
203 30 : auto poGridMetadata = std::make_unique<GridMetadata>();
204 15 : poGridMetadata->osGridName = osGridName;
205 72 : for (const auto &oDimension : oDimensions.GetChildren())
206 : {
207 57 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
208 : {
209 : std::string osDimensionName =
210 81 : oDimension.GetString("DimensionName");
211 27 : int nSize = oDimension.GetInteger("Size");
212 27 : oMapDimensionNameToSize[osDimensionName] = nSize;
213 54 : Dimension oDim;
214 27 : oDim.osName = std::move(osDimensionName);
215 27 : oDim.nSize = nSize;
216 27 : poGridMetadata->aoDimensions.push_back(std::move(oDim));
217 : }
218 : }
219 :
220 : // Happens for example for products following
221 : // AMSR-E/AMSR2 Unified L3 Daily 12.5 km Brightness Temperatures,
222 : // Sea Ice Concentration, Motion & Snow Depth Polar Grids
223 : // (https://nsidc.org/sites/default/files/au_si12-v001-userguide_1.pdf)
224 : // such as
225 : // https://n5eil01u.ecs.nsidc.org/AMSA/AU_SI12.001/2012.07.02/AMSR_U2_L3_SeaIce12km_B04_20120702.he5
226 15 : const int nXDim = oGrid.GetInteger("XDim", 0);
227 15 : const int nYDim = oGrid.GetInteger("YDim", 0);
228 15 : if (nXDim > 0 && nYDim > 0 &&
229 35 : !cpl::contains(oMapDimensionNameToSize, "XDim") &&
230 5 : !cpl::contains(oMapDimensionNameToSize, "YDim"))
231 : {
232 : // Check that we have at least one data field with DimList=(YDim,XDim)
233 : // property.
234 5 : bool bHasDimListYDimXDim = false;
235 15 : for (const auto &oDataField : oDataFields.GetChildren())
236 : {
237 10 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
238 : {
239 10 : const auto oDimList = oDataField.GetArray("DimList");
240 5 : if (oDimList.Size() == 2 &&
241 15 : oDimList[0].ToString() == "YDim" &&
242 10 : oDimList[1].ToString() == "XDim")
243 : {
244 5 : bHasDimListYDimXDim = true;
245 5 : break;
246 : }
247 : }
248 : }
249 5 : if (bHasDimListYDimXDim)
250 : {
251 : {
252 10 : std::string osDimensionName("YDim");
253 5 : oMapDimensionNameToSize[osDimensionName] = nYDim;
254 10 : Dimension oDim;
255 5 : oDim.osName = std::move(osDimensionName);
256 5 : oDim.nSize = nYDim;
257 5 : poGridMetadata->aoDimensions.push_back(std::move(oDim));
258 : }
259 : {
260 10 : std::string osDimensionName("XDim");
261 5 : oMapDimensionNameToSize[osDimensionName] = nXDim;
262 10 : Dimension oDim;
263 5 : oDim.osName = std::move(osDimensionName);
264 5 : oDim.nSize = nXDim;
265 5 : poGridMetadata->aoDimensions.push_back(std::move(oDim));
266 : }
267 : }
268 : }
269 :
270 15 : poGridMetadata->osProjection = oGrid.GetString("Projection");
271 30 : poGridMetadata->nProjCode =
272 15 : GetGTCPProjectionCode(poGridMetadata->osProjection);
273 15 : poGridMetadata->osGridOrigin = oGrid.GetString("GridOrigin");
274 15 : poGridMetadata->nZone = oGrid.GetInteger("ZoneCode", -1);
275 15 : poGridMetadata->nSphereCode = oGrid.GetInteger("SphereCode", -1);
276 :
277 45 : const auto oProjParams = oGrid.GetArray("ProjParams");
278 106 : for (int j = 0; j < oProjParams.Size(); ++j)
279 91 : poGridMetadata->adfProjParams.push_back(
280 91 : oProjParams[j].ToDouble());
281 :
282 : const auto oUpperLeftPointMtrs =
283 45 : oGrid.GetArray("UpperLeftPointMtrs");
284 45 : for (int j = 0; j < oUpperLeftPointMtrs.Size(); ++j)
285 30 : poGridMetadata->adfUpperLeftPointMeters.push_back(
286 30 : oUpperLeftPointMtrs[j].ToDouble());
287 :
288 45 : const auto oLowerRightMtrs = oGrid.GetArray("LowerRightMtrs");
289 45 : for (int j = 0; j < oLowerRightMtrs.Size(); ++j)
290 30 : poGridMetadata->adfLowerRightPointMeters.push_back(
291 30 : oLowerRightMtrs[j].ToDouble());
292 :
293 15 : m_oMapGridNameToGridMetadata[osGridName] =
294 30 : std::move(poGridMetadata);
295 : const auto poGridMetadataRef =
296 15 : m_oMapGridNameToGridMetadata[osGridName].get();
297 :
298 60 : for (const auto &oDataField : oDataFields.GetChildren())
299 : {
300 45 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
301 : {
302 : const auto osDataFieldName =
303 45 : oDataField.GetString("DataFieldName");
304 45 : const auto oDimList = oDataField.GetArray("DimList");
305 30 : GridDataFieldMetadata oDataFieldMetadata;
306 15 : bool bValid = oDimList.Size() > 0;
307 49 : for (int j = 0; j < oDimList.Size(); ++j)
308 : {
309 68 : std::string osDimensionName = oDimList[j].ToString();
310 : const auto oIter = oMapDimensionNameToSize.find(
311 34 : osDimensionName.c_str());
312 34 : if (oIter == oMapDimensionNameToSize.end())
313 : {
314 0 : bValid = false;
315 0 : break;
316 : }
317 68 : Dimension oDim;
318 34 : oDim.osName = std::move(osDimensionName);
319 34 : oDim.nSize = oIter->second;
320 34 : oDataFieldMetadata.aoDimensions.push_back(
321 34 : std::move(oDim));
322 : }
323 15 : if (bValid)
324 : {
325 15 : oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
326 : m_oMapSubdatasetNameToGridDataFieldMetadata
327 30 : ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
328 30 : osDataFieldName] = std::move(oDataFieldMetadata);
329 : }
330 : }
331 : }
332 : }
333 : }
334 15 : }
335 :
336 : /************************************************************************/
337 : /* GetGridMetadata() */
338 : /************************************************************************/
339 :
340 9 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
341 : GridMetadata &gridMetadataOut) const
342 : {
343 9 : const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
344 9 : if (oIter == m_oMapGridNameToGridMetadata.end())
345 6 : return false;
346 3 : gridMetadataOut = *(oIter->second);
347 3 : return true;
348 : }
349 :
350 : /************************************************************************/
351 : /* GetGridDataFieldMetadata() */
352 : /************************************************************************/
353 :
354 12 : bool HDF5EOSParser::GetGridDataFieldMetadata(
355 : const char *pszSubdatasetName,
356 : GridDataFieldMetadata &gridDataFieldMetadataOut) const
357 : {
358 : const auto oIter =
359 12 : m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
360 12 : if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
361 3 : return false;
362 9 : gridDataFieldMetadataOut = oIter->second;
363 9 : return true;
364 : }
365 :
366 : /************************************************************************/
367 : /* ParseSwathStructure() */
368 : /************************************************************************/
369 :
370 16 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
371 : {
372 64 : for (const auto &oSwath : oSwathStructure.GetChildren())
373 : {
374 48 : if (oSwath.GetType() == CPLJSONObject::Type::Object)
375 : {
376 48 : const auto osSwathName = oSwath.GetString("SwathName");
377 :
378 48 : const auto oDimensions = oSwath.GetObj("Dimension");
379 32 : std::map<std::string, int> oMapDimensionNameToSize;
380 32 : auto poSwathMetadata = std::make_unique<SwathMetadata>();
381 16 : poSwathMetadata->osSwathName = osSwathName;
382 132 : for (const auto &oDimension : oDimensions.GetChildren())
383 : {
384 116 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
385 : {
386 : auto osDimensionName =
387 252 : oDimension.GetString("DimensionName");
388 84 : int nSize = oDimension.GetInteger("Size");
389 84 : oMapDimensionNameToSize[osDimensionName] = nSize;
390 168 : Dimension oDim;
391 84 : oDim.osName = std::move(osDimensionName);
392 84 : oDim.nSize = nSize;
393 84 : poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
394 : }
395 : }
396 :
397 16 : m_oMapSwathNameToSwathMetadata[osSwathName] =
398 32 : std::move(poSwathMetadata);
399 : const auto poSwathMetadataRef =
400 16 : m_oMapSwathNameToSwathMetadata[osSwathName].get();
401 :
402 : struct DimensionMap
403 : {
404 : std::string osGeoDimName{};
405 : std::string osDataDimName{};
406 : int nOffset = 0;
407 : int nIncrement = 1;
408 : };
409 :
410 32 : std::vector<DimensionMap> aoDimensionMaps;
411 32 : std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
412 :
413 48 : const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
414 60 : for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
415 : {
416 44 : if (jsonDimensionMap.GetType() == CPLJSONObject::Type::Object)
417 : {
418 24 : DimensionMap oDimensionMap;
419 : oDimensionMap.osGeoDimName =
420 12 : jsonDimensionMap.GetString("GeoDimension");
421 : oDimensionMap.osDataDimName =
422 12 : jsonDimensionMap.GetString("DataDimension");
423 12 : oDimensionMap.nOffset =
424 12 : jsonDimensionMap.GetInteger("Offset", 0);
425 12 : oDimensionMap.nIncrement =
426 12 : jsonDimensionMap.GetInteger("Increment", 1);
427 : oMapDataDimensionToGeoDimension[oDimensionMap
428 12 : .osDataDimName] =
429 12 : oDimensionMap.osGeoDimName;
430 12 : aoDimensionMaps.emplace_back(oDimensionMap);
431 : }
432 : }
433 :
434 48 : const auto oGeoFields = oSwath.GetObj("GeoField");
435 32 : std::vector<Dimension> aoLongitudeDimensions;
436 32 : std::vector<Dimension> aoLatitudeDimensions;
437 :
438 : // First pass to create dimensions
439 96 : for (const auto &oGeoField : oGeoFields.GetChildren())
440 : {
441 80 : if (oGeoField.GetType() == CPLJSONObject::Type::Object)
442 : {
443 144 : auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
444 144 : auto oDimList = oGeoField.GetArray("DimList");
445 48 : bool bValid = true;
446 96 : std::vector<Dimension> aoDimensions;
447 128 : for (int j = 0; j < oDimList.Size(); ++j)
448 : {
449 160 : const auto osDimensionName = oDimList[j].ToString();
450 : const auto oIter = oMapDimensionNameToSize.find(
451 80 : osDimensionName.c_str());
452 80 : if (oIter == oMapDimensionNameToSize.end())
453 : {
454 0 : bValid = false;
455 0 : break;
456 : }
457 160 : Dimension oDim;
458 80 : oDim.osName = osDimensionName;
459 80 : oDim.nSize = oIter->second;
460 80 : aoDimensions.push_back(std::move(oDim));
461 80 : if (oMapDataDimensionToGeoDimension.find(
462 80 : osDimensionName) ==
463 160 : oMapDataDimensionToGeoDimension.end())
464 : {
465 : // Create a fake dimension map for this dim
466 64 : DimensionMap oDimensionMap;
467 32 : oDimensionMap.osGeoDimName = osDimensionName;
468 32 : oDimensionMap.osDataDimName = osDimensionName;
469 32 : oDimensionMap.nOffset = 0;
470 32 : oDimensionMap.nIncrement = 1;
471 32 : oMapDataDimensionToGeoDimension[osDimensionName] =
472 32 : osDimensionName;
473 32 : aoDimensionMaps.emplace_back(oDimensionMap);
474 : }
475 : }
476 48 : if (bValid)
477 : {
478 48 : if (osGeoFieldName == "Longitude")
479 16 : aoLongitudeDimensions = std::move(aoDimensions);
480 32 : else if (osGeoFieldName == "Latitude")
481 16 : aoLatitudeDimensions = std::move(aoDimensions);
482 : }
483 : }
484 : }
485 :
486 : // Second pass to register field
487 96 : for (const auto &oGeoField : oGeoFields.GetChildren())
488 : {
489 80 : if (oGeoField.GetType() == CPLJSONObject::Type::Object)
490 : {
491 : const auto osGeoFieldName =
492 144 : oGeoField.GetString("GeoFieldName");
493 144 : const auto oDimList = oGeoField.GetArray("DimList");
494 48 : bool bValid = true;
495 96 : SwathFieldMetadata oMetadata;
496 48 : oMetadata.poSwathMetadata = poSwathMetadataRef;
497 128 : for (int j = 0; j < oDimList.Size(); ++j)
498 : {
499 160 : const auto osDimensionName = oDimList[j].ToString();
500 : const auto oIter = oMapDimensionNameToSize.find(
501 80 : osDimensionName.c_str());
502 80 : if (oIter == oMapDimensionNameToSize.end())
503 : {
504 0 : bValid = false;
505 0 : break;
506 : }
507 160 : Dimension oDim;
508 80 : oDim.osName = std::move(osDimensionName);
509 80 : oDim.nSize = oIter->second;
510 80 : oMetadata.aoDimensions.push_back(std::move(oDim));
511 : }
512 48 : if (bValid)
513 : {
514 80 : if (oMetadata.aoDimensions.size() >= 2 &&
515 80 : aoLongitudeDimensions.size() == 2 &&
516 32 : aoLongitudeDimensions == aoLatitudeDimensions)
517 : {
518 32 : int i = 0;
519 64 : std::string osDataXDimName;
520 64 : std::string osDataYDimName;
521 96 : for (const auto &oDimSwath : oMetadata.aoDimensions)
522 : {
523 : auto oIter =
524 : oMapDataDimensionToGeoDimension.find(
525 64 : oDimSwath.osName);
526 64 : if (oIter !=
527 128 : oMapDataDimensionToGeoDimension.end())
528 : {
529 64 : const auto &osGeoDimName = oIter->second;
530 64 : if (osGeoDimName ==
531 64 : aoLongitudeDimensions[0].osName)
532 : {
533 32 : osDataYDimName = oDimSwath.osName;
534 32 : oMetadata.iYDim = i;
535 : }
536 32 : else if (osGeoDimName ==
537 32 : aoLongitudeDimensions[1].osName)
538 : {
539 32 : osDataXDimName = oDimSwath.osName;
540 32 : oMetadata.iXDim = i;
541 : }
542 : }
543 : else
544 : {
545 0 : oMetadata.iOtherDim = i;
546 : }
547 64 : ++i;
548 : }
549 32 : if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
550 : {
551 : oMetadata.osLongitudeSubdataset =
552 64 : "//HDFEOS/SWATHS/" + osSwathName +
553 32 : "/Geolocation_Fields/Longitude";
554 : oMetadata.osLatitudeSubdataset =
555 64 : "//HDFEOS/SWATHS/" + osSwathName +
556 32 : "/Geolocation_Fields/Latitude";
557 :
558 120 : for (const auto &oDimMap : aoDimensionMaps)
559 : {
560 88 : if (oDimMap.osDataDimName == osDataYDimName)
561 : {
562 32 : oMetadata.nLineOffset = oDimMap.nOffset;
563 32 : oMetadata.nLineStep =
564 32 : oDimMap.nIncrement;
565 : }
566 56 : else if (oDimMap.osDataDimName ==
567 : osDataXDimName)
568 : {
569 32 : oMetadata.nPixelOffset =
570 32 : oDimMap.nOffset;
571 32 : oMetadata.nPixelStep =
572 32 : oDimMap.nIncrement;
573 : }
574 : }
575 : }
576 : }
577 :
578 : const std::string osSubdatasetName =
579 96 : "//HDFEOS/SWATHS/" + osSwathName +
580 96 : "/Geolocation_Fields/" + osGeoFieldName;
581 : m_oMapSubdatasetNameToSwathFieldMetadata
582 48 : [osSubdatasetName] = std::move(oMetadata);
583 : }
584 : }
585 : }
586 :
587 48 : const auto oDataFields = oSwath.GetObj("DataField");
588 94 : for (const auto &oDataField : oDataFields.GetChildren())
589 : {
590 78 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
591 : {
592 : const auto osDataFieldName =
593 138 : oDataField.GetString("DataFieldName");
594 138 : const auto oDimList = oDataField.GetArray("DimList");
595 92 : SwathFieldMetadata oMetadata;
596 46 : oMetadata.poSwathMetadata = poSwathMetadataRef;
597 46 : bool bValid = oDimList.Size() > 0;
598 130 : for (int j = 0; j < oDimList.Size(); ++j)
599 : {
600 168 : std::string osDimensionName = oDimList[j].ToString();
601 : const auto oIter = oMapDimensionNameToSize.find(
602 84 : osDimensionName.c_str());
603 84 : if (oIter == oMapDimensionNameToSize.end())
604 : {
605 0 : bValid = false;
606 0 : break;
607 : }
608 168 : Dimension oDim;
609 84 : oDim.osName = std::move(osDimensionName);
610 84 : oDim.nSize = oIter->second;
611 84 : oMetadata.aoDimensions.push_back(std::move(oDim));
612 : }
613 46 : if (bValid)
614 : {
615 68 : if (oMetadata.aoDimensions.size() >= 2 &&
616 68 : aoLongitudeDimensions.size() == 2 &&
617 22 : aoLongitudeDimensions == aoLatitudeDimensions)
618 : {
619 22 : int i = 0;
620 44 : std::string osDataXDimName;
621 44 : std::string osDataYDimName;
622 82 : for (const auto &oDimSwath : oMetadata.aoDimensions)
623 : {
624 : auto oIter =
625 : oMapDataDimensionToGeoDimension.find(
626 60 : oDimSwath.osName);
627 60 : if (oIter !=
628 120 : oMapDataDimensionToGeoDimension.end())
629 : {
630 44 : const auto &osGeoDimName = oIter->second;
631 44 : if (osGeoDimName ==
632 44 : aoLongitudeDimensions[0].osName)
633 : {
634 22 : osDataYDimName = oDimSwath.osName;
635 22 : oMetadata.iYDim = i;
636 : }
637 22 : else if (osGeoDimName ==
638 22 : aoLongitudeDimensions[1].osName)
639 : {
640 22 : osDataXDimName = oDimSwath.osName;
641 22 : oMetadata.iXDim = i;
642 : }
643 : }
644 : else
645 : {
646 16 : oMetadata.iOtherDim = i;
647 : }
648 60 : ++i;
649 : }
650 22 : if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
651 : {
652 : oMetadata.osLongitudeSubdataset =
653 44 : "//HDFEOS/SWATHS/" + osSwathName +
654 22 : "/Geolocation_Fields/Longitude";
655 : oMetadata.osLatitudeSubdataset =
656 44 : "//HDFEOS/SWATHS/" + osSwathName +
657 22 : "/Geolocation_Fields/Latitude";
658 :
659 90 : for (const auto &oDimMap : aoDimensionMaps)
660 : {
661 68 : if (oDimMap.osDataDimName == osDataYDimName)
662 : {
663 22 : oMetadata.nLineOffset = oDimMap.nOffset;
664 22 : oMetadata.nLineStep =
665 22 : oDimMap.nIncrement;
666 : }
667 46 : else if (oDimMap.osDataDimName ==
668 : osDataXDimName)
669 : {
670 22 : oMetadata.nPixelOffset =
671 22 : oDimMap.nOffset;
672 22 : oMetadata.nPixelStep =
673 22 : oDimMap.nIncrement;
674 : }
675 : }
676 : }
677 : }
678 :
679 : m_oMapSubdatasetNameToSwathFieldMetadata
680 92 : ["//HDFEOS/SWATHS/" + osSwathName +
681 138 : "/Data_Fields/" + osDataFieldName] =
682 92 : std::move(oMetadata);
683 : }
684 : }
685 : }
686 : }
687 : }
688 16 : }
689 :
690 : /************************************************************************/
691 : /* GetSwathMetadata() */
692 : /************************************************************************/
693 :
694 3 : bool HDF5EOSParser::GetSwathMetadata(const std::string &osSwathName,
695 : SwathMetadata &swathMetadataOut) const
696 : {
697 3 : const auto oIter = m_oMapSwathNameToSwathMetadata.find(osSwathName);
698 3 : if (oIter == m_oMapSwathNameToSwathMetadata.end())
699 0 : return false;
700 3 : swathMetadataOut = *(oIter->second.get());
701 3 : return true;
702 : }
703 :
704 : /************************************************************************/
705 : /* GetSwathFieldMetadata() */
706 : /************************************************************************/
707 :
708 23 : bool HDF5EOSParser::GetSwathFieldMetadata(
709 : const char *pszSubdatasetName,
710 : SwathFieldMetadata &swathFieldMetadataOut) const
711 : {
712 : const auto oIter =
713 23 : m_oMapSubdatasetNameToSwathFieldMetadata.find(pszSubdatasetName);
714 23 : if (oIter == m_oMapSubdatasetNameToSwathFieldMetadata.end())
715 2 : return false;
716 21 : swathFieldMetadataOut = oIter->second;
717 21 : return true;
718 : }
719 :
720 : /************************************************************************/
721 : /* GetGeoTransform() */
722 : /************************************************************************/
723 :
724 9 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(GDALGeoTransform >) const
725 : {
726 9 : if (nProjCode >= 0 &&
727 20 : (osGridOrigin == "HE5_HDFE_GD_UL" || osGridOrigin.empty()) &&
728 27 : adfUpperLeftPointMeters.size() == 2 &&
729 9 : adfLowerRightPointMeters.size() == 2)
730 : {
731 9 : int nRasterXSize = 0;
732 9 : int nRasterYSize = 0;
733 :
734 31 : for (const auto &oDim : aoDimensions)
735 : {
736 22 : if (oDim.osName == "XDim")
737 9 : nRasterXSize = oDim.nSize;
738 13 : else if (oDim.osName == "YDim")
739 9 : nRasterYSize = oDim.nSize;
740 : }
741 9 : if (nRasterXSize <= 0 || nRasterYSize <= 0)
742 0 : return false;
743 9 : if (nProjCode == 0) // GEO
744 : {
745 2 : gt[0] = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
746 2 : gt[1] = (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
747 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
748 : nRasterXSize;
749 2 : gt[2] = 0;
750 2 : gt[3] = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
751 2 : gt[4] = 0;
752 4 : gt[5] = (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
753 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
754 : nRasterYSize;
755 : }
756 : else
757 : {
758 7 : gt[0] = adfUpperLeftPointMeters[0];
759 7 : gt[1] = (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
760 : nRasterXSize;
761 7 : gt[2] = 0;
762 7 : gt[3] = adfUpperLeftPointMeters[1];
763 7 : gt[4] = 0;
764 7 : gt[5] = (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
765 : nRasterYSize;
766 : }
767 9 : return true;
768 : }
769 0 : return false;
770 : }
771 :
772 : /************************************************************************/
773 : /* GetSRS() */
774 : /************************************************************************/
775 :
776 9 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
777 : {
778 18 : std::vector<double> l_adfProjParams = adfProjParams;
779 9 : l_adfProjParams.resize(15);
780 18 : auto poSRS = std::make_unique<OGRSpatialReference>();
781 9 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
782 18 : if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
783 18 : nSphereCode) == OGRERR_NONE)
784 : {
785 9 : return poSRS;
786 : }
787 0 : return nullptr;
788 : }
|