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