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