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