Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Simple client for viewing OGR driver data.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2013, 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_port.h"
31 : #include "cpl_json.h"
32 : #include "ogrgeojsonreader.h"
33 : #include "cpl_string.h"
34 : #include "gdal_utils.h"
35 : #include "gdal_utils_priv.h"
36 : #include "gdal_priv.h"
37 : #include "ogr_feature.h"
38 : #include "ogrsf_frmts.h"
39 : #include "ogr_geometry.h"
40 : #include "commonutils.h"
41 : #include "gdalargumentparser.h"
42 :
43 : #include <set>
44 :
45 : /*! output format */
46 : typedef enum
47 : {
48 : /*! output in text format */ FORMAT_TEXT = 0,
49 : /*! output in json format */ FORMAT_JSON = 1
50 : } GDALVectorInfoFormat;
51 :
52 : struct GDALVectorInfoOptions
53 : {
54 : GDALVectorInfoFormat eFormat = FORMAT_TEXT;
55 : std::string osWHERE{};
56 : CPLStringList aosLayers{};
57 : std::unique_ptr<OGRGeometry> poSpatialFilter;
58 : bool bAllLayers = false;
59 : std::string osSQLStatement{};
60 : std::string osDialect{};
61 : std::string osGeomField{};
62 : CPLStringList aosExtraMDDomains{};
63 : bool bListMDD = false;
64 : bool bShowMetadata = true;
65 : bool bFeatureCount = true;
66 : bool bExtent = true;
67 : bool bExtent3D = false;
68 : bool bGeomType = true;
69 : bool bDatasetGetNextFeature = false;
70 : bool bVerbose = true;
71 : bool bSuperQuiet = false;
72 : bool bSummaryOnly = false;
73 : GIntBig nFetchFID = OGRNullFID;
74 : std::string osWKTFormat = "WKT2";
75 : std::string osFieldDomain{};
76 : CPLStringList aosOptions{};
77 : bool bStdoutOutput = false; // only set by ogrinfo_bin
78 : int nRepeatCount = 1;
79 :
80 : /*! Maximum number of features, or -1 if no limit. */
81 : GIntBig nLimit = -1;
82 :
83 : // Only used during argument parsing
84 : bool bSummaryParser = false;
85 : bool bFeaturesParser = false;
86 : };
87 :
88 : /************************************************************************/
89 : /* GDALVectorInfoOptionsFree() */
90 : /************************************************************************/
91 :
92 : /**
93 : * Frees the GDALVectorInfoOptions struct.
94 : *
95 : * @param psOptions the options struct for GDALVectorInfo().
96 : *
97 : * @since GDAL 3.7
98 : */
99 :
100 76 : void GDALVectorInfoOptionsFree(GDALVectorInfoOptions *psOptions)
101 : {
102 76 : delete psOptions;
103 76 : }
104 :
105 : /************************************************************************/
106 : /* Concat() */
107 : /************************************************************************/
108 :
109 : #ifndef Concat_defined
110 : #define Concat_defined
111 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
112 : ...) CPL_PRINT_FUNC_FORMAT(3, 4);
113 :
114 2618 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
115 : ...)
116 : {
117 : va_list args;
118 2618 : va_start(args, pszFormat);
119 :
120 2618 : if (bStdoutOutput)
121 : {
122 2147 : vfprintf(stdout, pszFormat, args);
123 : }
124 : else
125 : {
126 : try
127 : {
128 942 : CPLString osTarget;
129 471 : osTarget.vPrintf(pszFormat, args);
130 :
131 471 : osRet += osTarget;
132 : }
133 0 : catch (const std::bad_alloc &)
134 : {
135 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
136 : }
137 : }
138 :
139 2618 : va_end(args);
140 2618 : }
141 : #endif
142 :
143 693 : static void ConcatStr(CPLString &osRet, bool bStdoutOutput, const char *pszStr)
144 : {
145 693 : if (bStdoutOutput)
146 616 : fwrite(pszStr, 1, strlen(pszStr), stdout);
147 : else
148 77 : osRet += pszStr;
149 693 : }
150 :
151 : /************************************************************************/
152 : /* ReportFieldDomain() */
153 : /************************************************************************/
154 :
155 14 : static void ReportFieldDomain(CPLString &osRet, CPLJSONObject &oDomains,
156 : const GDALVectorInfoOptions *psOptions,
157 : const OGRFieldDomain *poDomain)
158 : {
159 14 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
160 28 : CPLJSONObject oDomain;
161 14 : oDomains.Add(poDomain->GetName(), oDomain);
162 14 : Concat(osRet, psOptions->bStdoutOutput, "Domain %s:\n",
163 14 : poDomain->GetName().c_str());
164 14 : const std::string &osDesc = poDomain->GetDescription();
165 14 : if (!osDesc.empty())
166 : {
167 2 : if (bJson)
168 1 : oDomain.Set("description", osDesc);
169 : else
170 1 : Concat(osRet, psOptions->bStdoutOutput, " Description: %s\n",
171 : osDesc.c_str());
172 : }
173 14 : const char *pszType = "";
174 14 : switch (poDomain->GetDomainType())
175 : {
176 2 : case OFDT_CODED:
177 2 : pszType = "coded";
178 2 : break;
179 10 : case OFDT_RANGE:
180 10 : pszType = "range";
181 10 : break;
182 2 : case OFDT_GLOB:
183 2 : pszType = "glob";
184 2 : break;
185 : }
186 14 : if (bJson)
187 : {
188 7 : oDomain.Set("type", pszType);
189 : }
190 : else
191 : {
192 7 : Concat(osRet, psOptions->bStdoutOutput, " Type: %s\n", pszType);
193 : }
194 : const char *pszFieldType =
195 14 : OGRFieldDefn::GetFieldTypeName(poDomain->GetFieldType());
196 : const char *pszFieldSubType =
197 14 : OGRFieldDefn::GetFieldSubTypeName(poDomain->GetFieldSubType());
198 14 : if (bJson)
199 : {
200 7 : oDomain.Set("fieldType", pszFieldType);
201 7 : if (poDomain->GetFieldSubType() != OFSTNone)
202 0 : oDomain.Set("fieldSubType", pszFieldSubType);
203 : }
204 : else
205 : {
206 : const char *pszFieldTypeDisplay =
207 7 : (poDomain->GetFieldSubType() != OFSTNone)
208 7 : ? CPLSPrintf("%s(%s)", pszFieldType, pszFieldSubType)
209 7 : : pszFieldType;
210 7 : Concat(osRet, psOptions->bStdoutOutput, " Field type: %s\n",
211 : pszFieldTypeDisplay);
212 : }
213 :
214 14 : const char *pszSplitPolicy = "";
215 14 : switch (poDomain->GetSplitPolicy())
216 : {
217 14 : case OFDSP_DEFAULT_VALUE:
218 14 : pszSplitPolicy = "default value";
219 14 : break;
220 0 : case OFDSP_DUPLICATE:
221 0 : pszSplitPolicy = "duplicate";
222 0 : break;
223 0 : case OFDSP_GEOMETRY_RATIO:
224 0 : pszSplitPolicy = "geometry ratio";
225 0 : break;
226 : }
227 14 : if (bJson)
228 : {
229 7 : oDomain.Set("splitPolicy", pszSplitPolicy);
230 : }
231 : else
232 : {
233 7 : Concat(osRet, psOptions->bStdoutOutput, " Split policy: %s\n",
234 : pszSplitPolicy);
235 : }
236 :
237 14 : const char *pszMergePolicy = "";
238 14 : switch (poDomain->GetMergePolicy())
239 : {
240 14 : case OFDMP_DEFAULT_VALUE:
241 14 : pszMergePolicy = "default value";
242 14 : break;
243 0 : case OFDMP_SUM:
244 0 : pszMergePolicy = "sum";
245 0 : break;
246 0 : case OFDMP_GEOMETRY_WEIGHTED:
247 0 : pszMergePolicy = "geometry weighted";
248 0 : break;
249 : }
250 14 : if (bJson)
251 : {
252 7 : oDomain.Set("mergePolicy", pszMergePolicy);
253 : }
254 : else
255 : {
256 7 : Concat(osRet, psOptions->bStdoutOutput, " Merge policy: %s\n",
257 : pszMergePolicy);
258 : }
259 :
260 14 : switch (poDomain->GetDomainType())
261 : {
262 2 : case OFDT_CODED:
263 : {
264 : const auto poCodedFieldDomain =
265 2 : cpl::down_cast<const OGRCodedFieldDomain *>(poDomain);
266 : const OGRCodedValue *enumeration =
267 2 : poCodedFieldDomain->GetEnumeration();
268 2 : if (!bJson)
269 1 : Concat(osRet, psOptions->bStdoutOutput, " Coded values:\n");
270 4 : CPLJSONObject oCodedValues;
271 2 : oDomain.Add("codedValues", oCodedValues);
272 6 : for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
273 : {
274 4 : if (enumeration[i].pszValue)
275 : {
276 2 : if (bJson)
277 : {
278 1 : oCodedValues.Set(enumeration[i].pszCode,
279 1 : enumeration[i].pszValue);
280 : }
281 : else
282 : {
283 1 : Concat(osRet, psOptions->bStdoutOutput, " %s: %s\n",
284 1 : enumeration[i].pszCode, enumeration[i].pszValue);
285 : }
286 : }
287 : else
288 : {
289 2 : if (bJson)
290 : {
291 1 : oCodedValues.SetNull(enumeration[i].pszCode);
292 : }
293 : else
294 : {
295 1 : Concat(osRet, psOptions->bStdoutOutput, " %s\n",
296 1 : enumeration[i].pszCode);
297 : }
298 : }
299 : }
300 2 : break;
301 : }
302 :
303 10 : case OFDT_RANGE:
304 : {
305 : const auto poRangeFieldDomain =
306 10 : cpl::down_cast<const OGRRangeFieldDomain *>(poDomain);
307 10 : bool bMinIsIncluded = false;
308 10 : const OGRField &sMin = poRangeFieldDomain->GetMin(bMinIsIncluded);
309 10 : bool bMaxIsIncluded = false;
310 10 : const OGRField &sMax = poRangeFieldDomain->GetMax(bMaxIsIncluded);
311 10 : if (poDomain->GetFieldType() == OFTInteger)
312 : {
313 2 : if (!OGR_RawField_IsUnset(&sMin))
314 : {
315 2 : if (bJson)
316 : {
317 1 : oDomain.Set("minValue", sMin.Integer);
318 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
319 : }
320 : else
321 : {
322 1 : Concat(osRet, psOptions->bStdoutOutput,
323 1 : " Minimum value: %d%s\n", sMin.Integer,
324 : bMinIsIncluded ? "" : " (excluded)");
325 : }
326 : }
327 2 : if (!OGR_RawField_IsUnset(&sMax))
328 : {
329 2 : if (bJson)
330 : {
331 1 : oDomain.Set("maxValue", sMax.Integer);
332 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
333 : }
334 : else
335 : {
336 1 : Concat(osRet, psOptions->bStdoutOutput,
337 1 : " Maximum value: %d%s\n", sMax.Integer,
338 : bMaxIsIncluded ? "" : " (excluded)");
339 : }
340 : }
341 : }
342 8 : else if (poDomain->GetFieldType() == OFTInteger64)
343 : {
344 2 : if (!OGR_RawField_IsUnset(&sMin))
345 : {
346 2 : if (bJson)
347 : {
348 1 : oDomain.Set("minValue", sMin.Integer64);
349 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
350 : }
351 : else
352 : {
353 1 : Concat(osRet, psOptions->bStdoutOutput,
354 : " Minimum value: " CPL_FRMT_GIB "%s\n",
355 1 : sMin.Integer64,
356 : bMinIsIncluded ? "" : " (excluded)");
357 : }
358 : }
359 2 : if (!OGR_RawField_IsUnset(&sMax))
360 : {
361 2 : if (bJson)
362 : {
363 1 : oDomain.Set("maxValue", sMax.Integer64);
364 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
365 : }
366 : else
367 : {
368 1 : Concat(osRet, psOptions->bStdoutOutput,
369 : " Maximum value: " CPL_FRMT_GIB "%s\n",
370 1 : sMax.Integer64,
371 : bMaxIsIncluded ? "" : " (excluded)");
372 : }
373 : }
374 : }
375 6 : else if (poDomain->GetFieldType() == OFTReal)
376 : {
377 4 : if (!OGR_RawField_IsUnset(&sMin))
378 : {
379 2 : if (bJson)
380 : {
381 1 : oDomain.Set("minValue", sMin.Real);
382 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
383 : }
384 : else
385 : {
386 1 : Concat(osRet, psOptions->bStdoutOutput,
387 1 : " Minimum value: %g%s\n", sMin.Real,
388 : bMinIsIncluded ? "" : " (excluded)");
389 : }
390 : }
391 4 : if (!OGR_RawField_IsUnset(&sMax))
392 : {
393 2 : if (bJson)
394 : {
395 1 : oDomain.Set("maxValue", sMax.Real);
396 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
397 : }
398 : else
399 : {
400 1 : Concat(osRet, psOptions->bStdoutOutput,
401 1 : " Maximum value: %g%s\n", sMax.Real,
402 : bMaxIsIncluded ? "" : " (excluded)");
403 : }
404 : }
405 : }
406 2 : else if (poDomain->GetFieldType() == OFTDateTime)
407 : {
408 2 : if (!OGR_RawField_IsUnset(&sMin))
409 : {
410 4 : const char *pszVal = CPLSPrintf(
411 2 : "%04d-%02d-%02dT%02d:%02d:%02d", sMin.Date.Year,
412 2 : sMin.Date.Month, sMin.Date.Day, sMin.Date.Hour,
413 2 : sMin.Date.Minute,
414 2 : static_cast<int>(sMin.Date.Second + 0.5));
415 2 : if (bJson)
416 : {
417 1 : oDomain.Set("minValue", pszVal);
418 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
419 : }
420 : else
421 : {
422 1 : Concat(osRet, psOptions->bStdoutOutput,
423 : " Minimum value: %s%s\n", pszVal,
424 : bMinIsIncluded ? "" : " (excluded)");
425 : }
426 : }
427 2 : if (!OGR_RawField_IsUnset(&sMax))
428 : {
429 4 : const char *pszVal = CPLSPrintf(
430 2 : "%04d-%02d-%02dT%02d:%02d:%02d", sMax.Date.Year,
431 2 : sMax.Date.Month, sMax.Date.Day, sMax.Date.Hour,
432 2 : sMax.Date.Minute,
433 2 : static_cast<int>(sMax.Date.Second + 0.5));
434 2 : if (bJson)
435 : {
436 1 : oDomain.Set("maxValue", pszVal);
437 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
438 : }
439 : else
440 : {
441 1 : Concat(osRet, psOptions->bStdoutOutput,
442 : " Maximum value: %s%s\n", pszVal,
443 : bMaxIsIncluded ? "" : " (excluded)");
444 : }
445 : }
446 : }
447 10 : break;
448 : }
449 :
450 2 : case OFDT_GLOB:
451 : {
452 : const auto poGlobFieldDomain =
453 2 : cpl::down_cast<const OGRGlobFieldDomain *>(poDomain);
454 2 : if (bJson)
455 1 : oDomain.Set("glob", poGlobFieldDomain->GetGlob());
456 : else
457 1 : Concat(osRet, psOptions->bStdoutOutput, " Glob: %s\n",
458 1 : poGlobFieldDomain->GetGlob().c_str());
459 2 : break;
460 : }
461 : }
462 14 : }
463 :
464 : /************************************************************************/
465 : /* ReportRelationships() */
466 : /************************************************************************/
467 :
468 61 : static void ReportRelationships(CPLString &osRet, CPLJSONObject &oRoot,
469 : const GDALVectorInfoOptions *psOptions,
470 : const GDALDataset *poDS)
471 : {
472 61 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
473 122 : CPLJSONObject oRelationships;
474 61 : if (bJson)
475 22 : oRoot.Add("relationships", oRelationships);
476 :
477 122 : const auto aosRelationshipNames = poDS->GetRelationshipNames();
478 83 : for (const std::string &osRelationshipName : aosRelationshipNames)
479 : {
480 22 : const auto poRelationship = poDS->GetRelationship(osRelationshipName);
481 22 : if (!poRelationship)
482 0 : continue;
483 :
484 22 : const char *pszType = "";
485 22 : switch (poRelationship->GetType())
486 : {
487 8 : case GRT_COMPOSITE:
488 8 : pszType = "Composite";
489 8 : break;
490 14 : case GRT_ASSOCIATION:
491 14 : pszType = "Association";
492 14 : break;
493 0 : case GRT_AGGREGATION:
494 0 : pszType = "Aggregation";
495 0 : break;
496 : }
497 :
498 22 : const char *pszCardinality = "";
499 22 : switch (poRelationship->GetCardinality())
500 : {
501 12 : case GRC_ONE_TO_ONE:
502 12 : pszCardinality = "OneToOne";
503 12 : break;
504 6 : case GRC_ONE_TO_MANY:
505 6 : pszCardinality = "OneToMany";
506 6 : break;
507 0 : case GRC_MANY_TO_ONE:
508 0 : pszCardinality = "ManyToOne";
509 0 : break;
510 4 : case GRC_MANY_TO_MANY:
511 4 : pszCardinality = "ManyToMany";
512 4 : break;
513 : }
514 :
515 22 : const auto &aosLeftTableFields = poRelationship->GetLeftTableFields();
516 22 : const auto &aosRightTableFields = poRelationship->GetRightTableFields();
517 22 : const auto &osMappingTableName = poRelationship->GetMappingTableName();
518 : const auto &aosLeftMappingTableFields =
519 22 : poRelationship->GetLeftMappingTableFields();
520 : const auto &aosRightMappingTableFields =
521 22 : poRelationship->GetRightMappingTableFields();
522 :
523 22 : if (bJson)
524 : {
525 22 : CPLJSONObject oRelationship;
526 11 : oRelationships.Add(osRelationshipName, oRelationship);
527 :
528 11 : oRelationship.Add("type", pszType);
529 11 : oRelationship.Add("related_table_type",
530 : poRelationship->GetRelatedTableType());
531 11 : oRelationship.Add("cardinality", pszCardinality);
532 11 : oRelationship.Add("left_table_name",
533 : poRelationship->GetLeftTableName());
534 11 : oRelationship.Add("right_table_name",
535 : poRelationship->GetRightTableName());
536 :
537 22 : CPLJSONArray oLeftTableFields;
538 11 : oRelationship.Add("left_table_fields", oLeftTableFields);
539 22 : for (const auto &osName : aosLeftTableFields)
540 11 : oLeftTableFields.Add(osName);
541 :
542 11 : CPLJSONArray oRightTableFields;
543 11 : oRelationship.Add("right_table_fields", oRightTableFields);
544 23 : for (const auto &osName : aosRightTableFields)
545 12 : oRightTableFields.Add(osName);
546 :
547 11 : if (!osMappingTableName.empty())
548 : {
549 2 : oRelationship.Add("mapping_table_name", osMappingTableName);
550 :
551 4 : CPLJSONArray oLeftMappingTableFields;
552 2 : oRelationship.Add("left_mapping_table_fields",
553 : oLeftMappingTableFields);
554 4 : for (const auto &osName : aosLeftMappingTableFields)
555 2 : oLeftMappingTableFields.Add(osName);
556 :
557 4 : CPLJSONArray oRightMappingTableFields;
558 2 : oRelationship.Add("right_mapping_table_fields",
559 : oRightMappingTableFields);
560 4 : for (const auto &osName : aosRightMappingTableFields)
561 2 : oRightMappingTableFields.Add(osName);
562 : }
563 :
564 11 : oRelationship.Add("forward_path_label",
565 : poRelationship->GetForwardPathLabel());
566 11 : oRelationship.Add("backward_path_label",
567 : poRelationship->GetBackwardPathLabel());
568 : }
569 : else
570 : {
571 : const auto ConcatStringList =
572 80 : [&osRet, psOptions](const std::vector<std::string> &aosList)
573 : {
574 26 : bool bFirstName = true;
575 53 : for (const auto &osName : aosList)
576 : {
577 27 : if (!bFirstName)
578 1 : ConcatStr(osRet, psOptions->bStdoutOutput, ", ");
579 27 : bFirstName = false;
580 27 : ConcatStr(osRet, psOptions->bStdoutOutput, osName.c_str());
581 : }
582 26 : Concat(osRet, psOptions->bStdoutOutput, "\n");
583 26 : };
584 :
585 11 : if (!psOptions->bAllLayers)
586 : {
587 0 : Concat(osRet, psOptions->bStdoutOutput,
588 : "Relationship: %s (%s, %s, %s)\n",
589 : osRelationshipName.c_str(), pszType,
590 0 : poRelationship->GetLeftTableName().c_str(),
591 0 : poRelationship->GetRightTableName().c_str());
592 0 : continue;
593 : }
594 11 : Concat(osRet, psOptions->bStdoutOutput, "\nRelationship: %s\n",
595 : osRelationshipName.c_str());
596 11 : Concat(osRet, psOptions->bStdoutOutput, " Type: %s\n", pszType);
597 11 : Concat(osRet, psOptions->bStdoutOutput,
598 : " Related table type: %s\n",
599 11 : poRelationship->GetRelatedTableType().c_str());
600 11 : Concat(osRet, psOptions->bStdoutOutput, " Cardinality: %s\n",
601 : pszCardinality);
602 11 : Concat(osRet, psOptions->bStdoutOutput, " Left table name: %s\n",
603 11 : poRelationship->GetLeftTableName().c_str());
604 11 : Concat(osRet, psOptions->bStdoutOutput, " Right table name: %s\n",
605 11 : poRelationship->GetRightTableName().c_str());
606 11 : Concat(osRet, psOptions->bStdoutOutput, " Left table fields: ");
607 11 : ConcatStringList(aosLeftTableFields);
608 11 : Concat(osRet, psOptions->bStdoutOutput, " Right table fields: ");
609 11 : ConcatStringList(aosRightTableFields);
610 :
611 11 : if (!osMappingTableName.empty())
612 : {
613 2 : Concat(osRet, psOptions->bStdoutOutput,
614 : " Mapping table name: %s\n",
615 : osMappingTableName.c_str());
616 :
617 2 : Concat(osRet, psOptions->bStdoutOutput,
618 : " Left mapping table fields: ");
619 2 : ConcatStringList(aosLeftMappingTableFields);
620 :
621 2 : Concat(osRet, psOptions->bStdoutOutput,
622 : " Right mapping table fields: ");
623 2 : ConcatStringList(aosRightMappingTableFields);
624 : }
625 :
626 11 : Concat(osRet, psOptions->bStdoutOutput,
627 : " Forward path label: %s\n",
628 11 : poRelationship->GetForwardPathLabel().c_str());
629 11 : Concat(osRet, psOptions->bStdoutOutput,
630 : " Backward path label: %s\n",
631 11 : poRelationship->GetBackwardPathLabel().c_str());
632 : }
633 : }
634 61 : }
635 :
636 : /************************************************************************/
637 : /* GDALVectorInfoPrintMetadata() */
638 : /************************************************************************/
639 :
640 : static void
641 421 : GDALVectorInfoPrintMetadata(CPLString &osRet, CPLJSONObject &oMetadata,
642 : const GDALVectorInfoOptions *psOptions,
643 : GDALMajorObjectH hObject, const char *pszDomain,
644 : const char *pszDisplayedname, const char *pszIndent)
645 : {
646 421 : const bool bJsonOutput = psOptions->eFormat == FORMAT_JSON;
647 421 : bool bIsxml = false;
648 421 : bool bMDIsJson = false;
649 :
650 421 : if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:"))
651 0 : bIsxml = true;
652 421 : else if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:"))
653 1 : bMDIsJson = true;
654 :
655 421 : CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
656 421 : if (CSLCount(papszMetadata) > 0)
657 : {
658 31 : CPLJSONObject oMetadataDomain;
659 31 : if (!bJsonOutput)
660 17 : Concat(osRet, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
661 : pszDisplayedname);
662 61 : for (int i = 0; papszMetadata[i] != nullptr; i++)
663 : {
664 31 : if (bJsonOutput)
665 : {
666 14 : if (bIsxml)
667 : {
668 0 : oMetadata.Add(pszDomain, papszMetadata[i]);
669 0 : return;
670 : }
671 14 : else if (bMDIsJson)
672 : {
673 1 : CPLJSONDocument oDoc;
674 1 : if (oDoc.LoadMemory(papszMetadata[i]))
675 1 : oMetadata.Add(pszDomain, oDoc.GetRoot());
676 1 : return;
677 : }
678 : else
679 : {
680 13 : char *pszKey = nullptr;
681 : const char *pszValue =
682 13 : CPLParseNameValue(papszMetadata[i], &pszKey);
683 13 : if (pszKey)
684 : {
685 13 : oMetadataDomain.Add(pszKey, pszValue);
686 13 : CPLFree(pszKey);
687 : }
688 : }
689 : }
690 17 : else if (bIsxml)
691 0 : Concat(osRet, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
692 0 : papszMetadata[i]);
693 : else
694 17 : Concat(osRet, psOptions->bStdoutOutput, "%s %s\n", pszIndent,
695 17 : papszMetadata[i]);
696 : }
697 30 : if (bJsonOutput)
698 : {
699 13 : oMetadata.Add(pszDomain ? pszDomain : "", oMetadataDomain);
700 : }
701 : }
702 : }
703 :
704 : /************************************************************************/
705 : /* GDALVectorInfoReportMetadata() */
706 : /************************************************************************/
707 :
708 208 : static void GDALVectorInfoReportMetadata(CPLString &osRet, CPLJSONObject &oRoot,
709 : const GDALVectorInfoOptions *psOptions,
710 : GDALMajorObject *poMajorObject,
711 : bool bListMDD, bool bShowMetadata,
712 : CSLConstList papszExtraMDDomains)
713 : {
714 208 : const char *pszIndent = "";
715 208 : auto hObject = GDALMajorObject::ToHandle(poMajorObject);
716 :
717 208 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
718 : /* -------------------------------------------------------------------- */
719 : /* Report list of Metadata domains */
720 : /* -------------------------------------------------------------------- */
721 208 : if (bListMDD)
722 : {
723 0 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
724 :
725 0 : CPLJSONArray metadataDomains;
726 :
727 0 : if (!aosMDDList.empty() && !bJson)
728 0 : Concat(osRet, psOptions->bStdoutOutput, "%sMetadata domains:\n",
729 : pszIndent);
730 0 : for (const char *pszDomain : aosMDDList)
731 : {
732 0 : if (EQUAL(pszDomain, ""))
733 : {
734 0 : if (bJson)
735 0 : metadataDomains.Add("");
736 : else
737 0 : Concat(osRet, psOptions->bStdoutOutput, "%s (default)\n",
738 : pszIndent);
739 : }
740 : else
741 : {
742 0 : if (bJson)
743 0 : metadataDomains.Add(pszDomain);
744 : else
745 0 : Concat(osRet, psOptions->bStdoutOutput, "%s %s\n",
746 : pszIndent, pszDomain);
747 : }
748 : }
749 :
750 0 : if (bJson)
751 0 : oRoot.Add("metadataDomains", metadataDomains);
752 : }
753 :
754 208 : if (!bShowMetadata)
755 2 : return;
756 :
757 : /* -------------------------------------------------------------------- */
758 : /* Report default Metadata domain. */
759 : /* -------------------------------------------------------------------- */
760 412 : CPLJSONObject oMetadata;
761 206 : oRoot.Add("metadata", oMetadata);
762 206 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject, nullptr,
763 : "Metadata", pszIndent);
764 :
765 : /* -------------------------------------------------------------------- */
766 : /* Report extra Metadata domains */
767 : /* -------------------------------------------------------------------- */
768 206 : if (papszExtraMDDomains != nullptr)
769 : {
770 128 : CPLStringList aosExtraMDDomainsExpanded;
771 :
772 64 : if (EQUAL(papszExtraMDDomains[0], "all") &&
773 64 : papszExtraMDDomains[1] == nullptr)
774 : {
775 128 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
776 81 : for (const char *pszDomain : aosMDDList)
777 : {
778 17 : if (!EQUAL(pszDomain, "") && !EQUAL(pszDomain, "SUBDATASETS"))
779 : {
780 9 : aosExtraMDDomainsExpanded.AddString(pszDomain);
781 : }
782 64 : }
783 : }
784 : else
785 : {
786 0 : aosExtraMDDomainsExpanded = CSLDuplicate(papszExtraMDDomains);
787 : }
788 :
789 73 : for (const char *pszDomain : aosExtraMDDomainsExpanded)
790 : {
791 : const std::string osDisplayedName =
792 27 : std::string("Metadata (").append(pszDomain).append(")");
793 9 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
794 : pszDomain, osDisplayedName.c_str(),
795 : pszIndent);
796 : }
797 : }
798 206 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
799 : "SUBDATASETS", "Subdatasets", pszIndent);
800 : }
801 :
802 : /************************************************************************/
803 : /* ReportOnLayer() */
804 : /************************************************************************/
805 :
806 136 : static void ReportOnLayer(CPLString &osRet, CPLJSONObject &oLayer,
807 : const GDALVectorInfoOptions *psOptions,
808 : OGRLayer *poLayer, bool bForceSummary,
809 : bool bTakeIntoAccountWHERE,
810 : bool bTakeIntoAccountSpatialFilter,
811 : bool bTakeIntoAccountGeomField)
812 : {
813 136 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
814 136 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
815 :
816 136 : oLayer.Set("name", poLayer->GetName());
817 :
818 : /* -------------------------------------------------------------------- */
819 : /* Set filters if provided. */
820 : /* -------------------------------------------------------------------- */
821 136 : if (bTakeIntoAccountWHERE && !psOptions->osWHERE.empty())
822 : {
823 2 : if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
824 : OGRERR_NONE)
825 : {
826 0 : CPLError(CE_Failure, CPLE_AppDefined,
827 : "SetAttributeFilter(%s) failed.",
828 0 : psOptions->osWHERE.c_str());
829 0 : return;
830 : }
831 : }
832 :
833 136 : if (bTakeIntoAccountSpatialFilter && psOptions->poSpatialFilter != nullptr)
834 : {
835 2 : if (bTakeIntoAccountGeomField && !psOptions->osGeomField.empty())
836 : {
837 : const int iGeomField =
838 1 : poDefn->GetGeomFieldIndex(psOptions->osGeomField.c_str());
839 1 : if (iGeomField >= 0)
840 1 : poLayer->SetSpatialFilter(iGeomField,
841 1 : psOptions->poSpatialFilter.get());
842 : else
843 0 : CPLError(CE_Warning, CPLE_AppDefined,
844 : "Cannot find geometry field %s.",
845 0 : psOptions->osGeomField.c_str());
846 : }
847 : else
848 : {
849 1 : poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
850 : }
851 : }
852 :
853 : /* -------------------------------------------------------------------- */
854 : /* Report various overall information. */
855 : /* -------------------------------------------------------------------- */
856 136 : if (!bJson && !psOptions->bSuperQuiet)
857 : {
858 99 : Concat(osRet, psOptions->bStdoutOutput, "\n");
859 99 : Concat(osRet, psOptions->bStdoutOutput, "Layer name: %s\n",
860 99 : poLayer->GetName());
861 : }
862 :
863 272 : GDALVectorInfoReportMetadata(osRet, oLayer, psOptions, poLayer,
864 136 : psOptions->bListMDD, psOptions->bShowMetadata,
865 136 : psOptions->aosExtraMDDomains.List());
866 :
867 136 : if (psOptions->bVerbose)
868 : {
869 : const int nGeomFieldCount =
870 135 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount()
871 135 : : 0;
872 :
873 270 : CPLString osWKTFormat("FORMAT=");
874 135 : osWKTFormat += psOptions->osWKTFormat;
875 135 : const char *const apszWKTOptions[] = {osWKTFormat.c_str(),
876 135 : "MULTILINE=YES", nullptr};
877 :
878 135 : if (bJson || nGeomFieldCount > 1)
879 : {
880 78 : CPLJSONArray oGeometryFields;
881 39 : if (bJson)
882 37 : oLayer.Add("geometryFields", oGeometryFields);
883 68 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
884 : {
885 : const OGRGeomFieldDefn *poGFldDefn =
886 29 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
887 29 : if (bJson)
888 : {
889 50 : CPLJSONObject oGeometryField;
890 25 : oGeometryFields.Add(oGeometryField);
891 25 : oGeometryField.Set("name", poGFldDefn->GetNameRef());
892 25 : oGeometryField.Set(
893 : "type", OGRToOGCGeomType(poGFldDefn->GetType(),
894 : /*bCamelCase=*/true,
895 : /*bAddZm=*/true,
896 : /*bSpaceBeforeZM=*/false));
897 25 : oGeometryField.Set("nullable",
898 25 : CPL_TO_BOOL(poGFldDefn->IsNullable()));
899 25 : if (psOptions->bExtent3D)
900 : {
901 3 : OGREnvelope3D oExt;
902 3 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) ==
903 : OGRERR_NONE)
904 : {
905 : {
906 3 : CPLJSONArray oBbox;
907 3 : oBbox.Add(oExt.MinX);
908 3 : oBbox.Add(oExt.MinY);
909 3 : oBbox.Add(oExt.MaxX);
910 3 : oBbox.Add(oExt.MaxY);
911 3 : oGeometryField.Add("extent", oBbox);
912 : }
913 : {
914 3 : CPLJSONArray oBbox;
915 3 : oBbox.Add(oExt.MinX);
916 3 : oBbox.Add(oExt.MinY);
917 3 : if (std::isfinite(oExt.MinZ))
918 2 : oBbox.Add(oExt.MinZ);
919 : else
920 1 : oBbox.AddNull();
921 3 : oBbox.Add(oExt.MaxX);
922 3 : oBbox.Add(oExt.MaxY);
923 3 : if (std::isfinite(oExt.MaxZ))
924 2 : oBbox.Add(oExt.MaxZ);
925 : else
926 1 : oBbox.AddNull();
927 3 : oGeometryField.Add("extent3D", oBbox);
928 : }
929 : }
930 : }
931 22 : else if (psOptions->bExtent)
932 : {
933 22 : OGREnvelope oExt;
934 22 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) ==
935 : OGRERR_NONE)
936 : {
937 14 : CPLJSONArray oBbox;
938 14 : oBbox.Add(oExt.MinX);
939 14 : oBbox.Add(oExt.MinY);
940 14 : oBbox.Add(oExt.MaxX);
941 14 : oBbox.Add(oExt.MaxY);
942 14 : oGeometryField.Add("extent", oBbox);
943 : }
944 : }
945 : const OGRSpatialReference *poSRS =
946 25 : poGFldDefn->GetSpatialRef();
947 25 : if (poSRS)
948 : {
949 32 : CPLJSONObject oCRS;
950 16 : oGeometryField.Add("coordinateSystem", oCRS);
951 16 : char *pszWKT = nullptr;
952 16 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
953 16 : if (pszWKT)
954 : {
955 16 : oCRS.Set("wkt", pszWKT);
956 16 : CPLFree(pszWKT);
957 : }
958 :
959 : {
960 16 : char *pszProjJson = nullptr;
961 : // PROJJSON requires PROJ >= 6.2
962 : CPLErrorStateBackuper oCPLErrorHandlerPusher(
963 32 : CPLQuietErrorHandler);
964 16 : CPL_IGNORE_RET_VAL(
965 16 : poSRS->exportToPROJJSON(&pszProjJson, nullptr));
966 16 : if (pszProjJson)
967 : {
968 32 : CPLJSONDocument oDoc;
969 16 : if (oDoc.LoadMemory(pszProjJson))
970 : {
971 16 : oCRS.Add("projjson", oDoc.GetRoot());
972 : }
973 16 : CPLFree(pszProjJson);
974 : }
975 : }
976 :
977 : const auto &anAxes =
978 16 : poSRS->GetDataAxisToSRSAxisMapping();
979 32 : CPLJSONArray oAxisMapping;
980 49 : for (const auto nAxis : anAxes)
981 : {
982 33 : oAxisMapping.Add(nAxis);
983 : }
984 16 : oCRS.Add("dataAxisToSRSAxisMapping", oAxisMapping);
985 :
986 : const double dfCoordinateEpoch =
987 16 : poSRS->GetCoordinateEpoch();
988 16 : if (dfCoordinateEpoch > 0)
989 0 : oCRS.Set("coordinateEpoch", dfCoordinateEpoch);
990 : }
991 : else
992 : {
993 9 : oGeometryField.SetNull("coordinateSystem");
994 : }
995 :
996 25 : const auto &srsList = poLayer->GetSupportedSRSList(iGeom);
997 25 : if (!srsList.empty())
998 : {
999 1 : CPLJSONArray oSupportedSRSList;
1000 3 : for (const auto &poSupportedSRS : srsList)
1001 : {
1002 : const char *pszAuthName =
1003 2 : poSupportedSRS->GetAuthorityName(nullptr);
1004 : const char *pszAuthCode =
1005 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1006 4 : CPLJSONObject oSupportedSRS;
1007 2 : if (pszAuthName && pszAuthCode)
1008 : {
1009 4 : CPLJSONObject id;
1010 2 : id.Set("authority", pszAuthName);
1011 2 : id.Set("code", pszAuthCode);
1012 2 : oSupportedSRS.Add("id", id);
1013 4 : oSupportedSRSList.Add(oSupportedSRS);
1014 : }
1015 : else
1016 : {
1017 0 : char *pszWKT = nullptr;
1018 0 : poSupportedSRS->exportToWkt(&pszWKT,
1019 : apszWKTOptions);
1020 0 : if (pszWKT)
1021 : {
1022 0 : oSupportedSRS.Add("wkt", pszWKT);
1023 0 : oSupportedSRSList.Add(oSupportedSRS);
1024 : }
1025 0 : CPLFree(pszWKT);
1026 : }
1027 : }
1028 1 : oGeometryField.Add("supportedSRSList",
1029 : oSupportedSRSList);
1030 : }
1031 :
1032 : const auto &oCoordPrec =
1033 25 : poGFldDefn->GetCoordinatePrecision();
1034 25 : if (oCoordPrec.dfXYResolution !=
1035 : OGRGeomCoordinatePrecision::UNKNOWN)
1036 : {
1037 4 : oGeometryField.Add("xyCoordinateResolution",
1038 4 : oCoordPrec.dfXYResolution);
1039 : }
1040 25 : if (oCoordPrec.dfZResolution !=
1041 : OGRGeomCoordinatePrecision::UNKNOWN)
1042 : {
1043 4 : oGeometryField.Add("zCoordinateResolution",
1044 4 : oCoordPrec.dfZResolution);
1045 : }
1046 25 : if (oCoordPrec.dfMResolution !=
1047 : OGRGeomCoordinatePrecision::UNKNOWN)
1048 : {
1049 3 : oGeometryField.Add("mCoordinateResolution",
1050 3 : oCoordPrec.dfMResolution);
1051 : }
1052 :
1053 : // For example set by OpenFileGDB driver
1054 25 : if (!oCoordPrec.oFormatSpecificOptions.empty())
1055 : {
1056 3 : CPLJSONObject oFormatSpecificOptions;
1057 3 : for (const auto &formatOptionsPair :
1058 9 : oCoordPrec.oFormatSpecificOptions)
1059 : {
1060 6 : CPLJSONObject oThisFormatSpecificOptions;
1061 66 : for (const auto &[pszKey, pszValue] :
1062 : cpl::IterateNameValue(
1063 69 : formatOptionsPair.second))
1064 : {
1065 : const auto eValueType =
1066 33 : CPLGetValueType(pszValue);
1067 33 : if (eValueType == CPL_VALUE_INTEGER)
1068 : {
1069 21 : oThisFormatSpecificOptions.Add(
1070 : pszKey, CPLAtoGIntBig(pszValue));
1071 : }
1072 12 : else if (eValueType == CPL_VALUE_REAL)
1073 : {
1074 9 : oThisFormatSpecificOptions.Add(
1075 : pszKey, CPLAtof(pszValue));
1076 : }
1077 : else
1078 : {
1079 3 : oThisFormatSpecificOptions.Add(pszKey,
1080 : pszValue);
1081 : }
1082 : }
1083 3 : oFormatSpecificOptions.Add(
1084 3 : formatOptionsPair.first,
1085 : oThisFormatSpecificOptions);
1086 : }
1087 3 : oGeometryField.Add(
1088 : "coordinatePrecisionFormatSpecificOptions",
1089 : oFormatSpecificOptions);
1090 : }
1091 : }
1092 : else
1093 : {
1094 4 : Concat(osRet, psOptions->bStdoutOutput,
1095 : "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
1096 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1097 : }
1098 39 : }
1099 : }
1100 96 : else if (psOptions->bGeomType)
1101 : {
1102 96 : Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s\n",
1103 96 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1104 : }
1105 :
1106 135 : if (psOptions->bFeatureCount)
1107 : {
1108 133 : if (bJson)
1109 36 : oLayer.Set("featureCount", poLayer->GetFeatureCount());
1110 : else
1111 : {
1112 97 : Concat(osRet, psOptions->bStdoutOutput,
1113 : "Feature Count: " CPL_FRMT_GIB "\n",
1114 97 : poLayer->GetFeatureCount());
1115 : }
1116 : }
1117 :
1118 135 : if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
1119 : {
1120 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1121 : {
1122 4 : if (psOptions->bExtent3D)
1123 : {
1124 0 : OGREnvelope3D oExt;
1125 0 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
1126 : {
1127 : OGRGeomFieldDefn *poGFldDefn =
1128 0 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1129 0 : Concat(osRet, psOptions->bStdoutOutput,
1130 : "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
1131 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1132 0 : std::isfinite(oExt.MinZ)
1133 0 : ? CPLSPrintf("%f", oExt.MinZ)
1134 : : "none",
1135 : oExt.MaxX, oExt.MaxY,
1136 0 : std::isfinite(oExt.MaxZ)
1137 0 : ? CPLSPrintf("%f", oExt.MaxZ)
1138 : : "none");
1139 : }
1140 : }
1141 : else
1142 : {
1143 4 : OGREnvelope oExt;
1144 4 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
1145 : {
1146 : OGRGeomFieldDefn *poGFldDefn =
1147 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1148 4 : Concat(osRet, psOptions->bStdoutOutput,
1149 : "Extent (%s): (%f, %f) - (%f, %f)\n",
1150 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1151 : oExt.MaxX, oExt.MaxY);
1152 : }
1153 : }
1154 2 : }
1155 : }
1156 133 : else if (!bJson && psOptions->bExtent)
1157 : {
1158 96 : if (psOptions->bExtent3D)
1159 : {
1160 2 : OGREnvelope3D oExt;
1161 2 : if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
1162 : {
1163 4 : Concat(
1164 2 : osRet, psOptions->bStdoutOutput,
1165 : "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
1166 : oExt.MinY,
1167 3 : std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
1168 : : "none",
1169 : oExt.MaxX, oExt.MaxY,
1170 3 : std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
1171 : : "none");
1172 : }
1173 : }
1174 : else
1175 : {
1176 94 : OGREnvelope oExt;
1177 94 : if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
1178 : {
1179 18 : Concat(osRet, psOptions->bStdoutOutput,
1180 : "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
1181 : oExt.MinY, oExt.MaxX, oExt.MaxY);
1182 : }
1183 : }
1184 : }
1185 :
1186 : const auto displayExtraInfoSRS =
1187 158 : [&osRet, &psOptions](const OGRSpatialReference *poSRS)
1188 : {
1189 26 : const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
1190 26 : if (dfCoordinateEpoch > 0)
1191 : {
1192 : std::string osCoordinateEpoch =
1193 0 : CPLSPrintf("%f", dfCoordinateEpoch);
1194 0 : if (osCoordinateEpoch.find('.') != std::string::npos)
1195 : {
1196 0 : while (osCoordinateEpoch.back() == '0')
1197 0 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
1198 : }
1199 0 : Concat(osRet, psOptions->bStdoutOutput,
1200 : "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
1201 : }
1202 :
1203 26 : const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
1204 26 : Concat(osRet, psOptions->bStdoutOutput,
1205 : "Data axis to CRS axis mapping: ");
1206 79 : for (size_t i = 0; i < mapping.size(); i++)
1207 : {
1208 53 : if (i > 0)
1209 : {
1210 27 : Concat(osRet, psOptions->bStdoutOutput, ",");
1211 : }
1212 53 : Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
1213 : }
1214 26 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1215 26 : };
1216 :
1217 100 : const auto DisplaySupportedCRSList = [&](int iGeomField)
1218 : {
1219 100 : const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
1220 100 : if (!srsList.empty())
1221 : {
1222 1 : Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
1223 1 : bool bFirst = true;
1224 3 : for (const auto &poSupportedSRS : srsList)
1225 : {
1226 : const char *pszAuthName =
1227 2 : poSupportedSRS->GetAuthorityName(nullptr);
1228 : const char *pszAuthCode =
1229 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1230 2 : if (!bFirst)
1231 1 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1232 2 : bFirst = false;
1233 2 : if (pszAuthName && pszAuthCode)
1234 : {
1235 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
1236 : pszAuthName, pszAuthCode);
1237 : }
1238 : else
1239 : {
1240 0 : ConcatStr(osRet, psOptions->bStdoutOutput,
1241 : poSupportedSRS->GetName());
1242 : }
1243 : }
1244 1 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1245 : }
1246 100 : };
1247 :
1248 135 : if (!bJson && nGeomFieldCount > 1)
1249 : {
1250 :
1251 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1252 : {
1253 : OGRGeomFieldDefn *poGFldDefn =
1254 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1255 4 : const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
1256 4 : char *pszWKT = nullptr;
1257 4 : if (poSRS == nullptr)
1258 : {
1259 0 : pszWKT = CPLStrdup("(unknown)");
1260 : }
1261 : else
1262 : {
1263 4 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1264 : }
1265 :
1266 4 : Concat(osRet, psOptions->bStdoutOutput, "SRS WKT (%s):\n%s\n",
1267 : poGFldDefn->GetNameRef(), pszWKT);
1268 4 : CPLFree(pszWKT);
1269 4 : if (poSRS)
1270 : {
1271 4 : displayExtraInfoSRS(poSRS);
1272 : }
1273 4 : DisplaySupportedCRSList(iGeom);
1274 2 : }
1275 : }
1276 133 : else if (!bJson)
1277 : {
1278 96 : char *pszWKT = nullptr;
1279 96 : auto poSRS = poLayer->GetSpatialRef();
1280 96 : if (poSRS == nullptr)
1281 : {
1282 74 : pszWKT = CPLStrdup("(unknown)");
1283 : }
1284 : else
1285 : {
1286 22 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1287 : }
1288 :
1289 96 : Concat(osRet, psOptions->bStdoutOutput, "Layer SRS WKT:\n%s\n",
1290 : pszWKT);
1291 96 : CPLFree(pszWKT);
1292 96 : if (poSRS)
1293 : {
1294 22 : displayExtraInfoSRS(poSRS);
1295 : }
1296 96 : DisplaySupportedCRSList(0);
1297 : }
1298 :
1299 135 : const char *pszFIDColumn = poLayer->GetFIDColumn();
1300 135 : if (pszFIDColumn[0] != '\0')
1301 : {
1302 33 : if (bJson)
1303 18 : oLayer.Set("fidColumnName", pszFIDColumn);
1304 : else
1305 : {
1306 15 : Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
1307 : pszFIDColumn);
1308 : }
1309 : }
1310 :
1311 143 : for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
1312 : {
1313 : OGRGeomFieldDefn *poGFldDefn =
1314 26 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1315 44 : if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
1316 18 : poGFldDefn->IsNullable())
1317 18 : break;
1318 8 : Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
1319 8 : if (nGeomFieldCount > 1)
1320 4 : Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
1321 8 : if (!poGFldDefn->IsNullable())
1322 0 : Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
1323 8 : Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
1324 : poGFldDefn->GetNameRef());
1325 : }
1326 :
1327 270 : CPLJSONArray oFields;
1328 135 : if (bJson)
1329 37 : oLayer.Add("fields", oFields);
1330 659 : for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
1331 : {
1332 524 : const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
1333 524 : const char *pszAlias = poField->GetAlternativeNameRef();
1334 524 : const std::string &osDomain = poField->GetDomainName();
1335 524 : const std::string &osComment = poField->GetComment();
1336 524 : const auto eType = poField->GetType();
1337 1048 : std::string osTimeZone;
1338 524 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
1339 : {
1340 24 : const int nTZFlag = poField->GetTZFlag();
1341 24 : if (nTZFlag == OGR_TZFLAG_LOCALTIME)
1342 : {
1343 1 : osTimeZone = "localtime";
1344 : }
1345 23 : else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
1346 : {
1347 1 : osTimeZone = "mixed timezones";
1348 : }
1349 22 : else if (nTZFlag == OGR_TZFLAG_UTC)
1350 : {
1351 1 : osTimeZone = "UTC";
1352 : }
1353 21 : else if (nTZFlag > 0)
1354 : {
1355 : char chSign;
1356 2 : const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
1357 2 : int nHours =
1358 : static_cast<int>(nOffset / 60); // Round towards zero.
1359 2 : const int nMinutes = std::abs(nOffset - nHours * 60);
1360 :
1361 2 : if (nOffset < 0)
1362 : {
1363 1 : chSign = '-';
1364 1 : nHours = std::abs(nHours);
1365 : }
1366 : else
1367 : {
1368 1 : chSign = '+';
1369 : }
1370 : osTimeZone =
1371 2 : CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
1372 : }
1373 : }
1374 :
1375 524 : if (bJson)
1376 : {
1377 126 : CPLJSONObject oField;
1378 63 : oFields.Add(oField);
1379 63 : oField.Set("name", poField->GetNameRef());
1380 63 : oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
1381 63 : if (poField->GetSubType() != OFSTNone)
1382 1 : oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
1383 : poField->GetSubType()));
1384 63 : if (poField->GetWidth() > 0)
1385 29 : oField.Set("width", poField->GetWidth());
1386 63 : if (poField->GetPrecision() > 0)
1387 6 : oField.Set("precision", poField->GetPrecision());
1388 63 : oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
1389 63 : oField.Set("uniqueConstraint",
1390 63 : CPL_TO_BOOL(poField->IsUnique()));
1391 63 : if (poField->GetDefault() != nullptr)
1392 0 : oField.Set("defaultValue", poField->GetDefault());
1393 63 : if (pszAlias != nullptr && pszAlias[0])
1394 0 : oField.Set("alias", pszAlias);
1395 63 : if (!osDomain.empty())
1396 5 : oField.Set("domainName", osDomain);
1397 63 : if (!osComment.empty())
1398 0 : oField.Set("comment", osComment);
1399 63 : if (!osTimeZone.empty())
1400 5 : oField.Set("timezone", osTimeZone);
1401 : }
1402 : else
1403 : {
1404 : const char *pszType =
1405 461 : (poField->GetSubType() != OFSTNone)
1406 461 : ? CPLSPrintf("%s(%s)",
1407 : OGRFieldDefn::GetFieldTypeName(
1408 : poField->GetType()),
1409 : OGRFieldDefn::GetFieldSubTypeName(
1410 : poField->GetSubType()))
1411 439 : : OGRFieldDefn::GetFieldTypeName(poField->GetType());
1412 461 : Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
1413 : poField->GetNameRef(), pszType);
1414 461 : if (eType == OFTTime || eType == OFTDate ||
1415 : eType == OFTDateTime)
1416 : {
1417 18 : if (!osTimeZone.empty())
1418 0 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1419 : osTimeZone.c_str());
1420 : }
1421 : else
1422 : {
1423 443 : Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
1424 : poField->GetWidth(), poField->GetPrecision());
1425 : }
1426 461 : if (poField->IsUnique())
1427 0 : Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
1428 461 : if (!poField->IsNullable())
1429 204 : Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
1430 461 : if (poField->GetDefault() != nullptr)
1431 8 : Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
1432 : poField->GetDefault());
1433 461 : if (pszAlias != nullptr && pszAlias[0])
1434 0 : Concat(osRet, psOptions->bStdoutOutput,
1435 : ", alternative name=\"%s\"", pszAlias);
1436 461 : if (!osDomain.empty())
1437 5 : Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
1438 : osDomain.c_str());
1439 461 : if (!osComment.empty())
1440 0 : Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
1441 : osComment.c_str());
1442 461 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1443 : }
1444 : }
1445 : }
1446 :
1447 : /* -------------------------------------------------------------------- */
1448 : /* Read, and dump features. */
1449 : /* -------------------------------------------------------------------- */
1450 :
1451 136 : if (psOptions->nFetchFID == OGRNullFID && !bForceSummary &&
1452 135 : !psOptions->bSummaryOnly)
1453 : {
1454 84 : if (!psOptions->bSuperQuiet)
1455 : {
1456 168 : CPLJSONArray oFeatures;
1457 : const bool bDisplayFields =
1458 84 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1459 : "DISPLAY_FIELDS", "YES"));
1460 : const int nFields =
1461 84 : bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
1462 : const bool bDisplayGeometry =
1463 84 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1464 : "DISPLAY_GEOMETRY", "YES"));
1465 : const int nGeomFields =
1466 84 : bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
1467 84 : : 0;
1468 84 : if (bJson)
1469 8 : oLayer.Add("features", oFeatures);
1470 84 : GIntBig nFeatureCount = 0;
1471 720 : for (auto &poFeature : poLayer)
1472 : {
1473 636 : if (psOptions->nLimit >= 0 &&
1474 2 : nFeatureCount >= psOptions->nLimit)
1475 : {
1476 1 : break;
1477 : }
1478 635 : ++nFeatureCount;
1479 :
1480 635 : if (bJson)
1481 : {
1482 80 : CPLJSONObject oFeature;
1483 80 : CPLJSONObject oProperties;
1484 40 : oFeatures.Add(oFeature);
1485 40 : oFeature.Add("type", "Feature");
1486 40 : oFeature.Add("properties", oProperties);
1487 40 : oFeature.Add("fid", poFeature->GetFID());
1488 140 : for (int i = 0; i < nFields; ++i)
1489 : {
1490 100 : const auto poFDefn = poFeature->GetFieldDefnRef(i);
1491 100 : const auto eType = poFDefn->GetType();
1492 100 : if (!poFeature->IsFieldSet(i))
1493 0 : continue;
1494 100 : if (poFeature->IsFieldNull(i))
1495 : {
1496 1 : oProperties.SetNull(poFDefn->GetNameRef());
1497 : }
1498 99 : else if (eType == OFTInteger)
1499 : {
1500 1 : if (poFDefn->GetSubType() == OFSTBoolean)
1501 0 : oProperties.Add(
1502 : poFDefn->GetNameRef(),
1503 0 : CPL_TO_BOOL(
1504 : poFeature->GetFieldAsInteger(i)));
1505 : else
1506 1 : oProperties.Add(
1507 : poFDefn->GetNameRef(),
1508 : poFeature->GetFieldAsInteger(i));
1509 : }
1510 98 : else if (eType == OFTInteger64)
1511 : {
1512 31 : oProperties.Add(poFDefn->GetNameRef(),
1513 : poFeature->GetFieldAsInteger64(i));
1514 : }
1515 67 : else if (eType == OFTReal)
1516 : {
1517 31 : oProperties.Add(poFDefn->GetNameRef(),
1518 : poFeature->GetFieldAsDouble(i));
1519 : }
1520 36 : else if ((eType == OFTString &&
1521 41 : poFDefn->GetSubType() != OFSTJSON) ||
1522 72 : eType == OFTDate || eType == OFTTime ||
1523 : eType == OFTDateTime)
1524 : {
1525 31 : oProperties.Add(poFDefn->GetNameRef(),
1526 : poFeature->GetFieldAsString(i));
1527 : }
1528 : else
1529 : {
1530 : char *pszSerialized =
1531 5 : poFeature->GetFieldAsSerializedJSon(i);
1532 5 : if (pszSerialized)
1533 : {
1534 : const auto eStrType =
1535 5 : CPLGetValueType(pszSerialized);
1536 5 : if (eStrType == CPL_VALUE_INTEGER)
1537 : {
1538 1 : oProperties.Add(
1539 : poFDefn->GetNameRef(),
1540 : CPLAtoGIntBig(pszSerialized));
1541 : }
1542 4 : else if (eStrType == CPL_VALUE_REAL)
1543 : {
1544 0 : oProperties.Add(poFDefn->GetNameRef(),
1545 : CPLAtof(pszSerialized));
1546 : }
1547 : else
1548 : {
1549 8 : CPLJSONDocument oDoc;
1550 4 : if (oDoc.LoadMemory(pszSerialized))
1551 4 : oProperties.Add(poFDefn->GetNameRef(),
1552 8 : oDoc.GetRoot());
1553 : }
1554 5 : CPLFree(pszSerialized);
1555 : }
1556 : }
1557 : }
1558 :
1559 78 : const auto GetGeoJSONOptions = [poLayer](int iGeomField)
1560 : {
1561 39 : CPLStringList aosGeoJSONOptions;
1562 : const auto &oCoordPrec =
1563 39 : poLayer->GetLayerDefn()
1564 39 : ->GetGeomFieldDefn(iGeomField)
1565 39 : ->GetCoordinatePrecision();
1566 39 : if (oCoordPrec.dfXYResolution !=
1567 : OGRGeomCoordinatePrecision::UNKNOWN)
1568 : {
1569 : aosGeoJSONOptions.SetNameValue(
1570 : "XY_COORD_PRECISION",
1571 : CPLSPrintf("%d",
1572 : OGRGeomCoordinatePrecision::
1573 : ResolutionToPrecision(
1574 1 : oCoordPrec.dfXYResolution)));
1575 : }
1576 39 : if (oCoordPrec.dfZResolution !=
1577 : OGRGeomCoordinatePrecision::UNKNOWN)
1578 : {
1579 : aosGeoJSONOptions.SetNameValue(
1580 : "Z_COORD_PRECISION",
1581 : CPLSPrintf("%d",
1582 : OGRGeomCoordinatePrecision::
1583 : ResolutionToPrecision(
1584 1 : oCoordPrec.dfZResolution)));
1585 : }
1586 39 : return aosGeoJSONOptions;
1587 40 : };
1588 :
1589 40 : if (nGeomFields == 0)
1590 1 : oFeature.SetNull("geometry");
1591 : else
1592 : {
1593 39 : if (const auto poGeom = poFeature->GetGeometryRef())
1594 : {
1595 : char *pszSerialized =
1596 39 : wkbFlatten(poGeom->getGeometryType()) <=
1597 : wkbGeometryCollection
1598 78 : ? poGeom->exportToJson(
1599 78 : GetGeoJSONOptions(0).List())
1600 39 : : nullptr;
1601 39 : if (pszSerialized)
1602 : {
1603 78 : CPLJSONDocument oDoc;
1604 39 : if (oDoc.LoadMemory(pszSerialized))
1605 39 : oFeature.Add("geometry", oDoc.GetRoot());
1606 39 : CPLFree(pszSerialized);
1607 : }
1608 : else
1609 : {
1610 0 : CPLJSONObject oGeometry;
1611 0 : oFeature.SetNull("geometry");
1612 0 : oFeature.Add("wkt_geometry",
1613 0 : poGeom->exportToWkt());
1614 : }
1615 : }
1616 : else
1617 0 : oFeature.SetNull("geometry");
1618 :
1619 39 : if (nGeomFields > 1)
1620 : {
1621 0 : CPLJSONArray oGeometries;
1622 0 : oFeature.Add("geometries", oGeometries);
1623 0 : for (int i = 0; i < nGeomFields; ++i)
1624 : {
1625 0 : auto poGeom = poFeature->GetGeomFieldRef(i);
1626 0 : if (poGeom)
1627 : {
1628 : char *pszSerialized =
1629 0 : wkbFlatten(poGeom->getGeometryType()) <=
1630 : wkbGeometryCollection
1631 0 : ? poGeom->exportToJson(
1632 0 : GetGeoJSONOptions(i).List())
1633 0 : : nullptr;
1634 0 : if (pszSerialized)
1635 : {
1636 0 : CPLJSONDocument oDoc;
1637 0 : if (oDoc.LoadMemory(pszSerialized))
1638 0 : oGeometries.Add(oDoc.GetRoot());
1639 0 : CPLFree(pszSerialized);
1640 : }
1641 : else
1642 : {
1643 0 : CPLJSONObject oGeometry;
1644 0 : oGeometries.Add(poGeom->exportToWkt());
1645 : }
1646 : }
1647 : else
1648 0 : oGeometries.AddNull();
1649 : }
1650 : }
1651 : }
1652 : }
1653 : else
1654 : {
1655 595 : ConcatStr(
1656 595 : osRet, psOptions->bStdoutOutput,
1657 : poFeature
1658 1190 : ->DumpReadableAsString(psOptions->aosOptions.List())
1659 : .c_str());
1660 : }
1661 : }
1662 84 : }
1663 : }
1664 52 : else if (!bJson && psOptions->nFetchFID != OGRNullFID)
1665 : {
1666 1 : OGRFeature *poFeature = poLayer->GetFeature(psOptions->nFetchFID);
1667 1 : if (poFeature == nullptr)
1668 : {
1669 0 : Concat(osRet, psOptions->bStdoutOutput,
1670 : "Unable to locate feature id " CPL_FRMT_GIB
1671 : " on this layer.\n",
1672 0 : psOptions->nFetchFID);
1673 : }
1674 : else
1675 : {
1676 1 : ConcatStr(
1677 1 : osRet, psOptions->bStdoutOutput,
1678 2 : poFeature->DumpReadableAsString(psOptions->aosOptions.List())
1679 : .c_str());
1680 1 : OGRFeature::DestroyFeature(poFeature);
1681 : }
1682 : }
1683 : }
1684 :
1685 : /************************************************************************/
1686 : /* PrintLayerSummary() */
1687 : /************************************************************************/
1688 :
1689 19 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
1690 : const GDALVectorInfoOptions *psOptions,
1691 : OGRLayer *poLayer, bool bIsPrivate)
1692 : {
1693 19 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1694 19 : if (bJson)
1695 0 : oLayer.Set("name", poLayer->GetName());
1696 : else
1697 19 : ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
1698 :
1699 19 : const char *pszTitle = poLayer->GetMetadataItem("TITLE");
1700 19 : if (pszTitle)
1701 : {
1702 0 : if (bJson)
1703 0 : oLayer.Set("title", pszTitle);
1704 : else
1705 0 : Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
1706 : }
1707 :
1708 : const int nGeomFieldCount =
1709 19 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
1710 19 : if (bJson || nGeomFieldCount > 1)
1711 : {
1712 1 : if (!bJson)
1713 1 : Concat(osRet, psOptions->bStdoutOutput, " (");
1714 2 : CPLJSONArray oGeometryFields;
1715 1 : oLayer.Add("geometryFields", oGeometryFields);
1716 3 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1717 : {
1718 : OGRGeomFieldDefn *poGFldDefn =
1719 2 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1720 2 : if (bJson)
1721 : {
1722 0 : oGeometryFields.Add(
1723 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1724 : }
1725 : else
1726 : {
1727 2 : if (iGeom > 0)
1728 1 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1729 2 : ConcatStr(osRet, psOptions->bStdoutOutput,
1730 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1731 : }
1732 : }
1733 1 : if (!bJson)
1734 2 : Concat(osRet, psOptions->bStdoutOutput, ")");
1735 : }
1736 18 : else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
1737 10 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1738 10 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1739 :
1740 19 : if (bIsPrivate)
1741 : {
1742 0 : if (bJson)
1743 0 : oLayer.Set("isPrivate", true);
1744 : else
1745 0 : Concat(osRet, psOptions->bStdoutOutput, " [private]");
1746 : }
1747 :
1748 19 : if (!bJson)
1749 19 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1750 19 : }
1751 :
1752 : /************************************************************************/
1753 : /* ReportHiearchicalLayers() */
1754 : /************************************************************************/
1755 :
1756 6 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
1757 : const GDALVectorInfoOptions *psOptions,
1758 : const GDALGroup *group,
1759 : const std::string &indent, bool bGeomType)
1760 : {
1761 6 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1762 12 : const auto aosVectorLayerNames = group->GetVectorLayerNames();
1763 12 : CPLJSONArray oLayerNames;
1764 6 : oRoot.Add("layerNames", oLayerNames);
1765 25 : for (const auto &osVectorLayerName : aosVectorLayerNames)
1766 : {
1767 19 : OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
1768 19 : if (poLayer)
1769 : {
1770 38 : CPLJSONObject oLayer;
1771 19 : if (!bJson)
1772 : {
1773 4 : Concat(osRet, psOptions->bStdoutOutput,
1774 : "%sLayer: ", indent.c_str());
1775 4 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
1776 : /* bIsPrivate=*/false);
1777 : }
1778 : else
1779 : {
1780 15 : oLayerNames.Add(poLayer->GetName());
1781 : }
1782 : }
1783 : }
1784 :
1785 12 : const std::string subIndent(indent + " ");
1786 12 : auto aosSubGroupNames = group->GetGroupNames();
1787 12 : CPLJSONArray oGroupArray;
1788 6 : oRoot.Add("groups", oGroupArray);
1789 8 : for (const auto &osSubGroupName : aosSubGroupNames)
1790 : {
1791 4 : auto poSubGroup = group->OpenGroup(osSubGroupName);
1792 2 : if (poSubGroup)
1793 : {
1794 4 : CPLJSONObject oGroup;
1795 2 : if (!bJson)
1796 : {
1797 2 : Concat(osRet, psOptions->bStdoutOutput, "Group %s",
1798 : indent.c_str());
1799 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
1800 : osSubGroupName.c_str());
1801 : }
1802 : else
1803 : {
1804 0 : oGroupArray.Add(oGroup);
1805 0 : oGroup.Set("name", osSubGroupName);
1806 : }
1807 2 : ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
1808 : subIndent, bGeomType);
1809 : }
1810 : }
1811 6 : }
1812 :
1813 : /************************************************************************/
1814 : /* GDALVectorInfo() */
1815 : /************************************************************************/
1816 :
1817 : /**
1818 : * Lists various information about a GDAL supported vector dataset.
1819 : *
1820 : * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
1821 : * utility.
1822 : *
1823 : * GDALVectorInfoOptions* must be allocated and freed with
1824 : * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
1825 : *
1826 : * @param hDataset the dataset handle.
1827 : * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
1828 : * or NULL.
1829 : * @return string corresponding to the information about the raster dataset
1830 : * (must be freed with CPLFree()), or NULL in case of error.
1831 : *
1832 : * @since GDAL 3.7
1833 : */
1834 72 : char *GDALVectorInfo(GDALDatasetH hDataset,
1835 : const GDALVectorInfoOptions *psOptions)
1836 : {
1837 72 : auto poDS = GDALDataset::FromHandle(hDataset);
1838 72 : if (poDS == nullptr)
1839 0 : return nullptr;
1840 :
1841 72 : GDALDriver *poDriver = poDS->GetDriver();
1842 :
1843 144 : CPLString osRet;
1844 144 : CPLJSONObject oRoot;
1845 144 : const std::string osFilename(poDS->GetDescription());
1846 :
1847 72 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1848 144 : CPLJSONArray oLayerArray;
1849 72 : if (bJson)
1850 : {
1851 25 : oRoot.Set("description", poDS->GetDescription());
1852 25 : if (poDriver)
1853 : {
1854 25 : oRoot.Set("driverShortName", poDriver->GetDescription());
1855 25 : oRoot.Set("driverLongName",
1856 25 : poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
1857 : }
1858 25 : oRoot.Add("layers", oLayerArray);
1859 : }
1860 :
1861 : /* -------------------------------------------------------------------- */
1862 : /* Some information messages. */
1863 : /* -------------------------------------------------------------------- */
1864 72 : if (!bJson && psOptions->bVerbose)
1865 : {
1866 90 : Concat(osRet, psOptions->bStdoutOutput,
1867 : "INFO: Open of `%s'\n"
1868 : " using driver `%s' successful.\n",
1869 : osFilename.c_str(),
1870 45 : poDriver ? poDriver->GetDescription() : "(null)");
1871 : }
1872 :
1873 117 : if (!bJson && psOptions->bVerbose &&
1874 45 : !EQUAL(osFilename.c_str(), poDS->GetDescription()))
1875 : {
1876 0 : Concat(osRet, psOptions->bStdoutOutput,
1877 : "INFO: Internal data source name `%s'\n"
1878 : " different from user name `%s'.\n",
1879 0 : poDS->GetDescription(), osFilename.c_str());
1880 : }
1881 :
1882 72 : GDALVectorInfoReportMetadata(osRet, oRoot, psOptions, poDS,
1883 72 : psOptions->bListMDD, psOptions->bShowMetadata,
1884 : psOptions->aosExtraMDDomains.List());
1885 :
1886 144 : CPLJSONObject oDomains;
1887 72 : oRoot.Add("domains", oDomains);
1888 72 : if (!psOptions->osFieldDomain.empty())
1889 : {
1890 7 : auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
1891 7 : if (poDomain == nullptr)
1892 : {
1893 0 : CPLError(CE_Failure, CPLE_AppDefined, "Domain %s cannot be found.",
1894 : psOptions->osFieldDomain.c_str());
1895 0 : return nullptr;
1896 : }
1897 7 : if (!bJson)
1898 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1899 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1900 7 : if (!bJson)
1901 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1902 : }
1903 65 : else if (bJson)
1904 : {
1905 32 : for (const auto &osDomainName : poDS->GetFieldDomainNames())
1906 : {
1907 7 : auto poDomain = poDS->GetFieldDomain(osDomainName);
1908 7 : if (poDomain)
1909 : {
1910 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1911 : }
1912 : }
1913 : }
1914 :
1915 72 : int nRepeatCount = psOptions->nRepeatCount;
1916 72 : if (psOptions->bDatasetGetNextFeature)
1917 : {
1918 1 : nRepeatCount = 0; // skip layer reporting.
1919 :
1920 : /* --------------------------------------------------------------------
1921 : */
1922 : /* Set filters if provided. */
1923 : /* --------------------------------------------------------------------
1924 : */
1925 2 : if (!psOptions->osWHERE.empty() ||
1926 1 : psOptions->poSpatialFilter != nullptr)
1927 : {
1928 0 : for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
1929 : {
1930 0 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
1931 :
1932 0 : if (poLayer == nullptr)
1933 : {
1934 0 : CPLError(CE_Failure, CPLE_AppDefined,
1935 : "Couldn't fetch advertised layer %d.", iLayer);
1936 0 : return nullptr;
1937 : }
1938 :
1939 0 : if (!psOptions->osWHERE.empty())
1940 : {
1941 0 : if (poLayer->SetAttributeFilter(
1942 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
1943 : {
1944 0 : CPLError(CE_Warning, CPLE_AppDefined,
1945 : "SetAttributeFilter(%s) failed on layer %s.",
1946 : psOptions->osWHERE.c_str(),
1947 0 : poLayer->GetName());
1948 : }
1949 : }
1950 :
1951 0 : if (psOptions->poSpatialFilter != nullptr)
1952 : {
1953 0 : if (!psOptions->osGeomField.empty())
1954 : {
1955 0 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
1956 0 : const int iGeomField = poDefn->GetGeomFieldIndex(
1957 0 : psOptions->osGeomField.c_str());
1958 0 : if (iGeomField >= 0)
1959 0 : poLayer->SetSpatialFilter(
1960 0 : iGeomField, psOptions->poSpatialFilter.get());
1961 : else
1962 0 : CPLError(CE_Warning, CPLE_AppDefined,
1963 : "Cannot find geometry field %s.",
1964 : psOptions->osGeomField.c_str());
1965 : }
1966 : else
1967 : {
1968 0 : poLayer->SetSpatialFilter(
1969 0 : psOptions->poSpatialFilter.get());
1970 : }
1971 : }
1972 : }
1973 : }
1974 :
1975 1 : std::set<OGRLayer *> oSetLayers;
1976 : while (true)
1977 : {
1978 11 : OGRLayer *poLayer = nullptr;
1979 : OGRFeature *poFeature =
1980 11 : poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
1981 11 : if (poFeature == nullptr)
1982 1 : break;
1983 10 : if (psOptions->aosLayers.empty() || poLayer == nullptr ||
1984 0 : CSLFindString(psOptions->aosLayers.List(),
1985 0 : poLayer->GetName()) >= 0)
1986 : {
1987 10 : if (psOptions->bVerbose && poLayer != nullptr &&
1988 10 : oSetLayers.find(poLayer) == oSetLayers.end())
1989 : {
1990 0 : oSetLayers.insert(poLayer);
1991 0 : CPLJSONObject oLayer;
1992 0 : oLayerArray.Add(oLayer);
1993 0 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
1994 : /*bForceSummary = */ true,
1995 : /*bTakeIntoAccountWHERE = */ false,
1996 : /*bTakeIntoAccountSpatialFilter = */ false,
1997 : /*bTakeIntoAccountGeomField = */ false);
1998 : }
1999 10 : if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
2000 10 : poFeature->DumpReadable(
2001 : nullptr,
2002 : const_cast<char **>(psOptions->aosOptions.List()));
2003 : }
2004 10 : OGRFeature::DestroyFeature(poFeature);
2005 10 : }
2006 : }
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Special case for -sql clause. No source layers required. */
2010 : /* -------------------------------------------------------------------- */
2011 71 : else if (!psOptions->osSQLStatement.empty())
2012 : {
2013 3 : nRepeatCount = 0; // skip layer reporting.
2014 :
2015 3 : if (!bJson && !psOptions->aosLayers.empty())
2016 0 : Concat(osRet, psOptions->bStdoutOutput,
2017 : "layer names ignored in combination with -sql.\n");
2018 :
2019 3 : CPLErrorReset();
2020 9 : OGRLayer *poResultSet = poDS->ExecuteSQL(
2021 : psOptions->osSQLStatement.c_str(),
2022 6 : psOptions->osGeomField.empty() ? psOptions->poSpatialFilter.get()
2023 : : nullptr,
2024 3 : psOptions->osDialect.empty() ? nullptr
2025 3 : : psOptions->osDialect.c_str());
2026 :
2027 3 : if (poResultSet != nullptr)
2028 : {
2029 2 : if (!psOptions->osWHERE.empty())
2030 : {
2031 0 : if (poResultSet->SetAttributeFilter(
2032 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
2033 : {
2034 0 : CPLError(CE_Failure, CPLE_AppDefined,
2035 : "SetAttributeFilter(%s) failed.",
2036 : psOptions->osWHERE.c_str());
2037 0 : return nullptr;
2038 : }
2039 : }
2040 :
2041 4 : CPLJSONObject oLayer;
2042 2 : oLayerArray.Add(oLayer);
2043 2 : if (!psOptions->osGeomField.empty())
2044 0 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2045 : /*bForceSummary = */ false,
2046 : /*bTakeIntoAccountWHERE = */ false,
2047 : /*bTakeIntoAccountSpatialFilter = */ true,
2048 : /*bTakeIntoAccountGeomField = */ true);
2049 : else
2050 2 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2051 : /*bForceSummary = */ false,
2052 : /*bTakeIntoAccountWHERE = */ false,
2053 : /*bTakeIntoAccountSpatialFilter = */ false,
2054 : /*bTakeIntoAccountGeomField = */ false);
2055 :
2056 2 : poDS->ReleaseResultSet(poResultSet);
2057 : }
2058 1 : else if (CPLGetLastErrorType() != CE_None)
2059 : {
2060 1 : return nullptr;
2061 : }
2062 : }
2063 :
2064 : // coverity[tainted_data]
2065 71 : auto papszLayers = psOptions->aosLayers.List();
2066 138 : for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
2067 : {
2068 68 : if (papszLayers == nullptr || papszLayers[0] == nullptr)
2069 : {
2070 58 : const int nLayerCount = poDS->GetLayerCount();
2071 58 : if (iRepeat == 0)
2072 58 : CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
2073 :
2074 58 : bool bDone = false;
2075 58 : auto poRootGroup = poDS->GetRootGroup();
2076 62 : if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
2077 62 : (!poRootGroup->GetGroupNames().empty() ||
2078 61 : !poRootGroup->GetVectorLayerNames().empty()))
2079 : {
2080 8 : CPLJSONObject oGroup;
2081 4 : oRoot.Add("rootGroup", oGroup);
2082 4 : ReportHiearchicalLayers(osRet, oGroup, psOptions,
2083 8 : poRootGroup.get(), std::string(),
2084 4 : psOptions->bGeomType);
2085 4 : if (!bJson)
2086 1 : bDone = true;
2087 : }
2088 :
2089 : /* --------------------------------------------------------------------
2090 : */
2091 : /* Process each data source layer. */
2092 : /* --------------------------------------------------------------------
2093 : */
2094 197 : for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
2095 : {
2096 139 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
2097 :
2098 139 : if (poLayer == nullptr)
2099 : {
2100 0 : CPLError(CE_Failure, CPLE_AppDefined,
2101 : "Couldn't fetch advertised layer %d.", iLayer);
2102 0 : return nullptr;
2103 : }
2104 :
2105 278 : CPLJSONObject oLayer;
2106 139 : oLayerArray.Add(oLayer);
2107 139 : if (!psOptions->bAllLayers)
2108 : {
2109 15 : if (!bJson)
2110 15 : Concat(osRet, psOptions->bStdoutOutput,
2111 : "%d: ", iLayer + 1);
2112 15 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
2113 15 : poDS->IsLayerPrivate(iLayer));
2114 : }
2115 : else
2116 : {
2117 124 : if (iRepeat != 0)
2118 0 : poLayer->ResetReading();
2119 :
2120 124 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2121 : /*bForceSummary = */ false,
2122 : /*bTakeIntoAccountWHERE = */ true,
2123 : /*bTakeIntoAccountSpatialFilter = */ true,
2124 : /*bTakeIntoAccountGeomField = */ true);
2125 : }
2126 58 : }
2127 : }
2128 : else
2129 : {
2130 : /* --------------------------------------------------------------------
2131 : */
2132 : /* Process specified data source layers. */
2133 : /* --------------------------------------------------------------------
2134 : */
2135 :
2136 20 : for (const char *pszLayer : cpl::Iterate(papszLayers))
2137 : {
2138 11 : OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
2139 :
2140 11 : if (poLayer == nullptr)
2141 : {
2142 1 : CPLError(CE_Failure, CPLE_AppDefined,
2143 : "Couldn't fetch requested layer %s.", pszLayer);
2144 1 : return nullptr;
2145 : }
2146 :
2147 10 : if (iRepeat != 0)
2148 0 : poLayer->ResetReading();
2149 :
2150 20 : CPLJSONObject oLayer;
2151 10 : oLayerArray.Add(oLayer);
2152 10 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2153 : /*bForceSummary = */ false,
2154 : /*bTakeIntoAccountWHERE = */ true,
2155 : /*bTakeIntoAccountSpatialFilter = */ true,
2156 : /*bTakeIntoAccountGeomField = */ true);
2157 : }
2158 : }
2159 : }
2160 :
2161 70 : if (!papszLayers)
2162 : {
2163 61 : ReportRelationships(osRet, oRoot, psOptions, poDS);
2164 : }
2165 :
2166 70 : if (bJson)
2167 : {
2168 24 : osRet.clear();
2169 24 : ConcatStr(
2170 24 : osRet, psOptions->bStdoutOutput,
2171 : json_object_to_json_string_ext(
2172 24 : static_cast<struct json_object *>(oRoot.GetInternalHandle()),
2173 : JSON_C_TO_STRING_PRETTY
2174 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2175 : | JSON_C_TO_STRING_NOSLASHESCAPE
2176 : #endif
2177 : ));
2178 24 : ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
2179 : }
2180 :
2181 70 : return VSI_STRDUP_VERBOSE(osRet);
2182 : }
2183 :
2184 : /************************************************************************/
2185 : /* GDALVectorInfoOptionsGetParser() */
2186 : /************************************************************************/
2187 :
2188 79 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
2189 : GDALVectorInfoOptions *psOptions,
2190 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2191 : {
2192 : auto argParser = std::make_unique<GDALArgumentParser>(
2193 79 : "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
2194 :
2195 79 : argParser->add_description(
2196 79 : _("Lists information about an OGR-supported data source."));
2197 :
2198 79 : argParser->add_epilog(
2199 79 : _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
2200 :
2201 79 : argParser->add_argument("-json")
2202 79 : .flag()
2203 : .action(
2204 25 : [psOptions](const std::string &)
2205 : {
2206 25 : psOptions->eFormat = FORMAT_JSON;
2207 25 : psOptions->bAllLayers = true;
2208 25 : psOptions->bSummaryOnly = true;
2209 79 : })
2210 79 : .help(_("Display the output in json format."));
2211 :
2212 79 : argParser->add_argument("-ro")
2213 79 : .flag()
2214 : .action(
2215 14 : [psOptionsForBinary](const std::string &)
2216 : {
2217 7 : if (psOptionsForBinary)
2218 7 : psOptionsForBinary->bReadOnly = true;
2219 79 : })
2220 79 : .help(_("Open the data source in read-only mode."));
2221 :
2222 79 : argParser->add_argument("-update")
2223 79 : .flag()
2224 : .action(
2225 0 : [psOptionsForBinary](const std::string &)
2226 : {
2227 0 : if (psOptionsForBinary)
2228 0 : psOptionsForBinary->bUpdate = true;
2229 79 : })
2230 79 : .help(_("Open the data source in update mode."));
2231 :
2232 79 : argParser->add_argument("-q", "--quiet")
2233 79 : .flag()
2234 : .action(
2235 4 : [psOptions, psOptionsForBinary](const std::string &)
2236 : {
2237 2 : psOptions->bVerbose = false;
2238 2 : if (psOptionsForBinary)
2239 2 : psOptionsForBinary->bVerbose = false;
2240 79 : })
2241 : .help(_("Quiet mode. No progress message is emitted on the standard "
2242 79 : "output."));
2243 :
2244 : #ifdef __AFL_HAVE_MANUAL_CONTROL
2245 : /* Undocumented: mainly only useful for AFL testing */
2246 : argParser->add_argument("-qq")
2247 : .flag()
2248 : .hidden()
2249 : .action(
2250 : [psOptions, psOptionsForBinary](const std::string &)
2251 : {
2252 : psOptions->bVerbose = false;
2253 : if (psOptionsForBinary)
2254 : psOptionsForBinary->bVerbose = false;
2255 : psOptions->bSuperQuiet = true;
2256 : })
2257 : .help(_("Super quiet mode."));
2258 : #endif
2259 :
2260 79 : argParser->add_argument("-fid")
2261 158 : .metavar("<FID>")
2262 79 : .store_into(psOptions->nFetchFID)
2263 79 : .help(_("Only the feature with this feature id will be reported."));
2264 :
2265 79 : argParser->add_argument("-spat")
2266 158 : .metavar("<xmin> <ymin> <xmax> <ymax>")
2267 79 : .nargs(4)
2268 79 : .scan<'g', double>()
2269 : .help(_("The area of interest. Only features within the rectangle will "
2270 79 : "be reported."));
2271 :
2272 79 : argParser->add_argument("-geomfield")
2273 158 : .metavar("<field>")
2274 79 : .store_into(psOptions->osGeomField)
2275 : .help(_("Name of the geometry field on which the spatial filter "
2276 79 : "operates."));
2277 :
2278 79 : argParser->add_argument("-where")
2279 158 : .metavar("<restricted_where>")
2280 79 : .store_into(psOptions->osWHERE)
2281 : .help(_("An attribute query in a restricted form of the queries used "
2282 79 : "in the SQL WHERE statement."));
2283 :
2284 : {
2285 79 : auto &group = argParser->add_mutually_exclusive_group();
2286 79 : group.add_argument("-sql")
2287 158 : .metavar("<statement|@filename>")
2288 79 : .store_into(psOptions->osSQLStatement)
2289 : .help(_(
2290 79 : "Execute the indicated SQL statement and return the result."));
2291 :
2292 79 : group.add_argument("-rl")
2293 79 : .store_into(psOptions->bDatasetGetNextFeature)
2294 79 : .help(_("Enable random layer reading mode."));
2295 : }
2296 :
2297 79 : argParser->add_argument("-dialect")
2298 158 : .metavar("<dialect>")
2299 79 : .store_into(psOptions->osDialect)
2300 79 : .help(_("SQL dialect."));
2301 :
2302 : // Only for fuzzing
2303 79 : argParser->add_argument("-rc")
2304 79 : .hidden()
2305 158 : .metavar("<count>")
2306 79 : .store_into(psOptions->nRepeatCount)
2307 79 : .help(_("Repeat count"));
2308 :
2309 79 : argParser->add_argument("-al")
2310 79 : .store_into(psOptions->bAllLayers)
2311 : .help(_("List all layers (used instead of having to give layer names "
2312 79 : "as arguments)"));
2313 :
2314 : {
2315 79 : auto &group = argParser->add_mutually_exclusive_group();
2316 79 : group.add_argument("-so", "-summary")
2317 79 : .store_into(psOptions->bSummaryParser)
2318 : .help(_("Summary only: list all layers (used instead of having to "
2319 79 : "give layer names as arguments)"));
2320 :
2321 79 : group.add_argument("-features")
2322 79 : .store_into(psOptions->bFeaturesParser)
2323 79 : .help(_("Enable listing of features"));
2324 : }
2325 :
2326 79 : argParser->add_argument("-limit")
2327 158 : .metavar("<nb_features>")
2328 79 : .store_into(psOptions->nLimit)
2329 79 : .help(_("Limit the number of features per layer."));
2330 :
2331 79 : argParser->add_argument("-fields")
2332 79 : .choices("YES", "NO")
2333 158 : .metavar("YES|NO")
2334 : .action(
2335 2 : [psOptions](const std::string &s) {
2336 2 : psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
2337 79 : })
2338 : .help(
2339 79 : _("If set to NO, the feature dump will not display field values."));
2340 :
2341 79 : argParser->add_argument("-geom")
2342 79 : .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
2343 158 : .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
2344 : .action(
2345 3 : [psOptions](const std::string &s) {
2346 : psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
2347 3 : s.c_str());
2348 79 : })
2349 79 : .help(_("How to display geometries in feature dump."));
2350 :
2351 79 : argParser->add_argument("-oo")
2352 79 : .append()
2353 158 : .metavar("<NAME=VALUE>")
2354 : .action(
2355 20 : [psOptionsForBinary](const std::string &s)
2356 : {
2357 10 : if (psOptionsForBinary)
2358 10 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
2359 79 : })
2360 79 : .help(_("Dataset open option (format-specific)"));
2361 :
2362 79 : argParser->add_argument("-nomd")
2363 79 : .flag()
2364 1 : .action([psOptions](const std::string &)
2365 79 : { psOptions->bShowMetadata = false; })
2366 79 : .help(_("Suppress metadata printing"));
2367 :
2368 79 : argParser->add_argument("-listmdd")
2369 79 : .store_into(psOptions->bListMDD)
2370 79 : .help(_("List all metadata domains available for the dataset."));
2371 :
2372 79 : argParser->add_argument("-mdd")
2373 79 : .append()
2374 158 : .metavar("<domain>")
2375 1 : .action([psOptions](const std::string &s)
2376 80 : { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
2377 79 : .help(_("List metadata in the specified domain."));
2378 :
2379 79 : argParser->add_argument("-nocount")
2380 79 : .flag()
2381 2 : .action([psOptions](const std::string &)
2382 79 : { psOptions->bFeatureCount = false; })
2383 79 : .help(_("Suppress feature count printing."));
2384 :
2385 79 : argParser->add_argument("-noextent")
2386 79 : .flag()
2387 0 : .action([psOptions](const std::string &)
2388 79 : { psOptions->bExtent = false; })
2389 79 : .help(_("Suppress spatial extent printing."));
2390 :
2391 79 : argParser->add_argument("-extent3D")
2392 79 : .store_into(psOptions->bExtent3D)
2393 79 : .help(_("Request a 3D extent to be reported."));
2394 :
2395 79 : argParser->add_argument("-nogeomtype")
2396 79 : .flag()
2397 1 : .action([psOptions](const std::string &)
2398 79 : { psOptions->bGeomType = false; })
2399 79 : .help(_("Suppress layer geometry type printing."));
2400 :
2401 79 : argParser->add_argument("-wkt_format")
2402 79 : .store_into(psOptions->osWKTFormat)
2403 158 : .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
2404 79 : .help(_("The WKT format used to display the SRS."));
2405 :
2406 79 : argParser->add_argument("-fielddomain")
2407 79 : .store_into(psOptions->osFieldDomain)
2408 158 : .metavar("<name>")
2409 79 : .help(_("Display details about a field domain."));
2410 :
2411 79 : argParser->add_argument("-if")
2412 79 : .append()
2413 158 : .metavar("<format>")
2414 : .action(
2415 4 : [psOptionsForBinary](const std::string &s)
2416 : {
2417 2 : if (psOptionsForBinary)
2418 : {
2419 2 : if (GDALGetDriverByName(s.c_str()) == nullptr)
2420 : {
2421 0 : CPLError(CE_Warning, CPLE_AppDefined,
2422 : "%s is not a recognized driver", s.c_str());
2423 : }
2424 : psOptionsForBinary->aosAllowInputDrivers.AddString(
2425 2 : s.c_str());
2426 : }
2427 79 : })
2428 79 : .help(_("Format/driver name(s) to try when opening the input file."));
2429 :
2430 79 : argParser->add_argument("filename")
2431 79 : .nargs(argparse::nargs_pattern::optional)
2432 : .action(
2433 89 : [psOptionsForBinary](const std::string &s)
2434 : {
2435 46 : if (psOptionsForBinary)
2436 43 : psOptionsForBinary->osFilename = s;
2437 79 : })
2438 79 : .help(_("The data source to open."));
2439 :
2440 79 : argParser->add_argument("layer")
2441 79 : .remaining()
2442 158 : .metavar("<layer_name>")
2443 79 : .help(_("Layer name."));
2444 :
2445 79 : return argParser;
2446 : }
2447 :
2448 : /************************************************************************/
2449 : /* GDALVectorInfoGetParserUsage() */
2450 : /************************************************************************/
2451 :
2452 0 : std::string GDALVectorInfoGetParserUsage()
2453 : {
2454 : try
2455 : {
2456 0 : GDALVectorInfoOptions sOptions;
2457 0 : GDALVectorInfoOptionsForBinary sOptionsForBinary;
2458 : auto argParser =
2459 0 : GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
2460 0 : return argParser->usage();
2461 : }
2462 0 : catch (const std::exception &err)
2463 : {
2464 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
2465 0 : err.what());
2466 0 : return std::string();
2467 : }
2468 : }
2469 :
2470 : /************************************************************************/
2471 : /* GDALVectorInfoOptionsNew() */
2472 : /************************************************************************/
2473 :
2474 : /**
2475 : * Allocates a GDALVectorInfoOptions struct.
2476 : *
2477 : * Note that when this function is used a library function, and not from the
2478 : * ogrinfo utility, a dataset name must be specified if any layer names(s) are
2479 : * specified (if no layer name is specific, passing a dataset name is not
2480 : * needed). That dataset name may be a dummy one, as the dataset taken into
2481 : * account is the hDS parameter passed to GDALVectorInfo().
2482 : * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
2483 : * is the responsibility of the user to apply them when opening the hDS parameter
2484 : * passed to GDALVectorInfo().
2485 : *
2486 : * @param papszArgv NULL terminated list of options (potentially including
2487 : * filename and open options too), or NULL. The accepted options are the ones of
2488 : * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
2489 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2490 : * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
2491 : * GDALVectorInfoOptionsForBinaryNew() prior to this
2492 : * function. Will be filled with potentially present filename, open options,
2493 : * subdataset number...
2494 : * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
2495 : * with GDALVectorInfoOptionsFree().
2496 : *
2497 : * @since GDAL 3.7
2498 : */
2499 :
2500 : GDALVectorInfoOptions *
2501 79 : GDALVectorInfoOptionsNew(char **papszArgv,
2502 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2503 : {
2504 155 : auto psOptions = std::make_unique<GDALVectorInfoOptions>();
2505 :
2506 : try
2507 : {
2508 : auto argParser =
2509 155 : GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
2510 :
2511 : /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
2512 : * same for -geom=foo. */
2513 155 : CPLStringList aosArgv;
2514 313 : for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
2515 : ++papszIter)
2516 : {
2517 234 : if (STARTS_WITH(*papszIter, "-fields="))
2518 : {
2519 2 : aosArgv.AddString("-fields");
2520 : aosArgv.AddString(
2521 2 : CPLString(*papszIter + strlen("-fields=")).toupper());
2522 : }
2523 232 : else if (STARTS_WITH(*papszIter, "-geom="))
2524 : {
2525 3 : aosArgv.AddString("-geom");
2526 : aosArgv.AddString(
2527 3 : CPLString(*papszIter + strlen("-geom=")).toupper());
2528 : }
2529 : else
2530 : {
2531 229 : aosArgv.AddString(*papszIter);
2532 : }
2533 : }
2534 :
2535 79 : argParser->parse_args_without_binary_name(aosArgv.List());
2536 :
2537 152 : auto layers = argParser->present<std::vector<std::string>>("layer");
2538 76 : if (layers)
2539 : {
2540 21 : for (const auto &layer : *layers)
2541 : {
2542 11 : psOptions->aosLayers.AddString(layer.c_str());
2543 11 : psOptions->bAllLayers = false;
2544 : }
2545 : }
2546 :
2547 78 : if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
2548 : {
2549 4 : OGRLinearRing oRing;
2550 2 : const double dfMinX = (*oSpat)[0];
2551 2 : const double dfMinY = (*oSpat)[1];
2552 2 : const double dfMaxX = (*oSpat)[2];
2553 2 : const double dfMaxY = (*oSpat)[3];
2554 :
2555 2 : oRing.addPoint(dfMinX, dfMinY);
2556 2 : oRing.addPoint(dfMinX, dfMaxY);
2557 2 : oRing.addPoint(dfMaxX, dfMaxY);
2558 2 : oRing.addPoint(dfMaxX, dfMinY);
2559 2 : oRing.addPoint(dfMinX, dfMinY);
2560 :
2561 4 : auto poPolygon = std::make_unique<OGRPolygon>();
2562 2 : poPolygon->addRing(&oRing);
2563 2 : psOptions->poSpatialFilter.reset(poPolygon.release());
2564 : }
2565 :
2566 76 : if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
2567 : {
2568 0 : GByte *pabyRet = nullptr;
2569 0 : if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
2570 0 : &pabyRet, nullptr, 1024 * 1024))
2571 : {
2572 0 : GDALRemoveBOM(pabyRet);
2573 0 : psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
2574 0 : VSIFree(pabyRet);
2575 : }
2576 : else
2577 : {
2578 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2579 0 : psOptions->osWHERE.substr(1).c_str());
2580 0 : return nullptr;
2581 : }
2582 : }
2583 :
2584 79 : if (!psOptions->osSQLStatement.empty() &&
2585 3 : psOptions->osSQLStatement[0] == '@')
2586 : {
2587 1 : GByte *pabyRet = nullptr;
2588 1 : if (VSIIngestFile(nullptr,
2589 2 : psOptions->osSQLStatement.substr(1).c_str(),
2590 1 : &pabyRet, nullptr, 1024 * 1024))
2591 : {
2592 1 : GDALRemoveBOM(pabyRet);
2593 1 : char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
2594 1 : psOptions->osSQLStatement =
2595 2 : GDALRemoveSQLComments(pszSQLStatement);
2596 1 : VSIFree(pabyRet);
2597 : }
2598 : else
2599 : {
2600 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2601 0 : psOptions->osSQLStatement.substr(1).c_str());
2602 0 : return nullptr;
2603 : }
2604 : }
2605 :
2606 76 : if (psOptionsForBinary)
2607 : {
2608 43 : psOptions->bStdoutOutput = true;
2609 43 : psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
2610 : }
2611 :
2612 76 : if (psOptions->bSummaryParser)
2613 11 : psOptions->bSummaryOnly = true;
2614 65 : else if (psOptions->bFeaturesParser)
2615 8 : psOptions->bSummaryOnly = false;
2616 :
2617 76 : if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
2618 0 : psOptions->osSQLStatement.empty())
2619 : {
2620 0 : CPLError(CE_Warning, CPLE_AppDefined,
2621 : "-dialect is ignored with -where. Use -sql instead");
2622 : }
2623 :
2624 76 : if (psOptions->eFormat == FORMAT_JSON)
2625 : {
2626 25 : if (psOptions->aosExtraMDDomains.empty())
2627 25 : psOptions->aosExtraMDDomains.AddString("all");
2628 25 : psOptions->bStdoutOutput = false;
2629 : }
2630 :
2631 76 : return psOptions.release();
2632 : }
2633 0 : catch (const std::exception &err)
2634 : {
2635 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
2636 0 : return nullptr;
2637 : }
2638 : }
|