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