/src/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implementation of OGRGeoJSONDriver class (OGR GeoJSON Driver). |
5 | | * Author: Mateusz Loskot, mateusz@loskot.net |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Mateusz Loskot |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_port.h" |
14 | | #include "ogr_geojson.h" |
15 | | |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <limits> |
19 | | |
20 | | #include "cpl_conv.h" |
21 | | #include "cpl_error.h" |
22 | | #include "cpl_http.h" |
23 | | #include "cpl_multiproc.h" |
24 | | #include "cpl_string.h" |
25 | | #include "cpl_vsi.h" |
26 | | // #include "json_object.h" |
27 | | #include "gdal.h" |
28 | | #include "gdal_priv.h" |
29 | | #include "ogr_core.h" |
30 | | #include "ogr_feature.h" |
31 | | #include "ogrgeojsonutils.h" |
32 | | #include "ogrsf_frmts.h" |
33 | | |
34 | | static CPLMutex *ghMutex = nullptr; |
35 | | static char *gpszSource = nullptr; |
36 | | static char *gpszText = nullptr; |
37 | | |
38 | | class OGRESRIFeatureServiceDataset; |
39 | | |
40 | | /************************************************************************/ |
41 | | /* OGRESRIFeatureServiceLayer */ |
42 | | /************************************************************************/ |
43 | | |
44 | | class OGRESRIFeatureServiceLayer final : public OGRLayer |
45 | | { |
46 | | OGRESRIFeatureServiceDataset *poDS; |
47 | | OGRFeatureDefn *poFeatureDefn; |
48 | | GIntBig nFeaturesRead; |
49 | | GIntBig nFirstFID; |
50 | | GIntBig nLastFID; |
51 | | bool bOtherPage; |
52 | | bool bUseSequentialFID; |
53 | | |
54 | | public: |
55 | | explicit OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset *poDS); |
56 | | virtual ~OGRESRIFeatureServiceLayer(); |
57 | | |
58 | | void ResetReading() override; |
59 | | OGRFeature *GetNextFeature() override; |
60 | | GIntBig GetFeatureCount(int bForce = TRUE) override; |
61 | | OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent, |
62 | | bool bForce = true) override; |
63 | | |
64 | | int TestCapability(const char *pszCap) override; |
65 | | |
66 | | OGRFeatureDefn *GetLayerDefn() override |
67 | 0 | { |
68 | 0 | return poFeatureDefn; |
69 | 0 | } |
70 | | }; |
71 | | |
72 | | /************************************************************************/ |
73 | | /* OGRESRIFeatureServiceDataset */ |
74 | | /************************************************************************/ |
75 | | |
76 | | class OGRESRIFeatureServiceDataset final : public GDALDataset |
77 | | { |
78 | | CPLString m_osURL{}; |
79 | | GIntBig m_nFirstOffset = 0; |
80 | | GIntBig m_nLastOffset = 0; |
81 | | std::unique_ptr<OGRGeoJSONDataSource> m_poCurrent{}; |
82 | | std::unique_ptr<OGRESRIFeatureServiceLayer> m_poLayer{}; |
83 | | GeoJSONSourceType m_nSrcType = eGeoJSONSourceUnknown; |
84 | | |
85 | | bool LoadPage(); |
86 | | |
87 | | public: |
88 | | OGRESRIFeatureServiceDataset( |
89 | | const std::string &osURL, |
90 | | std::unique_ptr<OGRGeoJSONDataSource> &&poFirst, |
91 | | GeoJSONSourceType nSrcType); |
92 | | |
93 | | int GetLayerCount() override |
94 | 0 | { |
95 | 0 | return 1; |
96 | 0 | } |
97 | | |
98 | | OGRLayer *GetLayer(int nLayer) override; |
99 | | |
100 | | OGRLayer *GetUnderlyingLayer() |
101 | 0 | { |
102 | 0 | return m_poCurrent->GetLayer(0); |
103 | 0 | } |
104 | | |
105 | | bool MyResetReading(); |
106 | | bool LoadNextPage(); |
107 | | |
108 | | const CPLString &GetURL() const |
109 | 0 | { |
110 | 0 | return m_osURL; |
111 | 0 | } |
112 | | }; |
113 | | |
114 | | OGRLayer *OGRESRIFeatureServiceDataset::GetLayer(int nLayer) |
115 | 0 | { |
116 | 0 | return (nLayer == 0) ? m_poLayer.get() : nullptr; |
117 | 0 | } |
118 | | |
119 | | /************************************************************************/ |
120 | | /* OGRESRIFeatureServiceLayer() */ |
121 | | /************************************************************************/ |
122 | | |
123 | | OGRESRIFeatureServiceLayer::OGRESRIFeatureServiceLayer( |
124 | | OGRESRIFeatureServiceDataset *poDSIn) |
125 | 0 | : poDS(poDSIn), nFeaturesRead(0), nFirstFID(0), nLastFID(0), |
126 | 0 | bOtherPage(false), bUseSequentialFID(false) |
127 | 0 | { |
128 | 0 | OGRFeatureDefn *poSrcFeatDefn = poDS->GetUnderlyingLayer()->GetLayerDefn(); |
129 | 0 | poFeatureDefn = new OGRFeatureDefn(poSrcFeatDefn->GetName()); |
130 | 0 | SetDescription(poFeatureDefn->GetName()); |
131 | 0 | poFeatureDefn->Reference(); |
132 | 0 | poFeatureDefn->SetGeomType(wkbNone); |
133 | |
|
134 | 0 | for (int i = 0; i < poSrcFeatDefn->GetFieldCount(); i++) |
135 | 0 | poFeatureDefn->AddFieldDefn(poSrcFeatDefn->GetFieldDefn(i)); |
136 | |
|
137 | 0 | for (int i = 0; i < poSrcFeatDefn->GetGeomFieldCount(); i++) |
138 | 0 | poFeatureDefn->AddGeomFieldDefn(poSrcFeatDefn->GetGeomFieldDefn(i)); |
139 | 0 | } |
140 | | |
141 | | /************************************************************************/ |
142 | | /* ~OGRESRIFeatureServiceLayer() */ |
143 | | /************************************************************************/ |
144 | | |
145 | | OGRESRIFeatureServiceLayer::~OGRESRIFeatureServiceLayer() |
146 | 0 | { |
147 | 0 | poFeatureDefn->Release(); |
148 | 0 | } |
149 | | |
150 | | /************************************************************************/ |
151 | | /* ResetReading() */ |
152 | | /************************************************************************/ |
153 | | |
154 | | void OGRESRIFeatureServiceLayer::ResetReading() |
155 | 0 | { |
156 | 0 | poDS->MyResetReading(); |
157 | 0 | nFeaturesRead = 0; |
158 | 0 | nLastFID = 0; |
159 | 0 | bOtherPage = false; |
160 | 0 | bUseSequentialFID = false; |
161 | 0 | } |
162 | | |
163 | | /************************************************************************/ |
164 | | /* GetNextFeature() */ |
165 | | /************************************************************************/ |
166 | | |
167 | | OGRFeature *OGRESRIFeatureServiceLayer::GetNextFeature() |
168 | 0 | { |
169 | 0 | while (true) |
170 | 0 | { |
171 | 0 | const bool bWasInFirstPage = !bOtherPage; |
172 | 0 | #if defined(__GNUC__) |
173 | 0 | #pragma GCC diagnostic push |
174 | 0 | #pragma GCC diagnostic ignored "-Wnull-dereference" |
175 | 0 | #endif |
176 | 0 | OGRFeature *poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature(); |
177 | 0 | #if defined(__GNUC__) |
178 | 0 | #pragma GCC diagnostic pop |
179 | 0 | #endif |
180 | 0 | if (poSrcFeat == nullptr) |
181 | 0 | { |
182 | 0 | if (!poDS->LoadNextPage()) |
183 | 0 | return nullptr; |
184 | 0 | #if defined(__GNUC__) |
185 | 0 | #pragma GCC diagnostic push |
186 | 0 | #pragma GCC diagnostic ignored "-Wnull-dereference" |
187 | 0 | #endif |
188 | 0 | poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature(); |
189 | 0 | #if defined(__GNUC__) |
190 | 0 | #pragma GCC diagnostic pop |
191 | 0 | #endif |
192 | 0 | if (poSrcFeat == nullptr) |
193 | 0 | return nullptr; |
194 | 0 | bOtherPage = true; |
195 | 0 | if (bWasInFirstPage && poSrcFeat->GetFID() != 0 && |
196 | 0 | poSrcFeat->GetFID() == nFirstFID) |
197 | 0 | { |
198 | | // End-less looping |
199 | 0 | CPLDebug("ESRIJSON", "Scrolling not working. Stopping"); |
200 | 0 | delete poSrcFeat; |
201 | 0 | return nullptr; |
202 | 0 | } |
203 | 0 | if (bWasInFirstPage && poSrcFeat->GetFID() == 0 && |
204 | 0 | nLastFID == nFeaturesRead - 1) |
205 | 0 | { |
206 | 0 | bUseSequentialFID = true; |
207 | 0 | } |
208 | 0 | } |
209 | 0 | if (nFeaturesRead == 0) |
210 | 0 | nFirstFID = poSrcFeat->GetFID(); |
211 | |
|
212 | 0 | OGRFeature *poFeature = new OGRFeature(poFeatureDefn); |
213 | 0 | poFeature->SetFrom(poSrcFeat); |
214 | 0 | if (bUseSequentialFID) |
215 | 0 | poFeature->SetFID(nFeaturesRead); |
216 | 0 | else |
217 | 0 | poFeature->SetFID(poSrcFeat->GetFID()); |
218 | 0 | nLastFID = poFeature->GetFID(); |
219 | 0 | nFeaturesRead++; |
220 | 0 | delete poSrcFeat; |
221 | |
|
222 | 0 | if ((m_poFilterGeom == nullptr || |
223 | 0 | FilterGeometry(poFeature->GetGeometryRef())) && |
224 | 0 | (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature))) |
225 | 0 | { |
226 | 0 | return poFeature; |
227 | 0 | } |
228 | 0 | delete poFeature; |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | /************************************************************************/ |
233 | | /* TestCapability() */ |
234 | | /************************************************************************/ |
235 | | |
236 | | int OGRESRIFeatureServiceLayer::TestCapability(const char *pszCap) |
237 | 0 | { |
238 | 0 | if (EQUAL(pszCap, OLCFastFeatureCount)) |
239 | 0 | return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr; |
240 | 0 | if (EQUAL(pszCap, OLCFastGetExtent)) |
241 | 0 | return FALSE; |
242 | 0 | #if defined(__GNUC__) |
243 | 0 | #pragma GCC diagnostic push |
244 | 0 | #pragma GCC diagnostic ignored "-Wnull-dereference" |
245 | 0 | #endif |
246 | 0 | auto poUnderlyingLayer = poDS->GetUnderlyingLayer(); |
247 | 0 | return poUnderlyingLayer->TestCapability(pszCap); |
248 | 0 | #if defined(__GNUC__) |
249 | 0 | #pragma GCC diagnostic pop |
250 | 0 | #endif |
251 | 0 | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* GetFeatureCount() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | GIntBig OGRESRIFeatureServiceLayer::GetFeatureCount(int bForce) |
258 | 0 | { |
259 | 0 | GIntBig nFeatureCount = -1; |
260 | 0 | if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr) |
261 | 0 | { |
262 | 0 | CPLString osNewURL = |
263 | 0 | CPLURLAddKVP(poDS->GetURL(), "returnCountOnly", "true"); |
264 | 0 | osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr); |
265 | 0 | CPLErrorReset(); |
266 | 0 | CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr); |
267 | 0 | if (pResult != nullptr && pResult->nDataLen != 0 && |
268 | 0 | CPLGetLastErrorNo() == 0 && pResult->nStatus == 0) |
269 | 0 | { |
270 | 0 | const char *pszCount = |
271 | 0 | strstr((const char *)pResult->pabyData, "\"count\""); |
272 | 0 | if (pszCount) |
273 | 0 | { |
274 | 0 | pszCount = strchr(pszCount, ':'); |
275 | 0 | if (pszCount) |
276 | 0 | { |
277 | 0 | pszCount++; |
278 | 0 | nFeatureCount = CPLAtoGIntBig(pszCount); |
279 | 0 | } |
280 | 0 | } |
281 | 0 | } |
282 | 0 | CPLHTTPDestroyResult(pResult); |
283 | 0 | } |
284 | 0 | if (nFeatureCount < 0) |
285 | 0 | nFeatureCount = OGRLayer::GetFeatureCount(bForce); |
286 | 0 | return nFeatureCount; |
287 | 0 | } |
288 | | |
289 | | /************************************************************************/ |
290 | | /* IGetExtent() */ |
291 | | /************************************************************************/ |
292 | | |
293 | | OGRErr OGRESRIFeatureServiceLayer::IGetExtent(int iGeomField, |
294 | | OGREnvelope *psExtent, |
295 | | bool bForce) |
296 | 0 | { |
297 | 0 | OGRErr eErr = OGRERR_FAILURE; |
298 | 0 | CPLString osNewURL = |
299 | 0 | CPLURLAddKVP(poDS->GetURL(), "returnExtentOnly", "true"); |
300 | 0 | osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr); |
301 | 0 | osNewURL = CPLURLAddKVP(osNewURL, "f", "geojson"); |
302 | 0 | CPLErrorReset(); |
303 | 0 | CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr); |
304 | 0 | if (pResult != nullptr && pResult->nDataLen != 0 && |
305 | 0 | CPLGetLastErrorNo() == 0 && pResult->nStatus == 0) |
306 | 0 | { |
307 | 0 | const char *pszBBox = |
308 | 0 | strstr((const char *)pResult->pabyData, "\"bbox\""); |
309 | 0 | if (pszBBox) |
310 | 0 | { |
311 | 0 | pszBBox = strstr(pszBBox, ":["); |
312 | 0 | if (pszBBox) |
313 | 0 | { |
314 | 0 | pszBBox += 2; |
315 | 0 | char **papszTokens = CSLTokenizeString2(pszBBox, ",", 0); |
316 | 0 | if (CSLCount(papszTokens) >= 4) |
317 | 0 | { |
318 | 0 | psExtent->MinX = CPLAtof(papszTokens[0]); |
319 | 0 | psExtent->MinY = CPLAtof(papszTokens[1]); |
320 | 0 | psExtent->MaxX = CPLAtof(papszTokens[2]); |
321 | 0 | psExtent->MaxY = CPLAtof(papszTokens[3]); |
322 | 0 | eErr = OGRERR_NONE; |
323 | 0 | } |
324 | 0 | CSLDestroy(papszTokens); |
325 | 0 | } |
326 | 0 | } |
327 | 0 | } |
328 | 0 | CPLHTTPDestroyResult(pResult); |
329 | 0 | if (eErr == OGRERR_FAILURE) |
330 | 0 | eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce); |
331 | 0 | return eErr; |
332 | 0 | } |
333 | | |
334 | | /************************************************************************/ |
335 | | /* OGRESRIFeatureServiceDataset() */ |
336 | | /************************************************************************/ |
337 | | |
338 | | OGRESRIFeatureServiceDataset::OGRESRIFeatureServiceDataset( |
339 | | const std::string &osURL, std::unique_ptr<OGRGeoJSONDataSource> &&poFirst, |
340 | | GeoJSONSourceType nSrcType) |
341 | 0 | : m_osURL(osURL), m_poCurrent(std::move(poFirst)), m_nSrcType(nSrcType) |
342 | 0 | { |
343 | 0 | m_poLayer = std::make_unique<OGRESRIFeatureServiceLayer>(this); |
344 | 0 | if (CPLURLGetValue(m_osURL, "resultRecordCount").empty()) |
345 | 0 | { |
346 | | // We assume that if the server sets the exceededTransferLimit, the |
347 | | // and resultRecordCount is not set, the number of features returned |
348 | | // in our first request is the maximum allowed by the server |
349 | | // So set it for following requests. |
350 | 0 | m_osURL = CPLURLAddKVP( |
351 | 0 | m_osURL, "resultRecordCount", |
352 | 0 | CPLSPrintf("%d", static_cast<int>( |
353 | 0 | m_poCurrent->GetLayer(0)->GetFeatureCount()))); |
354 | 0 | } |
355 | 0 | else |
356 | 0 | { |
357 | 0 | const int nUserSetRecordCount = |
358 | 0 | atoi(CPLURLGetValue(m_osURL, "resultRecordCount")); |
359 | 0 | if (nUserSetRecordCount > m_poCurrent->GetLayer(0)->GetFeatureCount()) |
360 | 0 | { |
361 | 0 | CPLError( |
362 | 0 | CE_Warning, CPLE_AppDefined, |
363 | 0 | "Specified resultRecordCount=%d is greater than " |
364 | 0 | "the maximum %d supported by the server", |
365 | 0 | nUserSetRecordCount, |
366 | 0 | static_cast<int>(m_poCurrent->GetLayer(0)->GetFeatureCount())); |
367 | 0 | } |
368 | 0 | } |
369 | 0 | m_nFirstOffset = CPLAtoGIntBig(CPLURLGetValue(m_osURL, "resultOffset")); |
370 | 0 | m_nLastOffset = m_nFirstOffset; |
371 | 0 | } |
372 | | |
373 | | /************************************************************************/ |
374 | | /* MyResetReading() */ |
375 | | /************************************************************************/ |
376 | | |
377 | | bool OGRESRIFeatureServiceDataset::MyResetReading() |
378 | 0 | { |
379 | 0 | if (m_nLastOffset > m_nFirstOffset) |
380 | 0 | { |
381 | 0 | m_nLastOffset = m_nFirstOffset; |
382 | 0 | return LoadPage(); |
383 | 0 | } |
384 | | |
385 | 0 | #if defined(__GNUC__) |
386 | 0 | #pragma GCC diagnostic push |
387 | 0 | #pragma GCC diagnostic ignored "-Wnull-dereference" |
388 | 0 | #endif |
389 | 0 | m_poCurrent->GetLayer(0)->ResetReading(); |
390 | 0 | #if defined(__GNUC__) |
391 | 0 | #pragma GCC diagnostic pop |
392 | 0 | #endif |
393 | 0 | return true; |
394 | 0 | } |
395 | | |
396 | | /************************************************************************/ |
397 | | /* LoadNextPage() */ |
398 | | /************************************************************************/ |
399 | | |
400 | | bool OGRESRIFeatureServiceDataset::LoadNextPage() |
401 | 0 | { |
402 | 0 | if (!m_poCurrent->HasOtherPages()) |
403 | 0 | return false; |
404 | 0 | #if defined(__GNUC__) |
405 | 0 | #pragma GCC diagnostic push |
406 | 0 | #pragma GCC diagnostic ignored "-Wnull-dereference" |
407 | 0 | #endif |
408 | 0 | const auto nCurPageFC = m_poCurrent->GetLayer(0)->GetFeatureCount(); |
409 | 0 | #if defined(__GNUC__) |
410 | 0 | #pragma GCC diagnostic pop |
411 | 0 | #endif |
412 | 0 | if (m_nLastOffset > std::numeric_limits<GIntBig>::max() - nCurPageFC) |
413 | 0 | return false; |
414 | 0 | m_nLastOffset += nCurPageFC; |
415 | 0 | return LoadPage(); |
416 | 0 | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* LoadPage() */ |
420 | | /************************************************************************/ |
421 | | |
422 | | bool OGRESRIFeatureServiceDataset::LoadPage() |
423 | 0 | { |
424 | 0 | CPLString osNewURL = CPLURLAddKVP(m_osURL, "resultOffset", |
425 | 0 | CPLSPrintf(CPL_FRMT_GIB, m_nLastOffset)); |
426 | 0 | auto poDS = std::make_unique<OGRGeoJSONDataSource>(); |
427 | 0 | GDALOpenInfo oOpenInfo(osNewURL, GA_ReadOnly); |
428 | 0 | if (!poDS->Open(&oOpenInfo, m_nSrcType, m_poCurrent->GetJSonFlavor()) || |
429 | 0 | poDS->GetLayerCount() == 0) |
430 | 0 | { |
431 | 0 | return false; |
432 | 0 | } |
433 | 0 | m_poCurrent = std::move(poDS); |
434 | 0 | return true; |
435 | 0 | } |
436 | | |
437 | | /************************************************************************/ |
438 | | /* OGRGeoJSONDriverIdentify() */ |
439 | | /************************************************************************/ |
440 | | |
441 | | static int OGRGeoJSONDriverIdentifyInternal(GDALOpenInfo *poOpenInfo, |
442 | | GeoJSONSourceType &nSrcType) |
443 | 0 | { |
444 | | /* -------------------------------------------------------------------- */ |
445 | | /* Determine type of data source: text file (.geojson, .json), */ |
446 | | /* Web Service or text passed directly and load data. */ |
447 | | /* -------------------------------------------------------------------- */ |
448 | |
|
449 | 0 | nSrcType = GeoJSONGetSourceType(poOpenInfo); |
450 | 0 | if (nSrcType == eGeoJSONSourceUnknown) |
451 | 0 | { |
452 | 0 | const char *pszHeader = |
453 | 0 | reinterpret_cast<const char *>(poOpenInfo->pabyHeader); |
454 | 0 | if (pszHeader && STARTS_WITH(pszHeader, "{\"properties\":{")) |
455 | 0 | return GDAL_IDENTIFY_UNKNOWN; |
456 | | |
457 | 0 | return FALSE; |
458 | 0 | } |
459 | | |
460 | 0 | if (nSrcType == eGeoJSONSourceService) |
461 | 0 | { |
462 | 0 | if (poOpenInfo->IsSingleAllowedDriver("GeoJSON")) |
463 | 0 | return TRUE; |
464 | 0 | if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSON:")) |
465 | 0 | { |
466 | 0 | return -1; |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | // If this looks like a file that can be handled by the STACTA driver, |
471 | | // and that one is available, then don't identify the file. |
472 | 0 | const char *pszHeader = |
473 | 0 | reinterpret_cast<const char *>(poOpenInfo->pabyHeader); |
474 | 0 | if (pszHeader != nullptr && |
475 | 0 | strstr(pszHeader, "\"stac_extensions\"") != nullptr && |
476 | 0 | strstr(pszHeader, "\"tiled-assets\"") != nullptr && |
477 | 0 | GDALGetDriverByName("STACTA") != nullptr) |
478 | 0 | { |
479 | 0 | if (poOpenInfo->IsSingleAllowedDriver("GeoJSON")) |
480 | 0 | return TRUE; |
481 | 0 | return FALSE; |
482 | 0 | } |
483 | | |
484 | 0 | return TRUE; |
485 | 0 | } |
486 | | |
487 | | /************************************************************************/ |
488 | | /* OGRGeoJSONDriverIdentify() */ |
489 | | /************************************************************************/ |
490 | | |
491 | | static int OGRGeoJSONDriverIdentify(GDALOpenInfo *poOpenInfo) |
492 | 0 | { |
493 | 0 | GeoJSONSourceType nSrcType; |
494 | 0 | return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType); |
495 | 0 | } |
496 | | |
497 | | /************************************************************************/ |
498 | | /* Open() */ |
499 | | /************************************************************************/ |
500 | | |
501 | | static GDALDataset *OGRGeoJSONDriverOpen(GDALOpenInfo *poOpenInfo) |
502 | 0 | { |
503 | 0 | GeoJSONSourceType nSrcType; |
504 | 0 | if (OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE) |
505 | 0 | { |
506 | 0 | return nullptr; |
507 | 0 | } |
508 | 0 | return OGRGeoJSONDriverOpenInternal(poOpenInfo, nSrcType, "GeoJSON"); |
509 | 0 | } |
510 | | |
511 | | /************************************************************************/ |
512 | | /* OGRGeoJSONDriverOpenInternal() */ |
513 | | /************************************************************************/ |
514 | | |
515 | | GDALDataset *OGRGeoJSONDriverOpenInternal(GDALOpenInfo *poOpenInfo, |
516 | | GeoJSONSourceType nSrcType, |
517 | | const char *pszJSonFlavor) |
518 | 0 | { |
519 | 0 | auto poDS = std::make_unique<OGRGeoJSONDataSource>(); |
520 | | |
521 | | /* -------------------------------------------------------------------- */ |
522 | | /* Processing configuration options. */ |
523 | | /* -------------------------------------------------------------------- */ |
524 | | |
525 | | // TODO: Currently, options are based on environment variables. |
526 | | // This is workaround for not yet implemented Andrey's concept |
527 | | // described in document 'RFC 10: OGR Open Parameters'. |
528 | |
|
529 | 0 | poDS->SetGeometryTranslation(OGRGeoJSONDataSource::eGeometryPreserve); |
530 | 0 | const char *pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", nullptr); |
531 | 0 | if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES")) |
532 | 0 | { |
533 | 0 | poDS->SetGeometryTranslation( |
534 | 0 | OGRGeoJSONDataSource::eGeometryAsCollection); |
535 | 0 | } |
536 | |
|
537 | 0 | poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesPreserve); |
538 | 0 | pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", nullptr); |
539 | 0 | if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES")) |
540 | 0 | { |
541 | 0 | poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesSkip); |
542 | 0 | } |
543 | | |
544 | | /* -------------------------------------------------------------------- */ |
545 | | /* Open and start processing GeoJSON datasource to OGR objects. */ |
546 | | /* -------------------------------------------------------------------- */ |
547 | 0 | if (!poDS->Open(poOpenInfo, nSrcType, pszJSonFlavor)) |
548 | 0 | { |
549 | 0 | poDS.reset(); |
550 | 0 | } |
551 | |
|
552 | 0 | if (poDS != nullptr && poDS->HasOtherPages()) |
553 | 0 | { |
554 | 0 | const char *pszFilename = poOpenInfo->pszFilename; |
555 | 0 | if (STARTS_WITH_CI(pszFilename, "ESRIJSON:")) |
556 | 0 | pszFilename += strlen("ESRIJSON:"); |
557 | 0 | if (STARTS_WITH(pszFilename, "http") || |
558 | 0 | STARTS_WITH(pszFilename, "/vsimem/")) |
559 | 0 | { |
560 | 0 | const char *pszFSP = CSLFetchNameValue(poOpenInfo->papszOpenOptions, |
561 | 0 | "FEATURE_SERVER_PAGING"); |
562 | 0 | const bool bHasResultOffset = |
563 | 0 | !CPLURLGetValue(pszFilename, "resultOffset").empty(); |
564 | 0 | if ((!bHasResultOffset && |
565 | 0 | (pszFSP == nullptr || CPLTestBool(pszFSP))) || |
566 | 0 | (bHasResultOffset && pszFSP != nullptr && CPLTestBool(pszFSP))) |
567 | 0 | { |
568 | 0 | return new OGRESRIFeatureServiceDataset( |
569 | 0 | pszFilename, std::move(poDS), nSrcType); |
570 | 0 | } |
571 | 0 | } |
572 | 0 | } |
573 | | |
574 | 0 | return poDS.release(); |
575 | 0 | } |
576 | | |
577 | | /************************************************************************/ |
578 | | /* Create() */ |
579 | | /************************************************************************/ |
580 | | |
581 | | static GDALDataset *OGRGeoJSONDriverCreate(const char *pszName, |
582 | | int /* nBands */, int /* nXSize */, |
583 | | int /* nYSize */, |
584 | | GDALDataType /* eDT */, |
585 | | char **papszOptions) |
586 | 0 | { |
587 | 0 | OGRGeoJSONDataSource *poDS = new OGRGeoJSONDataSource(); |
588 | |
|
589 | 0 | if (!poDS->Create(pszName, papszOptions)) |
590 | 0 | { |
591 | 0 | delete poDS; |
592 | 0 | poDS = nullptr; |
593 | 0 | } |
594 | |
|
595 | 0 | return poDS; |
596 | 0 | } |
597 | | |
598 | | /************************************************************************/ |
599 | | /* Delete() */ |
600 | | /************************************************************************/ |
601 | | |
602 | | static CPLErr OGRGeoJSONDriverDelete(const char *pszFilename) |
603 | 0 | { |
604 | 0 | if (VSIUnlink(pszFilename) == 0) |
605 | 0 | { |
606 | 0 | return CE_None; |
607 | 0 | } |
608 | | |
609 | 0 | CPLDebug("GeoJSON", "Failed to delete \'%s\'", pszFilename); |
610 | |
|
611 | 0 | return CE_Failure; |
612 | 0 | } |
613 | | |
614 | | /************************************************************************/ |
615 | | /* OGRGeoJSONDriverStoreContent() */ |
616 | | /************************************************************************/ |
617 | | |
618 | | void OGRGeoJSONDriverStoreContent(const char *pszSource, char *pszText) |
619 | 0 | { |
620 | 0 | CPLMutexHolderD(&ghMutex); |
621 | 0 | CPLAssert(pszSource); |
622 | 0 | CPLAssert(pszText); |
623 | | |
624 | 0 | CPLFree(gpszSource); |
625 | 0 | CPLFree(gpszText); |
626 | 0 | gpszSource = CPLStrdup(pszSource); |
627 | 0 | gpszText = pszText; |
628 | 0 | } |
629 | | |
630 | | /************************************************************************/ |
631 | | /* OGRGeoJSONDriverStealStoredContent() */ |
632 | | /************************************************************************/ |
633 | | |
634 | | char *OGRGeoJSONDriverStealStoredContent(const char *pszSource) |
635 | 0 | { |
636 | 0 | CPLMutexHolderD(&ghMutex); |
637 | 0 | if (gpszSource && EQUAL(pszSource, gpszSource)) |
638 | 0 | { |
639 | 0 | char *pszRet = gpszText; |
640 | 0 | CPLFree(gpszSource); |
641 | 0 | gpszSource = nullptr; |
642 | 0 | gpszText = nullptr; |
643 | 0 | return pszRet; |
644 | 0 | } |
645 | 0 | return nullptr; |
646 | 0 | } |
647 | | |
648 | | /************************************************************************/ |
649 | | /* OGRGeoJSONDriverUnload() */ |
650 | | /************************************************************************/ |
651 | | |
652 | | static void OGRGeoJSONDriverUnload(GDALDriver *) |
653 | 0 | { |
654 | 0 | if (ghMutex) |
655 | 0 | CPLDestroyMutex(ghMutex); |
656 | 0 | ghMutex = nullptr; |
657 | 0 | CPLFree(gpszSource); |
658 | 0 | CPLFree(gpszText); |
659 | 0 | gpszSource = nullptr; |
660 | 0 | gpszText = nullptr; |
661 | 0 | } |
662 | | |
663 | | /************************************************************************/ |
664 | | /* RegisterOGRGeoJSON() */ |
665 | | /************************************************************************/ |
666 | | |
667 | | void RegisterOGRGeoJSON() |
668 | 0 | { |
669 | 0 | if (!GDAL_CHECK_VERSION("OGR/GeoJSON driver")) |
670 | 0 | return; |
671 | | |
672 | 0 | if (GDALGetDriverByName("GeoJSON") != nullptr) |
673 | 0 | return; |
674 | | |
675 | 0 | GDALDriver *poDriver = new GDALDriver(); |
676 | |
|
677 | 0 | poDriver->SetDescription("GeoJSON"); |
678 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); |
679 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES"); |
680 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES"); |
681 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES"); |
682 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES"); |
683 | 0 | poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name Type"); |
684 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); |
685 | 0 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON"); |
686 | 0 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "json geojson"); |
687 | 0 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, |
688 | 0 | "drivers/vector/geojson.html"); |
689 | |
|
690 | 0 | poDriver->SetMetadataItem( |
691 | 0 | GDAL_DMD_OPENOPTIONLIST, |
692 | 0 | "<OpenOptionList>" |
693 | 0 | " <Option name='FLATTEN_NESTED_ATTRIBUTES' type='boolean' " |
694 | 0 | "description='Whether to recursively explore nested objects and " |
695 | 0 | "produce flatten OGR attributes' default='NO'/>" |
696 | 0 | " <Option name='NESTED_ATTRIBUTE_SEPARATOR' type='string' " |
697 | 0 | "description='Separator between components of nested attributes' " |
698 | 0 | "default='_'/>" |
699 | 0 | " <Option name='FEATURE_SERVER_PAGING' type='boolean' " |
700 | 0 | "description='Whether to automatically scroll through results with a " |
701 | 0 | "ArcGIS Feature Service endpoint'/>" |
702 | 0 | " <Option name='NATIVE_DATA' type='boolean' description='Whether to " |
703 | 0 | "store the native JSon representation at FeatureCollection and Feature " |
704 | 0 | "level' default='NO'/>" |
705 | 0 | " <Option name='ARRAY_AS_STRING' type='boolean' description='Whether " |
706 | 0 | "to expose JSon arrays of strings, integers or reals as a OGR String' " |
707 | 0 | "default='NO'/>" |
708 | 0 | " <Option name='DATE_AS_STRING' type='boolean' description='Whether " |
709 | 0 | "to expose date/time/date-time content using dedicated OGR " |
710 | 0 | "date/time/date-time types or as a OGR String' default='NO'/>" |
711 | 0 | " <Option name='FOREIGN_MEMBERS' type='string-select' " |
712 | 0 | "description='Whether and how foreign members at the feature level " |
713 | 0 | "should be processed as OGR fields' default='AUTO'>" |
714 | 0 | " <Value>AUTO</Value>" |
715 | 0 | " <Value>ALL</Value>" |
716 | 0 | " <Value>NONE</Value>" |
717 | 0 | " <Value>STAC</Value>" |
718 | 0 | " </Option>" |
719 | 0 | " <Option name='OGR_SCHEMA' type='string' description='" |
720 | 0 | "Partially or totally overrides the auto-detected schema to use for " |
721 | 0 | "creating the layer. " |
722 | 0 | "The overrides are defined as a JSON list of field definitions. " |
723 | 0 | "This can be a filename or a JSON string or a URL.'/>" |
724 | 0 | "</OpenOptionList>"); |
725 | |
|
726 | 0 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, |
727 | 0 | "<CreationOptionList/>"); |
728 | |
|
729 | 0 | poDriver->SetMetadataItem( |
730 | 0 | GDAL_DS_LAYER_CREATIONOPTIONLIST, |
731 | 0 | "<LayerCreationOptionList>" |
732 | 0 | " <Option name='WRITE_BBOX' type='boolean' description='whether to " |
733 | 0 | "write a bbox property with the bounding box of the geometries at the " |
734 | 0 | "feature and feature collection level' default='NO'/>" |
735 | 0 | " <Option name='COORDINATE_PRECISION' type='int' description='Number " |
736 | 0 | "of decimal for coordinates. Default is 15 for GJ2008 and 7 for " |
737 | 0 | "RFC7946'/>" |
738 | 0 | " <Option name='SIGNIFICANT_FIGURES' type='int' description='Number " |
739 | 0 | "of significant figures for floating-point values' default='17'/>" |
740 | 0 | " <Option name='NATIVE_DATA' type='string' " |
741 | 0 | "description='FeatureCollection level elements.'/>" |
742 | 0 | " <Option name='NATIVE_MEDIA_TYPE' type='string' description='Format " |
743 | 0 | "of NATIVE_DATA. Must be \"application/vnd.geo+json\", otherwise " |
744 | 0 | "NATIVE_DATA will be ignored.'/>" |
745 | 0 | " <Option name='RFC7946' type='boolean' description='Whether to use " |
746 | 0 | "RFC 7946 standard. Otherwise GeoJSON 2008 initial version will be " |
747 | 0 | "used' default='NO'/>" |
748 | 0 | " <Option name='WRAPDATELINE' type='boolean' description='Whether to " |
749 | 0 | "apply heuristics to split geometries that cross dateline.' " |
750 | 0 | "default='YES'/>" |
751 | 0 | " <Option name='WRITE_NAME' type='boolean' description='Whether to " |
752 | 0 | "write a "name" property at feature collection level with " |
753 | 0 | "layer name' default='YES'/>" |
754 | 0 | " <Option name='DESCRIPTION' type='string' description='(Long) " |
755 | 0 | "description to write in a "description" property at feature " |
756 | 0 | "collection level'/>" |
757 | 0 | " <Option name='ID_FIELD' type='string' description='Name of the " |
758 | 0 | "source field that must be used as the id member of Feature features'/>" |
759 | 0 | " <Option name='ID_TYPE' type='string-select' description='Type of " |
760 | 0 | "the id member of Feature features'>" |
761 | 0 | " <Value>AUTO</Value>" |
762 | 0 | " <Value>String</Value>" |
763 | 0 | " <Value>Integer</Value>" |
764 | 0 | " </Option>" |
765 | 0 | " <Option name='ID_GENERATE' type='boolean' " |
766 | 0 | "description='Auto-generate feature ids' />" |
767 | 0 | " <Option name='WRITE_NON_FINITE_VALUES' type='boolean' " |
768 | 0 | "description='Whether to write NaN / Infinity values' default='NO'/>" |
769 | 0 | " <Option name='AUTODETECT_JSON_STRINGS' type='boolean' " |
770 | 0 | "description='Whether to try to interpret string fields as JSON " |
771 | 0 | "arrays or objects' default='YES'/>" |
772 | 0 | " <Option name='FOREIGN_MEMBERS_FEATURE' type='string' " |
773 | 0 | "description='Extra JSON content to add in each feature as a foreign " |
774 | 0 | "members'/>" |
775 | 0 | " <Option name='FOREIGN_MEMBERS_COLLECTION' type='string' " |
776 | 0 | "description='Extra JSON content to add to the feature collection as " |
777 | 0 | "a foreign members'/>" |
778 | 0 | "</LayerCreationOptionList>"); |
779 | |
|
780 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
781 | 0 | poDriver->SetMetadataItem( |
782 | 0 | GDAL_DMD_CREATIONFIELDDATATYPES, |
783 | 0 | "Integer Integer64 Real String IntegerList " |
784 | 0 | "Integer64List RealList StringList Date DateTime"); |
785 | 0 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean"); |
786 | 0 | poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE"); |
787 | |
|
788 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES"); |
789 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES"); |
790 | |
|
791 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES"); |
792 | 0 | poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "Features"); |
793 | |
|
794 | 0 | poDriver->pfnOpen = OGRGeoJSONDriverOpen; |
795 | 0 | poDriver->pfnIdentify = OGRGeoJSONDriverIdentify; |
796 | 0 | poDriver->pfnCreate = OGRGeoJSONDriverCreate; |
797 | 0 | poDriver->pfnDelete = OGRGeoJSONDriverDelete; |
798 | 0 | poDriver->pfnUnloadDriver = OGRGeoJSONDriverUnload; |
799 | |
|
800 | 0 | GetGDALDriverManager()->RegisterDriver(poDriver); |
801 | |
|
802 | | #ifdef BUILT_AS_PLUGIN |
803 | | RegisterOGRTopoJSON(); |
804 | | RegisterOGRESRIJSON(); |
805 | | RegisterOGRGeoJSONSeq(); |
806 | | #endif |
807 | 0 | } |