LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csw - ogrcswdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 497 530 93.8 %
Date: 2024-11-21 22:18:42 Functions: 31 32 96.9 %

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

Generated by: LCOV version 1.14