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