/src/gdal/ogr/ogrsf_frmts/jsonfg/ogr_jsonfg.h
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implementation of OGC Features and Geometries JSON (JSON-FG) |
5 | | * Author: Even Rouault <even.rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #ifndef OGR_JSONFG_H_INCLUDED |
14 | | #define OGR_JSONFG_H_INCLUDED |
15 | | |
16 | | #include "cpl_vsi_virtual.h" |
17 | | |
18 | | #include "gdal_priv.h" |
19 | | #include "ogrsf_frmts.h" |
20 | | #include "ogrgeojsonutils.h" |
21 | | #include "ogrgeojsonwriter.h" |
22 | | #include "ogrjsoncollectionstreamingparser.h" |
23 | | #include "memdataset.h" |
24 | | #include "directedacyclicgraph.hpp" |
25 | | |
26 | | #include <map> |
27 | | #include <set> |
28 | | #include <utility> |
29 | | |
30 | | /************************************************************************/ |
31 | | /* OGRJSONFGMemLayer */ |
32 | | /************************************************************************/ |
33 | | |
34 | | /** Layer with all features ingested into memory. */ |
35 | | class OGRJSONFGMemLayer final : public OGRMemLayer |
36 | | { |
37 | | public: |
38 | | OGRJSONFGMemLayer(GDALDataset *poDS, const char *pszName, |
39 | | OGRSpatialReference *poSRS, OGRwkbGeometryType eGType); |
40 | | ~OGRJSONFGMemLayer(); |
41 | | |
42 | | const char *GetFIDColumn() override |
43 | 5 | { |
44 | 5 | return osFIDColumn_.c_str(); |
45 | 5 | } |
46 | | |
47 | | int TestCapability(const char *pszCap) override; |
48 | | |
49 | | void SetFIDColumn(const char *pszName) |
50 | 0 | { |
51 | 0 | osFIDColumn_ = pszName; |
52 | 0 | } |
53 | | |
54 | | void AddFeature(std::unique_ptr<OGRFeature> poFeature); |
55 | | |
56 | | GDALDataset *GetDataset() override |
57 | 0 | { |
58 | 0 | return m_poDS; |
59 | 0 | } |
60 | | |
61 | | private: |
62 | | GDALDataset *m_poDS = nullptr; |
63 | | std::string osFIDColumn_{}; |
64 | | bool bOriginalIdModified_ = false; |
65 | | |
66 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGMemLayer) |
67 | | }; |
68 | | |
69 | | /************************************************************************/ |
70 | | /* OGRJSONFGStreamedLayer */ |
71 | | /************************************************************************/ |
72 | | |
73 | | class OGRJSONFGStreamingParser; |
74 | | |
75 | | /** Layer with features being acquired progressively through a streaming |
76 | | parser. |
77 | | |
78 | | Only applies for FeatureCollection read through a file |
79 | | */ |
80 | | class OGRJSONFGStreamedLayer final |
81 | | : public OGRLayer, |
82 | | public OGRGetNextFeatureThroughRaw<OGRJSONFGStreamedLayer> |
83 | | { |
84 | | public: |
85 | | OGRJSONFGStreamedLayer(GDALDataset *poDS, const char *pszName, |
86 | | OGRSpatialReference *poSRS, |
87 | | OGRwkbGeometryType eGType); |
88 | | ~OGRJSONFGStreamedLayer(); |
89 | | |
90 | | // BEGIN specific public API |
91 | | |
92 | | //! Set the FID column name |
93 | | void SetFIDColumn(const char *pszName) |
94 | 0 | { |
95 | 0 | osFIDColumn_ = pszName; |
96 | 0 | } |
97 | | |
98 | | //! Set the total feature count |
99 | | void SetFeatureCount(GIntBig nCount) |
100 | 3 | { |
101 | 3 | nFeatureCount_ = nCount; |
102 | 3 | } |
103 | | |
104 | | /** Set the file handle. |
105 | | |
106 | | Must be called before GetNextFeature() is called |
107 | | */ |
108 | | void SetFile(VSIVirtualHandleUniquePtr &&poFile); |
109 | | |
110 | | /** Set the streaming parser |
111 | | |
112 | | Must be called before GetNextFeature() is called |
113 | | */ |
114 | | void SetStreamingParser( |
115 | | std::unique_ptr<OGRJSONFGStreamingParser> &&poStreamingParser); |
116 | | |
117 | | // END specific public API |
118 | | |
119 | | const char *GetFIDColumn() override |
120 | 3 | { |
121 | 3 | return osFIDColumn_.c_str(); |
122 | 3 | } |
123 | | |
124 | | OGRFeatureDefn *GetLayerDefn() override |
125 | 29 | { |
126 | 29 | return poFeatureDefn_; |
127 | 29 | } |
128 | | |
129 | | int TestCapability(const char *pszCap) override; |
130 | | |
131 | | GIntBig GetFeatureCount(int bForce) override; |
132 | | |
133 | | void ResetReading() override; |
134 | | |
135 | | DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRJSONFGStreamedLayer) |
136 | | |
137 | | GDALDataset *GetDataset() override |
138 | 0 | { |
139 | 0 | return m_poDS; |
140 | 0 | } |
141 | | |
142 | | private: |
143 | | GDALDataset *m_poDS = nullptr; |
144 | | OGRFeatureDefn *poFeatureDefn_ = nullptr; |
145 | | std::string osFIDColumn_{}; |
146 | | |
147 | | /** Total number of features. */ |
148 | | GIntBig nFeatureCount_ = -1; |
149 | | |
150 | | VSIVirtualHandleUniquePtr poFile_{}; |
151 | | |
152 | | std::unique_ptr<OGRJSONFGStreamingParser> poStreamingParser_{}; |
153 | | |
154 | | /** Whether a warning has been emitted about feature IDs having been |
155 | | * modified */ |
156 | | bool bOriginalIdModified_ = false; |
157 | | /** Set of feature IDs read/allocated up to that point */ |
158 | | std::set<GIntBig> oSetUsedFIDs_{}; |
159 | | |
160 | | /** Ensure the FID of the feature is unique */ |
161 | | OGRFeature *EnsureUniqueFID(OGRFeature *poFeat); |
162 | | |
163 | | /** Return next feature (without filter) */ |
164 | | OGRFeature *GetNextRawFeature(); |
165 | | |
166 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGStreamedLayer) |
167 | | }; |
168 | | |
169 | | /************************************************************************/ |
170 | | /* OGRJSONFGWriteLayer */ |
171 | | /************************************************************************/ |
172 | | |
173 | | class OGRJSONFGDataset; |
174 | | |
175 | | class OGRJSONFGWriteLayer final : public OGRLayer |
176 | | { |
177 | | public: |
178 | | OGRJSONFGWriteLayer( |
179 | | const char *pszName, const OGRSpatialReference *poSRS, |
180 | | std::unique_ptr<OGRCoordinateTransformation> &&poCTToWGS84, |
181 | | const std::string &osCoordRefSys, OGRwkbGeometryType eGType, |
182 | | CSLConstList papszOptions, OGRJSONFGDataset *poDS); |
183 | | ~OGRJSONFGWriteLayer(); |
184 | | |
185 | | // |
186 | | // OGRLayer Interface |
187 | | // |
188 | | OGRFeatureDefn *GetLayerDefn() override |
189 | 0 | { |
190 | 0 | return poFeatureDefn_; |
191 | 0 | } |
192 | | |
193 | | OGRSpatialReference *GetSpatialRef() override |
194 | 0 | { |
195 | 0 | return nullptr; |
196 | 0 | } |
197 | | |
198 | | void ResetReading() override |
199 | 0 | { |
200 | 0 | } |
201 | | |
202 | | OGRFeature *GetNextFeature() override |
203 | 0 | { |
204 | 0 | return nullptr; |
205 | 0 | } |
206 | | |
207 | | OGRErr ICreateFeature(OGRFeature *poFeature) override; |
208 | | OGRErr CreateField(const OGRFieldDefn *poField, int bApproxOK) override; |
209 | | int TestCapability(const char *pszCap) override; |
210 | | |
211 | | OGRErr SyncToDisk() override; |
212 | | |
213 | | GDALDataset *GetDataset() override; |
214 | | |
215 | | private: |
216 | | OGRJSONFGDataset *poDS_{}; |
217 | | OGRFeatureDefn *poFeatureDefn_ = nullptr; |
218 | | std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84_; |
219 | | bool bIsWGS84CRS_ = false; |
220 | | bool m_bMustSwapForPlace = false; |
221 | | int nOutCounter_ = 0; |
222 | | std::string osCoordRefSys_{}; |
223 | | |
224 | | OGRGeoJSONWriteOptions oWriteOptions_{}; |
225 | | OGRGeoJSONWriteOptions oWriteOptionsPlace_{}; |
226 | | bool bWriteFallbackGeometry_ = true; |
227 | | |
228 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGWriteLayer) |
229 | | }; |
230 | | |
231 | | /************************************************************************/ |
232 | | /* OGRJSONFGDataset */ |
233 | | /************************************************************************/ |
234 | | |
235 | | class OGRJSONFGReader; |
236 | | |
237 | | class OGRJSONFGDataset final : public GDALDataset |
238 | | { |
239 | | public: |
240 | 1.13k | OGRJSONFGDataset() = default; |
241 | | ~OGRJSONFGDataset(); |
242 | | |
243 | | bool Open(GDALOpenInfo *poOpenInfo, GeoJSONSourceType nSrcType); |
244 | | bool Create(const char *pszName, CSLConstList papszOptions); |
245 | | |
246 | | int GetLayerCount() override |
247 | 8 | { |
248 | 8 | return static_cast<int>(apoLayers_.size()); |
249 | 8 | } |
250 | | |
251 | | OGRLayer *GetLayer(int i) override; |
252 | | |
253 | | //! Return the output file handle. Used by OGRJSONFGWriteLayer |
254 | | VSILFILE *GetOutputFile() const |
255 | 0 | { |
256 | 0 | return fpOut_; |
257 | 0 | } |
258 | | |
259 | | /** Return whether there is a single output layer. |
260 | | * Used by OGRJSONFGWriteLayer |
261 | | */ |
262 | | bool IsSingleOutputLayer() const |
263 | 0 | { |
264 | 0 | return bSingleOutputLayer_; |
265 | 0 | } |
266 | | |
267 | | //! Return whether the output file is seekable |
268 | | bool GetFpOutputIsSeekable() const |
269 | 0 | { |
270 | 0 | return bFpOutputIsSeekable_; |
271 | 0 | } |
272 | | |
273 | | void BeforeCreateFeature(); |
274 | | |
275 | | OGRLayer *ICreateLayer(const char *pszName, |
276 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
277 | | CSLConstList papszOptions) override; |
278 | | |
279 | | int TestCapability(const char *pszCap) override; |
280 | | |
281 | | OGRErr SyncToDiskInternal(); |
282 | | |
283 | | protected: |
284 | | friend class OGRJSONFGReader; |
285 | | OGRJSONFGMemLayer *AddLayer(std::unique_ptr<OGRJSONFGMemLayer> &&poLayer); |
286 | | OGRJSONFGStreamedLayer * |
287 | | AddLayer(std::unique_ptr<OGRJSONFGStreamedLayer> &&poLayer); |
288 | | |
289 | | private: |
290 | | char *pszGeoData_ = nullptr; |
291 | | size_t nGeoDataLen_ = 0; |
292 | | std::vector<std::unique_ptr<OGRLayer>> apoLayers_{}; |
293 | | std::unique_ptr<OGRJSONFGReader> poReader_{}; |
294 | | |
295 | | // Write side |
296 | | VSILFILE *fpOut_ = nullptr; |
297 | | bool bSingleOutputLayer_ = false; |
298 | | bool bHasEmittedFeatures_ = false; |
299 | | bool bFpOutputIsSeekable_ = false; |
300 | | |
301 | | /** Offset at which the '] }' terminating sequence has already been |
302 | | * written by SyncToDisk(). 0 if it has not been written. |
303 | | */ |
304 | | vsi_l_offset m_nPositionBeforeFCClosed = 0; |
305 | | |
306 | | bool ReadFromFile(GDALOpenInfo *poOpenInfo, const char *pszUnprefixed); |
307 | | bool ReadFromService(GDALOpenInfo *poOpenInfo, const char *pszSource); |
308 | | |
309 | | void FinishWriting(); |
310 | | |
311 | | bool EmitStartFeaturesIfNeededAndReturnIfFirstFeature(); |
312 | | |
313 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGDataset) |
314 | | }; |
315 | | |
316 | | /************************************************************************/ |
317 | | /* OGRJSONFGReader */ |
318 | | /************************************************************************/ |
319 | | |
320 | | class OGRJSONFGReader |
321 | | { |
322 | | public: |
323 | 746 | OGRJSONFGReader() = default; |
324 | | ~OGRJSONFGReader(); |
325 | | |
326 | | /** Load all features from the passed in JSON text in OGRJSONFGMemLayer(s) |
327 | | * |
328 | | * This method should only be called once, and is exclusive with |
329 | | * AnalyzeWithStreamingParser() |
330 | | */ |
331 | | bool Load(OGRJSONFGDataset *poDS, const char *pszText, |
332 | | const std::string &osDefaultLayerName); |
333 | | |
334 | | /** Do a first pass analysis of the content of the passed file to create |
335 | | * OGRJSONFGStreamedLayer's |
336 | | * |
337 | | * It is the responsibility of the caller to call |
338 | | * SetFile() and SetStreamingParser() on the created layers afterwards |
339 | | * |
340 | | * This method should only be called once, and is exclusive with |
341 | | * Load() |
342 | | */ |
343 | | bool AnalyzeWithStreamingParser(OGRJSONFGDataset *poDS, VSILFILE *fp, |
344 | | const std::string &osDefaultLayerName, |
345 | | bool &bCanTryWithNonStreamingParserOut); |
346 | | |
347 | | /** Geometry element we are interested in. */ |
348 | | enum class GeometryElement |
349 | | { |
350 | | /** Use "place" when possible, fallback to "geometry" otherwise. */ |
351 | | AUTO, |
352 | | /** Only use "place" */ |
353 | | PLACE, |
354 | | /** Only use "geometry" */ |
355 | | GEOMETRY, |
356 | | }; |
357 | | |
358 | | /** Sets the geometry element we are interested in. */ |
359 | | void SetGeometryElement(GeometryElement elt) |
360 | 0 | { |
361 | 0 | eGeometryElement_ = elt; |
362 | 0 | } |
363 | | |
364 | | /** Returns a OGRFeature built from the passed in JSON object. |
365 | | * |
366 | | * @param poObj JSON feature |
367 | | * @param pszRequestedLayer name of the layer of interest, or nullptr if |
368 | | * no filtering needed on the layer name. If the feature does not belong |
369 | | * to the requested layer, nullptr is returned. |
370 | | * @param pOutMemLayer Pointer to the OGRJSONFGMemLayer* layer to which |
371 | | * the returned feature belongs to. May be nullptr. Only applies when |
372 | | * the Load() method has been used. |
373 | | * @param pOutStreamedLayer Pointer to the OGRJSONFGStreamedLayer* layer to |
374 | | * which the returned feature belongs to. May be nullptr. Only applies when |
375 | | * the AnalyzeWithStreamingParser() method has been used. |
376 | | */ |
377 | | std::unique_ptr<OGRFeature> |
378 | | ReadFeature(json_object *poObj, const char *pszRequestedLayer, |
379 | | OGRJSONFGMemLayer **pOutMemLayer, |
380 | | OGRJSONFGStreamedLayer **pOutStreamedLayer); |
381 | | |
382 | | protected: |
383 | | friend class OGRJSONFGStreamingParser; |
384 | | |
385 | | bool GenerateLayerDefnFromFeature(json_object *poObj); |
386 | | |
387 | | private: |
388 | | GeometryElement eGeometryElement_ = GeometryElement::AUTO; |
389 | | |
390 | | OGRJSONFGDataset *poDS_ = nullptr; |
391 | | std::string osDefaultLayerName_{}; |
392 | | json_object *poObject_ = nullptr; |
393 | | |
394 | | bool bFlattenNestedAttributes_ = false; |
395 | | char chNestedAttributeSeparator_ = 0; |
396 | | bool bArrayAsString_ = false; |
397 | | bool bDateAsString_ = false; |
398 | | |
399 | | /** Layer building context, specific to one layer. */ |
400 | | struct LayerDefnBuildContext |
401 | | { |
402 | | //! Maps a field name to its index in apoFieldDefn[] |
403 | | std::map<std::string, int> oMapFieldNameToIdx{}; |
404 | | |
405 | | //! Vector of OGRFieldDefn |
406 | | std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn{}; |
407 | | |
408 | | //! Directed acyclic graph used to build the order of fields. |
409 | | gdal::DirectedAcyclicGraph<int, std::string> dag{}; |
410 | | |
411 | | /** Set of indices of apoFieldDefn[] for which no type information is |
412 | | * known yet. */ |
413 | | std::set<int> aoSetUndeterminedTypeFields{}; |
414 | | |
415 | | //! Whether at least one feature has a "coordRefSys" member. |
416 | | bool bHasCoordRefSysAtFeatureLevel = false; |
417 | | |
418 | | /** CRS object corresponding to "coordRefsys" member at feature level. |
419 | | * Only set if homogeneous among features. |
420 | | */ |
421 | | std::unique_ptr<OGRSpatialReference> poCRSAtFeatureLevel{}; |
422 | | |
423 | | /** Serialized JSON value of "coordRefsys" member at feature level. |
424 | | * Only set if homogeneous among features. |
425 | | */ |
426 | | std::string osCoordRefSysAtFeatureLevel{}; |
427 | | |
428 | | /** Whether to switch X/Y ordinates in geometries appearing in "place" |
429 | | * element. Only applies to CRS at layer level. |
430 | | */ |
431 | | bool bSwapPlacesXY = false; |
432 | | |
433 | | //! Whether the layer CRS is WGS 84. |
434 | | bool bLayerCRSIsWGS84 = false; |
435 | | |
436 | | //! Coordinate transformation from WGS 84 to layer CRS (might be null) |
437 | | std::unique_ptr<OGRCoordinateTransformation> poCTWGS84ToLayerCRS{}; |
438 | | |
439 | | /** Feature count */ |
440 | | GIntBig nFeatureCount = 0; |
441 | | |
442 | | //! Whether the Feature.id should be mapped to a OGR field. |
443 | | bool bFeatureLevelIdAsAttribute = false; |
444 | | |
445 | | //! Whether the Feature.id should be mapped to a OGR FID. |
446 | | bool bFeatureLevelIdAsFID = false; |
447 | | |
448 | | //! Whether 64-bit integers are needed for OGR FID. |
449 | | bool bNeedFID64 = false; |
450 | | |
451 | | //! Whether detection of layer geometry type is still needed. |
452 | | bool bDetectLayerGeomType = true; |
453 | | |
454 | | //! Whether no geometry has been analyzed yet. |
455 | | bool bFirstGeometry = true; |
456 | | |
457 | | //! Layer geometry type. |
458 | | OGRwkbGeometryType eLayerGeomType = wkbUnknown; |
459 | | |
460 | | //! Whether a Feature.time.date element has been found. |
461 | | bool bHasTimeDate = false; |
462 | | |
463 | | //! Whether a Feature.time.timestamp element has been found. |
464 | | bool bHasTimeTimestamp = false; |
465 | | |
466 | | /** Whether a Feature.time.interval[0] element of type timestamp has |
467 | | * been found */ |
468 | | bool bHasTimeIntervalStartTimestamp = false; |
469 | | |
470 | | /** Whether a Feature.time.interval[0] element of type date has |
471 | | * been found */ |
472 | | bool bHasTimeIntervalStartDate = false; |
473 | | |
474 | | /** Whether a Feature.time.interval[1] element of type timestamp has |
475 | | * been found */ |
476 | | bool bHasTimeIntervalEndTimestamp = false; |
477 | | |
478 | | /** Whether a Feature.time.interval[1] element of type date has |
479 | | * been found */ |
480 | | bool bHasTimeIntervalEndDate = false; |
481 | | |
482 | | //! Index of OGR field "time" / "jsonfg_time" |
483 | | int nIdxFieldTime = -1; |
484 | | |
485 | | //! Index of OGR field "time_start" / "jsonfg_time_start" |
486 | | int nIdxFieldTimeStart = -1; |
487 | | |
488 | | //! Index of OGR field "time_end" / "jsonfg_time_end" |
489 | | int nIdxFieldTimeEnd = -1; |
490 | | |
491 | | //! Corresponding OGRJSONFGMemLayer (only for Load() ingestion mode) |
492 | | OGRJSONFGMemLayer *poMemLayer = nullptr; |
493 | | |
494 | | /** Corresponding OGRJSONFGStreamedLayer(only for |
495 | | * AnalyzeWithStreamingParser() mode) */ |
496 | | OGRJSONFGStreamedLayer *poStreamedLayer = nullptr; |
497 | | |
498 | 240 | LayerDefnBuildContext() = default; |
499 | 0 | LayerDefnBuildContext(LayerDefnBuildContext &&) = default; |
500 | 120 | LayerDefnBuildContext &operator=(LayerDefnBuildContext &&) = default; |
501 | | |
502 | | private: |
503 | | CPL_DISALLOW_COPY_ASSIGN(LayerDefnBuildContext) |
504 | | }; |
505 | | |
506 | | //! Maps a layer name to its build context |
507 | | std::map<std::string, LayerDefnBuildContext> oMapBuildContext_{}; |
508 | | |
509 | | // |
510 | | // Copy operations not supported. |
511 | | // |
512 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGReader) |
513 | | |
514 | | const char *GetLayerNameForFeature(json_object *poObj) const; |
515 | | bool GenerateLayerDefns(); |
516 | | bool FinalizeGenerateLayerDefns(bool bStreamedLayer); |
517 | | void FinalizeBuildContext(LayerDefnBuildContext &oBuildContext, |
518 | | const char *pszLayerName, bool bStreamedLayer, |
519 | | bool bInvalidCRS, bool bSwapPlacesXYTopLevel, |
520 | | OGRSpatialReference *poSRSTopLevel); |
521 | | }; |
522 | | |
523 | | /************************************************************************/ |
524 | | /* OGRJSONFGStreamingParser */ |
525 | | /************************************************************************/ |
526 | | |
527 | | /** FeatureCollection streaming parser. */ |
528 | | class OGRJSONFGStreamingParser final : public OGRJSONCollectionStreamingParser |
529 | | { |
530 | | OGRJSONFGReader &m_oReader; |
531 | | std::string m_osRequestedLayer{}; |
532 | | |
533 | | std::vector<std::pair<std::unique_ptr<OGRFeature>, OGRLayer *>> |
534 | | m_apoFeatures{}; |
535 | | size_t m_nCurFeatureIdx = 0; |
536 | | |
537 | | CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGStreamingParser) |
538 | | |
539 | | protected: |
540 | | void GotFeature(json_object *poObj, bool bFirstPass, |
541 | | const std::string &osJson) override; |
542 | | void TooComplex() override; |
543 | | |
544 | | public: |
545 | | OGRJSONFGStreamingParser(OGRJSONFGReader &oReader, bool bFirstPass); |
546 | | ~OGRJSONFGStreamingParser(); |
547 | | |
548 | | void SetRequestedLayer(const char *pszRequestedLayer) |
549 | 3 | { |
550 | 3 | m_osRequestedLayer = pszRequestedLayer; |
551 | 3 | } |
552 | | |
553 | | std::unique_ptr<OGRJSONFGStreamingParser> Clone(); |
554 | | |
555 | | std::pair<std::unique_ptr<OGRFeature>, OGRLayer *> GetNextFeature(); |
556 | | }; |
557 | | |
558 | | bool OGRJSONFGMustSwapXY(const OGRSpatialReference *poSRS); |
559 | | |
560 | | #endif // OGR_JSONFG_H_INCLUDED |