LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csw - ogrcswdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 495 525 94.3 %
Date: 2025-05-31 00:00:17 Functions: 31 31 100.0 %

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

Generated by: LCOV version 1.14