Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of OGRGeoJSONDataSource class (OGR GeoJSON Driver).
5 : * Author: Mateusz Loskot, mateusz@loskot.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Mateusz Loskot
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_geojson.h"
16 :
17 : #include <cmath>
18 : #include <cstddef>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <string>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_http.h"
26 : #include "cpl_multiproc.h" // CPLSleep()
27 : #include "cpl_string.h"
28 : #include "cpl_vsi.h"
29 : #include "cpl_vsi_error.h"
30 : #include "json.h"
31 : // #include "json_object.h"
32 : #include "gdal_utils.h"
33 : #include "gdal.h"
34 : #include "gdal_priv.h"
35 : #include "ogr_core.h"
36 : #include "ogr_feature.h"
37 : #include "ogr_geometry.h"
38 : #include "ogr_spatialref.h"
39 : #include "ogrlibjsonutils.h"
40 : #include "ogrgeojsonreader.h"
41 : #include "ogrgeojsonutils.h"
42 : #include "ogrgeojsonwriter.h"
43 : #include "ogrsf_frmts.h"
44 : #include "ogr_schema_override.h"
45 :
46 : // #include "symbol_renames.h"
47 :
48 : /************************************************************************/
49 : /* OGRGeoJSONDataSource() */
50 : /************************************************************************/
51 :
52 724 : OGRGeoJSONDataSource::OGRGeoJSONDataSource()
53 : : pszName_(nullptr), pszGeoData_(nullptr), nGeoDataLen_(0),
54 : papoLayers_(nullptr), papoLayersWriter_(nullptr), nLayers_(0),
55 : fpOut_(nullptr), flTransGeom_(OGRGeoJSONDataSource::eGeometryPreserve),
56 : flTransAttrs_(OGRGeoJSONDataSource::eAttributesPreserve),
57 : bOtherPages_(false), bFpOutputIsSeekable_(false), nBBOXInsertLocation_(0),
58 724 : bUpdatable_(false)
59 : {
60 724 : }
61 :
62 : /************************************************************************/
63 : /* ~OGRGeoJSONDataSource() */
64 : /************************************************************************/
65 :
66 1448 : OGRGeoJSONDataSource::~OGRGeoJSONDataSource()
67 : {
68 724 : OGRGeoJSONDataSource::Close();
69 1448 : }
70 :
71 : /************************************************************************/
72 : /* Close() */
73 : /************************************************************************/
74 :
75 1261 : CPLErr OGRGeoJSONDataSource::Close()
76 : {
77 1261 : CPLErr eErr = CE_None;
78 1261 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
79 : {
80 724 : if (OGRGeoJSONDataSource::FlushCache(true) != CE_None)
81 0 : eErr = CE_Failure;
82 :
83 724 : if (!OGRGeoJSONDataSource::Clear())
84 0 : eErr = CE_Failure;
85 :
86 724 : if (GDALDataset::Close() != CE_None)
87 0 : eErr = CE_Failure;
88 : }
89 1261 : return eErr;
90 : }
91 :
92 : /************************************************************************/
93 : /* DealWithOgrSchemaOpenOption() */
94 : /************************************************************************/
95 :
96 537 : bool OGRGeoJSONDataSource::DealWithOgrSchemaOpenOption(
97 : const GDALOpenInfo *poOpenInfo)
98 : {
99 : const std::string osFieldsSchemaOverrideParam =
100 1074 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "OGR_SCHEMA", "");
101 :
102 537 : if (!osFieldsSchemaOverrideParam.empty())
103 : {
104 :
105 8 : if (poOpenInfo->eAccess == GA_Update)
106 : {
107 0 : CPLError(CE_Failure, CPLE_NotSupported,
108 : "OGR_SCHEMA open option is not supported in update mode.");
109 3 : return false;
110 : }
111 :
112 8 : OGRSchemaOverride oSchemaOverride;
113 8 : const auto nErrorCount = CPLGetErrorCounter();
114 15 : if (!oSchemaOverride.LoadFromJSON(osFieldsSchemaOverrideParam) ||
115 7 : !oSchemaOverride.IsValid())
116 : {
117 1 : if (nErrorCount == CPLGetErrorCounter())
118 : {
119 0 : CPLError(CE_Failure, CPLE_AppDefined,
120 : "Content of OGR_SCHEMA in %s is not valid",
121 : osFieldsSchemaOverrideParam.c_str());
122 : }
123 1 : return false;
124 : }
125 :
126 7 : if (!oSchemaOverride.DefaultApply(this, "GeoJSON"))
127 2 : return false;
128 : }
129 534 : return true;
130 : }
131 :
132 : /************************************************************************/
133 : /* Open() */
134 : /************************************************************************/
135 :
136 564 : int OGRGeoJSONDataSource::Open(GDALOpenInfo *poOpenInfo,
137 : GeoJSONSourceType nSrcType,
138 : const char *pszJSonFlavor)
139 : {
140 564 : osJSonFlavor_ = pszJSonFlavor;
141 :
142 564 : const char *pszUnprefixed = poOpenInfo->pszFilename;
143 564 : if (STARTS_WITH_CI(pszUnprefixed, pszJSonFlavor) &&
144 1 : pszUnprefixed[strlen(pszJSonFlavor)] == ':')
145 : {
146 1 : pszUnprefixed += strlen(pszJSonFlavor) + 1;
147 : }
148 :
149 564 : if (eGeoJSONSourceService == nSrcType)
150 : {
151 23 : if (!ReadFromService(poOpenInfo, pszUnprefixed))
152 19 : return FALSE;
153 4 : if (poOpenInfo->eAccess == GA_Update)
154 : {
155 0 : CPLError(CE_Failure, CPLE_NotSupported,
156 : "Update from remote service not supported");
157 0 : return FALSE;
158 : }
159 : }
160 541 : else if (eGeoJSONSourceText == nSrcType)
161 : {
162 183 : if (poOpenInfo->eAccess == GA_Update)
163 : {
164 1 : CPLError(CE_Failure, CPLE_NotSupported,
165 : "Update from inline definition not supported");
166 1 : return FALSE;
167 : }
168 182 : pszGeoData_ = CPLStrdup(pszUnprefixed);
169 : }
170 358 : else if (eGeoJSONSourceFile == nSrcType)
171 : {
172 357 : if (poOpenInfo->eAccess == GA_Update &&
173 22 : !EQUAL(pszJSonFlavor, "GeoJSON"))
174 : {
175 0 : CPLError(CE_Failure, CPLE_NotSupported,
176 : "Update of %s not supported", pszJSonFlavor);
177 0 : return FALSE;
178 : }
179 357 : pszName_ = CPLStrdup(pszUnprefixed);
180 357 : bUpdatable_ = (poOpenInfo->eAccess == GA_Update);
181 :
182 357 : if (!EQUAL(pszUnprefixed, poOpenInfo->pszFilename))
183 : {
184 1 : GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
185 1 : if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
186 0 : return FALSE;
187 1 : pszGeoData_ =
188 1 : CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
189 : }
190 356 : else if (poOpenInfo->fpL == nullptr)
191 6 : return FALSE;
192 : else
193 : {
194 350 : pszGeoData_ = CPLStrdup(
195 350 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader));
196 : }
197 : }
198 : else
199 : {
200 1 : Clear();
201 1 : return FALSE;
202 : }
203 :
204 : /* -------------------------------------------------------------------- */
205 : /* Construct OGR layer and feature objects from */
206 : /* GeoJSON text tree. */
207 : /* -------------------------------------------------------------------- */
208 537 : if (nullptr == pszGeoData_ ||
209 537 : STARTS_WITH(pszGeoData_, "{\"couchdb\":\"Welcome\"") ||
210 537 : STARTS_WITH(pszGeoData_, "{\"db_name\":\"") ||
211 537 : STARTS_WITH(pszGeoData_, "{\"total_rows\":") ||
212 537 : STARTS_WITH(pszGeoData_, "{\"rows\":["))
213 : {
214 0 : Clear();
215 0 : return FALSE;
216 : }
217 :
218 537 : SetDescription(poOpenInfo->pszFilename);
219 537 : LoadLayers(poOpenInfo, nSrcType, pszUnprefixed, pszJSonFlavor);
220 :
221 537 : if (!DealWithOgrSchemaOpenOption(poOpenInfo))
222 : {
223 3 : Clear();
224 3 : return FALSE;
225 : }
226 :
227 534 : if (nLayers_ == 0)
228 : {
229 33 : bool bEmitError = true;
230 33 : if (eGeoJSONSourceService == nSrcType)
231 : {
232 : const CPLString osTmpFilename =
233 : VSIMemGenerateHiddenFilename(CPLSPrintf(
234 0 : "geojson_%s", CPLGetFilename(poOpenInfo->pszFilename)));
235 0 : VSIFCloseL(VSIFileFromMemBuffer(osTmpFilename, (GByte *)pszGeoData_,
236 : nGeoDataLen_, TRUE));
237 0 : pszGeoData_ = nullptr;
238 0 : if (GDALIdentifyDriver(osTmpFilename, nullptr))
239 0 : bEmitError = false;
240 0 : VSIUnlink(osTmpFilename);
241 : }
242 33 : Clear();
243 :
244 33 : if (bEmitError)
245 : {
246 33 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to read %s data",
247 : pszJSonFlavor);
248 : }
249 33 : return FALSE;
250 : }
251 :
252 501 : return TRUE;
253 : }
254 :
255 : /************************************************************************/
256 : /* GetLayerCount() */
257 : /************************************************************************/
258 :
259 528 : int OGRGeoJSONDataSource::GetLayerCount() const
260 : {
261 528 : return nLayers_;
262 : }
263 :
264 : /************************************************************************/
265 : /* GetLayer() */
266 : /************************************************************************/
267 :
268 596 : const OGRLayer *OGRGeoJSONDataSource::GetLayer(int nLayer) const
269 : {
270 596 : if (0 <= nLayer && nLayer < nLayers_)
271 : {
272 592 : if (papoLayers_)
273 591 : return papoLayers_[nLayer];
274 : else
275 1 : return papoLayersWriter_[nLayer];
276 : }
277 :
278 4 : return nullptr;
279 : }
280 :
281 : /************************************************************************/
282 : /* ICreateLayer() */
283 : /************************************************************************/
284 :
285 : OGRLayer *
286 180 : OGRGeoJSONDataSource::ICreateLayer(const char *pszNameIn,
287 : const OGRGeomFieldDefn *poSrcGeomFieldDefn,
288 : CSLConstList papszOptions)
289 : {
290 180 : if (nullptr == fpOut_)
291 : {
292 0 : CPLError(CE_Failure, CPLE_NotSupported,
293 : "GeoJSON driver doesn't support creating a layer "
294 : "on a read-only datasource");
295 0 : return nullptr;
296 : }
297 :
298 180 : if (nLayers_ != 0)
299 : {
300 16 : CPLError(CE_Failure, CPLE_NotSupported,
301 : "GeoJSON driver doesn't support creating more than one layer");
302 16 : return nullptr;
303 : }
304 :
305 : const auto eGType =
306 164 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
307 : const auto poSRS =
308 164 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
309 :
310 : const char *pszForeignMembersCollection =
311 164 : CSLFetchNameValue(papszOptions, "FOREIGN_MEMBERS_COLLECTION");
312 164 : if (pszForeignMembersCollection)
313 : {
314 4 : if (pszForeignMembersCollection[0] != '{' ||
315 3 : pszForeignMembersCollection[strlen(pszForeignMembersCollection) -
316 3 : 1] != '}')
317 : {
318 2 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "Value of FOREIGN_MEMBERS_COLLECTION should start with { "
320 : "and end with }");
321 3 : return nullptr;
322 : }
323 2 : json_object *poTmp = nullptr;
324 2 : if (!OGRJSonParse(pszForeignMembersCollection, &poTmp, false))
325 : {
326 1 : pszForeignMembersCollection = nullptr;
327 : }
328 2 : json_object_put(poTmp);
329 2 : if (!pszForeignMembersCollection)
330 : {
331 1 : CPLError(CE_Failure, CPLE_AppDefined,
332 : "Value of FOREIGN_MEMBERS_COLLECTION is invalid JSON");
333 1 : return nullptr;
334 : }
335 : }
336 :
337 : std::string osForeignMembersFeature =
338 322 : CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "");
339 161 : if (!osForeignMembersFeature.empty())
340 : {
341 7 : if (osForeignMembersFeature.front() != '{' ||
342 3 : osForeignMembersFeature.back() != '}')
343 : {
344 2 : CPLError(CE_Failure, CPLE_AppDefined,
345 : "Value of FOREIGN_MEMBERS_FEATURE should start with { and "
346 : "end with }");
347 3 : return nullptr;
348 : }
349 2 : json_object *poTmp = nullptr;
350 2 : if (!OGRJSonParse(osForeignMembersFeature.c_str(), &poTmp, false))
351 : {
352 1 : osForeignMembersFeature.clear();
353 : }
354 2 : json_object_put(poTmp);
355 2 : if (osForeignMembersFeature.empty())
356 : {
357 1 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "Value of FOREIGN_MEMBERS_FEATURE is invalid JSON");
359 1 : return nullptr;
360 : }
361 : }
362 :
363 158 : VSIFPrintfL(fpOut_, "{\n\"type\": \"FeatureCollection\",\n");
364 :
365 158 : if (pszForeignMembersCollection)
366 : {
367 1 : VSIFWriteL(pszForeignMembersCollection + 1, 1,
368 1 : strlen(pszForeignMembersCollection) - 2, fpOut_);
369 1 : VSIFWriteL(",\n", 2, 1, fpOut_);
370 : }
371 :
372 : bool bWriteFC_BBOX =
373 158 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"));
374 :
375 : const bool bRFC7946 =
376 158 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"));
377 :
378 158 : const char *pszNativeData = CSLFetchNameValue(papszOptions, "NATIVE_DATA");
379 : const char *pszNativeMediaType =
380 158 : CSLFetchNameValue(papszOptions, "NATIVE_MEDIA_TYPE");
381 158 : bool bWriteCRSIfWGS84 = true;
382 158 : bool bFoundNameInNativeData = false;
383 158 : if (pszNativeData && pszNativeMediaType &&
384 26 : EQUAL(pszNativeMediaType, "application/vnd.geo+json"))
385 : {
386 26 : json_object *poObj = nullptr;
387 52 : if (OGRJSonParse(pszNativeData, &poObj) &&
388 26 : json_object_get_type(poObj) == json_type_object)
389 : {
390 : json_object_iter it;
391 26 : it.key = nullptr;
392 26 : it.val = nullptr;
393 26 : it.entry = nullptr;
394 52 : CPLString osNativeData;
395 26 : bWriteCRSIfWGS84 = false;
396 69 : json_object_object_foreachC(poObj, it)
397 : {
398 43 : if (strcmp(it.key, "type") == 0 ||
399 42 : strcmp(it.key, "features") == 0)
400 : {
401 2 : continue;
402 : }
403 41 : if (strcmp(it.key, "bbox") == 0)
404 : {
405 3 : if (CSLFetchNameValue(papszOptions, "WRITE_BBOX") ==
406 : nullptr)
407 3 : bWriteFC_BBOX = true;
408 3 : continue;
409 : }
410 38 : if (strcmp(it.key, "crs") == 0)
411 : {
412 8 : if (!bRFC7946)
413 7 : bWriteCRSIfWGS84 = true;
414 8 : continue;
415 : }
416 : // See https://tools.ietf.org/html/rfc7946#section-7.1
417 30 : if (bRFC7946 && (strcmp(it.key, "coordinates") == 0 ||
418 4 : strcmp(it.key, "geometries") == 0 ||
419 3 : strcmp(it.key, "geometry") == 0 ||
420 2 : strcmp(it.key, "properties") == 0))
421 : {
422 4 : continue;
423 : }
424 :
425 26 : if (strcmp(it.key, "name") == 0)
426 : {
427 17 : bFoundNameInNativeData = true;
428 34 : if (!CPLFetchBool(papszOptions, "WRITE_NAME", true) ||
429 17 : CSLFetchNameValue(papszOptions, "@NAME") != nullptr)
430 : {
431 0 : continue;
432 : }
433 : }
434 :
435 : // If a native description exists, ignore it if an explicit
436 : // DESCRIPTION option has been provided.
437 26 : if (strcmp(it.key, "description") == 0 &&
438 0 : CSLFetchNameValue(papszOptions, "DESCRIPTION"))
439 : {
440 0 : continue;
441 : }
442 :
443 26 : if (strcmp(it.key, "xy_coordinate_resolution") == 0 ||
444 25 : strcmp(it.key, "z_coordinate_resolution") == 0)
445 : {
446 2 : continue;
447 : }
448 :
449 24 : json_object *poKey = json_object_new_string(it.key);
450 24 : VSIFPrintfL(fpOut_, "%s: ", json_object_to_json_string(poKey));
451 24 : json_object_put(poKey);
452 24 : VSIFPrintfL(fpOut_, "%s,\n",
453 : json_object_to_json_string(it.val));
454 : }
455 26 : json_object_put(poObj);
456 : }
457 : }
458 :
459 : // Used by ogr2ogr in -nln mode
460 158 : const char *pszAtName = CSLFetchNameValue(papszOptions, "@NAME");
461 158 : if (pszAtName && CPLFetchBool(papszOptions, "WRITE_NAME", true))
462 : {
463 1 : json_object *poName = json_object_new_string(pszAtName);
464 1 : VSIFPrintfL(fpOut_, "\"name\": %s,\n",
465 : json_object_to_json_string(poName));
466 1 : json_object_put(poName);
467 : }
468 454 : else if (!bFoundNameInNativeData &&
469 140 : CPLFetchBool(papszOptions, "WRITE_NAME", true) &&
470 409 : !EQUAL(pszNameIn, OGRGeoJSONLayer::DefaultName) &&
471 112 : !EQUAL(pszNameIn, ""))
472 : {
473 112 : json_object *poName = json_object_new_string(pszNameIn);
474 112 : VSIFPrintfL(fpOut_, "\"name\": %s,\n",
475 : json_object_to_json_string(poName));
476 112 : json_object_put(poName);
477 : }
478 :
479 158 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
480 158 : if (pszDescription)
481 : {
482 1 : json_object *poDesc = json_object_new_string(pszDescription);
483 1 : VSIFPrintfL(fpOut_, "\"description\": %s,\n",
484 : json_object_to_json_string(poDesc));
485 1 : json_object_put(poDesc);
486 : }
487 :
488 158 : OGRCoordinateTransformation *poCT = nullptr;
489 158 : if (bRFC7946)
490 : {
491 23 : if (poSRS == nullptr)
492 : {
493 0 : CPLError(CE_Warning, CPLE_AppDefined,
494 : "No SRS set on layer. Assuming it is long/lat on WGS84 "
495 : "ellipsoid");
496 : }
497 23 : else if (poSRS->GetAxesCount() == 3)
498 : {
499 2 : OGRSpatialReference oSRS_EPSG_4979;
500 2 : oSRS_EPSG_4979.importFromEPSG(4979);
501 2 : oSRS_EPSG_4979.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
502 2 : if (!poSRS->IsSame(&oSRS_EPSG_4979))
503 : {
504 : poCT =
505 2 : OGRCreateCoordinateTransformation(poSRS, &oSRS_EPSG_4979);
506 2 : if (poCT == nullptr)
507 : {
508 0 : CPLError(CE_Warning, CPLE_AppDefined,
509 : "Failed to create coordinate transformation "
510 : "between the "
511 : "input coordinate system and WGS84.");
512 :
513 0 : return nullptr;
514 : }
515 : }
516 : }
517 : else
518 : {
519 21 : OGRSpatialReference oSRSWGS84;
520 21 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
521 21 : oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
522 21 : if (!poSRS->IsSame(&oSRSWGS84))
523 : {
524 9 : poCT = OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84);
525 9 : if (poCT == nullptr)
526 : {
527 0 : CPLError(CE_Warning, CPLE_AppDefined,
528 : "Failed to create coordinate transformation "
529 : "between the "
530 : "input coordinate system and WGS84.");
531 :
532 0 : return nullptr;
533 : }
534 : }
535 : }
536 : }
537 135 : else if (poSRS)
538 : {
539 57 : char *pszOGCURN = poSRS->GetOGCURN();
540 57 : if (pszOGCURN != nullptr &&
541 17 : (bWriteCRSIfWGS84 ||
542 17 : !EQUAL(pszOGCURN, "urn:ogc:def:crs:EPSG::4326")))
543 : {
544 41 : json_object *poObjCRS = json_object_new_object();
545 41 : json_object_object_add(poObjCRS, "type",
546 : json_object_new_string("name"));
547 41 : json_object *poObjProperties = json_object_new_object();
548 41 : json_object_object_add(poObjCRS, "properties", poObjProperties);
549 :
550 41 : if (EQUAL(pszOGCURN, "urn:ogc:def:crs:EPSG::4326"))
551 : {
552 13 : json_object_object_add(
553 : poObjProperties, "name",
554 : json_object_new_string("urn:ogc:def:crs:OGC:1.3:CRS84"));
555 : }
556 : else
557 : {
558 28 : json_object_object_add(poObjProperties, "name",
559 : json_object_new_string(pszOGCURN));
560 : }
561 :
562 41 : const char *pszCRS = json_object_to_json_string(poObjCRS);
563 41 : VSIFPrintfL(fpOut_, "\"crs\": %s,\n", pszCRS);
564 :
565 41 : json_object_put(poObjCRS);
566 : }
567 57 : CPLFree(pszOGCURN);
568 : }
569 :
570 158 : CPLStringList aosOptions(papszOptions);
571 :
572 158 : double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
573 158 : double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
574 :
575 158 : if (const char *pszCoordPrecision =
576 158 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION"))
577 : {
578 3 : dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecision));
579 3 : dfZResolution = dfXYResolution;
580 3 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
581 : dfXYResolution);
582 3 : if (poSRS && poSRS->GetAxesCount() == 3)
583 : {
584 0 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
585 : dfZResolution);
586 : }
587 : }
588 155 : else if (poSrcGeomFieldDefn)
589 : {
590 151 : const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
591 302 : OGRSpatialReference oSRSWGS84;
592 151 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
593 : const auto oCoordPrecWGS84 =
594 302 : oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
595 :
596 151 : if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
597 : {
598 3 : dfXYResolution = poSRS && bRFC7946 ? oCoordPrecWGS84.dfXYResolution
599 : : oCoordPrec.dfXYResolution;
600 :
601 : aosOptions.SetNameValue(
602 : "XY_COORD_PRECISION",
603 : CPLSPrintf("%d",
604 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
605 3 : dfXYResolution)));
606 3 : VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
607 : dfXYResolution);
608 : }
609 151 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
610 : {
611 3 : dfZResolution = poSRS && bRFC7946 ? oCoordPrecWGS84.dfZResolution
612 : : oCoordPrec.dfZResolution;
613 :
614 : aosOptions.SetNameValue(
615 : "Z_COORD_PRECISION",
616 : CPLSPrintf("%d",
617 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
618 3 : dfZResolution)));
619 3 : VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
620 : dfZResolution);
621 : }
622 : }
623 :
624 158 : if (bFpOutputIsSeekable_ && bWriteFC_BBOX)
625 : {
626 23 : nBBOXInsertLocation_ = static_cast<int>(VSIFTellL(fpOut_));
627 :
628 46 : const std::string osSpaceForBBOX(SPACE_FOR_BBOX + 1, ' ');
629 23 : VSIFPrintfL(fpOut_, "%s\n", osSpaceForBBOX.c_str());
630 : }
631 :
632 158 : VSIFPrintfL(fpOut_, "\"features\": [\n");
633 :
634 : OGRGeoJSONWriteLayer *poLayer = new OGRGeoJSONWriteLayer(
635 158 : pszNameIn, eGType, aosOptions.List(), bWriteFC_BBOX, poCT, this);
636 :
637 158 : if (eGType != wkbNone &&
638 : dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
639 : {
640 6 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
641 : OGRGeomCoordinatePrecision oCoordPrec(
642 12 : poGeomFieldDefn->GetCoordinatePrecision());
643 6 : oCoordPrec.dfXYResolution = dfXYResolution;
644 6 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
645 : }
646 :
647 158 : if (eGType != wkbNone &&
648 : dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
649 : {
650 6 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
651 : OGRGeomCoordinatePrecision oCoordPrec(
652 12 : poGeomFieldDefn->GetCoordinatePrecision());
653 6 : oCoordPrec.dfZResolution = dfZResolution;
654 6 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
655 : }
656 :
657 : /* -------------------------------------------------------------------- */
658 : /* Add layer to data source layer list. */
659 : /* -------------------------------------------------------------------- */
660 158 : CPLAssert(papoLayers_ == nullptr);
661 316 : papoLayersWriter_ = static_cast<OGRGeoJSONWriteLayer **>(CPLRealloc(
662 158 : papoLayers_, sizeof(OGRGeoJSONWriteLayer *) * (nLayers_ + 1)));
663 :
664 158 : papoLayersWriter_[nLayers_++] = poLayer;
665 :
666 158 : return poLayer;
667 : }
668 :
669 : /************************************************************************/
670 : /* TestCapability() */
671 : /************************************************************************/
672 :
673 345 : int OGRGeoJSONDataSource::TestCapability(const char *pszCap) const
674 : {
675 345 : if (EQUAL(pszCap, ODsCCreateLayer))
676 101 : return fpOut_ != nullptr && nLayers_ == 0;
677 244 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
678 2 : return m_bSupportsMGeometries;
679 242 : else if (EQUAL(pszCap, ODsCZGeometries))
680 4 : return m_bSupportsZGeometries;
681 :
682 238 : return FALSE;
683 : }
684 :
685 : /************************************************************************/
686 : /* Create() */
687 : /************************************************************************/
688 :
689 160 : int OGRGeoJSONDataSource::Create(const char *pszName,
690 : char ** /* papszOptions */)
691 : {
692 160 : CPLAssert(nullptr == fpOut_);
693 :
694 160 : if (strcmp(pszName, "/dev/stdout") == 0)
695 0 : pszName = "/vsistdout/";
696 :
697 318 : bFpOutputIsSeekable_ = !(strcmp(pszName, "/vsistdout/") == 0 ||
698 158 : STARTS_WITH(pszName, "/vsigzip/") ||
699 152 : STARTS_WITH(pszName, "/vsizip/"));
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* File overwrite not supported. */
703 : /* -------------------------------------------------------------------- */
704 : VSIStatBufL sStatBuf;
705 160 : if (0 == VSIStatL(pszName, &sStatBuf))
706 : {
707 0 : CPLError(CE_Failure, CPLE_NotSupported,
708 : "The GeoJSON driver does not overwrite existing files.");
709 0 : return FALSE;
710 : }
711 :
712 : /* -------------------------------------------------------------------- */
713 : /* Create the output file. */
714 : /* -------------------------------------------------------------------- */
715 160 : fpOut_ = VSIFOpenExL(pszName, "w", true);
716 160 : if (nullptr == fpOut_)
717 : {
718 2 : CPLError(CE_Failure, CPLE_OpenFailed,
719 : "Failed to create GeoJSON datasource: %s: %s", pszName,
720 : VSIGetLastErrorMsg());
721 2 : return FALSE;
722 : }
723 :
724 158 : pszName_ = CPLStrdup(pszName);
725 :
726 158 : return TRUE;
727 : }
728 :
729 : /************************************************************************/
730 : /* SetGeometryTranslation() */
731 : /************************************************************************/
732 :
733 542 : void OGRGeoJSONDataSource::SetGeometryTranslation(GeometryTranslation type)
734 : {
735 542 : flTransGeom_ = type;
736 542 : }
737 :
738 : /************************************************************************/
739 : /* SetAttributesTranslation() */
740 : /************************************************************************/
741 :
742 542 : void OGRGeoJSONDataSource::SetAttributesTranslation(AttributesTranslation type)
743 : {
744 542 : flTransAttrs_ = type;
745 542 : }
746 :
747 : /************************************************************************/
748 : /* PRIVATE FUNCTIONS IMPLEMENTATION */
749 : /************************************************************************/
750 :
751 761 : bool OGRGeoJSONDataSource::Clear()
752 : {
753 1430 : for (int i = 0; i < nLayers_; i++)
754 : {
755 669 : if (papoLayers_ != nullptr)
756 511 : delete papoLayers_[i];
757 : else
758 158 : delete papoLayersWriter_[i];
759 : }
760 :
761 761 : CPLFree(papoLayers_);
762 761 : papoLayers_ = nullptr;
763 761 : CPLFree(papoLayersWriter_);
764 761 : papoLayersWriter_ = nullptr;
765 761 : nLayers_ = 0;
766 :
767 761 : CPLFree(pszName_);
768 761 : pszName_ = nullptr;
769 :
770 761 : CPLFree(pszGeoData_);
771 761 : pszGeoData_ = nullptr;
772 761 : nGeoDataLen_ = 0;
773 :
774 761 : bool bRet = true;
775 761 : if (fpOut_)
776 : {
777 158 : if (VSIFCloseL(fpOut_) != 0)
778 0 : bRet = false;
779 158 : fpOut_ = nullptr;
780 : }
781 761 : return bRet;
782 : }
783 :
784 : /************************************************************************/
785 : /* ReadFromFile() */
786 : /************************************************************************/
787 :
788 66 : int OGRGeoJSONDataSource::ReadFromFile(GDALOpenInfo *poOpenInfo,
789 : const char *pszUnprefixed)
790 : {
791 66 : GByte *pabyOut = nullptr;
792 66 : if (!EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
793 : {
794 1 : GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
795 1 : if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
796 0 : return FALSE;
797 1 : VSIFSeekL(oOpenInfo.fpL, 0, SEEK_SET);
798 1 : if (!VSIIngestFile(oOpenInfo.fpL, pszUnprefixed, &pabyOut, nullptr, -1))
799 : {
800 0 : return FALSE;
801 : }
802 : }
803 : else
804 : {
805 65 : if (poOpenInfo->fpL == nullptr)
806 0 : return FALSE;
807 65 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
808 65 : if (!VSIIngestFile(poOpenInfo->fpL, poOpenInfo->pszFilename, &pabyOut,
809 : nullptr, -1))
810 : {
811 0 : return FALSE;
812 : }
813 :
814 65 : VSIFCloseL(poOpenInfo->fpL);
815 65 : poOpenInfo->fpL = nullptr;
816 : }
817 :
818 66 : CPLFree(pszGeoData_);
819 66 : pszGeoData_ = reinterpret_cast<char *>(pabyOut);
820 :
821 66 : CPLAssert(nullptr != pszGeoData_);
822 :
823 66 : return TRUE;
824 : }
825 :
826 : /************************************************************************/
827 : /* ReadFromService() */
828 : /************************************************************************/
829 :
830 23 : int OGRGeoJSONDataSource::ReadFromService(GDALOpenInfo *poOpenInfo,
831 : const char *pszSource)
832 : {
833 23 : CPLAssert(nullptr == pszGeoData_);
834 23 : CPLAssert(nullptr != pszSource);
835 :
836 23 : CPLErrorReset();
837 :
838 : /* -------------------------------------------------------------------- */
839 : /* Look if we already cached the content. */
840 : /* -------------------------------------------------------------------- */
841 23 : char *pszStoredContent = OGRGeoJSONDriverStealStoredContent(pszSource);
842 23 : if (pszStoredContent != nullptr)
843 : {
844 16 : if (!EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) &&
845 0 : ((osJSonFlavor_ == "ESRIJSON" &&
846 0 : ESRIJSONIsObject(pszStoredContent, poOpenInfo)) ||
847 0 : (osJSonFlavor_ == "TopoJSON" &&
848 0 : TopoJSONIsObject(pszStoredContent, poOpenInfo))))
849 : {
850 0 : pszGeoData_ = pszStoredContent;
851 0 : nGeoDataLen_ = strlen(pszGeoData_);
852 :
853 0 : pszName_ = CPLStrdup(pszSource);
854 0 : return true;
855 : }
856 :
857 16 : OGRGeoJSONDriverStoreContent(pszSource, pszStoredContent);
858 16 : return false;
859 : }
860 :
861 : /* -------------------------------------------------------------------- */
862 : /* Fetch the GeoJSON result. */
863 : /* -------------------------------------------------------------------- */
864 7 : CPLHTTPResult *pResult = GeoJSONHTTPFetchWithContentTypeHeader(pszSource);
865 7 : if (!pResult)
866 : {
867 0 : return FALSE;
868 : }
869 :
870 : /* -------------------------------------------------------------------- */
871 : /* Copy returned GeoJSON data to text buffer. */
872 : /* -------------------------------------------------------------------- */
873 7 : char *pszData = reinterpret_cast<char *>(pResult->pabyData);
874 :
875 : // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
876 7 : pszGeoData_ = pszData;
877 7 : nGeoDataLen_ = pResult->nDataLen;
878 7 : pResult->pabyData = nullptr;
879 7 : pResult->nDataLen = 0;
880 :
881 7 : pszName_ = CPLStrdup(pszSource);
882 :
883 : /* -------------------------------------------------------------------- */
884 : /* Cleanup HTTP resources. */
885 : /* -------------------------------------------------------------------- */
886 7 : CPLHTTPDestroyResult(pResult);
887 :
888 7 : CPLAssert(nullptr != pszGeoData_);
889 :
890 : /* -------------------------------------------------------------------- */
891 : /* Cache the content if it is not handled by this driver, but */
892 : /* another related one. */
893 : /* -------------------------------------------------------------------- */
894 7 : if (EQUAL(pszSource, poOpenInfo->pszFilename) && osJSonFlavor_ == "GeoJSON")
895 : {
896 7 : if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
897 : {
898 3 : if (ESRIJSONIsObject(pszGeoData_, poOpenInfo) ||
899 3 : TopoJSONIsObject(pszGeoData_, poOpenInfo) ||
900 9 : GeoJSONSeqIsObject(pszGeoData_, poOpenInfo) ||
901 3 : JSONFGIsObject(pszGeoData_, poOpenInfo))
902 : {
903 0 : OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
904 0 : pszGeoData_ = nullptr;
905 0 : nGeoDataLen_ = 0;
906 : }
907 : else
908 : {
909 3 : OGRGeoJSONDriverStoreContent(
910 : pszSource, CPLStrdup(INVALID_CONTENT_FOR_JSON_LIKE));
911 : }
912 3 : return false;
913 : }
914 : }
915 :
916 4 : return TRUE;
917 : }
918 :
919 : /************************************************************************/
920 : /* RemoveJSonPStuff() */
921 : /************************************************************************/
922 :
923 228 : void OGRGeoJSONDataSource::RemoveJSonPStuff()
924 : {
925 228 : const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
926 684 : for (size_t iP = 0; iP < CPL_ARRAYSIZE(apszPrefix); iP++)
927 : {
928 456 : if (strncmp(pszGeoData_, apszPrefix[iP], strlen(apszPrefix[iP])) == 0)
929 : {
930 2 : const size_t nDataLen = strlen(pszGeoData_);
931 2 : memmove(pszGeoData_, pszGeoData_ + strlen(apszPrefix[iP]),
932 2 : nDataLen - strlen(apszPrefix[iP]));
933 2 : size_t i = nDataLen - strlen(apszPrefix[iP]);
934 2 : pszGeoData_[i] = '\0';
935 4 : while (i > 0 && pszGeoData_[i] != ')')
936 : {
937 2 : i--;
938 : }
939 2 : pszGeoData_[i] = '\0';
940 : }
941 : }
942 228 : }
943 :
944 : /************************************************************************/
945 : /* LoadLayers() */
946 : /************************************************************************/
947 :
948 537 : void OGRGeoJSONDataSource::LoadLayers(GDALOpenInfo *poOpenInfo,
949 : GeoJSONSourceType nSrcType,
950 : const char *pszUnprefixed,
951 : const char *pszJSonFlavor)
952 : {
953 537 : if (nullptr == pszGeoData_)
954 : {
955 0 : CPLError(CE_Failure, CPLE_ObjectNull, "%s data buffer empty",
956 : pszJSonFlavor);
957 0 : return;
958 : }
959 :
960 537 : if (nSrcType != eGeoJSONSourceFile)
961 : {
962 186 : RemoveJSonPStuff();
963 : }
964 :
965 : /* -------------------------------------------------------------------- */
966 : /* Is it ESRI Feature Service data ? */
967 : /* -------------------------------------------------------------------- */
968 537 : if (EQUAL(pszJSonFlavor, "ESRIJSON"))
969 : {
970 42 : OGRESRIJSONReader reader;
971 21 : if (nSrcType == eGeoJSONSourceFile)
972 : {
973 19 : if (!ReadFromFile(poOpenInfo, pszUnprefixed))
974 0 : return;
975 : }
976 21 : OGRErr err = reader.Parse(pszGeoData_);
977 21 : if (OGRERR_NONE == err)
978 : {
979 21 : json_object *poObj = reader.GetJSonObject();
980 21 : CheckExceededTransferLimit(poObj);
981 21 : reader.ReadLayers(this, nSrcType);
982 : }
983 21 : return;
984 : }
985 :
986 : /* -------------------------------------------------------------------- */
987 : /* Is it TopoJSON data ? */
988 : /* -------------------------------------------------------------------- */
989 516 : if (EQUAL(pszJSonFlavor, "TOPOJSON"))
990 : {
991 10 : OGRTopoJSONReader reader;
992 5 : if (nSrcType == eGeoJSONSourceFile)
993 : {
994 5 : if (!ReadFromFile(poOpenInfo, pszUnprefixed))
995 0 : return;
996 : }
997 10 : OGRErr err = reader.Parse(
998 5 : pszGeoData_,
999 5 : nSrcType == eGeoJSONSourceService &&
1000 0 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "TopoJSON:"));
1001 5 : if (OGRERR_NONE == err)
1002 : {
1003 5 : reader.ReadLayers(this);
1004 : }
1005 5 : return;
1006 : }
1007 :
1008 511 : VSILFILE *fp = nullptr;
1009 511 : if (nSrcType == eGeoJSONSourceFile &&
1010 327 : !EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
1011 : {
1012 0 : GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
1013 0 : if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
1014 0 : return;
1015 0 : CPL_IGNORE_RET_VAL(oOpenInfo.TryToIngest(6000));
1016 0 : CPLFree(pszGeoData_);
1017 0 : pszGeoData_ =
1018 0 : CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
1019 0 : fp = oOpenInfo.fpL;
1020 0 : oOpenInfo.fpL = nullptr;
1021 : }
1022 :
1023 511 : if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
1024 : {
1025 0 : CPLDebug(pszJSonFlavor, "No valid %s data found in source '%s'",
1026 : pszJSonFlavor, pszName_);
1027 0 : if (fp)
1028 0 : VSIFCloseL(fp);
1029 0 : return;
1030 : }
1031 :
1032 : /* -------------------------------------------------------------------- */
1033 : /* Configure GeoJSON format translator. */
1034 : /* -------------------------------------------------------------------- */
1035 511 : OGRGeoJSONReader *poReader = new OGRGeoJSONReader();
1036 511 : SetOptionsOnReader(poOpenInfo, poReader);
1037 :
1038 : /* -------------------------------------------------------------------- */
1039 : /* Parse GeoJSON and build valid OGRLayer instance. */
1040 : /* -------------------------------------------------------------------- */
1041 511 : bool bUseStreamingInterface = false;
1042 511 : const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
1043 : CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
1044 511 : if ((fp != nullptr || poOpenInfo->fpL != nullptr) &&
1045 327 : (!STARTS_WITH(pszUnprefixed, "/vsistdin/") ||
1046 0 : (nMaxBytesFirstPass > 0 && nMaxBytesFirstPass <= 1000000)))
1047 : {
1048 327 : const char *pszStr = strstr(pszGeoData_, "\"features\"");
1049 327 : if (pszStr)
1050 : {
1051 286 : pszStr += strlen("\"features\"");
1052 309 : while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1053 23 : pszStr++;
1054 286 : if (*pszStr == ':')
1055 : {
1056 286 : pszStr++;
1057 646 : while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1058 360 : pszStr++;
1059 286 : if (*pszStr == '[')
1060 : {
1061 286 : bUseStreamingInterface = true;
1062 : }
1063 : }
1064 : }
1065 : }
1066 :
1067 511 : if (bUseStreamingInterface)
1068 : {
1069 286 : bool bTryStandardReading = false;
1070 286 : if (poReader->FirstPassReadLayer(this, fp ? fp : poOpenInfo->fpL,
1071 : bTryStandardReading))
1072 : {
1073 283 : if (fp)
1074 0 : fp = nullptr;
1075 : else
1076 283 : poOpenInfo->fpL = nullptr;
1077 283 : CheckExceededTransferLimit(poReader->GetJSonObject());
1078 : }
1079 : else
1080 : {
1081 3 : delete poReader;
1082 : }
1083 286 : if (!bTryStandardReading)
1084 : {
1085 285 : if (fp)
1086 0 : VSIFCloseL(fp);
1087 285 : return;
1088 : }
1089 :
1090 1 : poReader = new OGRGeoJSONReader();
1091 1 : SetOptionsOnReader(poOpenInfo, poReader);
1092 : }
1093 :
1094 226 : if (fp)
1095 0 : VSIFCloseL(fp);
1096 226 : if (nSrcType == eGeoJSONSourceFile)
1097 : {
1098 42 : if (!ReadFromFile(poOpenInfo, pszUnprefixed))
1099 : {
1100 0 : delete poReader;
1101 0 : return;
1102 : }
1103 42 : RemoveJSonPStuff();
1104 : }
1105 226 : const OGRErr err = poReader->Parse(pszGeoData_);
1106 226 : if (OGRERR_NONE == err)
1107 : {
1108 226 : CheckExceededTransferLimit(poReader->GetJSonObject());
1109 : }
1110 :
1111 226 : poReader->ReadLayers(this);
1112 226 : delete poReader;
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* SetOptionsOnReader() */
1117 : /************************************************************************/
1118 :
1119 512 : void OGRGeoJSONDataSource::SetOptionsOnReader(GDALOpenInfo *poOpenInfo,
1120 : OGRGeoJSONReader *poReader)
1121 : {
1122 512 : if (eGeometryAsCollection == flTransGeom_)
1123 : {
1124 0 : poReader->SetPreserveGeometryType(false);
1125 0 : CPLDebug("GeoJSON", "Geometry as OGRGeometryCollection type.");
1126 : }
1127 :
1128 512 : if (eAttributesSkip == flTransAttrs_)
1129 : {
1130 0 : poReader->SetSkipAttributes(true);
1131 0 : CPLDebug("GeoJSON", "Skip all attributes.");
1132 : }
1133 :
1134 1536 : poReader->SetFlattenNestedAttributes(
1135 512 : CPLFetchBool(poOpenInfo->papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES",
1136 : false),
1137 512 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
1138 512 : "NESTED_ATTRIBUTE_SEPARATOR", "_")[0]);
1139 :
1140 512 : const bool bDefaultNativeData = bUpdatable_;
1141 512 : poReader->SetStoreNativeData(CPLFetchBool(
1142 512 : poOpenInfo->papszOpenOptions, "NATIVE_DATA", bDefaultNativeData));
1143 :
1144 1024 : poReader->SetArrayAsString(CPLTestBool(CSLFetchNameValueDef(
1145 512 : poOpenInfo->papszOpenOptions, "ARRAY_AS_STRING",
1146 : CPLGetConfigOption("OGR_GEOJSON_ARRAY_AS_STRING", "NO"))));
1147 :
1148 1024 : poReader->SetDateAsString(CPLTestBool(CSLFetchNameValueDef(
1149 512 : poOpenInfo->papszOpenOptions, "DATE_AS_STRING",
1150 : CPLGetConfigOption("OGR_GEOJSON_DATE_AS_STRING", "NO"))));
1151 :
1152 1024 : const char *pszForeignMembers = CSLFetchNameValueDef(
1153 512 : poOpenInfo->papszOpenOptions, "FOREIGN_MEMBERS", "AUTO");
1154 512 : if (EQUAL(pszForeignMembers, "AUTO"))
1155 : {
1156 508 : poReader->SetForeignMemberProcessing(
1157 : OGRGeoJSONBaseReader::ForeignMemberProcessing::AUTO);
1158 : }
1159 4 : else if (EQUAL(pszForeignMembers, "ALL"))
1160 : {
1161 1 : poReader->SetForeignMemberProcessing(
1162 : OGRGeoJSONBaseReader::ForeignMemberProcessing::ALL);
1163 : }
1164 3 : else if (EQUAL(pszForeignMembers, "NONE"))
1165 : {
1166 2 : poReader->SetForeignMemberProcessing(
1167 : OGRGeoJSONBaseReader::ForeignMemberProcessing::NONE);
1168 : }
1169 1 : else if (EQUAL(pszForeignMembers, "STAC"))
1170 : {
1171 1 : poReader->SetForeignMemberProcessing(
1172 : OGRGeoJSONBaseReader::ForeignMemberProcessing::STAC);
1173 : }
1174 512 : }
1175 :
1176 : /************************************************************************/
1177 : /* CheckExceededTransferLimit() */
1178 : /************************************************************************/
1179 :
1180 530 : void OGRGeoJSONDataSource::CheckExceededTransferLimit(json_object *poObj)
1181 : {
1182 1572 : for (int i = 0; i < 2; i++)
1183 : {
1184 1058 : if (i == 1)
1185 : {
1186 528 : if (poObj && json_object_get_type(poObj) == json_type_object)
1187 : {
1188 528 : poObj = CPL_json_object_object_get(poObj, "properties");
1189 : }
1190 : }
1191 1058 : if (poObj && json_object_get_type(poObj) == json_type_object)
1192 : {
1193 : json_object *poExceededTransferLimit =
1194 570 : CPL_json_object_object_get(poObj, "exceededTransferLimit");
1195 586 : if (poExceededTransferLimit &&
1196 16 : json_object_get_type(poExceededTransferLimit) ==
1197 : json_type_boolean)
1198 : {
1199 16 : bOtherPages_ = CPL_TO_BOOL(
1200 : json_object_get_boolean(poExceededTransferLimit));
1201 16 : return;
1202 : }
1203 : }
1204 : }
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* AddLayer() */
1209 : /************************************************************************/
1210 :
1211 511 : void OGRGeoJSONDataSource::AddLayer(OGRGeoJSONLayer *poLayer)
1212 : {
1213 511 : CPLAssert(papoLayersWriter_ == nullptr);
1214 :
1215 : // Return layer in readable state.
1216 511 : poLayer->ResetReading();
1217 :
1218 511 : papoLayers_ = static_cast<OGRGeoJSONLayer **>(
1219 511 : CPLRealloc(papoLayers_, sizeof(OGRGeoJSONLayer *) * (nLayers_ + 1)));
1220 511 : papoLayers_[nLayers_] = poLayer;
1221 511 : nLayers_++;
1222 511 : }
1223 :
1224 : /************************************************************************/
1225 : /* FlushCache() */
1226 : /************************************************************************/
1227 :
1228 807 : CPLErr OGRGeoJSONDataSource::FlushCache(bool /*bAtClosing*/)
1229 : {
1230 807 : if (papoLayersWriter_ != nullptr)
1231 : {
1232 238 : return papoLayersWriter_[0]->SyncToDisk() == OGRERR_NONE ? CE_None
1233 238 : : CE_Failure;
1234 : }
1235 :
1236 569 : CPLErr eErr = CE_None;
1237 1080 : for (int i = 0; i < nLayers_; i++)
1238 : {
1239 511 : if (papoLayers_[i]->HasBeenUpdated())
1240 : {
1241 15 : papoLayers_[i]->SetUpdated(false);
1242 :
1243 15 : bool bOK = false;
1244 :
1245 : // Disable all filters.
1246 15 : OGRFeatureQuery *poAttrQueryBak = papoLayers_[i]->m_poAttrQuery;
1247 15 : papoLayers_[i]->m_poAttrQuery = nullptr;
1248 15 : OGRGeometry *poFilterGeomBak = papoLayers_[i]->m_poFilterGeom;
1249 15 : papoLayers_[i]->m_poFilterGeom = nullptr;
1250 :
1251 : // If the source data only contained one single feature and
1252 : // that's still the case, then do not use a FeatureCollection
1253 : // on writing.
1254 15 : bool bAlreadyDone = false;
1255 23 : if (papoLayers_[i]->GetFeatureCount(TRUE) == 1 &&
1256 8 : papoLayers_[i]->GetMetadata("NATIVE_DATA") == nullptr)
1257 : {
1258 1 : papoLayers_[i]->ResetReading();
1259 1 : OGRFeature *poFeature = papoLayers_[i]->GetNextFeature();
1260 1 : if (poFeature != nullptr)
1261 : {
1262 1 : if (poFeature->GetNativeData() != nullptr)
1263 : {
1264 1 : bAlreadyDone = true;
1265 2 : OGRGeoJSONWriteOptions oOptions;
1266 : json_object *poObj =
1267 1 : OGRGeoJSONWriteFeature(poFeature, oOptions);
1268 1 : VSILFILE *fp = VSIFOpenL(pszName_, "wb");
1269 1 : if (fp != nullptr)
1270 : {
1271 1 : bOK = VSIFPrintfL(
1272 : fp, "%s",
1273 : json_object_to_json_string(poObj)) > 0;
1274 1 : VSIFCloseL(fp);
1275 : }
1276 1 : json_object_put(poObj);
1277 : }
1278 1 : delete poFeature;
1279 : }
1280 : }
1281 :
1282 : // Otherwise do layer translation.
1283 15 : if (!bAlreadyDone)
1284 : {
1285 14 : char **papszOptions = CSLAddString(nullptr, "-f");
1286 14 : papszOptions = CSLAddString(papszOptions, "GeoJSON");
1287 : GDALVectorTranslateOptions *psOptions =
1288 14 : GDALVectorTranslateOptionsNew(papszOptions, nullptr);
1289 14 : CSLDestroy(papszOptions);
1290 14 : GDALDatasetH hSrcDS = this;
1291 28 : CPLString osNewFilename(pszName_);
1292 14 : osNewFilename += ".tmp";
1293 14 : GDALDatasetH hOutDS = GDALVectorTranslate(
1294 : osNewFilename, nullptr, 1, &hSrcDS, psOptions, nullptr);
1295 14 : GDALVectorTranslateOptionsFree(psOptions);
1296 :
1297 14 : if (hOutDS != nullptr)
1298 : {
1299 14 : CPLErrorReset();
1300 14 : GDALClose(hOutDS);
1301 14 : bOK = (CPLGetLastErrorType() == CE_None);
1302 : }
1303 14 : if (bOK)
1304 : {
1305 14 : const bool bOverwrite = CPLTestBool(
1306 : CPLGetConfigOption("OGR_GEOJSON_REWRITE_IN_PLACE",
1307 : #ifdef _WIN32
1308 : "YES"
1309 : #else
1310 : "NO"
1311 : #endif
1312 : ));
1313 14 : if (bOverwrite)
1314 : {
1315 1 : VSILFILE *fpTarget = nullptr;
1316 1 : for (int attempt = 0; attempt < 10; attempt++)
1317 : {
1318 1 : fpTarget = VSIFOpenL(pszName_, "rb+");
1319 1 : if (fpTarget)
1320 1 : break;
1321 0 : CPLSleep(0.1);
1322 : }
1323 1 : if (!fpTarget)
1324 : {
1325 0 : CPLError(CE_Failure, CPLE_AppDefined,
1326 : "Cannot rewrite %s", pszName_);
1327 : }
1328 : else
1329 : {
1330 1 : bool bCopyOK = CPL_TO_BOOL(
1331 : VSIOverwriteFile(fpTarget, osNewFilename));
1332 1 : if (VSIFCloseL(fpTarget) != 0)
1333 0 : bCopyOK = false;
1334 1 : if (bCopyOK)
1335 : {
1336 1 : VSIUnlink(osNewFilename);
1337 : }
1338 : else
1339 : {
1340 0 : CPLError(CE_Failure, CPLE_AppDefined,
1341 : "Cannot rewrite %s with content of %s",
1342 : pszName_, osNewFilename.c_str());
1343 : }
1344 : }
1345 : }
1346 : else
1347 : {
1348 26 : CPLString osBackup(pszName_);
1349 13 : osBackup += ".bak";
1350 13 : if (VSIRename(pszName_, osBackup) < 0)
1351 : {
1352 0 : CPLError(CE_Failure, CPLE_AppDefined,
1353 : "Cannot create backup copy");
1354 : }
1355 13 : else if (VSIRename(osNewFilename, pszName_) < 0)
1356 : {
1357 0 : CPLError(CE_Failure, CPLE_AppDefined,
1358 : "Cannot rename %s to %s",
1359 : osNewFilename.c_str(), pszName_);
1360 : }
1361 : else
1362 : {
1363 13 : VSIUnlink(osBackup);
1364 : }
1365 : }
1366 : }
1367 : }
1368 15 : if (!bOK)
1369 0 : eErr = CE_Failure;
1370 :
1371 : // Restore filters.
1372 15 : papoLayers_[i]->m_poAttrQuery = poAttrQueryBak;
1373 15 : papoLayers_[i]->m_poFilterGeom = poFilterGeomBak;
1374 : }
1375 : }
1376 569 : return eErr;
1377 : }
|