LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csw - ogrcswdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 501 536 93.5 %
Date: 2024-05-04 12:52:34 Functions: 32 34 94.1 %

          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             : }

Generated by: LCOV version 1.14