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