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