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