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