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