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