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 48294 : 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 48294 : nSrcType = GeoJSONGetSourceType(poOpenInfo);
451 48294 : if (nSrcType == eGeoJSONSourceUnknown)
452 : {
453 47410 : const char *pszHeader =
454 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
455 47410 : if (pszHeader && STARTS_WITH(pszHeader, "{\"properties\":{"))
456 3 : return GDAL_IDENTIFY_UNKNOWN;
457 :
458 47407 : return FALSE;
459 : }
460 :
461 884 : if (nSrcType == eGeoJSONSourceService)
462 : {
463 31 : if (poOpenInfo->IsSingleAllowedDriver("GeoJSON"))
464 1 : return TRUE;
465 30 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSON:"))
466 : {
467 30 : 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 853 : const char *pszHeader =
474 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
475 1434 : if (pszHeader != nullptr &&
476 581 : strstr(pszHeader, "\"stac_extensions\"") != nullptr &&
477 1436 : 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 851 : return TRUE;
486 : }
487 :
488 : /************************************************************************/
489 : /* OGRGeoJSONDriverIdentify() */
490 : /************************************************************************/
491 :
492 47863 : static int OGRGeoJSONDriverIdentify(GDALOpenInfo *poOpenInfo)
493 : {
494 : GeoJSONSourceType nSrcType;
495 95726 : return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType);
496 : }
497 :
498 : /************************************************************************/
499 : /* Open() */
500 : /************************************************************************/
501 :
502 431 : static GDALDataset *OGRGeoJSONDriverOpen(GDALOpenInfo *poOpenInfo)
503 : {
504 : GeoJSONSourceType nSrcType;
505 431 : if (OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
506 : {
507 0 : return nullptr;
508 : }
509 431 : return OGRGeoJSONDriverOpenInternal(poOpenInfo, nSrcType, "GeoJSON");
510 : }
511 :
512 : /************************************************************************/
513 : /* OGRGeoJSONDriverOpenInternal() */
514 : /************************************************************************/
515 :
516 463 : GDALDataset *OGRGeoJSONDriverOpenInternal(GDALOpenInfo *poOpenInfo,
517 : GeoJSONSourceType nSrcType,
518 : const char *pszJSonFlavor)
519 : {
520 926 : 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 463 : poDS->SetGeometryTranslation(OGRGeoJSONDataSource::eGeometryPreserve);
531 463 : const char *pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", nullptr);
532 463 : if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES"))
533 : {
534 0 : poDS->SetGeometryTranslation(
535 : OGRGeoJSONDataSource::eGeometryAsCollection);
536 : }
537 :
538 463 : poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesPreserve);
539 463 : pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", nullptr);
540 463 : 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 463 : if (!poDS->Open(poOpenInfo, nSrcType, pszJSonFlavor))
549 : {
550 25 : poDS.reset();
551 : }
552 :
553 463 : 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 455 : 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 38 : char *OGRGeoJSONDriverStealStoredContent(const char *pszSource)
636 : {
637 76 : CPLMutexHolderD(&ghMutex);
638 38 : 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 4 : return nullptr;
647 : }
648 :
649 : /************************************************************************/
650 : /* OGRGeoJSONDriverUnload() */
651 : /************************************************************************/
652 :
653 933 : static void OGRGeoJSONDriverUnload(GDALDriver *)
654 : {
655 933 : if (ghMutex)
656 0 : CPLDestroyMutex(ghMutex);
657 933 : ghMutex = nullptr;
658 933 : CPLFree(gpszSource);
659 933 : CPLFree(gpszText);
660 933 : gpszSource = nullptr;
661 933 : gpszText = nullptr;
662 933 : }
663 :
664 : /************************************************************************/
665 : /* RegisterOGRGeoJSON() */
666 : /************************************************************************/
667 :
668 1595 : void RegisterOGRGeoJSON()
669 : {
670 1595 : if (!GDAL_CHECK_VERSION("OGR/GeoJSON driver"))
671 0 : return;
672 :
673 1595 : if (GDALGetDriverByName("GeoJSON") != nullptr)
674 302 : return;
675 :
676 1293 : GDALDriver *poDriver = new GDALDriver();
677 :
678 1293 : poDriver->SetDescription("GeoJSON");
679 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
680 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
681 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
682 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
683 1293 : poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
684 1293 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name Type");
685 1293 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
686 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON");
687 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "json geojson");
688 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
689 1293 : "drivers/vector/geojson.html");
690 :
691 1293 : 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 1293 : "</OpenOptionList>");
713 :
714 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
715 1293 : "<CreationOptionList/>");
716 :
717 1293 : poDriver->SetMetadataItem(
718 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
719 : "<LayerCreationOptionList>"
720 : " <Option name='WRITE_BBOX' type='boolean' description='whether to "
721 : "write a bbox property with the bounding box of the geometries at the "
722 : "feature and feature collection level' default='NO'/>"
723 : " <Option name='COORDINATE_PRECISION' type='int' description='Number "
724 : "of decimal for coordinates. Default is 15 for GJ2008 and 7 for "
725 : "RFC7946'/>"
726 : " <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
727 : "of significant figures for floating-point values' default='17'/>"
728 : " <Option name='NATIVE_DATA' type='string' "
729 : "description='FeatureCollection level elements.'/>"
730 : " <Option name='NATIVE_MEDIA_TYPE' type='string' description='Format "
731 : "of NATIVE_DATA. Must be \"application/vnd.geo+json\", otherwise "
732 : "NATIVE_DATA will be ignored.'/>"
733 : " <Option name='RFC7946' type='boolean' description='Whether to use "
734 : "RFC 7946 standard. Otherwise GeoJSON 2008 initial version will be "
735 : "used' default='NO'/>"
736 : " <Option name='WRAPDATELINE' type='boolean' description='Whether to "
737 : "apply heuristics to split geometries that cross dateline.' "
738 : "default='YES'/>"
739 : " <Option name='WRITE_NAME' type='boolean' description='Whether to "
740 : "write a "name" property at feature collection level with "
741 : "layer name' default='YES'/>"
742 : " <Option name='DESCRIPTION' type='string' description='(Long) "
743 : "description to write in a "description" property at feature "
744 : "collection level'/>"
745 : " <Option name='ID_FIELD' type='string' description='Name of the "
746 : "source field that must be used as the id member of Feature features'/>"
747 : " <Option name='ID_TYPE' type='string-select' description='Type of "
748 : "the id member of Feature features'>"
749 : " <Value>AUTO</Value>"
750 : " <Value>String</Value>"
751 : " <Value>Integer</Value>"
752 : " </Option>"
753 : " <Option name='ID_GENERATE' type='boolean' "
754 : "description='Auto-generate feature ids' />"
755 : " <Option name='WRITE_NON_FINITE_VALUES' type='boolean' "
756 : "description='Whether to write NaN / Infinity values' default='NO'/>"
757 : " <Option name='AUTODETECT_JSON_STRINGS' type='boolean' "
758 : "description='Whether to try to interpret string fields as JSON "
759 : "arrays or objects' default='YES'/>"
760 : " <Option name='FOREIGN_MEMBERS_FEATURE' type='string' "
761 : "description='Extra JSON content to add in each feature as a foreign "
762 : "members'/>"
763 : " <Option name='FOREIGN_MEMBERS_COLLECTION' type='string' "
764 : "description='Extra JSON content to add to the feature collection as "
765 : "a foreign members'/>"
766 1293 : "</LayerCreationOptionList>");
767 :
768 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
769 1293 : poDriver->SetMetadataItem(
770 : GDAL_DMD_CREATIONFIELDDATATYPES,
771 : "Integer Integer64 Real String IntegerList "
772 1293 : "Integer64List RealList StringList Date DateTime");
773 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
774 1293 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
775 1293 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
776 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES");
777 1293 : poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
778 :
779 1293 : poDriver->pfnOpen = OGRGeoJSONDriverOpen;
780 1293 : poDriver->pfnIdentify = OGRGeoJSONDriverIdentify;
781 1293 : poDriver->pfnCreate = OGRGeoJSONDriverCreate;
782 1293 : poDriver->pfnDelete = OGRGeoJSONDriverDelete;
783 1293 : poDriver->pfnUnloadDriver = OGRGeoJSONDriverUnload;
784 :
785 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
786 :
787 : #ifdef BUILT_AS_PLUGIN
788 : RegisterOGRTopoJSON();
789 : RegisterOGRESRIJSON();
790 : RegisterOGRGeoJSONSeq();
791 : #endif
792 : }
|