Line data Source code
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 : #include "ogr_jsonfg.h"
14 : #include "ogr_geojson.h"
15 :
16 : #include "cpl_http.h"
17 : #include "cpl_vsi_error.h"
18 : #include "cpl_vsi_virtual.h"
19 :
20 : #include <cmath>
21 :
22 : /************************************************************************/
23 : /* OGRJSONFGDataset::~OGRJSONFGDataset() */
24 : /************************************************************************/
25 :
26 448 : OGRJSONFGDataset::~OGRJSONFGDataset()
27 : {
28 224 : CPLFree(pszGeoData_);
29 224 : if (fpOut_)
30 : {
31 72 : FinishWriting();
32 :
33 72 : VSIFCloseL(fpOut_);
34 : }
35 448 : }
36 :
37 : /************************************************************************/
38 : /* FinishWriting() */
39 : /************************************************************************/
40 :
41 74 : void OGRJSONFGDataset::FinishWriting()
42 : {
43 74 : if (m_nPositionBeforeFCClosed == 0)
44 : {
45 73 : m_nPositionBeforeFCClosed = fpOut_->Tell();
46 :
47 73 : if (!EmitStartFeaturesIfNeededAndReturnIfFirstFeature())
48 57 : VSIFPrintfL(fpOut_, "\n");
49 73 : VSIFPrintfL(fpOut_, "]");
50 :
51 : // When we didn't know if there was a single layer, we omitted writing
52 : // the coordinate precision at ICreateLayer() time.
53 : // Now we can check if there was a single layer, or several layers with
54 : // same precision setting, and write it when possible.
55 137 : if (!bSingleOutputLayer_ && !apoLayers_.empty() &&
56 64 : apoLayers_.front()->GetLayerDefn()->GetGeomFieldCount() > 0)
57 : {
58 53 : const auto &oCoordPrec = apoLayers_.front()
59 53 : ->GetLayerDefn()
60 53 : ->GetGeomFieldDefn(0)
61 53 : ->GetCoordinatePrecision();
62 53 : bool bSameGeomCoordPrec =
63 53 : (oCoordPrec.dfXYResolution !=
64 105 : OGRGeomCoordinatePrecision::UNKNOWN ||
65 52 : oCoordPrec.dfZResolution !=
66 : OGRGeomCoordinatePrecision::UNKNOWN);
67 71 : for (size_t i = 1; i < apoLayers_.size(); ++i)
68 : {
69 18 : if (apoLayers_[i]->GetLayerDefn()->GetGeomFieldCount() > 0)
70 : {
71 : const auto &oOtherCoordPrec =
72 18 : apoLayers_[i]
73 18 : ->GetLayerDefn()
74 18 : ->GetGeomFieldDefn(0)
75 18 : ->GetCoordinatePrecision();
76 36 : bSameGeomCoordPrec &= (oOtherCoordPrec.dfXYResolution ==
77 36 : oCoordPrec.dfXYResolution &&
78 18 : oOtherCoordPrec.dfZResolution ==
79 18 : oCoordPrec.dfZResolution);
80 : }
81 : }
82 53 : if (bSameGeomCoordPrec)
83 : {
84 1 : if (oCoordPrec.dfXYResolution !=
85 : OGRGeomCoordinatePrecision::UNKNOWN)
86 : {
87 1 : VSIFPrintfL(fpOut_,
88 : ",\n\"xy_coordinate_resolution_place\":%g",
89 1 : oCoordPrec.dfXYResolution);
90 : }
91 1 : if (oCoordPrec.dfZResolution !=
92 : OGRGeomCoordinatePrecision::UNKNOWN)
93 : {
94 1 : VSIFPrintfL(fpOut_,
95 : ",\n\"z_coordinate_resolution_place\":%g",
96 1 : oCoordPrec.dfZResolution);
97 : }
98 :
99 2 : OGRSpatialReference oSRSWGS84;
100 1 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
101 : const auto oCoordPrecWGS84 = oCoordPrec.ConvertToOtherSRS(
102 2 : apoLayers_.front()->GetSpatialRef(), &oSRSWGS84);
103 :
104 1 : if (oCoordPrecWGS84.dfXYResolution !=
105 : OGRGeomCoordinatePrecision::UNKNOWN)
106 : {
107 1 : VSIFPrintfL(fpOut_, ",\n\"xy_coordinate_resolution\":%g",
108 1 : oCoordPrecWGS84.dfXYResolution);
109 : }
110 1 : if (oCoordPrecWGS84.dfZResolution !=
111 : OGRGeomCoordinatePrecision::UNKNOWN)
112 : {
113 1 : VSIFPrintfL(fpOut_, ",\n\"z_coordinate_resolution\":%g",
114 1 : oCoordPrecWGS84.dfZResolution);
115 : }
116 : }
117 : }
118 :
119 73 : VSIFPrintfL(fpOut_, "\n}\n");
120 73 : fpOut_->Flush();
121 : }
122 74 : }
123 :
124 : /************************************************************************/
125 : /* SyncToDiskInternal() */
126 : /************************************************************************/
127 :
128 3 : OGRErr OGRJSONFGDataset::SyncToDiskInternal()
129 : {
130 3 : if (m_nPositionBeforeFCClosed == 0 && GetFpOutputIsSeekable())
131 : {
132 2 : FinishWriting();
133 : }
134 :
135 3 : return OGRERR_NONE;
136 : }
137 :
138 : /************************************************************************/
139 : /* BeforeCreateFeature() */
140 : /************************************************************************/
141 :
142 140 : void OGRJSONFGDataset::BeforeCreateFeature()
143 : {
144 140 : if (m_nPositionBeforeFCClosed)
145 : {
146 : // If we had called SyncToDisk() previously, undo its effects
147 1 : fpOut_->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
148 1 : m_nPositionBeforeFCClosed = 0;
149 : }
150 :
151 140 : if (!EmitStartFeaturesIfNeededAndReturnIfFirstFeature())
152 : {
153 84 : VSIFPrintfL(fpOut_, ",\n");
154 : }
155 140 : }
156 :
157 : /************************************************************************/
158 : /* Open() */
159 : /************************************************************************/
160 :
161 151 : bool OGRJSONFGDataset::Open(GDALOpenInfo *poOpenInfo,
162 : GeoJSONSourceType nSrcType)
163 : {
164 151 : const char *pszUnprefixed = poOpenInfo->pszFilename;
165 151 : if (STARTS_WITH_CI(pszUnprefixed, "JSONFG:"))
166 : {
167 0 : pszUnprefixed += strlen("JSONFG:");
168 : }
169 :
170 302 : std::string osDefaultLayerName;
171 :
172 151 : VSIVirtualHandleUniquePtr fp;
173 151 : if (nSrcType == eGeoJSONSourceService)
174 : {
175 9 : if (!ReadFromService(poOpenInfo, pszUnprefixed))
176 9 : return false;
177 0 : if (poOpenInfo->eAccess == GA_Update)
178 : {
179 0 : CPLError(CE_Failure, CPLE_NotSupported,
180 : "Update from remote service not supported");
181 0 : return false;
182 : }
183 : }
184 142 : else if (nSrcType == eGeoJSONSourceText)
185 : {
186 45 : if (poOpenInfo->eAccess == GA_Update)
187 : {
188 0 : CPLError(CE_Failure, CPLE_NotSupported,
189 : "Update from inline definition not supported");
190 0 : return false;
191 : }
192 45 : pszGeoData_ = CPLStrdup(pszUnprefixed);
193 : }
194 97 : else if (nSrcType == eGeoJSONSourceFile)
195 : {
196 97 : if (poOpenInfo->eAccess == GA_Update)
197 : {
198 0 : CPLError(CE_Failure, CPLE_NotSupported, "Update not supported");
199 0 : return false;
200 : }
201 97 : SetDescription(pszUnprefixed);
202 97 : osDefaultLayerName = CPLGetBasename(pszUnprefixed);
203 97 : eAccess = poOpenInfo->eAccess;
204 :
205 : // Ingests the first bytes of the file in pszGeoData_
206 97 : if (!EQUAL(pszUnprefixed, poOpenInfo->pszFilename))
207 : {
208 0 : GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
209 0 : if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
210 0 : return false;
211 0 : pszGeoData_ =
212 0 : CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
213 0 : fp.reset(oOpenInfo.fpL);
214 0 : oOpenInfo.fpL = nullptr;
215 : }
216 97 : else if (poOpenInfo->fpL == nullptr)
217 0 : return false;
218 : else
219 : {
220 97 : fp.reset(poOpenInfo->fpL);
221 97 : poOpenInfo->fpL = nullptr;
222 97 : pszGeoData_ = CPLStrdup(
223 97 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader));
224 : }
225 : }
226 : else
227 : {
228 0 : return false;
229 : }
230 :
231 142 : if (osDefaultLayerName.empty())
232 45 : osDefaultLayerName = "features";
233 :
234 147 : const auto SetReaderOptions = [poOpenInfo](OGRJSONFGReader &oReader)
235 : {
236 294 : const char *pszGeometryElement = CSLFetchNameValueDef(
237 147 : poOpenInfo->papszOpenOptions, "GEOMETRY_ELEMENT", "AUTO");
238 147 : if (EQUAL(pszGeometryElement, "PLACE"))
239 3 : oReader.SetGeometryElement(OGRJSONFGReader::GeometryElement::PLACE);
240 144 : else if (EQUAL(pszGeometryElement, "GEOMETRY"))
241 3 : oReader.SetGeometryElement(
242 : OGRJSONFGReader::GeometryElement::GEOMETRY);
243 289 : };
244 :
245 142 : if (nSrcType == eGeoJSONSourceFile)
246 : {
247 97 : auto poReader = std::make_unique<OGRJSONFGReader>();
248 97 : SetReaderOptions(*(poReader.get()));
249 :
250 : // Try to use a streaming parser if the content of the file seems
251 : // to be FeatureCollection
252 97 : bool bUseStreamingInterface = false;
253 97 : const char *pszStr = strstr(pszGeoData_, "\"features\"");
254 97 : if (pszStr)
255 : {
256 92 : pszStr += strlen("\"features\"");
257 155 : while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
258 63 : pszStr++;
259 92 : if (*pszStr == ':')
260 : {
261 92 : pszStr++;
262 184 : while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
263 92 : pszStr++;
264 92 : if (*pszStr == '[')
265 : {
266 92 : bUseStreamingInterface = true;
267 : }
268 : }
269 : }
270 97 : if (bUseStreamingInterface)
271 : {
272 92 : bool bCanTryWithNonStreamingParserOut = true;
273 92 : if (poReader->AnalyzeWithStreamingParser(
274 : this, fp.get(), osDefaultLayerName,
275 : bCanTryWithNonStreamingParserOut))
276 : {
277 92 : if (!apoLayers_.empty())
278 : {
279 76 : auto poLayer = cpl::down_cast<OGRJSONFGStreamedLayer *>(
280 76 : apoLayers_[0].get());
281 76 : poLayer->SetFile(std::move(fp));
282 : auto poParser = std::make_unique<OGRJSONFGStreamingParser>(
283 152 : *(poReader.get()), false);
284 76 : poLayer->SetStreamingParser(std::move(poParser));
285 : }
286 :
287 112 : for (size_t i = 1; i < apoLayers_.size(); ++i)
288 : {
289 20 : auto poLayer = cpl::down_cast<OGRJSONFGStreamedLayer *>(
290 20 : apoLayers_[i].get());
291 :
292 : auto fpNew = VSIVirtualHandleUniquePtr(
293 20 : VSIFOpenL(pszUnprefixed, "rb"));
294 20 : if (!fpNew)
295 : {
296 0 : CPLError(CE_Failure, CPLE_FileIO,
297 : "Cannot open %s again", pszUnprefixed);
298 0 : return false;
299 : }
300 20 : poLayer->SetFile(std::move(fpNew));
301 :
302 : auto poParser = std::make_unique<OGRJSONFGStreamingParser>(
303 40 : *(poReader.get()), false);
304 20 : poLayer->SetStreamingParser(std::move(poParser));
305 : }
306 92 : poReader_ = std::move(poReader);
307 92 : return true;
308 : }
309 0 : if (!bCanTryWithNonStreamingParserOut)
310 0 : return false;
311 : }
312 :
313 : // Fallback to in-memory ingestion
314 5 : CPLAssert(poOpenInfo->fpL == nullptr);
315 5 : poOpenInfo->fpL = fp.release();
316 5 : if (!ReadFromFile(poOpenInfo, pszUnprefixed))
317 0 : return false;
318 : }
319 :
320 : // In-memory ingestion of the file
321 50 : OGRJSONFGReader oReader;
322 50 : SetReaderOptions(oReader);
323 50 : const bool bRet = oReader.Load(this, pszGeoData_, osDefaultLayerName);
324 50 : CPLFree(pszGeoData_);
325 50 : pszGeoData_ = nullptr;
326 50 : return bRet;
327 : }
328 :
329 : /************************************************************************/
330 : /* OGRJSONFGDataset::GetLayer() */
331 : /************************************************************************/
332 :
333 138 : OGRLayer *OGRJSONFGDataset::GetLayer(int i)
334 : {
335 138 : if (i < 0 || i >= static_cast<int>(apoLayers_.size()))
336 2 : return nullptr;
337 136 : return apoLayers_[i].get();
338 : }
339 :
340 : /************************************************************************/
341 : /* OGRJSONFGDataset::AddLayer() */
342 : /************************************************************************/
343 :
344 : OGRJSONFGMemLayer *
345 50 : OGRJSONFGDataset::AddLayer(std::unique_ptr<OGRJSONFGMemLayer> &&poLayer)
346 : {
347 50 : apoLayers_.emplace_back(std::move(poLayer));
348 50 : return static_cast<OGRJSONFGMemLayer *>(apoLayers_.back().get());
349 : }
350 :
351 : /************************************************************************/
352 : /* OGRJSONFGDataset::AddLayer() */
353 : /************************************************************************/
354 :
355 : OGRJSONFGStreamedLayer *
356 96 : OGRJSONFGDataset::AddLayer(std::unique_ptr<OGRJSONFGStreamedLayer> &&poLayer)
357 : {
358 96 : apoLayers_.emplace_back(std::move(poLayer));
359 96 : return static_cast<OGRJSONFGStreamedLayer *>(apoLayers_.back().get());
360 : }
361 :
362 : /************************************************************************/
363 : /* ReadFromFile() */
364 : /************************************************************************/
365 :
366 5 : bool OGRJSONFGDataset::ReadFromFile(GDALOpenInfo *poOpenInfo,
367 : const char *pszUnprefixed)
368 : {
369 5 : GByte *pabyOut = nullptr;
370 5 : if (!EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
371 : {
372 0 : GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
373 0 : if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
374 0 : return false;
375 0 : VSIFSeekL(oOpenInfo.fpL, 0, SEEK_SET);
376 0 : if (!VSIIngestFile(oOpenInfo.fpL, pszUnprefixed, &pabyOut, nullptr, -1))
377 : {
378 0 : return false;
379 : }
380 : }
381 : else
382 : {
383 5 : if (poOpenInfo->fpL == nullptr)
384 0 : return false;
385 5 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
386 5 : if (!VSIIngestFile(poOpenInfo->fpL, poOpenInfo->pszFilename, &pabyOut,
387 : nullptr, -1))
388 : {
389 0 : return false;
390 : }
391 :
392 5 : VSIFCloseL(poOpenInfo->fpL);
393 5 : poOpenInfo->fpL = nullptr;
394 : }
395 :
396 5 : CPLFree(pszGeoData_);
397 5 : pszGeoData_ = reinterpret_cast<char *>(pabyOut);
398 :
399 5 : CPLAssert(nullptr != pszGeoData_);
400 :
401 5 : return true;
402 : }
403 :
404 : /************************************************************************/
405 : /* ReadFromService() */
406 : /************************************************************************/
407 :
408 9 : bool OGRJSONFGDataset::ReadFromService(GDALOpenInfo *poOpenInfo,
409 : const char *pszSource)
410 : {
411 9 : CPLAssert(nullptr == pszGeoData_);
412 9 : CPLAssert(nullptr != pszSource);
413 :
414 9 : CPLErrorReset();
415 :
416 : /* -------------------------------------------------------------------- */
417 : /* Look if we already cached the content. */
418 : /* -------------------------------------------------------------------- */
419 9 : char *pszStoredContent = OGRGeoJSONDriverStealStoredContent(pszSource);
420 9 : if (pszStoredContent != nullptr)
421 : {
422 9 : if (JSONFGIsObject(pszStoredContent, poOpenInfo))
423 : {
424 0 : pszGeoData_ = pszStoredContent;
425 0 : nGeoDataLen_ = strlen(pszGeoData_);
426 :
427 0 : SetDescription(pszSource);
428 0 : return true;
429 : }
430 :
431 9 : OGRGeoJSONDriverStoreContent(pszSource, pszStoredContent);
432 9 : return false;
433 : }
434 :
435 : /* -------------------------------------------------------------------- */
436 : /* Fetch the result. */
437 : /* -------------------------------------------------------------------- */
438 0 : char *papsOptions[] = {
439 : const_cast<char *>("HEADERS=Accept: text/plain, application/json"),
440 : nullptr};
441 :
442 0 : CPLHTTPResult *pResult = CPLHTTPFetch(pszSource, papsOptions);
443 :
444 : /* -------------------------------------------------------------------- */
445 : /* Try to handle CURL/HTTP errors. */
446 : /* -------------------------------------------------------------------- */
447 0 : if (nullptr == pResult || 0 == pResult->nDataLen ||
448 0 : 0 != CPLGetLastErrorNo())
449 : {
450 0 : CPLHTTPDestroyResult(pResult);
451 0 : return false;
452 : }
453 :
454 0 : if (0 != pResult->nStatus)
455 : {
456 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
457 : pResult->nStatus, pResult->pszErrBuf);
458 0 : CPLHTTPDestroyResult(pResult);
459 0 : return false;
460 : }
461 :
462 : /* -------------------------------------------------------------------- */
463 : /* Copy returned GeoJSON data to text buffer. */
464 : /* -------------------------------------------------------------------- */
465 0 : char *pszData = reinterpret_cast<char *>(pResult->pabyData);
466 :
467 : // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
468 0 : pszGeoData_ = pszData;
469 0 : nGeoDataLen_ = pResult->nDataLen;
470 0 : pResult->pabyData = nullptr;
471 0 : pResult->nDataLen = 0;
472 :
473 0 : SetDescription(pszSource);
474 :
475 : /* -------------------------------------------------------------------- */
476 : /* Cleanup HTTP resources. */
477 : /* -------------------------------------------------------------------- */
478 0 : CPLHTTPDestroyResult(pResult);
479 :
480 0 : CPLAssert(nullptr != pszGeoData_);
481 :
482 : /* -------------------------------------------------------------------- */
483 : /* Cache the content if it is not handled by this driver, but */
484 : /* another related one. */
485 : /* -------------------------------------------------------------------- */
486 0 : if (EQUAL(pszSource, poOpenInfo->pszFilename))
487 : {
488 0 : if (!JSONFGIsObject(pszGeoData_, poOpenInfo))
489 : {
490 0 : OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
491 0 : pszGeoData_ = nullptr;
492 0 : nGeoDataLen_ = 0;
493 0 : return false;
494 : }
495 : }
496 :
497 0 : return true;
498 : }
499 :
500 : /************************************************************************/
501 : /* Create() */
502 : /************************************************************************/
503 :
504 73 : bool OGRJSONFGDataset::Create(const char *pszName, CSLConstList papszOptions)
505 : {
506 73 : CPLAssert(nullptr == fpOut_);
507 73 : bSingleOutputLayer_ =
508 73 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SINGLE_LAYER", "NO"));
509 :
510 146 : bFpOutputIsSeekable_ = !(strcmp(pszName, "/vsistdout/") == 0 ||
511 73 : STARTS_WITH(pszName, "/vsigzip/") ||
512 73 : STARTS_WITH(pszName, "/vsizip/"));
513 :
514 73 : if (strcmp(pszName, "/dev/stdout") == 0)
515 0 : pszName = "/vsistdout/";
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* File overwrite not supported. */
519 : /* -------------------------------------------------------------------- */
520 : VSIStatBufL sStatBuf;
521 73 : if (0 == VSIStatL(pszName, &sStatBuf))
522 : {
523 0 : CPLError(CE_Failure, CPLE_NotSupported,
524 : "The JSONFG driver does not overwrite existing files.");
525 0 : return false;
526 : }
527 :
528 : /* -------------------------------------------------------------------- */
529 : /* Create the output file. */
530 : /* -------------------------------------------------------------------- */
531 73 : fpOut_ = VSIFOpenExL(pszName, "w", true);
532 73 : if (nullptr == fpOut_)
533 : {
534 1 : CPLError(CE_Failure, CPLE_OpenFailed,
535 : "Failed to create JSONFG dataset: %s: %s", pszName,
536 : VSIGetLastErrorMsg());
537 1 : return false;
538 : }
539 :
540 72 : SetDescription(pszName);
541 :
542 72 : VSIFPrintfL(fpOut_, "{\n\"type\": \"FeatureCollection\",\n");
543 72 : VSIFPrintfL(fpOut_, "\"conformsTo\" : [\"[ogc-json-fg-1-0.1:core]\"],\n");
544 :
545 72 : return true;
546 : }
547 :
548 : /************************************************************************/
549 : /* EmitStartFeaturesIfNeeded() */
550 : /************************************************************************/
551 :
552 213 : bool OGRJSONFGDataset::EmitStartFeaturesIfNeededAndReturnIfFirstFeature()
553 : {
554 213 : if (!bHasEmittedFeatures_)
555 : {
556 72 : bHasEmittedFeatures_ = true;
557 72 : VSIFPrintfL(fpOut_, "\"features\" : [\n");
558 72 : return true;
559 : }
560 141 : return false;
561 : }
562 :
563 : /************************************************************************/
564 : /* ICreateLayer() */
565 : /************************************************************************/
566 :
567 : OGRLayer *
568 90 : OGRJSONFGDataset::ICreateLayer(const char *pszNameIn,
569 : const OGRGeomFieldDefn *poSrcGeomFieldDefn,
570 : CSLConstList papszOptions)
571 : {
572 90 : if (nullptr == fpOut_)
573 : {
574 0 : CPLError(CE_Failure, CPLE_NotSupported,
575 : "JSONFG driver doesn't support creating a layer "
576 : "on a read-only datasource");
577 0 : return nullptr;
578 : }
579 :
580 90 : if (bSingleOutputLayer_ && !apoLayers_.empty())
581 : {
582 0 : CPLError(CE_Failure, CPLE_AppDefined,
583 : "Only one layer can be created since SINGLE_LAYER=YES "
584 : "creation option has been used");
585 0 : return nullptr;
586 : }
587 :
588 : const auto eGType =
589 90 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
590 : const auto poSRS =
591 90 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
592 :
593 180 : std::string osCoordRefSys;
594 90 : std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84;
595 90 : if (poSRS)
596 : {
597 : const auto GetCURIE =
598 37 : [](const char *pszAuthName, const char *pszAuthCode)
599 : {
600 37 : std::string osRet = "[";
601 37 : if (STARTS_WITH(pszAuthName, "IAU_"))
602 0 : osRet += "IAU";
603 : else
604 37 : osRet += pszAuthName;
605 37 : osRet += ':';
606 37 : osRet += pszAuthCode;
607 37 : osRet += ']';
608 37 : return osRet;
609 : };
610 :
611 37 : const auto GetCoordRefSys = [GetCURIE](const char *pszAuthName,
612 : const char *pszAuthCode,
613 : double dfCoordEpoch = 0)
614 : {
615 37 : if (dfCoordEpoch > 0)
616 : {
617 2 : json_object *poObj = json_object_new_object();
618 2 : json_object_object_add(poObj, "type",
619 : json_object_new_string("Reference"));
620 2 : json_object_object_add(
621 : poObj, "href",
622 : json_object_new_string(
623 4 : GetCURIE(pszAuthName, pszAuthCode).c_str()));
624 2 : json_object_object_add(poObj, "epoch",
625 : json_object_new_double(dfCoordEpoch));
626 2 : return poObj;
627 : }
628 : else
629 : {
630 35 : return json_object_new_string(
631 70 : GetCURIE(pszAuthName, pszAuthCode).c_str());
632 : }
633 : };
634 :
635 33 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
636 33 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
637 33 : const double dfCoordEpoch = poSRS->GetCoordinateEpoch();
638 33 : json_object *poObj = nullptr;
639 33 : if (pszAuthName && pszAuthCode)
640 : {
641 29 : poObj = GetCoordRefSys(pszAuthName, pszAuthCode, dfCoordEpoch);
642 : }
643 4 : else if (poSRS->IsCompound())
644 : {
645 4 : const char *pszAuthNameHoriz = poSRS->GetAuthorityName("HORIZCRS");
646 4 : const char *pszAuthCodeHoriz = poSRS->GetAuthorityCode("HORIZCRS");
647 4 : const char *pszAuthNameVert = poSRS->GetAuthorityName("VERTCRS");
648 4 : const char *pszAuthCodeVert = poSRS->GetAuthorityCode("VERTCRS");
649 4 : if (pszAuthNameHoriz && pszAuthCodeHoriz && pszAuthNameVert &&
650 : pszAuthCodeVert)
651 : {
652 4 : poObj = json_object_new_array();
653 4 : json_object_array_add(poObj, GetCoordRefSys(pszAuthNameHoriz,
654 : pszAuthCodeHoriz,
655 : dfCoordEpoch));
656 4 : json_object_array_add(
657 : poObj, GetCoordRefSys(pszAuthNameVert, pszAuthCodeVert));
658 : }
659 : }
660 :
661 33 : if (poObj)
662 : {
663 : osCoordRefSys =
664 33 : json_object_to_json_string_ext(poObj, JSON_C_TO_STRING_SPACED);
665 33 : json_object_put(poObj);
666 : }
667 : else
668 : {
669 0 : CPLError(CE_Failure, CPLE_NotSupported,
670 : "Input CRS %s cannot be expressed as a reference (ie "
671 : "well-known CRS by code). "
672 : "Retry be reprojecting to a known CRS first",
673 : poSRS->GetName());
674 0 : return nullptr;
675 : }
676 :
677 33 : if (!strstr(osCoordRefSys.c_str(), "[IAU:"))
678 : {
679 66 : OGRSpatialReference oSRSWGS84;
680 33 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
681 33 : oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
682 33 : poCTToWGS84.reset(
683 : OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84));
684 : }
685 : }
686 57 : else if (eGType != wkbNone)
687 : {
688 45 : if (OGR_GT_HasZ(eGType))
689 21 : osCoordRefSys = "[OGC:CRS84h]";
690 : else
691 24 : osCoordRefSys = "[OGC:CRS84]";
692 45 : CPLError(CE_Warning, CPLE_AppDefined,
693 : "No SRS set on layer. Assuming it is long/lat on WGS84 "
694 : "ellipsoid");
695 : }
696 :
697 180 : CPLStringList aosOptions(papszOptions);
698 :
699 90 : if (const char *pszCoordPrecisionGeom =
700 90 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_GEOMETRY"))
701 : {
702 : double dfXYResolutionGeometry =
703 1 : std::pow(10.0, -CPLAtof(pszCoordPrecisionGeom));
704 1 : double dfZResolutionGeometry = dfXYResolutionGeometry;
705 : aosOptions.SetNameValue("XY_COORD_PRECISION_GEOMETRY",
706 1 : pszCoordPrecisionGeom);
707 : aosOptions.SetNameValue("Z_COORD_PRECISION_GEOMETRY",
708 1 : pszCoordPrecisionGeom);
709 1 : if (IsSingleOutputLayer())
710 : {
711 1 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
712 : dfXYResolutionGeometry);
713 1 : if (poSRS && poSRS->GetAxesCount() == 3)
714 : {
715 0 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
716 : dfZResolutionGeometry);
717 : }
718 : }
719 : }
720 166 : else if (poSrcGeomFieldDefn &&
721 77 : poSrcGeomFieldDefn->GetCoordinatePrecision().dfXYResolution ==
722 166 : OGRGeomCoordinatePrecision::UNKNOWN &&
723 75 : CSLFetchNameValue(papszOptions, "SIGNIFICANT_FIGURES") == nullptr)
724 : {
725 75 : const int nXYPrecisionGeometry = 7;
726 75 : const int nZPrecisionGeometry = 3;
727 : aosOptions.SetNameValue("XY_COORD_PRECISION_GEOMETRY",
728 75 : CPLSPrintf("%d", nXYPrecisionGeometry));
729 : aosOptions.SetNameValue("Z_COORD_PRECISION_GEOMETRY",
730 75 : CPLSPrintf("%d", nZPrecisionGeometry));
731 : }
732 :
733 90 : double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
734 90 : double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
735 :
736 90 : if (const char *pszCoordPrecisionPlace =
737 90 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_PLACE"))
738 : {
739 1 : dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecisionPlace));
740 1 : dfZResolution = dfXYResolution;
741 1 : if (IsSingleOutputLayer())
742 : {
743 1 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution_place\": %g,\n",
744 : dfXYResolution);
745 1 : if (poSRS && poSRS->GetAxesCount() == 3)
746 : {
747 0 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution_place\": %g,\n",
748 : dfZResolution);
749 : }
750 : }
751 : }
752 166 : else if (poSrcGeomFieldDefn &&
753 77 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_PLACE") ==
754 166 : nullptr &&
755 77 : CSLFetchNameValue(papszOptions, "SIGNIFICANT_FIGURES") == nullptr)
756 : {
757 77 : const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
758 154 : OGRSpatialReference oSRSWGS84;
759 77 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
760 : const auto oCoordPrecWGS84 =
761 154 : oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
762 :
763 77 : if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
764 : {
765 2 : dfXYResolution = oCoordPrec.dfXYResolution;
766 : aosOptions.SetNameValue(
767 : "XY_COORD_PRECISION_PLACE",
768 : CPLSPrintf("%d",
769 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
770 2 : oCoordPrec.dfXYResolution)));
771 2 : if (IsSingleOutputLayer())
772 : {
773 1 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution_place\": %g,\n",
774 1 : oCoordPrec.dfXYResolution);
775 : }
776 :
777 2 : if (CSLFetchNameValue(papszOptions,
778 2 : "COORDINATE_PRECISION_GEOMETRY") == nullptr)
779 : {
780 2 : const double dfXYResolutionGeometry =
781 : oCoordPrecWGS84.dfXYResolution;
782 :
783 : aosOptions.SetNameValue(
784 : "XY_COORD_PRECISION_GEOMETRY",
785 : CPLSPrintf(
786 : "%d", OGRGeomCoordinatePrecision::ResolutionToPrecision(
787 2 : dfXYResolutionGeometry)));
788 2 : if (IsSingleOutputLayer())
789 : {
790 1 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
791 : dfXYResolutionGeometry);
792 : }
793 : }
794 : }
795 :
796 77 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
797 : {
798 2 : dfZResolution = oCoordPrec.dfZResolution;
799 : aosOptions.SetNameValue(
800 : "Z_COORD_PRECISION_PLACE",
801 : CPLSPrintf("%d",
802 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
803 2 : dfZResolution)));
804 2 : if (IsSingleOutputLayer())
805 : {
806 1 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution_place\": %g,\n",
807 : dfZResolution);
808 : }
809 :
810 2 : if (CSLFetchNameValue(papszOptions,
811 2 : "COORDINATE_PRECISION_GEOMETRY") == nullptr)
812 : {
813 2 : const double dfZResolutionGeometry =
814 : oCoordPrecWGS84.dfZResolution;
815 :
816 : aosOptions.SetNameValue(
817 : "Z_COORD_PRECISION_GEOMETRY",
818 : CPLSPrintf(
819 : "%d", OGRGeomCoordinatePrecision::ResolutionToPrecision(
820 2 : dfZResolutionGeometry)));
821 2 : if (IsSingleOutputLayer())
822 : {
823 1 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
824 : dfZResolutionGeometry);
825 : }
826 : }
827 : }
828 : }
829 :
830 : auto poLayer = std::make_unique<OGRJSONFGWriteLayer>(
831 90 : pszNameIn, poSRS, std::move(poCTToWGS84), osCoordRefSys, eGType,
832 90 : aosOptions.List(), this);
833 90 : apoLayers_.emplace_back(std::move(poLayer));
834 :
835 90 : auto poLayerAdded = apoLayers_.back().get();
836 90 : if (eGType != wkbNone &&
837 : dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
838 : {
839 : auto poGeomFieldDefn =
840 3 : poLayerAdded->GetLayerDefn()->GetGeomFieldDefn(0);
841 : OGRGeomCoordinatePrecision oCoordPrec(
842 6 : poGeomFieldDefn->GetCoordinatePrecision());
843 3 : oCoordPrec.dfXYResolution = dfXYResolution;
844 3 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
845 : }
846 :
847 90 : if (eGType != wkbNone &&
848 : dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
849 : {
850 : auto poGeomFieldDefn =
851 3 : poLayerAdded->GetLayerDefn()->GetGeomFieldDefn(0);
852 : OGRGeomCoordinatePrecision oCoordPrec(
853 6 : poGeomFieldDefn->GetCoordinatePrecision());
854 3 : oCoordPrec.dfZResolution = dfZResolution;
855 3 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
856 : }
857 :
858 90 : return poLayerAdded;
859 : }
860 :
861 : /************************************************************************/
862 : /* TestCapability() */
863 : /************************************************************************/
864 :
865 58 : int OGRJSONFGDataset::TestCapability(const char *pszCap)
866 : {
867 58 : if (EQUAL(pszCap, ODsCCreateLayer))
868 67 : return fpOut_ != nullptr &&
869 67 : (!bSingleOutputLayer_ || apoLayers_.empty());
870 24 : else if (EQUAL(pszCap, ODsCZGeometries))
871 2 : return TRUE;
872 :
873 22 : return FALSE;
874 : }
875 :
876 : /************************************************************************/
877 : /* OGRJSONFGMustSwapXY() */
878 : /************************************************************************/
879 :
880 113 : bool OGRJSONFGMustSwapXY(const OGRSpatialReference *poSRS)
881 : {
882 321 : return poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{2, 1} ||
883 321 : poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{2, 1, 3};
884 : }
|