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