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