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