Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CSW Translator
4 : * Purpose: Implements OGRCSWDriver.
5 : * Author: Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogrsf_frmts.h"
30 : #include "cpl_conv.h"
31 : #include "cpl_http.h"
32 : #include "ogr_wfs.h"
33 : #include "ogr_p.h"
34 : #include "gmlutils.h"
35 :
36 : extern "C" void RegisterOGRCSW();
37 :
38 : /************************************************************************/
39 : /* OGRCSWLayer */
40 : /************************************************************************/
41 :
42 : class OGRCSWDataSource;
43 :
44 : class OGRCSWLayer final : public OGRLayer
45 : {
46 : OGRCSWDataSource *poDS;
47 : OGRFeatureDefn *poFeatureDefn;
48 :
49 : GDALDataset *poBaseDS;
50 : OGRLayer *poBaseLayer;
51 :
52 : int nPagingStartIndex;
53 : int nFeatureRead;
54 : int nFeaturesInCurrentPage;
55 :
56 : CPLString osQuery;
57 : CPLString osCSWWhere;
58 :
59 : GDALDataset *FetchGetRecords();
60 : GIntBig GetFeatureCountWithHits();
61 : void BuildQuery();
62 :
63 : public:
64 : explicit OGRCSWLayer(OGRCSWDataSource *poDS);
65 : virtual ~OGRCSWLayer();
66 :
67 : virtual void ResetReading() override;
68 : virtual OGRFeature *GetNextFeature() override;
69 : virtual GIntBig GetFeatureCount(int bForce = FALSE) override;
70 :
71 3 : virtual OGRFeatureDefn *GetLayerDefn() override
72 : {
73 3 : return poFeatureDefn;
74 : }
75 :
76 1 : virtual int TestCapability(const char *) override
77 : {
78 1 : return FALSE;
79 : }
80 :
81 : virtual void SetSpatialFilter(OGRGeometry *) override;
82 :
83 0 : virtual void SetSpatialFilter(int iGeomField, OGRGeometry *poGeom) override
84 : {
85 0 : OGRLayer::SetSpatialFilter(iGeomField, poGeom);
86 0 : }
87 :
88 : virtual OGRErr SetAttributeFilter(const char *) override;
89 : };
90 :
91 : /************************************************************************/
92 : /* OGRCSWDataSource */
93 : /************************************************************************/
94 :
95 : class OGRCSWDataSource final : public OGRDataSource
96 : {
97 : char *pszName;
98 : CPLString osBaseURL;
99 : CPLString osVersion;
100 : CPLString osElementSetName;
101 : CPLString osOutputSchema;
102 : int nMaxRecords;
103 :
104 : OGRCSWLayer *poLayer;
105 : bool bFullExtentRecordsAsNonSpatial;
106 :
107 : CPLHTTPResult *SendGetCapabilities();
108 :
109 : public:
110 : OGRCSWDataSource();
111 : virtual ~OGRCSWDataSource();
112 :
113 : int Open(const char *pszFilename, char **papszOpenOptions);
114 :
115 0 : virtual const char *GetName() override
116 : {
117 0 : return pszName;
118 : }
119 :
120 1 : virtual int GetLayerCount() override
121 : {
122 1 : return poLayer != nullptr;
123 : }
124 :
125 : virtual OGRLayer *GetLayer(int) override;
126 :
127 1 : virtual int TestCapability(const char *) override
128 : {
129 1 : return FALSE;
130 : }
131 :
132 : static CPLHTTPResult *HTTPFetch(const char *pszURL, const char *pszPost);
133 :
134 39 : const CPLString &GetBaseURL()
135 : {
136 39 : return osBaseURL;
137 : }
138 :
139 39 : const CPLString &GetVersion()
140 : {
141 39 : return osVersion;
142 : }
143 :
144 39 : const CPLString &GetElementSetName()
145 : {
146 39 : return osElementSetName;
147 : }
148 :
149 60 : const CPLString &GetOutputSchema()
150 : {
151 60 : return osOutputSchema;
152 : }
153 :
154 10 : bool FullExtentRecordsAsNonSpatial()
155 : {
156 10 : return bFullExtentRecordsAsNonSpatial;
157 : }
158 :
159 31 : int GetMaxRecords()
160 : {
161 31 : return nMaxRecords;
162 : }
163 : };
164 :
165 : /************************************************************************/
166 : /* OGRCSWLayer() */
167 : /************************************************************************/
168 :
169 4 : OGRCSWLayer::OGRCSWLayer(OGRCSWDataSource *poDSIn)
170 4 : : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("records")),
171 : poBaseDS(nullptr), poBaseLayer(nullptr), nPagingStartIndex(0),
172 8 : nFeatureRead(0), nFeaturesInCurrentPage(0)
173 : {
174 4 : SetDescription(poFeatureDefn->GetName());
175 4 : poFeatureDefn->Reference();
176 4 : poFeatureDefn->SetGeomType(wkbPolygon);
177 : OGRSpatialReference *poSRS =
178 4 : new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
179 4 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
180 4 : poFeatureDefn->GetGeomFieldDefn(0)->SetName("boundingbox");
181 4 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
182 : {
183 8 : OGRFieldDefn oField("identifier", OFTString);
184 4 : poFeatureDefn->AddFieldDefn(&oField);
185 : }
186 : {
187 8 : OGRFieldDefn oField("other_identifiers", OFTStringList);
188 4 : poFeatureDefn->AddFieldDefn(&oField);
189 : }
190 : {
191 8 : OGRFieldDefn oField("title", OFTString);
192 4 : poFeatureDefn->AddFieldDefn(&oField);
193 : }
194 : {
195 8 : OGRFieldDefn oField("type", OFTString);
196 4 : poFeatureDefn->AddFieldDefn(&oField);
197 : }
198 : {
199 8 : OGRFieldDefn oField("subject", OFTString);
200 4 : poFeatureDefn->AddFieldDefn(&oField);
201 : }
202 : {
203 8 : OGRFieldDefn oField("other_subjects", OFTStringList);
204 4 : poFeatureDefn->AddFieldDefn(&oField);
205 : }
206 : {
207 8 : OGRFieldDefn oField("references", OFTString);
208 4 : poFeatureDefn->AddFieldDefn(&oField);
209 : }
210 : {
211 8 : OGRFieldDefn oField("other_references", OFTStringList);
212 4 : poFeatureDefn->AddFieldDefn(&oField);
213 : }
214 : {
215 8 : OGRFieldDefn oField("modified", OFTString);
216 4 : poFeatureDefn->AddFieldDefn(&oField);
217 : }
218 : {
219 8 : OGRFieldDefn oField("abstract", OFTString);
220 4 : poFeatureDefn->AddFieldDefn(&oField);
221 : }
222 : {
223 8 : OGRFieldDefn oField("date", OFTString);
224 4 : poFeatureDefn->AddFieldDefn(&oField);
225 : }
226 : {
227 8 : OGRFieldDefn oField("language", OFTString);
228 4 : poFeatureDefn->AddFieldDefn(&oField);
229 : }
230 : {
231 8 : OGRFieldDefn oField("rights", OFTString);
232 4 : poFeatureDefn->AddFieldDefn(&oField);
233 : }
234 : {
235 8 : OGRFieldDefn oField("format", OFTString);
236 4 : poFeatureDefn->AddFieldDefn(&oField);
237 : }
238 : {
239 8 : OGRFieldDefn oField("other_formats", OFTStringList);
240 4 : poFeatureDefn->AddFieldDefn(&oField);
241 : }
242 : {
243 8 : OGRFieldDefn oField("creator", OFTString);
244 4 : poFeatureDefn->AddFieldDefn(&oField);
245 : }
246 : {
247 8 : OGRFieldDefn oField("source", OFTString);
248 4 : poFeatureDefn->AddFieldDefn(&oField);
249 : }
250 : {
251 8 : OGRFieldDefn oField("anytext", OFTString);
252 4 : poFeatureDefn->AddFieldDefn(&oField);
253 : }
254 4 : if (!poDS->GetOutputSchema().empty())
255 : {
256 6 : OGRFieldDefn oField("raw_xml", OFTString);
257 3 : poFeatureDefn->AddFieldDefn(&oField);
258 : }
259 :
260 4 : poSRS->Release();
261 4 : }
262 :
263 : /************************************************************************/
264 : /* ~OGRCSWLayer() */
265 : /************************************************************************/
266 :
267 8 : OGRCSWLayer::~OGRCSWLayer()
268 : {
269 4 : poFeatureDefn->Release();
270 4 : GDALClose(poBaseDS);
271 8 : CPLString osTmpDirName = CPLSPrintf("/vsimem/tempcsw_%p", this);
272 4 : OGRWFSRecursiveUnlink(osTmpDirName);
273 8 : }
274 :
275 : /************************************************************************/
276 : /* ResetReading() */
277 : /************************************************************************/
278 :
279 28 : void OGRCSWLayer::ResetReading()
280 : {
281 28 : nPagingStartIndex = 0;
282 28 : nFeatureRead = 0;
283 28 : nFeaturesInCurrentPage = 0;
284 28 : GDALClose(poBaseDS);
285 28 : poBaseDS = nullptr;
286 28 : poBaseLayer = nullptr;
287 28 : }
288 :
289 : /************************************************************************/
290 : /* GetNextFeature() */
291 : /************************************************************************/
292 :
293 38 : OGRFeature *OGRCSWLayer::GetNextFeature()
294 : {
295 : while (true)
296 : {
297 38 : if (nFeatureRead == nPagingStartIndex + nFeaturesInCurrentPage)
298 : {
299 31 : nPagingStartIndex = nFeatureRead;
300 :
301 31 : GDALClose(poBaseDS);
302 31 : poBaseLayer = nullptr;
303 :
304 31 : poBaseDS = FetchGetRecords();
305 31 : if (poBaseDS)
306 : {
307 16 : poBaseLayer = poBaseDS->GetLayer(0);
308 16 : poBaseLayer->ResetReading();
309 16 : nFeaturesInCurrentPage = (int)poBaseLayer->GetFeatureCount();
310 : }
311 : }
312 38 : if (!poBaseLayer)
313 15 : return nullptr;
314 :
315 23 : OGRFeature *poSrcFeature = poBaseLayer->GetNextFeature();
316 23 : if (poSrcFeature == nullptr)
317 0 : return nullptr;
318 23 : nFeatureRead++;
319 :
320 23 : OGRFeature *poNewFeature = new OGRFeature(poFeatureDefn);
321 :
322 440 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
323 : {
324 : const char *pszFieldname =
325 417 : poFeatureDefn->GetFieldDefn(i)->GetNameRef();
326 417 : int iSrcField = poSrcFeature->GetFieldIndex(pszFieldname);
327 : /* http://www.paikkatietohakemisto.fi/geonetwork/srv/en/csw returns
328 : * URI ... */
329 417 : if (iSrcField < 0 && strcmp(pszFieldname, "references") == 0)
330 9 : iSrcField = poSrcFeature->GetFieldIndex("URI");
331 417 : if (iSrcField >= 0 && poSrcFeature->IsFieldSetAndNotNull(iSrcField))
332 : {
333 73 : OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
334 : OGRFieldType eSrcType =
335 73 : poSrcFeature->GetFieldDefnRef(iSrcField)->GetType();
336 73 : if (eType == eSrcType)
337 : {
338 45 : poNewFeature->SetField(
339 45 : i, poSrcFeature->GetRawFieldRef(iSrcField));
340 : }
341 : else
342 : {
343 28 : if (eType == OFTString && eSrcType == OFTStringList &&
344 28 : strcmp(pszFieldname, "identifier") == 0)
345 : {
346 : char **papszValues =
347 7 : poSrcFeature->GetFieldAsStringList(iSrcField);
348 7 : poNewFeature->SetField("identifier", *papszValues);
349 7 : if (papszValues[1])
350 7 : poNewFeature->SetField("other_identifiers",
351 7 : papszValues + 1);
352 : }
353 21 : else if (eType == OFTString && eSrcType == OFTStringList &&
354 21 : strcmp(pszFieldname, "subject") == 0)
355 : {
356 : char **papszValues =
357 7 : poSrcFeature->GetFieldAsStringList(iSrcField);
358 7 : poNewFeature->SetField("subject", *papszValues);
359 7 : if (papszValues[1])
360 7 : poNewFeature->SetField("other_subjects",
361 7 : papszValues + 1);
362 : }
363 14 : else if (eType == OFTString && eSrcType == OFTStringList &&
364 14 : strcmp(pszFieldname, "references") == 0)
365 : {
366 : char **papszValues =
367 7 : poSrcFeature->GetFieldAsStringList(iSrcField);
368 7 : poNewFeature->SetField("references", *papszValues);
369 7 : if (papszValues[1])
370 7 : poNewFeature->SetField("other_references",
371 7 : papszValues + 1);
372 : }
373 7 : else if (eType == OFTString && eSrcType == OFTStringList &&
374 7 : strcmp(pszFieldname, "format") == 0)
375 : {
376 : char **papszValues =
377 7 : poSrcFeature->GetFieldAsStringList(iSrcField);
378 7 : poNewFeature->SetField("format", *papszValues);
379 7 : if (papszValues[1])
380 7 : poNewFeature->SetField("other_formats",
381 7 : papszValues + 1);
382 : }
383 : else
384 0 : poNewFeature->SetField(
385 : i, poSrcFeature->GetFieldAsString(iSrcField));
386 : }
387 : }
388 : }
389 :
390 23 : OGRGeometry *poGeom = poSrcFeature->StealGeometry();
391 23 : if (poGeom)
392 : {
393 10 : if (poDS->FullExtentRecordsAsNonSpatial())
394 : {
395 0 : OGREnvelope sEnvelope;
396 0 : poGeom->getEnvelope(&sEnvelope);
397 0 : if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
398 0 : sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
399 : {
400 0 : delete poGeom;
401 0 : poGeom = nullptr;
402 : }
403 : }
404 10 : if (poGeom)
405 : {
406 10 : poGeom->assignSpatialReference(
407 10 : poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
408 10 : poNewFeature->SetGeometryDirectly(poGeom);
409 : }
410 : }
411 :
412 23 : poNewFeature->SetFID(nFeatureRead);
413 23 : delete poSrcFeature;
414 :
415 23 : if (osCSWWhere.empty() && m_poAttrQuery != nullptr &&
416 0 : !m_poAttrQuery->Evaluate(poNewFeature))
417 : {
418 0 : delete poNewFeature;
419 : }
420 : else
421 : {
422 23 : return poNewFeature;
423 : }
424 0 : }
425 : }
426 :
427 : /************************************************************************/
428 : /* GetFeatureCount() */
429 : /************************************************************************/
430 :
431 8 : GIntBig OGRCSWLayer::GetFeatureCount(int bForce)
432 : {
433 8 : GIntBig nFeatures = GetFeatureCountWithHits();
434 8 : if (nFeatures >= 0)
435 2 : return nFeatures;
436 6 : return OGRLayer::GetFeatureCount(bForce);
437 : }
438 :
439 : /************************************************************************/
440 : /* GetFeatureCountWithHits() */
441 : /************************************************************************/
442 :
443 8 : GIntBig OGRCSWLayer::GetFeatureCountWithHits()
444 : {
445 : CPLString osPost = CPLSPrintf(
446 : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
447 : "<csw:GetRecords resultType=\"hits\" service=\"CSW\" version=\"%s\""
448 : " xmlns:csw=\"http://www.opengis.net/cat/csw/2.0.2\""
449 : " xmlns:gml=\"http://www.opengis.net/gml\""
450 : " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
451 : " xmlns:dct=\"http://purl.org/dc/terms/\""
452 : " xmlns:ogc=\"http://www.opengis.net/ogc\""
453 : " xmlns:ows=\"http://www.opengis.net/ows\""
454 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
455 : " xsi:schemaLocation=\"http://www.opengis.net/cat/csw/2.0.2 "
456 : "http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\">"
457 : "<csw:Query typeNames=\"csw:Record\">"
458 : "<csw:ElementSetName>%s</csw:ElementSetName>"
459 : "%s"
460 : "</csw:Query>"
461 : "</csw:GetRecords>",
462 16 : poDS->GetVersion().c_str(), poDS->GetElementSetName().c_str(),
463 32 : osQuery.c_str());
464 :
465 : CPLHTTPResult *psResult =
466 8 : OGRCSWDataSource::HTTPFetch(poDS->GetBaseURL(), osPost);
467 8 : if (psResult == nullptr)
468 : {
469 3 : return -1;
470 : }
471 :
472 5 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
473 5 : if (psXML == nullptr)
474 : {
475 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
476 : psResult->pabyData);
477 1 : CPLHTTPDestroyResult(psResult);
478 1 : return -1;
479 : }
480 4 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
481 4 : CPLHTTPDestroyResult(psResult);
482 4 : psResult = nullptr;
483 :
484 4 : GIntBig nFeatures = CPLAtoGIntBig(CPLGetXMLValue(
485 : psXML, "=GetRecordsResponse.SearchResults.numberOfRecordsMatched",
486 : "-1"));
487 :
488 4 : CPLDestroyXMLNode(psXML);
489 4 : return nFeatures;
490 : }
491 :
492 : /************************************************************************/
493 : /* FetchGetRecords() */
494 : /************************************************************************/
495 :
496 31 : GDALDataset *OGRCSWLayer::FetchGetRecords()
497 : {
498 31 : CPLHTTPResult *psResult = nullptr;
499 :
500 62 : CPLString osOutputSchema = poDS->GetOutputSchema();
501 31 : if (!osOutputSchema.empty())
502 5 : osOutputSchema = " outputSchema=\"" + osOutputSchema + "\"";
503 :
504 : CPLString osPost = CPLSPrintf(
505 : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
506 : "<csw:GetRecords resultType=\"results\" service=\"CSW\" version=\"%s\""
507 : "%s"
508 : " startPosition=\"%d\""
509 : " maxRecords=\"%d\""
510 : " xmlns:csw=\"http://www.opengis.net/cat/csw/2.0.2\""
511 : " xmlns:gml=\"http://www.opengis.net/gml\""
512 : " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
513 : " xmlns:dct=\"http://purl.org/dc/terms/\""
514 : " xmlns:ogc=\"http://www.opengis.net/ogc\""
515 : " xmlns:ows=\"http://www.opengis.net/ows\""
516 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
517 : " xsi:schemaLocation=\"http://www.opengis.net/cat/csw/2.0.2 "
518 : "http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\">"
519 : "<csw:Query typeNames=\"csw:Record\">"
520 : "<csw:ElementSetName>%s</csw:ElementSetName>"
521 : "%s"
522 : "</csw:Query>"
523 : "</csw:GetRecords>",
524 31 : poDS->GetVersion().c_str(), osOutputSchema.c_str(),
525 62 : nPagingStartIndex + 1, poDS->GetMaxRecords(),
526 93 : poDS->GetElementSetName().c_str(), osQuery.c_str());
527 :
528 31 : psResult = OGRCSWDataSource::HTTPFetch(poDS->GetBaseURL(), osPost);
529 31 : if (psResult == nullptr)
530 : {
531 5 : return nullptr;
532 : }
533 :
534 52 : CPLString osTmpDirName = CPLSPrintf("/vsimem/tempcsw_%p", this);
535 26 : VSIMkdir(osTmpDirName, 0);
536 :
537 26 : GByte *pabyData = psResult->pabyData;
538 26 : int nDataLen = psResult->nDataLen;
539 :
540 26 : if (strstr((const char *)pabyData, "<ServiceExceptionReport") != nullptr ||
541 25 : strstr((const char *)pabyData, "<ows:ExceptionReport") != nullptr)
542 : {
543 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
544 : pabyData);
545 1 : CPLHTTPDestroyResult(psResult);
546 1 : return nullptr;
547 : }
548 : // CPLDebug("CSW", "%s", (const char*)pabyData);
549 :
550 50 : CPLString osTmpFileName;
551 :
552 25 : osTmpFileName = osTmpDirName + "/file.gfs";
553 25 : VSIUnlink(osTmpFileName);
554 :
555 25 : osTmpFileName = osTmpDirName + "/file.gml";
556 :
557 : VSILFILE *fp =
558 25 : VSIFileFromMemBuffer(osTmpFileName, pabyData, nDataLen, TRUE);
559 25 : VSIFCloseL(fp);
560 25 : psResult->pabyData = nullptr;
561 :
562 25 : CPLHTTPDestroyResult(psResult);
563 :
564 25 : GDALDataset *l_poBaseDS = nullptr;
565 :
566 25 : if (!poDS->GetOutputSchema().empty())
567 : {
568 5 : GDALDriver *poDrv = (GDALDriver *)GDALGetDriverByName("Memory");
569 5 : if (poDrv == nullptr)
570 2 : return nullptr;
571 5 : CPLXMLNode *psRoot = CPLParseXMLFile(osTmpFileName);
572 5 : if (psRoot == nullptr)
573 : {
574 1 : if (strstr((const char *)pabyData, "<csw:GetRecordsResponse") ==
575 1 : nullptr &&
576 1 : strstr((const char *)pabyData, "<GetRecordsResponse") ==
577 : nullptr)
578 : {
579 1 : if (nDataLen > 1000)
580 0 : pabyData[1000] = 0;
581 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
582 : pabyData);
583 : }
584 1 : return nullptr;
585 : }
586 : CPLXMLNode *psSearchResults =
587 4 : CPLGetXMLNode(psRoot, "=csw:GetRecordsResponse.csw:SearchResults");
588 4 : if (psSearchResults == nullptr)
589 : {
590 1 : CPLError(CE_Failure, CPLE_AppDefined,
591 : "Cannot find GetRecordsResponse.SearchResults");
592 1 : CPLDestroyXMLNode(psRoot);
593 1 : return nullptr;
594 : }
595 :
596 3 : l_poBaseDS = poDrv->Create("", 0, 0, 0, GDT_Unknown, nullptr);
597 3 : OGRLayer *poLyr = l_poBaseDS->CreateLayer("records");
598 6 : OGRFieldDefn oField("raw_xml", OFTString);
599 3 : poLyr->CreateField(&oField);
600 21 : for (CPLXMLNode *psIter = psSearchResults->psChild; psIter;
601 18 : psIter = psIter->psNext)
602 : {
603 18 : if (psIter->eType == CXT_Element)
604 : {
605 3 : OGRFeature *poFeature = new OGRFeature(poLyr->GetLayerDefn());
606 :
607 3 : CPLXMLNode *psNext = psIter->psNext;
608 3 : psIter->psNext = nullptr;
609 3 : char *pszXML = CPLSerializeXMLTree(psIter);
610 :
611 3 : const char *pszWest = nullptr;
612 3 : const char *pszEast = nullptr;
613 3 : const char *pszSouth = nullptr;
614 3 : const char *pszNorth = nullptr;
615 : CPLXMLNode *psBBox =
616 3 : CPLSearchXMLNode(psIter, "gmd:EX_GeographicBoundingBox");
617 3 : if (psBBox)
618 : {
619 : /* ISO 19115/19119: http://www.isotc211.org/2005/gmd */
620 1 : pszWest = CPLGetXMLValue(
621 : psBBox, "gmd:westBoundLongitude.gco:Decimal", nullptr);
622 1 : pszEast = CPLGetXMLValue(
623 : psBBox, "gmd:eastBoundLongitude.gco:Decimal", nullptr);
624 1 : pszSouth = CPLGetXMLValue(
625 : psBBox, "gmd:southBoundLatitude.gco:Decimal", nullptr);
626 1 : pszNorth = CPLGetXMLValue(
627 : psBBox, "gmd:northBoundLatitude.gco:Decimal", nullptr);
628 : }
629 2 : else if ((psBBox = CPLSearchXMLNode(psIter, "spdom")) !=
630 : nullptr)
631 : {
632 : /* FGDC: http://www.opengis.net/cat/csw/csdgm */
633 : pszWest =
634 1 : CPLGetXMLValue(psBBox, "bounding.westbc", nullptr);
635 : pszEast =
636 1 : CPLGetXMLValue(psBBox, "bounding.eastbc", nullptr);
637 : pszSouth =
638 1 : CPLGetXMLValue(psBBox, "bounding.southbc", nullptr);
639 : pszNorth =
640 1 : CPLGetXMLValue(psBBox, "bounding.northbc", nullptr);
641 : }
642 3 : if (pszWest && pszEast && pszSouth && pszNorth)
643 : {
644 2 : double dfMinX = CPLAtof(pszWest);
645 2 : double dfMaxX = CPLAtof(pszEast);
646 2 : double dfMinY = CPLAtof(pszSouth);
647 2 : double dfMaxY = CPLAtof(pszNorth);
648 2 : OGRLinearRing *poLR = new OGRLinearRing();
649 2 : poLR->addPoint(dfMinX, dfMinY);
650 2 : poLR->addPoint(dfMinX, dfMaxY);
651 2 : poLR->addPoint(dfMaxX, dfMaxY);
652 2 : poLR->addPoint(dfMaxX, dfMinY);
653 2 : poLR->addPoint(dfMinX, dfMinY);
654 2 : OGRPolygon *poPoly = new OGRPolygon();
655 2 : poPoly->addRingDirectly(poLR);
656 2 : poFeature->SetGeometryDirectly(poPoly);
657 : }
658 1 : else if ((psBBox = CPLSearchXMLNode(
659 1 : psIter, "ows:BoundingBox")) != nullptr)
660 : {
661 1 : CPLFree(psBBox->pszValue);
662 1 : psBBox->pszValue = CPLStrdup("gml:Envelope");
663 2 : CPLString osSRS = CPLGetXMLValue(psBBox, "crs", "");
664 1 : OGRGeometry *poGeom = GML2OGRGeometry_XMLNode(
665 : psBBox, FALSE, 0, 0, false, true, false);
666 1 : if (poGeom)
667 : {
668 1 : bool bLatLongOrder = true;
669 1 : if (!osSRS.empty())
670 1 : bLatLongOrder = GML_IsSRSLatLongOrder(osSRS);
671 2 : if (bLatLongOrder &&
672 1 : CPLTestBool(CPLGetConfigOption(
673 : "GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")))
674 1 : poGeom->swapXY();
675 1 : poFeature->SetGeometryDirectly(poGeom);
676 : }
677 : }
678 :
679 3 : psIter->psNext = psNext;
680 :
681 3 : poFeature->SetField(0, pszXML);
682 3 : CPL_IGNORE_RET_VAL(poLyr->CreateFeature(poFeature));
683 3 : CPLFree(pszXML);
684 3 : delete poFeature;
685 : }
686 : }
687 3 : CPLDestroyXMLNode(psRoot);
688 : }
689 : else
690 : {
691 20 : l_poBaseDS = (GDALDataset *)OGROpen(osTmpFileName, FALSE, nullptr);
692 20 : if (l_poBaseDS == nullptr)
693 : {
694 2 : if (strstr((const char *)pabyData, "<csw:GetRecordsResponse") ==
695 2 : nullptr &&
696 2 : strstr((const char *)pabyData, "<GetRecordsResponse") ==
697 : nullptr)
698 : {
699 2 : if (nDataLen > 1000)
700 0 : pabyData[1000] = 0;
701 2 : CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
702 : pabyData);
703 : }
704 2 : return nullptr;
705 : }
706 : }
707 :
708 21 : OGRLayer *poLayer = l_poBaseDS->GetLayer(0);
709 21 : if (poLayer == nullptr)
710 : {
711 5 : GDALClose(l_poBaseDS);
712 5 : return nullptr;
713 : }
714 :
715 16 : return l_poBaseDS;
716 : }
717 :
718 : /************************************************************************/
719 : /* SetSpatialFilter() */
720 : /************************************************************************/
721 :
722 2 : void OGRCSWLayer::SetSpatialFilter(OGRGeometry *poGeom)
723 : {
724 2 : OGRLayer::SetSpatialFilter(poGeom);
725 2 : ResetReading();
726 2 : BuildQuery();
727 2 : }
728 :
729 : /************************************************************************/
730 : /* OGRCSWAddRightPrefixes() */
731 : /************************************************************************/
732 :
733 39 : static void OGRCSWAddRightPrefixes(swq_expr_node *poNode)
734 : {
735 39 : if (poNode->eNodeType == SNT_COLUMN)
736 : {
737 9 : if (EQUAL(poNode->string_value, "identifier") ||
738 7 : EQUAL(poNode->string_value, "title") ||
739 7 : EQUAL(poNode->string_value, "type") ||
740 7 : EQUAL(poNode->string_value, "subject") ||
741 7 : EQUAL(poNode->string_value, "date") ||
742 7 : EQUAL(poNode->string_value, "language") ||
743 7 : EQUAL(poNode->string_value, "rights") ||
744 7 : EQUAL(poNode->string_value, "format") ||
745 7 : EQUAL(poNode->string_value, "creator") ||
746 7 : EQUAL(poNode->string_value, "source"))
747 : {
748 : char *pszNewVal =
749 2 : CPLStrdup(CPLSPrintf("dc:%s", poNode->string_value));
750 2 : CPLFree(poNode->string_value);
751 2 : poNode->string_value = pszNewVal;
752 : }
753 7 : else if (EQUAL(poNode->string_value, "references") ||
754 6 : EQUAL(poNode->string_value, "modified") ||
755 6 : EQUAL(poNode->string_value, "abstract"))
756 : {
757 : char *pszNewVal =
758 1 : CPLStrdup(CPLSPrintf("dct:%s", poNode->string_value));
759 1 : CPLFree(poNode->string_value);
760 1 : poNode->string_value = pszNewVal;
761 : }
762 6 : else if (EQUAL(poNode->string_value, "other_identifiers"))
763 : {
764 1 : CPLFree(poNode->string_value);
765 1 : poNode->string_value = CPLStrdup("dc:identifier");
766 : }
767 5 : else if (EQUAL(poNode->string_value, "other_subjects"))
768 : {
769 1 : CPLFree(poNode->string_value);
770 1 : poNode->string_value = CPLStrdup("dc:subject");
771 : }
772 4 : else if (EQUAL(poNode->string_value, "other_references"))
773 : {
774 1 : CPLFree(poNode->string_value);
775 1 : poNode->string_value = CPLStrdup("dct:references");
776 : }
777 3 : else if (EQUAL(poNode->string_value, "other_formats"))
778 : {
779 1 : CPLFree(poNode->string_value);
780 1 : poNode->string_value = CPLStrdup("dc:format");
781 : }
782 2 : else if (EQUAL(poNode->string_value, "AnyText"))
783 : {
784 1 : CPLFree(poNode->string_value);
785 1 : poNode->string_value = CPLStrdup("csw:AnyText");
786 : }
787 1 : else if (EQUAL(poNode->string_value, "boundingbox"))
788 : {
789 1 : CPLFree(poNode->string_value);
790 1 : poNode->string_value = CPLStrdup("ows:BoundingBox");
791 : }
792 : }
793 30 : else if (poNode->eNodeType == SNT_OPERATION)
794 : {
795 54 : for (int i = 0; i < poNode->nSubExprCount; i++)
796 37 : OGRCSWAddRightPrefixes(poNode->papoSubExpr[i]);
797 : }
798 39 : }
799 :
800 : /************************************************************************/
801 : /* SetAttributeFilter() */
802 : /************************************************************************/
803 :
804 3 : OGRErr OGRCSWLayer::SetAttributeFilter(const char *pszFilter)
805 : {
806 3 : if (pszFilter != nullptr && pszFilter[0] == 0)
807 0 : pszFilter = nullptr;
808 :
809 3 : CPLFree(m_pszAttrQueryString);
810 3 : m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : nullptr;
811 :
812 3 : delete m_poAttrQuery;
813 3 : m_poAttrQuery = nullptr;
814 :
815 3 : if (pszFilter != nullptr)
816 : {
817 2 : m_poAttrQuery = new OGRFeatureQuery();
818 :
819 2 : OGRErr eErr = m_poAttrQuery->Compile(GetLayerDefn(), pszFilter, TRUE,
820 : WFSGetCustomFuncRegistrar());
821 2 : if (eErr != OGRERR_NONE)
822 : {
823 0 : delete m_poAttrQuery;
824 0 : m_poAttrQuery = nullptr;
825 0 : return eErr;
826 : }
827 : }
828 :
829 3 : if (m_poAttrQuery != nullptr)
830 : {
831 2 : swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
832 2 : swq_expr_node *poNodeClone = poNode->Clone();
833 2 : poNodeClone->ReplaceBetweenByGEAndLERecurse();
834 2 : OGRCSWAddRightPrefixes(poNodeClone);
835 :
836 2 : int bNeedsNullCheck = FALSE;
837 2 : if (poNode->field_type != SWQ_BOOLEAN)
838 0 : osCSWWhere = "";
839 : else
840 4 : osCSWWhere = WFS_TurnSQLFilterToOGCFilter(
841 : poNodeClone, nullptr, nullptr, 110, FALSE, FALSE, FALSE,
842 2 : "ogc:", &bNeedsNullCheck);
843 2 : delete poNodeClone;
844 : }
845 : else
846 1 : osCSWWhere = "";
847 :
848 3 : if (m_poAttrQuery != nullptr && osCSWWhere.empty())
849 : {
850 0 : CPLDebug("CSW", "Using client-side only mode for filter \"%s\"",
851 : pszFilter);
852 0 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
853 0 : if (eErr != OGRERR_NONE)
854 0 : return eErr;
855 : }
856 :
857 3 : ResetReading();
858 3 : BuildQuery();
859 :
860 3 : return OGRERR_NONE;
861 : }
862 :
863 : /************************************************************************/
864 : /* BuildQuery() */
865 : /************************************************************************/
866 :
867 5 : void OGRCSWLayer::BuildQuery()
868 : {
869 5 : if (m_poFilterGeom != nullptr || !osCSWWhere.empty())
870 : {
871 4 : osQuery = "<csw:Constraint version=\"1.1.0\">";
872 4 : osQuery += "<ogc:Filter>";
873 4 : if (m_poFilterGeom != nullptr && !osCSWWhere.empty())
874 2 : osQuery += "<ogc:And>";
875 4 : if (m_poFilterGeom != nullptr)
876 : {
877 3 : osQuery += "<ogc:BBOX>";
878 3 : osQuery += "<ogc:PropertyName>ows:BoundingBox</ogc:PropertyName>";
879 3 : osQuery += "<gml:Envelope srsName=\"urn:ogc:def:crs:EPSG::4326\">";
880 3 : OGREnvelope sEnvelope;
881 3 : m_poFilterGeom->getEnvelope(&sEnvelope);
882 3 : if (CPLTestBool(CPLGetConfigOption(
883 : "GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")))
884 : {
885 : osQuery +=
886 : CPLSPrintf("<gml:lowerCorner>%.16g %.16g</gml:lowerCorner>",
887 3 : sEnvelope.MinY, sEnvelope.MinX);
888 : osQuery +=
889 : CPLSPrintf("<gml:upperCorner>%.16g %.16g</gml:upperCorner>",
890 3 : sEnvelope.MaxY, sEnvelope.MaxX);
891 : }
892 : else
893 : {
894 : osQuery +=
895 : CPLSPrintf("<gml:lowerCorner>%.16g %.16g</gml:lowerCorner>",
896 0 : sEnvelope.MinX, sEnvelope.MinY);
897 : osQuery +=
898 : CPLSPrintf("<gml:upperCorner>%.16g %.16g</gml:upperCorner>",
899 0 : sEnvelope.MaxX, sEnvelope.MaxY);
900 : }
901 3 : osQuery += "</gml:Envelope>";
902 3 : osQuery += "</ogc:BBOX>";
903 : }
904 4 : osQuery += osCSWWhere;
905 4 : if (m_poFilterGeom != nullptr && !osCSWWhere.empty())
906 2 : osQuery += "</ogc:And>";
907 4 : osQuery += "</ogc:Filter>";
908 4 : osQuery += "</csw:Constraint>";
909 : }
910 : else
911 1 : osQuery = "";
912 5 : }
913 :
914 : /************************************************************************/
915 : /* OGRCSWDataSource() */
916 : /************************************************************************/
917 :
918 11 : OGRCSWDataSource::OGRCSWDataSource()
919 : : pszName(nullptr), nMaxRecords(500), poLayer(nullptr),
920 11 : bFullExtentRecordsAsNonSpatial(false)
921 : {
922 11 : }
923 :
924 : /************************************************************************/
925 : /* ~OGRCSWDataSource() */
926 : /************************************************************************/
927 :
928 22 : OGRCSWDataSource::~OGRCSWDataSource()
929 : {
930 11 : delete poLayer;
931 11 : CPLFree(pszName);
932 22 : }
933 :
934 : /************************************************************************/
935 : /* SendGetCapabilities() */
936 : /************************************************************************/
937 :
938 11 : CPLHTTPResult *OGRCSWDataSource::SendGetCapabilities()
939 : {
940 22 : CPLString osURL(osBaseURL);
941 :
942 11 : osURL = CPLURLAddKVP(osURL, "SERVICE", "CSW");
943 11 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities");
944 :
945 11 : CPLDebug("CSW", "%s", osURL.c_str());
946 :
947 11 : CPLHTTPResult *psResult = HTTPFetch(osURL, nullptr);
948 11 : if (psResult == nullptr)
949 : {
950 3 : return nullptr;
951 : }
952 :
953 8 : if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
954 7 : nullptr ||
955 7 : strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
956 7 : nullptr ||
957 7 : strstr((const char *)psResult->pabyData, "<ExceptionReport") != nullptr)
958 : {
959 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
960 : psResult->pabyData);
961 1 : CPLHTTPDestroyResult(psResult);
962 1 : return nullptr;
963 : }
964 :
965 7 : return psResult;
966 : }
967 :
968 : /************************************************************************/
969 : /* Open() */
970 : /************************************************************************/
971 :
972 11 : int OGRCSWDataSource::Open(const char *pszFilename, char **papszOpenOptionsIn)
973 : {
974 11 : const char *pszBaseURL = CSLFetchNameValue(papszOpenOptionsIn, "URL");
975 11 : if (pszBaseURL == nullptr)
976 : {
977 11 : pszBaseURL = pszFilename;
978 11 : if (STARTS_WITH_CI(pszFilename, "CSW:"))
979 11 : pszBaseURL += 4;
980 11 : if (pszBaseURL[0] == '\0')
981 : {
982 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing URL open option");
983 0 : return FALSE;
984 : }
985 : }
986 11 : osBaseURL = pszBaseURL;
987 : osElementSetName =
988 11 : CSLFetchNameValueDef(papszOpenOptionsIn, "ELEMENTSETNAME", "full");
989 11 : bFullExtentRecordsAsNonSpatial = CPLFetchBool(
990 : papszOpenOptionsIn, "FULL_EXTENT_RECORDS_AS_NON_SPATIAL", false);
991 : osOutputSchema =
992 11 : CSLFetchNameValueDef(papszOpenOptionsIn, "OUTPUT_SCHEMA", "");
993 11 : if (EQUAL(osOutputSchema, "gmd"))
994 1 : osOutputSchema = "http://www.isotc211.org/2005/gmd";
995 10 : else if (EQUAL(osOutputSchema, "csw"))
996 1 : osOutputSchema = "http://www.opengis.net/cat/csw/2.0.2";
997 11 : nMaxRecords =
998 11 : atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RECORDS", "500"));
999 :
1000 11 : if (!STARTS_WITH(osBaseURL, "http://") &&
1001 22 : !STARTS_WITH(osBaseURL, "https://") &&
1002 11 : !STARTS_WITH(osBaseURL, "/vsimem/"))
1003 0 : return FALSE;
1004 :
1005 11 : CPLHTTPResult *psResult = SendGetCapabilities();
1006 11 : if (psResult == nullptr)
1007 4 : return FALSE;
1008 :
1009 7 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
1010 7 : if (psXML == nullptr)
1011 : {
1012 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
1013 : psResult->pabyData);
1014 1 : CPLHTTPDestroyResult(psResult);
1015 1 : return FALSE;
1016 : }
1017 6 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
1018 6 : CPLHTTPDestroyResult(psResult);
1019 6 : psResult = nullptr;
1020 :
1021 : const char *pszVersion =
1022 6 : CPLGetXMLValue(psXML, "=Capabilities.version", nullptr);
1023 6 : if (pszVersion == nullptr)
1024 : {
1025 2 : CPLError(CE_Failure, CPLE_AppDefined,
1026 : "Cannot find Capabilities.version");
1027 2 : CPLDestroyXMLNode(psXML);
1028 2 : return FALSE;
1029 : }
1030 4 : if (!EQUAL(pszVersion, "2.0.2"))
1031 0 : CPLDebug(
1032 : "CSW",
1033 : "Presumably only work properly with 2.0.2. Reported version is %s",
1034 : pszVersion);
1035 4 : osVersion = pszVersion;
1036 4 : CPLDestroyXMLNode(psXML);
1037 :
1038 4 : poLayer = new OGRCSWLayer(this);
1039 :
1040 4 : return TRUE;
1041 : }
1042 :
1043 : /************************************************************************/
1044 : /* GetLayer() */
1045 : /************************************************************************/
1046 :
1047 6 : OGRLayer *OGRCSWDataSource::GetLayer(int iLayer)
1048 :
1049 : {
1050 6 : if (iLayer < 0 || iLayer >= ((poLayer != nullptr) ? 1 : 0))
1051 2 : return nullptr;
1052 : else
1053 4 : return poLayer;
1054 : }
1055 :
1056 : /************************************************************************/
1057 : /* HTTPFetch() */
1058 : /************************************************************************/
1059 :
1060 50 : CPLHTTPResult *OGRCSWDataSource::HTTPFetch(const char *pszURL,
1061 : const char *pszPost)
1062 : {
1063 50 : char **papszOptions = nullptr;
1064 50 : if (pszPost)
1065 : {
1066 39 : papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", pszPost);
1067 : papszOptions =
1068 39 : CSLAddNameValue(papszOptions, "HEADERS",
1069 : "Content-Type: application/xml; charset=UTF-8");
1070 : }
1071 50 : CPLHTTPResult *psResult = CPLHTTPFetch(pszURL, papszOptions);
1072 50 : CSLDestroy(papszOptions);
1073 :
1074 50 : if (psResult == nullptr)
1075 : {
1076 0 : return nullptr;
1077 : }
1078 50 : if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
1079 : {
1080 8 : CPLError(CE_Failure, CPLE_AppDefined,
1081 : "Error returned by server : %s (%d)",
1082 8 : (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
1083 : psResult->nStatus);
1084 8 : CPLHTTPDestroyResult(psResult);
1085 8 : return nullptr;
1086 : }
1087 42 : if (psResult->pabyData == nullptr)
1088 : {
1089 3 : CPLError(CE_Failure, CPLE_AppDefined,
1090 : "Empty content returned by server");
1091 3 : CPLHTTPDestroyResult(psResult);
1092 3 : return nullptr;
1093 : }
1094 39 : return psResult;
1095 : }
1096 :
1097 : /************************************************************************/
1098 : /* Identify() */
1099 : /************************************************************************/
1100 :
1101 41989 : static int OGRCSWDriverIdentify(GDALOpenInfo *poOpenInfo)
1102 :
1103 : {
1104 41989 : return STARTS_WITH_CI(poOpenInfo->pszFilename, "CSW:");
1105 : }
1106 :
1107 : /************************************************************************/
1108 : /* Open() */
1109 : /************************************************************************/
1110 :
1111 11 : static GDALDataset *OGRCSWDriverOpen(GDALOpenInfo *poOpenInfo)
1112 :
1113 : {
1114 11 : if (!OGRCSWDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
1115 0 : return nullptr;
1116 :
1117 11 : OGRCSWDataSource *poDS = new OGRCSWDataSource();
1118 :
1119 11 : if (!poDS->Open(poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions))
1120 : {
1121 7 : delete poDS;
1122 7 : poDS = nullptr;
1123 : }
1124 :
1125 11 : return poDS;
1126 : }
1127 :
1128 : /************************************************************************/
1129 : /* RegisterOGRCSW() */
1130 : /************************************************************************/
1131 :
1132 1520 : void RegisterOGRCSW()
1133 :
1134 : {
1135 1520 : if (GDALGetDriverByName("CSW") != nullptr)
1136 301 : return;
1137 :
1138 1219 : GDALDriver *poDriver = new GDALDriver();
1139 :
1140 1219 : poDriver->SetDescription("CSW");
1141 1219 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
1142 1219 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1143 1219 : "OGC CSW (Catalog Service for the Web)");
1144 1219 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/csw.html");
1145 :
1146 1219 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "CSW:");
1147 1219 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
1148 :
1149 1219 : poDriver->SetMetadataItem(
1150 : GDAL_DMD_OPENOPTIONLIST,
1151 : "<OpenOptionList>"
1152 : " <Option name='URL' type='string' description='URL to the CSW server "
1153 : "endpoint' required='true'/>"
1154 : " <Option name='ELEMENTSETNAME' type='string-select' "
1155 : "description='Level of details of properties' default='full'>"
1156 : " <Value>brief</Value>"
1157 : " <Value>summary</Value>"
1158 : " <Value>full</Value>"
1159 : " </Option>"
1160 : " <Option name='FULL_EXTENT_RECORDS_AS_NON_SPATIAL' type='boolean' "
1161 : "description='Whether records with (-180,-90,180,90) extent should be "
1162 : "considered non-spatial' default='false'/>"
1163 : " <Option name='OUTPUT_SCHEMA' type='string' description='Value of "
1164 : "outputSchema parameter'/>"
1165 : " <Option name='MAX_RECORDS' type='int' description='Maximum number "
1166 : "of records to retrieve in a single time' default='500'/>"
1167 1219 : "</OpenOptionList>");
1168 :
1169 1219 : poDriver->pfnIdentify = OGRCSWDriverIdentify;
1170 1219 : poDriver->pfnOpen = OGRCSWDriverOpen;
1171 :
1172 1219 : GetGDALDriverManager()->RegisterDriver(poDriver);
1173 : }
|