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