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 3088 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
109 : ...)
110 : {
111 : va_list args;
112 3088 : va_start(args, pszFormat);
113 :
114 3088 : if (bStdoutOutput)
115 : {
116 2357 : vfprintf(stdout, pszFormat, args);
117 : }
118 : else
119 : {
120 : try
121 : {
122 1462 : CPLString osTarget;
123 731 : osTarget.vPrintf(pszFormat, args);
124 :
125 731 : 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 3088 : va_end(args);
134 3088 : }
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 : const auto eGeomType = poLayer->GetGeomType();
1131 110 : Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s",
1132 : OGRGeometryTypeToName(eGeomType));
1133 110 : if (eGeomType != wkbNone)
1134 : {
1135 36 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1136 : OGRToOGCGeomType(eGeomType,
1137 : /* camelCase = */ false,
1138 : /* includeZM = */ true));
1139 : }
1140 110 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1141 : }
1142 :
1143 189 : if (psOptions->bFeatureCount)
1144 : {
1145 164 : if (bJson)
1146 53 : oLayer.Set("featureCount", poLayer->GetFeatureCount());
1147 : else
1148 : {
1149 111 : Concat(osRet, psOptions->bStdoutOutput,
1150 : "Feature Count: " CPL_FRMT_GIB "\n",
1151 111 : poLayer->GetFeatureCount());
1152 : }
1153 : }
1154 :
1155 189 : if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
1156 : {
1157 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1158 : {
1159 4 : if (psOptions->bExtent3D)
1160 : {
1161 0 : OGREnvelope3D oExt;
1162 0 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
1163 : {
1164 : OGRGeomFieldDefn *poGFldDefn =
1165 0 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1166 0 : Concat(osRet, psOptions->bStdoutOutput,
1167 : "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
1168 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1169 0 : std::isfinite(oExt.MinZ)
1170 0 : ? CPLSPrintf("%f", oExt.MinZ)
1171 : : "none",
1172 : oExt.MaxX, oExt.MaxY,
1173 0 : std::isfinite(oExt.MaxZ)
1174 0 : ? CPLSPrintf("%f", oExt.MaxZ)
1175 : : "none");
1176 : }
1177 : }
1178 : else
1179 : {
1180 4 : OGREnvelope oExt;
1181 4 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
1182 : {
1183 : OGRGeomFieldDefn *poGFldDefn =
1184 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1185 4 : Concat(osRet, psOptions->bStdoutOutput,
1186 : "Extent (%s): (%f, %f) - (%f, %f)\n",
1187 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1188 : oExt.MaxX, oExt.MaxY);
1189 : }
1190 : }
1191 2 : }
1192 : }
1193 187 : else if (!bJson && psOptions->bExtent)
1194 : {
1195 110 : if (psOptions->bExtent3D)
1196 : {
1197 2 : OGREnvelope3D oExt;
1198 2 : if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
1199 : {
1200 4 : Concat(
1201 2 : osRet, psOptions->bStdoutOutput,
1202 : "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
1203 : oExt.MinY,
1204 3 : std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
1205 : : "none",
1206 : oExt.MaxX, oExt.MaxY,
1207 3 : std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
1208 : : "none");
1209 : }
1210 : }
1211 : else
1212 : {
1213 108 : OGREnvelope oExt;
1214 108 : if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
1215 : {
1216 30 : Concat(osRet, psOptions->bStdoutOutput,
1217 : "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
1218 : oExt.MinY, oExt.MaxX, oExt.MaxY);
1219 : }
1220 : }
1221 : }
1222 :
1223 : const auto DisplayExtraInfoSRS =
1224 244 : [&osRet, &psOptions](const OGRSpatialReference *poSRS)
1225 : {
1226 40 : const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
1227 40 : if (dfCoordinateEpoch > 0)
1228 : {
1229 : std::string osCoordinateEpoch =
1230 4 : CPLSPrintf("%f", dfCoordinateEpoch);
1231 2 : const size_t nDotPos = osCoordinateEpoch.find('.');
1232 2 : if (nDotPos != std::string::npos)
1233 : {
1234 22 : while (osCoordinateEpoch.size() > nDotPos + 2 &&
1235 10 : osCoordinateEpoch.back() == '0')
1236 10 : osCoordinateEpoch.pop_back();
1237 : }
1238 2 : Concat(osRet, psOptions->bStdoutOutput,
1239 : "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
1240 : }
1241 :
1242 40 : const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
1243 40 : Concat(osRet, psOptions->bStdoutOutput,
1244 : "Data axis to CRS axis mapping: ");
1245 121 : for (size_t i = 0; i < mapping.size(); i++)
1246 : {
1247 81 : if (i > 0)
1248 : {
1249 41 : Concat(osRet, psOptions->bStdoutOutput, ",");
1250 : }
1251 81 : Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
1252 : }
1253 40 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1254 40 : };
1255 :
1256 : const auto DisplaySRS =
1257 114 : [&osRet, &psOptions, &apszWKTOptions,
1258 : DisplayExtraInfoSRS](const OGRSpatialReference *poSRS,
1259 394 : const OGRGeomFieldDefn *poGFldDefn)
1260 : {
1261 228 : std::string osWkt;
1262 114 : if (poSRS)
1263 40 : osWkt = poSRS->exportToWkt(apszWKTOptions);
1264 :
1265 114 : if (psOptions->bIsCli && !poSRS)
1266 : {
1267 0 : if (poGFldDefn)
1268 0 : Concat(osRet, psOptions->bStdoutOutput,
1269 : "Coordinate Reference System of field %s: none\n",
1270 : poGFldDefn->GetNameRef());
1271 : else
1272 0 : Concat(osRet, psOptions->bStdoutOutput,
1273 : "Layer Coordinate Reference System: none\n");
1274 : }
1275 114 : else if (psOptions->bIsCli)
1276 : {
1277 12 : std::string osIntroText;
1278 12 : if (poGFldDefn)
1279 : {
1280 : osIntroText =
1281 0 : std::string("Coordinate Reference System of field ")
1282 0 : .append(poGFldDefn->GetNameRef());
1283 : }
1284 : else
1285 : {
1286 12 : osIntroText = "Layer Coordinate Reference System";
1287 : }
1288 :
1289 12 : EmitTextDisplayOfCRS(poSRS, psOptions->osCRSFormat, osIntroText,
1290 138 : [&osRet, psOptions](const std::string &s)
1291 : {
1292 69 : Concat(osRet, psOptions->bStdoutOutput,
1293 : "%s", s.c_str());
1294 69 : });
1295 : }
1296 : else
1297 : {
1298 102 : if (osWkt.empty())
1299 74 : osWkt = "(unknown)";
1300 :
1301 102 : if (poGFldDefn)
1302 : {
1303 4 : Concat(osRet, psOptions->bStdoutOutput,
1304 : "SRS WKT (%s):\n%s\n", poGFldDefn->GetNameRef(),
1305 : osWkt.c_str());
1306 : }
1307 : else
1308 : {
1309 98 : Concat(osRet, psOptions->bStdoutOutput,
1310 : "Layer SRS WKT:\n%s\n", osWkt.c_str());
1311 : }
1312 : }
1313 :
1314 114 : if (poSRS)
1315 40 : DisplayExtraInfoSRS(poSRS);
1316 114 : };
1317 :
1318 114 : const auto DisplaySupportedCRSList = [&](int iGeomField)
1319 : {
1320 114 : const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
1321 114 : if (!srsList.empty())
1322 : {
1323 1 : Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
1324 1 : bool bFirst = true;
1325 3 : for (const auto &poSupportedSRS : srsList)
1326 : {
1327 : const char *pszAuthName =
1328 2 : poSupportedSRS->GetAuthorityName();
1329 : const char *pszAuthCode =
1330 2 : poSupportedSRS->GetAuthorityCode();
1331 2 : if (!bFirst)
1332 1 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1333 2 : bFirst = false;
1334 2 : if (pszAuthName && pszAuthCode)
1335 : {
1336 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
1337 : pszAuthName, pszAuthCode);
1338 : }
1339 : else
1340 : {
1341 0 : ConcatStr(osRet, psOptions->bStdoutOutput,
1342 : poSupportedSRS->GetName());
1343 : }
1344 : }
1345 1 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1346 : }
1347 114 : };
1348 :
1349 189 : if (!bJson && nGeomFieldCount > 1)
1350 : {
1351 :
1352 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1353 : {
1354 : const OGRGeomFieldDefn *poGFldDefn =
1355 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1356 4 : const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
1357 4 : DisplaySRS(poSRS, poGFldDefn);
1358 4 : DisplaySupportedCRSList(iGeom);
1359 2 : }
1360 : }
1361 187 : else if (!bJson)
1362 : {
1363 110 : const auto poSRS = poLayer->GetSpatialRef();
1364 110 : DisplaySRS(poSRS, nullptr);
1365 110 : DisplaySupportedCRSList(0);
1366 : }
1367 :
1368 189 : const char *pszFIDColumn = poLayer->GetFIDColumn();
1369 189 : if (pszFIDColumn[0] != '\0')
1370 : {
1371 48 : if (bJson)
1372 33 : oLayer.Set("fidColumnName", pszFIDColumn);
1373 : else
1374 : {
1375 15 : Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
1376 : pszFIDColumn);
1377 : }
1378 : }
1379 :
1380 197 : for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
1381 : {
1382 : OGRGeomFieldDefn *poGFldDefn =
1383 40 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1384 72 : if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
1385 32 : poGFldDefn->IsNullable())
1386 32 : break;
1387 8 : Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
1388 8 : if (nGeomFieldCount > 1)
1389 4 : Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
1390 8 : if (!poGFldDefn->IsNullable())
1391 0 : Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
1392 8 : Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
1393 : poGFldDefn->GetNameRef());
1394 : }
1395 :
1396 378 : CPLJSONArray oFields;
1397 189 : if (bJson)
1398 77 : oLayer.Add("fields", oFields);
1399 803 : for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
1400 : {
1401 614 : const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
1402 614 : const char *pszAlias = poField->GetAlternativeNameRef();
1403 614 : const std::string &osDomain = poField->GetDomainName();
1404 614 : const std::string &osComment = poField->GetComment();
1405 614 : const auto eType = poField->GetType();
1406 1228 : std::string osTimeZone;
1407 614 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
1408 : {
1409 26 : const int nTZFlag = poField->GetTZFlag();
1410 26 : if (nTZFlag == OGR_TZFLAG_LOCALTIME)
1411 : {
1412 1 : osTimeZone = "localtime";
1413 : }
1414 25 : else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
1415 : {
1416 1 : osTimeZone = "mixed timezones";
1417 : }
1418 24 : else if (nTZFlag == OGR_TZFLAG_UTC)
1419 : {
1420 2 : osTimeZone = "UTC";
1421 : }
1422 22 : else if (nTZFlag > 0)
1423 : {
1424 : char chSign;
1425 3 : const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
1426 3 : int nHours =
1427 : static_cast<int>(nOffset / 60); // Round towards zero.
1428 3 : const int nMinutes = std::abs(nOffset - nHours * 60);
1429 :
1430 3 : if (nOffset < 0)
1431 : {
1432 1 : chSign = '-';
1433 1 : nHours = std::abs(nHours);
1434 : }
1435 : else
1436 : {
1437 2 : chSign = '+';
1438 : }
1439 : osTimeZone =
1440 3 : CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
1441 : }
1442 : }
1443 :
1444 614 : if (bJson)
1445 : {
1446 252 : CPLJSONObject oField;
1447 126 : oFields.Add(oField);
1448 126 : oField.Set("name", poField->GetNameRef());
1449 126 : oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
1450 126 : if (poField->GetSubType() != OFSTNone)
1451 2 : oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
1452 : poField->GetSubType()));
1453 126 : if (poField->GetWidth() > 0)
1454 71 : oField.Set("width", poField->GetWidth());
1455 126 : if (poField->GetPrecision() > 0)
1456 12 : oField.Set("precision", poField->GetPrecision());
1457 126 : oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
1458 126 : oField.Set("uniqueConstraint",
1459 126 : CPL_TO_BOOL(poField->IsUnique()));
1460 126 : if (poField->GetDefault() != nullptr)
1461 2 : oField.Set("defaultValue", poField->GetDefault());
1462 126 : if (pszAlias != nullptr && pszAlias[0])
1463 1 : oField.Set("alias", pszAlias);
1464 126 : if (!osDomain.empty())
1465 6 : oField.Set("domainName", osDomain);
1466 126 : if (!osComment.empty())
1467 1 : oField.Set("comment", osComment);
1468 126 : if (!osTimeZone.empty())
1469 7 : oField.Set("timezone", osTimeZone);
1470 : }
1471 : else
1472 : {
1473 : const char *pszType =
1474 488 : (poField->GetSubType() != OFSTNone)
1475 488 : ? CPLSPrintf("%s(%s)",
1476 : OGRFieldDefn::GetFieldTypeName(
1477 : poField->GetType()),
1478 : OGRFieldDefn::GetFieldSubTypeName(
1479 : poField->GetSubType()))
1480 466 : : OGRFieldDefn::GetFieldTypeName(poField->GetType());
1481 488 : Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
1482 : poField->GetNameRef(), pszType);
1483 488 : if (eType == OFTTime || eType == OFTDate ||
1484 : eType == OFTDateTime)
1485 : {
1486 18 : if (!osTimeZone.empty())
1487 0 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1488 : osTimeZone.c_str());
1489 : }
1490 : else
1491 : {
1492 470 : Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
1493 : poField->GetWidth(), poField->GetPrecision());
1494 : }
1495 488 : if (poField->IsUnique())
1496 0 : Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
1497 488 : if (!poField->IsNullable())
1498 204 : Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
1499 488 : if (poField->GetDefault() != nullptr)
1500 8 : Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
1501 : poField->GetDefault());
1502 488 : if (pszAlias != nullptr && pszAlias[0])
1503 0 : Concat(osRet, psOptions->bStdoutOutput,
1504 : ", alternative name=\"%s\"", pszAlias);
1505 488 : if (!osDomain.empty())
1506 5 : Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
1507 : osDomain.c_str());
1508 488 : if (!osComment.empty())
1509 0 : Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
1510 : osComment.c_str());
1511 488 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1512 : }
1513 : }
1514 : }
1515 :
1516 : /* -------------------------------------------------------------------- */
1517 : /* Read, and dump features. */
1518 : /* -------------------------------------------------------------------- */
1519 :
1520 190 : if ((psOptions->nFetchFID == OGRNullFID || bJson) && !bForceSummary &&
1521 189 : ((psOptions->bIsCli && psOptions->bFeaturesUserRequested) ||
1522 184 : (!psOptions->bIsCli && !psOptions->bSummaryOnly)))
1523 : {
1524 89 : if (!psOptions->bSuperQuiet)
1525 : {
1526 178 : CPLJSONArray oFeatures;
1527 : const bool bDisplayFields =
1528 89 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1529 : "DISPLAY_FIELDS", "YES"));
1530 : const int nFields =
1531 89 : bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
1532 : const bool bDisplayGeometry =
1533 89 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1534 : "DISPLAY_GEOMETRY", "YES"));
1535 : const int nGeomFields =
1536 89 : bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
1537 89 : : 0;
1538 89 : if (bJson)
1539 12 : oLayer.Add("features", oFeatures);
1540 :
1541 : const auto EmitFeatureJSON =
1542 45 : [poLayer, nFields, nGeomFields,
1543 290 : &oFeatures](const OGRFeature *poFeature)
1544 : {
1545 90 : CPLJSONObject oFeature;
1546 90 : CPLJSONObject oProperties;
1547 45 : oFeatures.Add(oFeature);
1548 45 : oFeature.Add("type", "Feature");
1549 45 : oFeature.Add("properties", oProperties);
1550 45 : oFeature.Add("fid", poFeature->GetFID());
1551 157 : for (int i = 0; i < nFields; ++i)
1552 : {
1553 112 : const auto poFDefn = poFeature->GetFieldDefnRef(i);
1554 112 : const auto eType = poFDefn->GetType();
1555 112 : if (!poFeature->IsFieldSet(i))
1556 0 : continue;
1557 112 : if (poFeature->IsFieldNull(i))
1558 : {
1559 2 : oProperties.SetNull(poFDefn->GetNameRef());
1560 : }
1561 110 : else if (eType == OFTInteger)
1562 : {
1563 1 : if (poFDefn->GetSubType() == OFSTBoolean)
1564 0 : oProperties.Add(
1565 : poFDefn->GetNameRef(),
1566 0 : CPL_TO_BOOL(poFeature->GetFieldAsInteger(i)));
1567 : else
1568 1 : oProperties.Add(poFDefn->GetNameRef(),
1569 : poFeature->GetFieldAsInteger(i));
1570 : }
1571 109 : else if (eType == OFTInteger64)
1572 : {
1573 34 : oProperties.Add(poFDefn->GetNameRef(),
1574 : poFeature->GetFieldAsInteger64(i));
1575 : }
1576 75 : else if (eType == OFTReal)
1577 : {
1578 34 : oProperties.Add(poFDefn->GetNameRef(),
1579 : poFeature->GetFieldAsDouble(i));
1580 : }
1581 41 : else if ((eType == OFTString &&
1582 46 : poFDefn->GetSubType() != OFSTJSON) ||
1583 82 : eType == OFTDate || eType == OFTTime ||
1584 : eType == OFTDateTime)
1585 : {
1586 36 : oProperties.Add(poFDefn->GetNameRef(),
1587 : poFeature->GetFieldAsString(i));
1588 : }
1589 : else
1590 : {
1591 : char *pszSerialized =
1592 5 : poFeature->GetFieldAsSerializedJSon(i);
1593 5 : if (pszSerialized)
1594 : {
1595 : const auto eStrType =
1596 5 : CPLGetValueType(pszSerialized);
1597 5 : if (eStrType == CPL_VALUE_INTEGER)
1598 : {
1599 1 : oProperties.Add(poFDefn->GetNameRef(),
1600 : CPLAtoGIntBig(pszSerialized));
1601 : }
1602 4 : else if (eStrType == CPL_VALUE_REAL)
1603 : {
1604 0 : oProperties.Add(poFDefn->GetNameRef(),
1605 : CPLAtof(pszSerialized));
1606 : }
1607 : else
1608 : {
1609 8 : CPLJSONDocument oDoc;
1610 4 : if (oDoc.LoadMemory(pszSerialized))
1611 4 : oProperties.Add(poFDefn->GetNameRef(),
1612 8 : oDoc.GetRoot());
1613 : }
1614 5 : CPLFree(pszSerialized);
1615 : }
1616 : }
1617 : }
1618 :
1619 86 : const auto GetGeoJSONOptions = [poLayer](int iGeomField)
1620 : {
1621 43 : CPLStringList aosGeoJSONOptions;
1622 43 : const auto &oCoordPrec = poLayer->GetLayerDefn()
1623 43 : ->GetGeomFieldDefn(iGeomField)
1624 43 : ->GetCoordinatePrecision();
1625 43 : if (oCoordPrec.dfXYResolution !=
1626 : OGRGeomCoordinatePrecision::UNKNOWN)
1627 : {
1628 : aosGeoJSONOptions.SetNameValue(
1629 : "XY_COORD_PRECISION",
1630 : CPLSPrintf("%d",
1631 : OGRGeomCoordinatePrecision::
1632 : ResolutionToPrecision(
1633 1 : oCoordPrec.dfXYResolution)));
1634 : }
1635 43 : if (oCoordPrec.dfZResolution !=
1636 : OGRGeomCoordinatePrecision::UNKNOWN)
1637 : {
1638 : aosGeoJSONOptions.SetNameValue(
1639 : "Z_COORD_PRECISION",
1640 : CPLSPrintf("%d",
1641 : OGRGeomCoordinatePrecision::
1642 : ResolutionToPrecision(
1643 1 : oCoordPrec.dfZResolution)));
1644 : }
1645 43 : return aosGeoJSONOptions;
1646 45 : };
1647 :
1648 45 : if (nGeomFields == 0)
1649 2 : oFeature.SetNull("geometry");
1650 : else
1651 : {
1652 43 : if (const auto poGeom = poFeature->GetGeometryRef())
1653 : {
1654 : char *pszSerialized =
1655 43 : wkbFlatten(poGeom->getGeometryType()) <=
1656 : wkbGeometryCollection
1657 86 : ? poGeom->exportToJson(
1658 86 : GetGeoJSONOptions(0).List())
1659 43 : : nullptr;
1660 43 : if (pszSerialized)
1661 : {
1662 86 : CPLJSONDocument oDoc;
1663 43 : if (oDoc.LoadMemory(pszSerialized))
1664 43 : oFeature.Add("geometry", oDoc.GetRoot());
1665 43 : CPLFree(pszSerialized);
1666 : }
1667 : else
1668 : {
1669 0 : CPLJSONObject oGeometry;
1670 0 : oFeature.SetNull("geometry");
1671 0 : oFeature.Add("wkt_geometry", poGeom->exportToWkt());
1672 : }
1673 : }
1674 : else
1675 0 : oFeature.SetNull("geometry");
1676 :
1677 43 : if (nGeomFields > 1)
1678 : {
1679 0 : CPLJSONArray oGeometries;
1680 0 : oFeature.Add("geometries", oGeometries);
1681 0 : for (int i = 0; i < nGeomFields; ++i)
1682 : {
1683 0 : auto poGeom = poFeature->GetGeomFieldRef(i);
1684 0 : if (poGeom)
1685 : {
1686 : char *pszSerialized =
1687 0 : wkbFlatten(poGeom->getGeometryType()) <=
1688 : wkbGeometryCollection
1689 0 : ? poGeom->exportToJson(
1690 0 : GetGeoJSONOptions(i).List())
1691 0 : : nullptr;
1692 0 : if (pszSerialized)
1693 : {
1694 0 : CPLJSONDocument oDoc;
1695 0 : if (oDoc.LoadMemory(pszSerialized))
1696 0 : oGeometries.Add(oDoc.GetRoot());
1697 0 : CPLFree(pszSerialized);
1698 : }
1699 : else
1700 : {
1701 0 : CPLJSONObject oGeometry;
1702 0 : oGeometries.Add(poGeom->exportToWkt());
1703 : }
1704 : }
1705 : else
1706 0 : oGeometries.AddNull();
1707 : }
1708 : }
1709 : }
1710 45 : };
1711 :
1712 89 : if (psOptions->nFetchFID != OGRNullFID)
1713 : {
1714 : auto poFeature = std::unique_ptr<OGRFeature>(
1715 2 : poLayer->GetFeature(psOptions->nFetchFID));
1716 1 : if (poFeature)
1717 : {
1718 1 : EmitFeatureJSON(poFeature.get());
1719 : }
1720 : }
1721 88 : else if (psOptions->nLimit < 0 || psOptions->nLimit > 0)
1722 : {
1723 88 : GIntBig nFeatureCount = 0;
1724 728 : for (auto &poFeature : poLayer)
1725 : {
1726 640 : if (bJson)
1727 : {
1728 44 : EmitFeatureJSON(poFeature.get());
1729 : }
1730 : else
1731 : {
1732 596 : ConcatStr(osRet, psOptions->bStdoutOutput,
1733 : poFeature
1734 1788 : ->DumpReadableAsString(
1735 596 : psOptions->aosOptions.List())
1736 : .c_str());
1737 : }
1738 :
1739 640 : ++nFeatureCount;
1740 640 : if (psOptions->nLimit >= 0 &&
1741 3 : nFeatureCount >= psOptions->nLimit)
1742 : {
1743 2 : break;
1744 : }
1745 : }
1746 : }
1747 89 : }
1748 : }
1749 101 : else if (!bJson && psOptions->nFetchFID != OGRNullFID)
1750 : {
1751 : auto poFeature = std::unique_ptr<OGRFeature>(
1752 2 : poLayer->GetFeature(psOptions->nFetchFID));
1753 1 : if (poFeature == nullptr)
1754 : {
1755 0 : Concat(osRet, psOptions->bStdoutOutput,
1756 : "Unable to locate feature id " CPL_FRMT_GIB
1757 : " on this layer.\n",
1758 0 : psOptions->nFetchFID);
1759 : }
1760 : else
1761 : {
1762 1 : ConcatStr(
1763 1 : osRet, psOptions->bStdoutOutput,
1764 2 : poFeature->DumpReadableAsString(psOptions->aosOptions.List())
1765 : .c_str());
1766 : }
1767 : }
1768 : }
1769 :
1770 : /************************************************************************/
1771 : /* PrintLayerSummary() */
1772 : /************************************************************************/
1773 :
1774 23 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
1775 : const GDALVectorInfoOptions *psOptions,
1776 : OGRLayer *poLayer, bool bIsPrivate)
1777 : {
1778 23 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1779 23 : const bool bIsSummaryCli = psOptions->bIsCli && psOptions->bSummaryOnly;
1780 23 : if (bJson)
1781 : {
1782 2 : oLayer.Set("name", poLayer->GetName());
1783 : }
1784 : else
1785 21 : ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
1786 :
1787 23 : const char *pszTitle = poLayer->GetMetadataItem("TITLE");
1788 23 : if (pszTitle)
1789 : {
1790 0 : if (bJson)
1791 0 : oLayer.Set("title", pszTitle);
1792 : else
1793 0 : Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
1794 : }
1795 :
1796 : const int nGeomFieldCount =
1797 23 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
1798 :
1799 23 : if (bIsSummaryCli && bJson)
1800 : {
1801 2 : CPLJSONArray oGeometryTypes;
1802 7 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1803 : {
1804 : OGRGeomFieldDefn *poGFldDefn =
1805 5 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1806 5 : oGeometryTypes.Add(OGRGeometryTypeToName(poGFldDefn->GetType()));
1807 : }
1808 2 : oLayer.Add("geometryType", oGeometryTypes);
1809 2 : return;
1810 : }
1811 :
1812 21 : if (bJson || nGeomFieldCount > 1)
1813 : {
1814 2 : if (!bJson)
1815 2 : Concat(osRet, psOptions->bStdoutOutput, " (");
1816 4 : CPLJSONArray oGeometryFields;
1817 2 : oLayer.Add("geometryFields", oGeometryFields);
1818 8 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1819 : {
1820 : OGRGeomFieldDefn *poGFldDefn =
1821 6 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1822 6 : if (bJson)
1823 : {
1824 0 : oGeometryFields.Add(
1825 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1826 : }
1827 : else
1828 : {
1829 6 : if (iGeom > 0)
1830 4 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1831 6 : ConcatStr(osRet, psOptions->bStdoutOutput,
1832 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1833 : }
1834 : }
1835 2 : if (!bJson)
1836 4 : Concat(osRet, psOptions->bStdoutOutput, ")");
1837 : }
1838 19 : else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
1839 11 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1840 11 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1841 :
1842 21 : if (bIsPrivate)
1843 : {
1844 0 : if (bJson)
1845 0 : oLayer.Set("isPrivate", true);
1846 : else
1847 0 : Concat(osRet, psOptions->bStdoutOutput, " [private]");
1848 : }
1849 :
1850 21 : if (!bJson)
1851 21 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1852 : }
1853 :
1854 : /************************************************************************/
1855 : /* ReportHiearchicalLayers() */
1856 : /************************************************************************/
1857 :
1858 5 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
1859 : const GDALVectorInfoOptions *psOptions,
1860 : const GDALGroup *group,
1861 : const std::string &indent, bool bGeomType)
1862 : {
1863 5 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1864 10 : const auto aosVectorLayerNames = group->GetVectorLayerNames();
1865 10 : CPLJSONArray oLayerNames;
1866 5 : oRoot.Add("layerNames", oLayerNames);
1867 23 : for (const auto &osVectorLayerName : aosVectorLayerNames)
1868 : {
1869 18 : OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
1870 18 : if (poLayer)
1871 : {
1872 36 : CPLJSONObject oLayer;
1873 18 : if (!bJson)
1874 : {
1875 4 : Concat(osRet, psOptions->bStdoutOutput,
1876 : "%sLayer: ", indent.c_str());
1877 4 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
1878 : /* bIsPrivate=*/false);
1879 : }
1880 : else
1881 : {
1882 14 : oLayerNames.Add(poLayer->GetName());
1883 : }
1884 : }
1885 : }
1886 :
1887 10 : const std::string subIndent(indent + " ");
1888 10 : auto aosSubGroupNames = group->GetGroupNames();
1889 10 : CPLJSONArray oGroupArray;
1890 5 : oRoot.Add("groups", oGroupArray);
1891 7 : for (const auto &osSubGroupName : aosSubGroupNames)
1892 : {
1893 4 : auto poSubGroup = group->OpenGroup(osSubGroupName);
1894 2 : if (poSubGroup)
1895 : {
1896 4 : CPLJSONObject oGroup;
1897 2 : if (!bJson)
1898 : {
1899 2 : Concat(osRet, psOptions->bStdoutOutput, "Group %s",
1900 : indent.c_str());
1901 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
1902 : osSubGroupName.c_str());
1903 : }
1904 : else
1905 : {
1906 0 : oGroupArray.Add(oGroup);
1907 0 : oGroup.Set("name", osSubGroupName);
1908 : }
1909 2 : ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
1910 : subIndent, bGeomType);
1911 : }
1912 : }
1913 5 : }
1914 :
1915 : /************************************************************************/
1916 : /* GDALVectorInfo() */
1917 : /************************************************************************/
1918 :
1919 : /**
1920 : * Lists various information about a GDAL supported vector dataset.
1921 : *
1922 : * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
1923 : * utility.
1924 : *
1925 : * GDALVectorInfoOptions* must be allocated and freed with
1926 : * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
1927 : *
1928 : * @param hDataset the dataset handle.
1929 : * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
1930 : * or NULL.
1931 : * @return string corresponding to the information about the raster dataset
1932 : * (must be freed with CPLFree()), or NULL in case of error.
1933 : *
1934 : * @since GDAL 3.7
1935 : */
1936 121 : char *GDALVectorInfo(GDALDatasetH hDataset,
1937 : const GDALVectorInfoOptions *psOptions)
1938 : {
1939 121 : auto poDS = GDALDataset::FromHandle(hDataset);
1940 121 : if (poDS == nullptr)
1941 0 : return nullptr;
1942 :
1943 242 : const GDALVectorInfoOptions sDefaultOptions;
1944 121 : if (!psOptions)
1945 0 : psOptions = &sDefaultOptions;
1946 :
1947 121 : GDALDriver *poDriver = poDS->GetDriver();
1948 :
1949 242 : CPLString osRet;
1950 242 : CPLJSONObject oRoot;
1951 242 : const std::string osFilename(poDS->GetDescription());
1952 :
1953 121 : const bool bExportOgrSchema = psOptions->bExportOgrSchema;
1954 121 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1955 121 : const bool bIsSummaryCli =
1956 121 : (psOptions->bIsCli && psOptions->bSummaryUserRequested);
1957 :
1958 242 : CPLJSONArray oLayerArray;
1959 121 : if (bJson)
1960 : {
1961 58 : if (!bExportOgrSchema)
1962 : {
1963 44 : oRoot.Set("description", poDS->GetDescription());
1964 44 : if (poDriver)
1965 : {
1966 44 : oRoot.Set("driverShortName", poDriver->GetDescription());
1967 44 : oRoot.Set("driverLongName",
1968 88 : poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
1969 : }
1970 : }
1971 58 : oRoot.Add("layers", oLayerArray);
1972 : }
1973 :
1974 : /* -------------------------------------------------------------------- */
1975 : /* Some information messages. */
1976 : /* -------------------------------------------------------------------- */
1977 121 : if (!bJson && psOptions->bVerbose)
1978 : {
1979 122 : Concat(osRet, psOptions->bStdoutOutput,
1980 : "INFO: Open of `%s'\n"
1981 : " using driver `%s' successful.\n",
1982 : osFilename.c_str(),
1983 61 : poDriver ? poDriver->GetDescription() : "(null)");
1984 : }
1985 :
1986 182 : if (!bJson && psOptions->bVerbose &&
1987 61 : !EQUAL(osFilename.c_str(), poDS->GetDescription()))
1988 : {
1989 0 : Concat(osRet, psOptions->bStdoutOutput,
1990 : "INFO: Internal data source name `%s'\n"
1991 : " different from user name `%s'.\n",
1992 0 : poDS->GetDescription(), osFilename.c_str());
1993 : }
1994 :
1995 121 : int nRepeatCount = psOptions->nRepeatCount;
1996 :
1997 121 : if (!bIsSummaryCli && !bExportOgrSchema)
1998 : {
1999 103 : GDALVectorInfoReportMetadata(
2000 103 : osRet, oRoot, psOptions, poDS, psOptions->bListMDD,
2001 103 : psOptions->bShowMetadata, psOptions->aosExtraMDDomains.List());
2002 :
2003 103 : CPLJSONObject oDomains;
2004 103 : oRoot.Add("domains", oDomains);
2005 103 : if (!psOptions->osFieldDomain.empty())
2006 : {
2007 7 : auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
2008 7 : if (poDomain == nullptr)
2009 : {
2010 0 : CPLError(CE_Failure, CPLE_AppDefined,
2011 : "Domain %s cannot be found.",
2012 : psOptions->osFieldDomain.c_str());
2013 0 : return nullptr;
2014 : }
2015 7 : if (!bJson)
2016 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
2017 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
2018 7 : if (!bJson)
2019 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
2020 : }
2021 96 : else if (bJson)
2022 : {
2023 49 : for (const auto &osDomainName : poDS->GetFieldDomainNames())
2024 : {
2025 7 : auto poDomain = poDS->GetFieldDomain(osDomainName);
2026 7 : if (poDomain)
2027 : {
2028 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
2029 : }
2030 : }
2031 : }
2032 :
2033 103 : if (psOptions->bDatasetGetNextFeature)
2034 : {
2035 1 : nRepeatCount = 0; // skip layer reporting.
2036 :
2037 : /* --------------------------------------------------------------------
2038 : */
2039 : /* Set filters if provided. */
2040 : /* --------------------------------------------------------------------
2041 : */
2042 2 : if (!psOptions->osWHERE.empty() ||
2043 1 : psOptions->poSpatialFilter != nullptr)
2044 : {
2045 0 : for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
2046 : {
2047 0 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
2048 :
2049 0 : if (poLayer == nullptr)
2050 : {
2051 0 : CPLError(CE_Failure, CPLE_AppDefined,
2052 : "Couldn't fetch advertised layer %d.", iLayer);
2053 0 : return nullptr;
2054 : }
2055 :
2056 0 : if (!psOptions->osWHERE.empty())
2057 : {
2058 0 : if (poLayer->SetAttributeFilter(
2059 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
2060 : {
2061 0 : CPLError(
2062 : CE_Warning, CPLE_AppDefined,
2063 : "SetAttributeFilter(%s) failed on layer %s.",
2064 0 : psOptions->osWHERE.c_str(), poLayer->GetName());
2065 : }
2066 : }
2067 :
2068 0 : if (psOptions->poSpatialFilter != nullptr)
2069 : {
2070 0 : if (!psOptions->osGeomField.empty())
2071 : {
2072 0 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
2073 0 : const int iGeomField = poDefn->GetGeomFieldIndex(
2074 0 : psOptions->osGeomField.c_str());
2075 0 : if (iGeomField >= 0)
2076 0 : poLayer->SetSpatialFilter(
2077 : iGeomField,
2078 0 : psOptions->poSpatialFilter.get());
2079 : else
2080 0 : CPLError(CE_Warning, CPLE_AppDefined,
2081 : "Cannot find geometry field %s.",
2082 : psOptions->osGeomField.c_str());
2083 : }
2084 : else
2085 : {
2086 0 : poLayer->SetSpatialFilter(
2087 0 : psOptions->poSpatialFilter.get());
2088 : }
2089 : }
2090 : }
2091 : }
2092 :
2093 1 : std::set<OGRLayer *> oSetLayers;
2094 : while (true)
2095 : {
2096 11 : OGRLayer *poLayer = nullptr;
2097 : OGRFeature *poFeature =
2098 11 : poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
2099 11 : if (poFeature == nullptr)
2100 1 : break;
2101 10 : if (psOptions->aosLayers.empty() || poLayer == nullptr ||
2102 0 : CSLFindString(psOptions->aosLayers.List(),
2103 0 : poLayer->GetName()) >= 0)
2104 : {
2105 10 : if (psOptions->bVerbose && poLayer != nullptr &&
2106 10 : oSetLayers.find(poLayer) == oSetLayers.end())
2107 : {
2108 0 : oSetLayers.insert(poLayer);
2109 0 : CPLJSONObject oLayer;
2110 0 : oLayerArray.Add(oLayer);
2111 0 : ReportOnLayer(
2112 : osRet, oLayer, psOptions, poLayer,
2113 : /*bForceSummary = */ true,
2114 : /*bTakeIntoAccountWHERE = */ false,
2115 : /*bTakeIntoAccountSpatialFilter = */ false,
2116 : /*bTakeIntoAccountGeomField = */ false);
2117 : }
2118 10 : if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
2119 10 : poFeature->DumpReadable(
2120 : nullptr,
2121 : const_cast<char **>(psOptions->aosOptions.List()));
2122 : }
2123 10 : OGRFeature::DestroyFeature(poFeature);
2124 10 : }
2125 : }
2126 :
2127 : /* -------------------------------------------------------------------- */
2128 : /* Special case for -sql clause. No source layers required. */
2129 : /* -------------------------------------------------------------------- */
2130 102 : else if (!psOptions->osSQLStatement.empty())
2131 : {
2132 5 : nRepeatCount = 0; // skip layer reporting.
2133 :
2134 5 : if (!bJson && !psOptions->aosLayers.empty())
2135 0 : Concat(osRet, psOptions->bStdoutOutput,
2136 : "layer names ignored in combination with -sql.\n");
2137 :
2138 5 : CPLErrorReset();
2139 15 : OGRLayer *poResultSet = poDS->ExecuteSQL(
2140 : psOptions->osSQLStatement.c_str(),
2141 5 : psOptions->osGeomField.empty()
2142 5 : ? psOptions->poSpatialFilter.get()
2143 : : nullptr,
2144 5 : psOptions->osDialect.empty() ? nullptr
2145 6 : : psOptions->osDialect.c_str());
2146 :
2147 5 : if (poResultSet != nullptr)
2148 : {
2149 4 : if (!psOptions->osWHERE.empty())
2150 : {
2151 0 : if (poResultSet->SetAttributeFilter(
2152 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
2153 : {
2154 0 : CPLError(CE_Failure, CPLE_AppDefined,
2155 : "SetAttributeFilter(%s) failed.",
2156 : psOptions->osWHERE.c_str());
2157 0 : return nullptr;
2158 : }
2159 : }
2160 :
2161 8 : CPLJSONObject oLayer;
2162 4 : oLayerArray.Add(oLayer);
2163 4 : if (!psOptions->osGeomField.empty())
2164 0 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2165 : /*bForceSummary = */ false,
2166 : /*bTakeIntoAccountWHERE = */ false,
2167 : /*bTakeIntoAccountSpatialFilter = */ true,
2168 : /*bTakeIntoAccountGeomField = */ true);
2169 : else
2170 4 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2171 : /*bForceSummary = */ false,
2172 : /*bTakeIntoAccountWHERE = */ false,
2173 : /*bTakeIntoAccountSpatialFilter = */ false,
2174 : /*bTakeIntoAccountGeomField = */ false);
2175 :
2176 4 : poDS->ReleaseResultSet(poResultSet);
2177 : }
2178 1 : else if (CPLGetLastErrorType() != CE_None)
2179 : {
2180 : // sqlite3 emits messages with "readonly" and GDAL with "read-only"
2181 1 : if (psOptions->bIsCli &&
2182 0 : (strstr(CPLGetLastErrorMsg(), "readonly") ||
2183 0 : strstr(CPLGetLastErrorMsg(), "read-only")))
2184 : {
2185 0 : CPLError(CE_Warning, CPLE_AppDefined,
2186 : "Perhaps you want to run \"gdal vector sql "
2187 : "--update\" instead?");
2188 : }
2189 1 : return nullptr;
2190 : }
2191 : }
2192 : }
2193 :
2194 : // coverity[tainted_data]
2195 120 : auto papszLayers = psOptions->aosLayers.List();
2196 234 : for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
2197 : {
2198 115 : if (papszLayers == nullptr || papszLayers[0] == nullptr)
2199 : {
2200 104 : const int nLayerCount = poDS->GetLayerCount();
2201 104 : if (iRepeat == 0)
2202 104 : CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
2203 :
2204 104 : bool bDone = false;
2205 104 : auto poRootGroup = poDS->GetRootGroup();
2206 107 : if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
2207 107 : (!poRootGroup->GetGroupNames().empty() ||
2208 106 : !poRootGroup->GetVectorLayerNames().empty()))
2209 : {
2210 6 : CPLJSONObject oGroup;
2211 3 : oRoot.Add("rootGroup", oGroup);
2212 3 : ReportHiearchicalLayers(osRet, oGroup, psOptions,
2213 6 : poRootGroup.get(), std::string(),
2214 3 : psOptions->bGeomType);
2215 3 : if (!bJson)
2216 1 : bDone = true;
2217 : }
2218 :
2219 : /* --------------------------------------------------------------------
2220 : */
2221 : /* Process each data source layer. */
2222 : /* --------------------------------------------------------------------
2223 : */
2224 298 : for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
2225 : {
2226 194 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
2227 :
2228 194 : if (poLayer == nullptr)
2229 : {
2230 0 : CPLError(CE_Failure, CPLE_AppDefined,
2231 : "Couldn't fetch advertised layer %d.", iLayer);
2232 0 : return nullptr;
2233 : }
2234 :
2235 388 : CPLJSONObject oLayer;
2236 194 : oLayerArray.Add(oLayer);
2237 194 : if (!psOptions->bAllLayers || bIsSummaryCli)
2238 : {
2239 19 : if (!bJson)
2240 17 : Concat(osRet, psOptions->bStdoutOutput,
2241 : "%d: ", iLayer + 1);
2242 19 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
2243 19 : poDS->IsLayerPrivate(iLayer));
2244 : }
2245 : else
2246 : {
2247 175 : if (iRepeat != 0)
2248 0 : poLayer->ResetReading();
2249 :
2250 175 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2251 : /*bForceSummary = */ false,
2252 : /*bTakeIntoAccountWHERE = */ true,
2253 : /*bTakeIntoAccountSpatialFilter = */ true,
2254 : /*bTakeIntoAccountGeomField = */ true);
2255 : }
2256 104 : }
2257 : }
2258 : else
2259 : {
2260 : /* --------------------------------------------------------------------
2261 : */
2262 : /* Process specified data source layers. */
2263 : /* --------------------------------------------------------------------
2264 : */
2265 :
2266 22 : for (const char *pszLayer : cpl::Iterate(papszLayers))
2267 : {
2268 12 : OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
2269 :
2270 12 : if (poLayer == nullptr)
2271 : {
2272 1 : CPLError(CE_Failure, CPLE_AppDefined,
2273 : "Couldn't fetch requested layer %s.", pszLayer);
2274 1 : return nullptr;
2275 : }
2276 :
2277 11 : if (iRepeat != 0)
2278 0 : poLayer->ResetReading();
2279 :
2280 22 : CPLJSONObject oLayer;
2281 11 : oLayerArray.Add(oLayer);
2282 11 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2283 : /*bForceSummary = */ false,
2284 : /*bTakeIntoAccountWHERE = */ true,
2285 : /*bTakeIntoAccountSpatialFilter = */ true,
2286 : /*bTakeIntoAccountGeomField = */ true);
2287 : }
2288 : }
2289 : }
2290 :
2291 119 : if (!papszLayers && !bIsSummaryCli && !bExportOgrSchema)
2292 : {
2293 91 : ReportRelationships(osRet, oRoot, psOptions, poDS);
2294 : }
2295 :
2296 119 : if (bJson)
2297 : {
2298 57 : osRet.clear();
2299 57 : ConcatStr(
2300 57 : osRet, psOptions->bStdoutOutput,
2301 : json_object_to_json_string_ext(
2302 57 : static_cast<struct json_object *>(oRoot.GetInternalHandle()),
2303 : JSON_C_TO_STRING_PRETTY
2304 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2305 : | JSON_C_TO_STRING_NOSLASHESCAPE
2306 : #endif
2307 : ));
2308 57 : ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
2309 : }
2310 :
2311 119 : return VSI_STRDUP_VERBOSE(osRet);
2312 : }
2313 :
2314 : /************************************************************************/
2315 : /* GDALVectorInfoOptionsGetParser() */
2316 : /************************************************************************/
2317 :
2318 127 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
2319 : GDALVectorInfoOptions *psOptions,
2320 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2321 : {
2322 : auto argParser = std::make_unique<GDALArgumentParser>(
2323 127 : "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
2324 :
2325 127 : argParser->add_description(
2326 127 : _("Lists information about an OGR-supported data source."));
2327 :
2328 127 : argParser->add_epilog(
2329 127 : _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
2330 :
2331 127 : argParser->add_argument("-json")
2332 127 : .flag()
2333 44 : .action([psOptions](const std::string &)
2334 127 : { psOptions->eFormat = FORMAT_JSON; })
2335 127 : .help(_("Display the output in json format."));
2336 :
2337 : // Hidden argument to select OGR_SCHEMA output
2338 127 : argParser->add_argument("-schema")
2339 127 : .flag()
2340 127 : .hidden()
2341 14 : .action([psOptions](const std::string &)
2342 127 : { psOptions->bExportOgrSchema = true; })
2343 127 : .help(_("Export the OGR_SCHEMA in json format."));
2344 :
2345 127 : argParser->add_argument("-ro")
2346 127 : .flag()
2347 : .action(
2348 14 : [psOptionsForBinary](const std::string &)
2349 : {
2350 7 : if (psOptionsForBinary)
2351 7 : psOptionsForBinary->bReadOnly = true;
2352 127 : })
2353 127 : .help(_("Open the data source in read-only mode."));
2354 :
2355 127 : argParser->add_argument("-update")
2356 127 : .flag()
2357 : .action(
2358 0 : [psOptionsForBinary](const std::string &)
2359 : {
2360 0 : if (psOptionsForBinary)
2361 0 : psOptionsForBinary->bUpdate = true;
2362 127 : })
2363 127 : .help(_("Open the data source in update mode."));
2364 :
2365 127 : argParser->add_argument("-q", "--quiet")
2366 127 : .flag()
2367 : .action(
2368 4 : [psOptions, psOptionsForBinary](const std::string &)
2369 : {
2370 2 : psOptions->bVerbose = false;
2371 2 : if (psOptionsForBinary)
2372 2 : psOptionsForBinary->bVerbose = false;
2373 127 : })
2374 : .help(_("Quiet mode. No progress message is emitted on the standard "
2375 127 : "output."));
2376 :
2377 : #ifdef __AFL_HAVE_MANUAL_CONTROL
2378 : /* Undocumented: mainly only useful for AFL testing */
2379 : argParser->add_argument("-qq")
2380 : .flag()
2381 : .hidden()
2382 : .action(
2383 : [psOptions, psOptionsForBinary](const std::string &)
2384 : {
2385 : psOptions->bVerbose = false;
2386 : if (psOptionsForBinary)
2387 : psOptionsForBinary->bVerbose = false;
2388 : psOptions->bSuperQuiet = true;
2389 : })
2390 : .help(_("Super quiet mode."));
2391 : #endif
2392 :
2393 127 : argParser->add_argument("-fid")
2394 254 : .metavar("<FID>")
2395 127 : .store_into(psOptions->nFetchFID)
2396 127 : .help(_("Only the feature with this feature id will be reported."));
2397 :
2398 127 : argParser->add_argument("-spat")
2399 254 : .metavar("<xmin> <ymin> <xmax> <ymax>")
2400 127 : .nargs(4)
2401 127 : .scan<'g', double>()
2402 : .help(_("The area of interest. Only features within the rectangle will "
2403 127 : "be reported."));
2404 :
2405 127 : argParser->add_argument("-geomfield")
2406 254 : .metavar("<field>")
2407 127 : .store_into(psOptions->osGeomField)
2408 : .help(_("Name of the geometry field on which the spatial filter "
2409 127 : "operates."));
2410 :
2411 127 : argParser->add_argument("-where")
2412 254 : .metavar("<restricted_where>")
2413 127 : .store_into(psOptions->osWHERE)
2414 : .help(_("An attribute query in a restricted form of the queries used "
2415 127 : "in the SQL WHERE statement."));
2416 :
2417 : {
2418 127 : auto &group = argParser->add_mutually_exclusive_group();
2419 127 : group.add_argument("-sql")
2420 254 : .metavar("<statement|@filename>")
2421 127 : .store_into(psOptions->osSQLStatement)
2422 : .help(_(
2423 127 : "Execute the indicated SQL statement and return the result."));
2424 :
2425 127 : group.add_argument("-rl")
2426 127 : .store_into(psOptions->bDatasetGetNextFeature)
2427 127 : .help(_("Enable random layer reading mode."));
2428 : }
2429 :
2430 127 : argParser->add_argument("-dialect")
2431 254 : .metavar("<dialect>")
2432 127 : .store_into(psOptions->osDialect)
2433 127 : .help(_("SQL dialect."));
2434 :
2435 : // Only for fuzzing
2436 127 : argParser->add_argument("-rc")
2437 127 : .hidden()
2438 254 : .metavar("<count>")
2439 127 : .store_into(psOptions->nRepeatCount)
2440 127 : .help(_("Repeat count"));
2441 :
2442 127 : argParser->add_argument("-al")
2443 127 : .store_into(psOptions->bAllLayers)
2444 : .help(_("List all layers (used instead of having to give layer names "
2445 127 : "as arguments)."));
2446 :
2447 : {
2448 127 : auto &group = argParser->add_mutually_exclusive_group();
2449 127 : group.add_argument("-so", "-summary")
2450 127 : .store_into(psOptions->bSummaryUserRequested)
2451 : .help(_("Summary only: show only summary information like "
2452 127 : "projection, schema, feature count and extents."));
2453 :
2454 127 : group.add_argument("-features")
2455 127 : .store_into(psOptions->bFeaturesUserRequested)
2456 127 : .help(_("Enable listing of features."));
2457 : }
2458 :
2459 127 : argParser->add_argument("-limit")
2460 254 : .metavar("<nb_features>")
2461 127 : .store_into(psOptions->nLimit)
2462 127 : .help(_("Limit the number of features per layer."));
2463 :
2464 127 : argParser->add_argument("-fields")
2465 127 : .choices("YES", "NO")
2466 254 : .metavar("YES|NO")
2467 : .action(
2468 2 : [psOptions](const std::string &s)
2469 : {
2470 2 : psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
2471 127 : })
2472 : .help(
2473 127 : _("If set to NO, the feature dump will not display field values."));
2474 :
2475 127 : argParser->add_argument("-geom")
2476 127 : .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
2477 254 : .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
2478 : .action(
2479 3 : [psOptions](const std::string &s)
2480 : {
2481 : psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
2482 3 : s.c_str());
2483 127 : })
2484 127 : .help(_("How to display geometries in feature dump."));
2485 :
2486 127 : argParser->add_argument("-oo")
2487 127 : .append()
2488 254 : .metavar("<NAME=VALUE>")
2489 : .action(
2490 20 : [psOptionsForBinary](const std::string &s)
2491 : {
2492 10 : if (psOptionsForBinary)
2493 10 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
2494 127 : })
2495 127 : .help(_("Dataset open option (format-specific)."));
2496 :
2497 127 : argParser->add_argument("-nomd")
2498 127 : .flag()
2499 1 : .action([psOptions](const std::string &)
2500 127 : { psOptions->bShowMetadata = false; })
2501 127 : .help(_("Suppress metadata printing."));
2502 :
2503 127 : argParser->add_argument("-listmdd")
2504 127 : .store_into(psOptions->bListMDD)
2505 127 : .help(_("List all metadata domains available for the dataset."));
2506 :
2507 127 : argParser->add_argument("-mdd")
2508 127 : .append()
2509 254 : .metavar("<domain>")
2510 1 : .action([psOptions](const std::string &s)
2511 128 : { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
2512 127 : .help(_("List metadata in the specified domain."));
2513 :
2514 127 : argParser->add_argument("-nocount")
2515 127 : .flag()
2516 2 : .action([psOptions](const std::string &)
2517 127 : { psOptions->bFeatureCount = false; })
2518 127 : .help(_("Suppress feature count printing."));
2519 :
2520 127 : argParser->add_argument("-noextent")
2521 127 : .flag()
2522 0 : .action([psOptions](const std::string &)
2523 127 : { psOptions->bExtent = false; })
2524 127 : .help(_("Suppress spatial extent printing."));
2525 :
2526 127 : argParser->add_argument("-extent3D")
2527 127 : .store_into(psOptions->bExtent3D)
2528 127 : .help(_("Request a 3D extent to be reported."));
2529 :
2530 127 : argParser->add_argument("-nogeomtype")
2531 127 : .flag()
2532 1 : .action([psOptions](const std::string &)
2533 127 : { psOptions->bGeomType = false; })
2534 127 : .help(_("Suppress layer geometry type printing."));
2535 :
2536 127 : argParser->add_argument("-wkt_format")
2537 127 : .store_into(psOptions->osWKTFormat)
2538 254 : .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
2539 127 : .help(_("The WKT format used to display the SRS."));
2540 :
2541 127 : argParser->add_argument("-fielddomain")
2542 127 : .store_into(psOptions->osFieldDomain)
2543 254 : .metavar("<name>")
2544 127 : .help(_("Display details about a field domain."));
2545 :
2546 127 : argParser->add_argument("-if")
2547 127 : .append()
2548 254 : .metavar("<format>")
2549 : .action(
2550 4 : [psOptionsForBinary](const std::string &s)
2551 : {
2552 2 : if (psOptionsForBinary)
2553 : {
2554 2 : if (GDALGetDriverByName(s.c_str()) == nullptr)
2555 : {
2556 0 : CPLError(CE_Warning, CPLE_AppDefined,
2557 : "%s is not a recognized driver", s.c_str());
2558 : }
2559 : psOptionsForBinary->aosAllowInputDrivers.AddString(
2560 2 : s.c_str());
2561 : }
2562 127 : })
2563 127 : .help(_("Format/driver name(s) to try when opening the input file."));
2564 :
2565 127 : argParser->add_argument("-stdout")
2566 127 : .flag()
2567 127 : .store_into(psOptions->bStdoutOutput)
2568 127 : .hidden()
2569 127 : .help(_("Directly output on stdout (format=text mode only)"));
2570 :
2571 127 : argParser->add_argument("--cli")
2572 127 : .hidden()
2573 127 : .store_into(psOptions->bIsCli)
2574 : .help(_("Indicates that this is called from the gdal vector info CLI "
2575 127 : "utility."));
2576 :
2577 : // Hidden: only for gdal vector info
2578 127 : argParser->add_argument("--crs-format")
2579 127 : .choices("AUTO", "WKT2", "PROJJSON")
2580 127 : .store_into(psOptions->osCRSFormat)
2581 127 : .hidden();
2582 :
2583 127 : auto &argFilename = argParser->add_argument("filename")
2584 : .action(
2585 134 : [psOptionsForBinary](const std::string &s)
2586 : {
2587 91 : if (psOptionsForBinary)
2588 43 : psOptionsForBinary->osFilename = s;
2589 127 : })
2590 127 : .help(_("The data source to open."));
2591 127 : if (!psOptionsForBinary)
2592 82 : argFilename.nargs(argparse::nargs_pattern::optional);
2593 :
2594 127 : argParser->add_argument("layer")
2595 127 : .remaining()
2596 254 : .metavar("<layer_name>")
2597 127 : .help(_("Layer name."));
2598 :
2599 127 : return argParser;
2600 : }
2601 :
2602 : /************************************************************************/
2603 : /* GDALVectorInfoGetParserUsage() */
2604 : /************************************************************************/
2605 :
2606 1 : std::string GDALVectorInfoGetParserUsage()
2607 : {
2608 : try
2609 : {
2610 2 : GDALVectorInfoOptions sOptions;
2611 2 : GDALVectorInfoOptionsForBinary sOptionsForBinary;
2612 : auto argParser =
2613 2 : GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
2614 1 : return argParser->usage();
2615 : }
2616 0 : catch (const std::exception &err)
2617 : {
2618 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
2619 0 : err.what());
2620 0 : return std::string();
2621 : }
2622 : }
2623 :
2624 : /************************************************************************/
2625 : /* GDALVectorInfoOptionsNew() */
2626 : /************************************************************************/
2627 :
2628 : /**
2629 : * Allocates a GDALVectorInfoOptions struct.
2630 : *
2631 : * Note that when this function is used a library function, and not from the
2632 : * ogrinfo utility, a dataset name must be specified if any layer names(s) are
2633 : * specified (if no layer name is specific, passing a dataset name is not
2634 : * needed). That dataset name may be a dummy one, as the dataset taken into
2635 : * account is the hDS parameter passed to GDALVectorInfo().
2636 : * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
2637 : * is the responsibility of the user to apply them when opening the hDS parameter
2638 : * passed to GDALVectorInfo().
2639 : *
2640 : * @param papszArgv NULL terminated list of options (potentially including
2641 : * filename and open options too), or NULL. The accepted options are the ones of
2642 : * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
2643 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2644 : * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
2645 : * GDALVectorInfoOptionsForBinaryNew() prior to this
2646 : * function. Will be filled with potentially present filename, open options,
2647 : * subdataset number...
2648 : * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
2649 : * with GDALVectorInfoOptionsFree().
2650 : *
2651 : * @since GDAL 3.7
2652 : */
2653 :
2654 : GDALVectorInfoOptions *
2655 126 : GDALVectorInfoOptionsNew(char **papszArgv,
2656 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2657 : {
2658 252 : auto psOptions = std::make_unique<GDALVectorInfoOptions>();
2659 :
2660 : try
2661 : {
2662 : auto argParser =
2663 252 : GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
2664 :
2665 : /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
2666 : * same for -geom=foo. */
2667 252 : CPLStringList aosArgv;
2668 587 : for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
2669 : ++papszIter)
2670 : {
2671 461 : if (STARTS_WITH(*papszIter, "-fields="))
2672 : {
2673 2 : aosArgv.AddString("-fields");
2674 : aosArgv.AddString(
2675 2 : CPLString(*papszIter + strlen("-fields=")).toupper());
2676 : }
2677 459 : else if (STARTS_WITH(*papszIter, "-geom="))
2678 : {
2679 3 : aosArgv.AddString("-geom");
2680 : aosArgv.AddString(
2681 3 : CPLString(*papszIter + strlen("-geom=")).toupper());
2682 : }
2683 : else
2684 : {
2685 456 : aosArgv.AddString(*papszIter);
2686 : }
2687 : }
2688 :
2689 126 : argParser->parse_args_without_binary_name(aosArgv.List());
2690 :
2691 250 : auto layers = argParser->present<std::vector<std::string>>("layer");
2692 125 : if (layers)
2693 : {
2694 23 : for (const auto &layer : *layers)
2695 : {
2696 12 : psOptions->aosLayers.AddString(layer.c_str());
2697 12 : psOptions->bAllLayers = false;
2698 : }
2699 : }
2700 :
2701 127 : if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
2702 : {
2703 2 : const double dfMinX = (*oSpat)[0];
2704 2 : const double dfMinY = (*oSpat)[1];
2705 2 : const double dfMaxX = (*oSpat)[2];
2706 2 : const double dfMaxY = (*oSpat)[3];
2707 :
2708 : auto poPolygon =
2709 4 : std::make_unique<OGRPolygon>(dfMinX, dfMinY, dfMaxX, dfMaxY);
2710 2 : psOptions->poSpatialFilter.reset(poPolygon.release());
2711 : }
2712 :
2713 125 : if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
2714 : {
2715 0 : GByte *pabyRet = nullptr;
2716 0 : if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
2717 0 : &pabyRet, nullptr, 10 * 1024 * 1024))
2718 : {
2719 0 : GDALRemoveBOM(pabyRet);
2720 0 : psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
2721 0 : VSIFree(pabyRet);
2722 : }
2723 : else
2724 : {
2725 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2726 0 : psOptions->osWHERE.substr(1).c_str());
2727 0 : return nullptr;
2728 : }
2729 : }
2730 :
2731 130 : if (!psOptions->osSQLStatement.empty() &&
2732 5 : psOptions->osSQLStatement[0] == '@')
2733 : {
2734 1 : GByte *pabyRet = nullptr;
2735 1 : if (VSIIngestFile(nullptr,
2736 2 : psOptions->osSQLStatement.substr(1).c_str(),
2737 1 : &pabyRet, nullptr, 10 * 1024 * 1024))
2738 : {
2739 1 : GDALRemoveBOM(pabyRet);
2740 1 : char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
2741 1 : psOptions->osSQLStatement =
2742 2 : CPLRemoveSQLComments(pszSQLStatement);
2743 1 : VSIFree(pabyRet);
2744 : }
2745 : else
2746 : {
2747 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2748 0 : psOptions->osSQLStatement.substr(1).c_str());
2749 0 : return nullptr;
2750 : }
2751 : }
2752 :
2753 125 : if (psOptionsForBinary)
2754 : {
2755 43 : psOptions->bStdoutOutput = true;
2756 43 : psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
2757 : }
2758 :
2759 125 : if (psOptions->eFormat == FORMAT_JSON)
2760 : {
2761 44 : psOptions->bAllLayers = true;
2762 44 : psOptions->bSummaryOnly = true;
2763 44 : if (psOptions->aosExtraMDDomains.empty())
2764 44 : psOptions->aosExtraMDDomains.AddString("all");
2765 44 : psOptions->bStdoutOutput = false;
2766 : }
2767 :
2768 125 : if (psOptions->bSummaryUserRequested)
2769 17 : psOptions->bSummaryOnly = true;
2770 108 : else if (psOptions->bFeaturesUserRequested)
2771 13 : psOptions->bSummaryOnly = false;
2772 :
2773 125 : if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
2774 0 : psOptions->osSQLStatement.empty())
2775 : {
2776 0 : CPLError(CE_Warning, CPLE_AppDefined,
2777 : "-dialect is ignored with -where. Use -sql instead");
2778 : }
2779 :
2780 : // Patch options when -schema is set
2781 125 : if (psOptions->bExportOgrSchema)
2782 : {
2783 : // TODO: validate and raise an error if incompatible options are set?
2784 : // not strictly necessary given that -schema is an hidden option
2785 14 : psOptions->eFormat = FORMAT_JSON;
2786 14 : psOptions->bAllLayers = true;
2787 14 : psOptions->bShowMetadata = false;
2788 14 : psOptions->bListMDD = false;
2789 14 : psOptions->bFeatureCount = false;
2790 14 : psOptions->bIsCli = true;
2791 14 : psOptions->bSummaryOnly = false;
2792 14 : psOptions->bExtent = false;
2793 14 : psOptions->bExtent3D = false;
2794 : }
2795 :
2796 125 : return psOptions.release();
2797 : }
2798 1 : catch (const std::exception &err)
2799 : {
2800 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
2801 1 : return nullptr;
2802 : }
2803 : }
|