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