1 : /******************************************************************************
2 : * $Id: ogrwfsdatasource.cpp 23165 2011-10-02 17:27:02Z rouault $
3 : *
4 : * Project: WFS Translator
5 : * Purpose: Implements OGRWFSDataSource class
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault <even dot rouault at mines dash paris dot org>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "ogr_wfs.h"
32 : #include "ogr_api.h"
33 : #include "cpl_minixml.h"
34 : #include "cpl_http.h"
35 : #include "gmlutils.h"
36 : #include "parsexsd.h"
37 :
38 : CPL_CVSID("$Id: ogrwfsdatasource.cpp 23165 2011-10-02 17:27:02Z rouault $");
39 :
40 :
41 : /************************************************************************/
42 : /* WFSFindNode() */
43 : /************************************************************************/
44 :
45 26 : CPLXMLNode* WFSFindNode(CPLXMLNode* psXML, const char* pszRootName)
46 : {
47 26 : CPLXMLNode* psIter = psXML;
48 76 : while(psIter)
49 : {
50 49 : if (psIter->eType == CXT_Element)
51 : {
52 46 : const char* pszNodeName = psIter->pszValue;
53 46 : const char* pszSep = strchr(pszNodeName, ':');
54 46 : if (pszSep)
55 20 : pszNodeName = pszSep + 1;
56 46 : if (EQUAL(pszNodeName, pszRootName))
57 : {
58 25 : return psIter;
59 : }
60 : }
61 24 : psIter = psIter->psNext;
62 : }
63 :
64 1 : psIter = psXML->psChild;
65 3 : while(psIter)
66 : {
67 2 : if (psIter->eType == CXT_Element)
68 : {
69 2 : const char* pszNodeName = psIter->pszValue;
70 2 : const char* pszSep = strchr(pszNodeName, ':');
71 2 : if (pszSep)
72 0 : pszNodeName = pszSep + 1;
73 2 : if (EQUAL(pszNodeName, pszRootName))
74 : {
75 1 : return psIter;
76 : }
77 : }
78 1 : psIter = psIter->psNext;
79 : }
80 0 : return NULL;
81 : }
82 :
83 : /************************************************************************/
84 : /* OGRWFSWrappedResultLayer */
85 : /************************************************************************/
86 :
87 : class OGRWFSWrappedResultLayer : public OGRLayer
88 : {
89 : OGRDataSource *poDS;
90 : OGRLayer *poLayer;
91 :
92 : public:
93 1 : OGRWFSWrappedResultLayer(OGRDataSource* poDS, OGRLayer* poLayer)
94 1 : {
95 1 : this->poDS = poDS;
96 1 : this->poLayer = poLayer;
97 1 : }
98 1 : ~OGRWFSWrappedResultLayer()
99 1 : {
100 1 : delete poDS;
101 1 : }
102 :
103 0 : virtual void ResetReading() { poLayer->ResetReading(); }
104 3 : virtual OGRFeature *GetNextFeature() { return poLayer->GetNextFeature(); }
105 0 : virtual OGRErr SetNextByIndex( long nIndex ) { return poLayer->SetNextByIndex(nIndex); }
106 0 : virtual OGRFeature *GetFeature( long nFID ) { return poLayer->GetFeature(nFID); }
107 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poLayer->GetLayerDefn(); }
108 1 : virtual int GetFeatureCount( int bForce = TRUE ) { return poLayer->GetFeatureCount(bForce); }
109 0 : virtual int TestCapability( const char * pszCap ) { return poLayer->TestCapability(pszCap); }
110 : };
111 :
112 :
113 : /************************************************************************/
114 : /* OGRWFSDataSource() */
115 : /************************************************************************/
116 :
117 84 : OGRWFSDataSource::OGRWFSDataSource()
118 :
119 : {
120 84 : papoLayers = NULL;
121 84 : nLayers = 0;
122 :
123 84 : pszName = NULL;
124 :
125 84 : bUpdate = FALSE;
126 84 : bGetFeatureSupportHits = FALSE;
127 84 : bNeedNAMESPACE = FALSE;
128 84 : bHasMinOperators = FALSE;
129 84 : bHasNullCheck = FALSE;
130 84 : bPropertyIsNotEqualToSupported = TRUE; /* advertized by deegree but not implemented */
131 84 : bTransactionSupport = FALSE;
132 84 : papszIdGenMethods = NULL;
133 84 : bUseFeatureId = FALSE; /* CubeWerx doesn't like GmlObjectId */
134 84 : bGmlObjectIdNeedsGMLPrefix = FALSE;
135 84 : bRequiresEnvelopeSpatialFilter = FALSE;
136 :
137 84 : bRewriteFile = FALSE;
138 84 : psFileXML = NULL;
139 :
140 84 : bUseHttp10 = FALSE;
141 84 : papszHttpOptions = NULL;
142 :
143 84 : bPagingAllowed = CSLTestBoolean(CPLGetConfigOption("OGR_WFS_PAGING_ALLOWED", "OFF"));
144 84 : nPageSize = 0;
145 84 : if (bPagingAllowed)
146 : {
147 0 : nPageSize = atoi(CPLGetConfigOption("OGR_WFS_PAGE_SIZE", "100"));
148 0 : if (nPageSize <= 0)
149 0 : nPageSize = 100;
150 : }
151 :
152 84 : bIsGEOSERVER = FALSE;
153 :
154 84 : bLoadMultipleLayerDefn = CSLTestBoolean(CPLGetConfigOption("OGR_WFS_LOAD_MULTIPLE_LAYER_DEFN", "TRUE"));
155 :
156 84 : poLayerMetadataDS = NULL;
157 84 : poLayerMetadataLayer = NULL;
158 :
159 84 : poLayerGetCapabilitiesDS = NULL;
160 84 : poLayerGetCapabilitiesLayer = NULL;
161 :
162 84 : bKeepLayerNamePrefix = FALSE;
163 84 : }
164 :
165 : /************************************************************************/
166 : /* ~OGRWFSDataSource() */
167 : /************************************************************************/
168 :
169 84 : OGRWFSDataSource::~OGRWFSDataSource()
170 :
171 : {
172 84 : if (psFileXML)
173 : {
174 1 : if (bRewriteFile)
175 : {
176 0 : CPLSerializeXMLTreeToFile(psFileXML, pszName);
177 : }
178 :
179 1 : CPLDestroyXMLNode(psFileXML);
180 : }
181 :
182 203 : for( int i = 0; i < nLayers; i++ )
183 119 : delete papoLayers[i];
184 84 : CPLFree( papoLayers );
185 84 : if (osLayerMetadataTmpFileName.size() != 0)
186 1 : VSIUnlink(osLayerMetadataTmpFileName);
187 84 : delete poLayerMetadataDS;
188 84 : delete poLayerGetCapabilitiesDS;
189 :
190 84 : CPLFree( pszName );
191 84 : CSLDestroy( papszIdGenMethods );
192 84 : CSLDestroy( papszHttpOptions );
193 84 : }
194 :
195 : /************************************************************************/
196 : /* TestCapability() */
197 : /************************************************************************/
198 :
199 0 : int OGRWFSDataSource::TestCapability( const char * pszCap )
200 :
201 : {
202 0 : return FALSE;
203 : }
204 :
205 : /************************************************************************/
206 : /* GetLayer() */
207 : /************************************************************************/
208 :
209 8 : OGRLayer *OGRWFSDataSource::GetLayer( int iLayer )
210 :
211 : {
212 8 : if( iLayer < 0 || iLayer >= nLayers )
213 0 : return NULL;
214 : else
215 8 : return papoLayers[iLayer];
216 : }
217 :
218 : /************************************************************************/
219 : /* GetLayerByName() */
220 : /************************************************************************/
221 :
222 48 : OGRLayer* OGRWFSDataSource::GetLayerByName(const char* pszName)
223 : {
224 48 : if ( ! pszName )
225 0 : return NULL;
226 :
227 : int i;
228 48 : int bHasFoundLayerWithColon = FALSE;
229 :
230 48 : if (EQUAL(pszName, "WFSLayerMetadata"))
231 : {
232 1 : if (osLayerMetadataTmpFileName.size() != 0)
233 0 : return poLayerMetadataLayer;
234 :
235 1 : osLayerMetadataTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/WFSLayerMetadata.csv", this);
236 2 : osLayerMetadataCSV = "layer_name,title,abstract\n" + osLayerMetadataCSV;
237 :
238 : VSIFCloseL(VSIFileFromMemBuffer(osLayerMetadataTmpFileName,
239 : (GByte*) osLayerMetadataCSV.c_str(),
240 1 : osLayerMetadataCSV.size(), FALSE));
241 : poLayerMetadataDS = (OGRDataSource*) OGROpen(osLayerMetadataTmpFileName,
242 1 : FALSE, NULL);
243 1 : if (poLayerMetadataDS)
244 1 : poLayerMetadataLayer = poLayerMetadataDS->GetLayer(0);
245 1 : return poLayerMetadataLayer;
246 : }
247 47 : else if (EQUAL(pszName, "WFSGetCapabilities"))
248 : {
249 1 : if (poLayerGetCapabilitiesLayer != NULL)
250 0 : return poLayerGetCapabilitiesLayer;
251 :
252 1 : OGRSFDriver* poMEMDrv = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory");
253 1 : if (poMEMDrv == NULL)
254 : {
255 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot load 'Memory' driver");
256 0 : return NULL;
257 : }
258 :
259 1 : poLayerGetCapabilitiesDS = poMEMDrv->CreateDataSource("WFSGetCapabilities", NULL);
260 1 : poLayerGetCapabilitiesLayer = poLayerGetCapabilitiesDS->CreateLayer("WFSGetCapabilities", NULL, wkbNone, NULL);
261 1 : OGRFieldDefn oFDefn("content", OFTString);
262 1 : poLayerGetCapabilitiesLayer->CreateField(&oFDefn);
263 1 : OGRFeature* poFeature = new OGRFeature(poLayerGetCapabilitiesLayer->GetLayerDefn());
264 2 : poFeature->SetField(0, osGetCapabilities);
265 1 : poLayerGetCapabilitiesLayer->CreateFeature(poFeature);
266 1 : delete poFeature;
267 :
268 1 : return poLayerGetCapabilitiesLayer;
269 : }
270 :
271 : /* first a case sensitive check */
272 497 : for( i = 0; i < nLayers; i++ )
273 : {
274 469 : OGRWFSLayer *poLayer = papoLayers[i];
275 :
276 469 : if( strcmp( pszName, poLayer->GetName() ) == 0 )
277 18 : return poLayer;
278 :
279 451 : bHasFoundLayerWithColon |= (strchr( poLayer->GetName(), ':') != NULL);
280 : }
281 :
282 : /* then case insensitive */
283 454 : for( i = 0; i < nLayers; i++ )
284 : {
285 426 : OGRWFSLayer *poLayer = papoLayers[i];
286 :
287 426 : if( EQUAL( pszName, poLayer->GetName() ) )
288 0 : return poLayer;
289 : }
290 :
291 : /* now try looking after the colon character */
292 28 : if (!bKeepLayerNamePrefix && bHasFoundLayerWithColon && strchr(pszName, ':') == NULL)
293 : {
294 219 : for( i = 0; i < nLayers; i++ )
295 : {
296 219 : OGRWFSLayer *poLayer = papoLayers[i];
297 :
298 219 : const char* pszAfterColon = strchr( poLayer->GetName(), ':');
299 219 : if( pszAfterColon && EQUAL( pszName, pszAfterColon + 1 ) )
300 28 : return poLayer;
301 : }
302 : }
303 :
304 0 : return NULL;
305 : }
306 :
307 : /************************************************************************/
308 : /* FindSubStringInsensitive() */
309 : /************************************************************************/
310 :
311 205 : const char* FindSubStringInsensitive(const char* pszStr,
312 : const char* pszSubStr)
313 : {
314 205 : size_t nSubStrPos = CPLString(pszStr).ifind(pszSubStr);
315 205 : if (nSubStrPos == std::string::npos)
316 204 : return NULL;
317 1 : return pszStr + nSubStrPos;
318 : }
319 :
320 : /************************************************************************/
321 : /* DetectIfGetFeatureSupportHits() */
322 : /************************************************************************/
323 :
324 12 : static int DetectIfGetFeatureSupportHits(CPLXMLNode* psRoot)
325 : {
326 : CPLXMLNode* psOperationsMetadata =
327 12 : CPLGetXMLNode(psRoot, "OperationsMetadata");
328 12 : if (!psOperationsMetadata)
329 : {
330 0 : CPLDebug("WFS", "Could not find <OperationsMetadata>");
331 0 : return FALSE;
332 : }
333 :
334 12 : CPLXMLNode* psChild = psOperationsMetadata->psChild;
335 45 : while(psChild)
336 : {
337 33 : if (psChild->eType == CXT_Element &&
338 : strcmp(psChild->pszValue, "Operation") == 0 &&
339 : strcmp(CPLGetXMLValue(psChild, "name", ""), "GetFeature") == 0)
340 : {
341 12 : break;
342 : }
343 21 : psChild = psChild->psNext;
344 : }
345 12 : if (!psChild)
346 : {
347 0 : CPLDebug("WFS", "Could not find <Operation name=\"GetFeature\">");
348 0 : return FALSE;
349 : }
350 :
351 12 : psChild = psChild->psChild;
352 49 : while(psChild)
353 : {
354 34 : if (psChild->eType == CXT_Element &&
355 : strcmp(psChild->pszValue, "Parameter") == 0 &&
356 : strcmp(CPLGetXMLValue(psChild, "name", ""), "resultType") == 0)
357 : {
358 9 : break;
359 : }
360 25 : psChild = psChild->psNext;
361 : }
362 12 : if (!psChild)
363 : {
364 3 : CPLDebug("WFS", "Could not find <Parameter name=\"resultType\">");
365 3 : return FALSE;
366 : }
367 :
368 9 : psChild = psChild->psChild;
369 36 : while(psChild)
370 : {
371 25 : if (psChild->eType == CXT_Element &&
372 : strcmp(psChild->pszValue, "Value") == 0)
373 : {
374 16 : CPLXMLNode* psChild2 = psChild->psChild;
375 41 : while(psChild2)
376 : {
377 16 : if (psChild2->eType == CXT_Text &&
378 : strcmp(psChild2->pszValue, "hits") == 0)
379 : {
380 7 : CPLDebug("WFS", "GetFeature operation supports hits");
381 7 : return TRUE;
382 : }
383 9 : psChild2 = psChild2->psNext;
384 : }
385 : }
386 18 : psChild = psChild->psNext;
387 : }
388 :
389 2 : return FALSE;
390 : }
391 :
392 : /************************************************************************/
393 : /* DetectRequiredOutputFormat() */
394 : /************************************************************************/
395 :
396 13 : CPLString OGRWFSDataSource::DetectRequiredOutputFormat(CPLXMLNode* psRoot)
397 : {
398 : CPLXMLNode* psOperationsMetadata =
399 13 : CPLGetXMLNode(psRoot, "OperationsMetadata");
400 13 : if (!psOperationsMetadata)
401 : {
402 0 : return "";
403 : }
404 :
405 13 : CPLXMLNode* psChild = psOperationsMetadata->psChild;
406 43 : while(psChild)
407 : {
408 30 : if (psChild->eType == CXT_Element &&
409 : strcmp(psChild->pszValue, "Operation") == 0 &&
410 : strcmp(CPLGetXMLValue(psChild, "name", ""), "DescribeFeatureType") == 0)
411 : {
412 13 : break;
413 : }
414 17 : psChild = psChild->psNext;
415 : }
416 13 : if (!psChild)
417 : {
418 : //CPLDebug("WFS", "Could not find <Operation name=\"DescribeFeatureType\">");
419 0 : return "";
420 : }
421 :
422 13 : psChild = psChild->psChild;
423 53 : while(psChild)
424 : {
425 39 : if (psChild->eType == CXT_Element &&
426 : strcmp(psChild->pszValue, "Parameter") == 0 &&
427 : strcmp(CPLGetXMLValue(psChild, "name", ""), "outputFormat") == 0)
428 : {
429 12 : break;
430 : }
431 27 : psChild = psChild->psNext;
432 : }
433 13 : if (!psChild)
434 : {
435 : //CPLDebug("WFS", "Could not find <Parameter name=\"outputFormat\">");
436 1 : return "";
437 : }
438 :
439 12 : psChild = psChild->psChild;
440 12 : int nCountValue = 0;
441 12 : const char* pszValue = NULL;
442 53 : while(psChild)
443 : {
444 29 : if (psChild->eType == CXT_Element &&
445 : strcmp(psChild->pszValue, "Value") == 0)
446 : {
447 17 : CPLXMLNode* psChild2 = psChild->psChild;
448 51 : while(psChild2)
449 : {
450 17 : if (psChild2->eType == CXT_Text)
451 : {
452 17 : pszValue = psChild2->pszValue;
453 17 : nCountValue ++;
454 : }
455 17 : psChild2 = psChild2->psNext;
456 : }
457 : }
458 29 : psChild = psChild->psNext;
459 : }
460 :
461 : /* If there's only one value and it is not GML 3.1.1, then we'll need */
462 : /* to specify it explicitely */
463 : /* This is the case for http://deegree3-testing.deegree.org/deegree-inspire-node/services */
464 : /* which only supports GML 3.2.1 */
465 12 : if (nCountValue == 1 && strcmp(pszValue, "text/xml; subtype=gml/3.1.1") != 0)
466 0 : return pszValue;
467 :
468 12 : return "";
469 : }
470 :
471 :
472 : /************************************************************************/
473 : /* DetectRequiresEnvelopeSpatialFilter() */
474 : /************************************************************************/
475 :
476 13 : int OGRWFSDataSource::DetectRequiresEnvelopeSpatialFilter(CPLXMLNode* psRoot)
477 : {
478 : /* This is a heuristic to detect Deegree 3 servers, such as */
479 : /* http://deegree3-demo.deegree.org:80/deegree-utah-demo/services */
480 : /* that are very GML3 strict, and don't like <gml:Box> in a <Filter><BBOX> */
481 : /* request, but requires instead <gml:Envelope>, but some servers (such as MapServer) */
482 : /* don't like <gml:Envelope> so we are obliged to detect the kind of server */
483 :
484 : int nCount;
485 : CPLXMLNode* psChild;
486 :
487 : CPLXMLNode* psGeometryOperands =
488 13 : CPLGetXMLNode(psRoot, "Filter_Capabilities.Spatial_Capabilities.GeometryOperands");
489 13 : if (!psGeometryOperands)
490 : {
491 0 : return FALSE;
492 : }
493 :
494 13 : nCount = 0;
495 13 : psChild = psGeometryOperands->psChild;
496 98 : while(psChild)
497 : {
498 72 : nCount ++;
499 72 : psChild = psChild->psNext;
500 : }
501 : /* Magic number... Might be fragile */
502 13 : return (nCount == 19);
503 : }
504 :
505 : /************************************************************************/
506 : /* GetPostTransactionURL() */
507 : /************************************************************************/
508 :
509 8 : CPLString OGRWFSDataSource::GetPostTransactionURL()
510 : {
511 8 : if (osPostTransactionURL.size())
512 8 : return osPostTransactionURL;
513 :
514 0 : osPostTransactionURL = osBaseURL;
515 0 : const char* pszPostTransactionURL = osPostTransactionURL.c_str();
516 0 : const char* pszEsperluet = strchr(pszPostTransactionURL, '?');
517 0 : if (pszEsperluet)
518 0 : osPostTransactionURL.resize(pszEsperluet - pszPostTransactionURL);
519 :
520 0 : return osPostTransactionURL;
521 : }
522 :
523 : /************************************************************************/
524 : /* DetectTransactionSupport() */
525 : /************************************************************************/
526 :
527 16 : int OGRWFSDataSource::DetectTransactionSupport(CPLXMLNode* psRoot)
528 : {
529 : CPLXMLNode* psTransactionWFS100 =
530 16 : CPLGetXMLNode(psRoot, "Capability.Request.Transaction");
531 16 : if (psTransactionWFS100)
532 : {
533 3 : CPLXMLNode* psPostURL = CPLGetXMLNode(psTransactionWFS100, "DCPType.HTTP.Post");
534 3 : if (psPostURL)
535 : {
536 2 : const char* pszPOSTURL = CPLGetXMLValue(psPostURL, "onlineResource", NULL);
537 2 : if (pszPOSTURL)
538 : {
539 2 : osPostTransactionURL = pszPOSTURL;
540 : }
541 : }
542 :
543 3 : bTransactionSupport = TRUE;
544 3 : return TRUE;
545 : }
546 :
547 : CPLXMLNode* psOperationsMetadata =
548 13 : CPLGetXMLNode(psRoot, "OperationsMetadata");
549 13 : if (!psOperationsMetadata)
550 : {
551 0 : return FALSE;
552 : }
553 :
554 13 : CPLXMLNode* psChild = psOperationsMetadata->psChild;
555 113 : while(psChild)
556 : {
557 97 : if (psChild->eType == CXT_Element &&
558 : strcmp(psChild->pszValue, "Operation") == 0 &&
559 : strcmp(CPLGetXMLValue(psChild, "name", ""), "Transaction") == 0)
560 : {
561 10 : break;
562 : }
563 87 : psChild = psChild->psNext;
564 : }
565 13 : if (!psChild)
566 : {
567 3 : CPLDebug("WFS", "No transaction support");
568 3 : return FALSE;
569 : }
570 :
571 10 : bTransactionSupport = TRUE;
572 10 : CPLDebug("WFS", "Transaction support !");
573 :
574 :
575 10 : CPLXMLNode* psPostURL = CPLGetXMLNode(psChild, "DCP.HTTP.Post");
576 10 : if (psPostURL)
577 : {
578 9 : const char* pszPOSTURL = CPLGetXMLValue(psPostURL, "href", NULL);
579 9 : if (pszPOSTURL)
580 9 : osPostTransactionURL = pszPOSTURL;
581 : }
582 :
583 10 : psChild = psChild->psChild;
584 51 : while(psChild)
585 : {
586 41 : if (psChild->eType == CXT_Element &&
587 : strcmp(psChild->pszValue, "Parameter") == 0 &&
588 : strcmp(CPLGetXMLValue(psChild, "name", ""), "idgen") == 0)
589 : {
590 10 : break;
591 : }
592 31 : psChild = psChild->psNext;
593 : }
594 10 : if (!psChild)
595 : {
596 0 : papszIdGenMethods = CSLAddString(NULL, "GenerateNew");
597 0 : return TRUE;
598 : }
599 :
600 10 : psChild = psChild->psChild;
601 56 : while(psChild)
602 : {
603 36 : if (psChild->eType == CXT_Element &&
604 : strcmp(psChild->pszValue, "Value") == 0)
605 : {
606 26 : CPLXMLNode* psChild2 = psChild->psChild;
607 78 : while(psChild2)
608 : {
609 26 : if (psChild2->eType == CXT_Text)
610 : {
611 : papszIdGenMethods = CSLAddString(papszIdGenMethods,
612 26 : psChild2->pszValue);
613 : }
614 26 : psChild2 = psChild2->psNext;
615 : }
616 : }
617 36 : psChild = psChild->psNext;
618 : }
619 :
620 10 : return TRUE;
621 : }
622 :
623 : /************************************************************************/
624 : /* FindComparisonOperator() */
625 : /************************************************************************/
626 :
627 113 : static int FindComparisonOperator(CPLXMLNode* psNode, const char* pszVal)
628 : {
629 113 : CPLXMLNode* psChild = psNode->psChild;
630 671 : while(psChild)
631 : {
632 547 : if (psChild->eType == CXT_Element &&
633 : strcmp(psChild->pszValue, "ComparisonOperator") == 0)
634 : {
635 523 : if (strcmp(CPLGetXMLValue(psChild, NULL, ""), pszVal) == 0)
636 94 : return TRUE;
637 :
638 : /* For WFS 2.0.0 */
639 429 : const char* pszName = CPLGetXMLValue(psChild, "name", NULL);
640 429 : if (pszName != NULL && strncmp(pszName, "PropertyIs", 10) == 0 &&
641 : strcmp(pszName + 10, pszVal) == 0)
642 8 : return TRUE;
643 : }
644 445 : psChild = psChild->psNext;
645 : }
646 11 : return FALSE;
647 : }
648 :
649 : /************************************************************************/
650 : /* LoadFromFile() */
651 : /************************************************************************/
652 :
653 84 : CPLXMLNode* OGRWFSDataSource::LoadFromFile( const char * pszFilename )
654 : {
655 : VSILFILE *fp;
656 : char achHeader[1024];
657 :
658 : VSIStatBufL sStatBuf;
659 84 : if (VSIStatExL( pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) != 0 ||
660 : VSI_ISDIR(sStatBuf.st_mode))
661 49 : return NULL;
662 :
663 35 : fp = VSIFOpenL( pszFilename, "rb" );
664 :
665 35 : if( fp == NULL )
666 0 : return NULL;
667 :
668 : int nRead;
669 35 : if( (nRead = VSIFReadL( achHeader, 1, sizeof(achHeader) - 1, fp )) == 0 )
670 : {
671 0 : VSIFCloseL( fp );
672 0 : return NULL;
673 : }
674 35 : achHeader[nRead] = 0;
675 :
676 35 : if( !EQUALN(achHeader,"<OGRWFSDataSource>",18) &&
677 : strstr(achHeader,"<WFS_Capabilities") == NULL &&
678 : strstr(achHeader,"<wfs:WFS_Capabilities") == NULL)
679 : {
680 34 : VSIFCloseL( fp );
681 34 : return NULL;
682 : }
683 :
684 : /* -------------------------------------------------------------------- */
685 : /* It is the right file, now load the full XML definition. */
686 : /* -------------------------------------------------------------------- */
687 : int nLen;
688 :
689 1 : VSIFSeekL( fp, 0, SEEK_END );
690 1 : nLen = (int) VSIFTellL( fp );
691 1 : VSIFSeekL( fp, 0, SEEK_SET );
692 :
693 1 : char* pszXML = (char *) VSIMalloc(nLen+1);
694 1 : if (pszXML == NULL)
695 : {
696 0 : VSIFCloseL( fp );
697 0 : return NULL;
698 : }
699 1 : pszXML[nLen] = '\0';
700 1 : if( ((int) VSIFReadL( pszXML, 1, nLen, fp )) != nLen )
701 : {
702 0 : CPLFree( pszXML );
703 0 : VSIFCloseL( fp );
704 :
705 0 : return NULL;
706 : }
707 1 : VSIFCloseL( fp );
708 :
709 1 : if (strstr(pszXML, "CubeWerx"))
710 : {
711 : /* At least true for CubeWerx Suite 4.15.1 */
712 0 : bUseFeatureId = TRUE;
713 : }
714 1 : else if (strstr(pszXML, "deegree"))
715 : {
716 0 : bGmlObjectIdNeedsGMLPrefix = TRUE;
717 : }
718 :
719 1 : CPLXMLNode* psXML = CPLParseXMLString( pszXML );
720 1 : CPLFree( pszXML );
721 :
722 1 : return psXML;
723 : }
724 :
725 : /************************************************************************/
726 : /* SendGetCapabilities() */
727 : /************************************************************************/
728 :
729 15 : CPLHTTPResult* OGRWFSDataSource::SendGetCapabilities(const char* pszBaseURL,
730 : CPLString& osTypeName)
731 : {
732 15 : CPLString osURL(pszBaseURL);
733 15 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
734 15 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities");
735 15 : osTypeName = CPLURLGetValue(osURL, "TYPENAME");
736 15 : osURL = CPLURLAddKVP(osURL, "TYPENAME", NULL);
737 15 : osURL = CPLURLAddKVP(osURL, "FILTER", NULL);
738 15 : osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", NULL);
739 15 : osURL = CPLURLAddKVP(osURL, "MAXFEATURES", NULL);
740 15 : osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", NULL);
741 :
742 : /* Don't accept WFS 2.0.0 for now, unless explicitely specified */
743 15 : if (CPLURLGetValue(osURL, "ACCEPTVERSIONS").size() == 0 &&
744 : CPLURLGetValue(osURL, "VERSION").size() == 0)
745 13 : osURL = CPLURLAddKVP(osURL, "ACCEPTVERSIONS", "1.1.0,1.0.0");
746 :
747 15 : CPLDebug("WFS", "%s", osURL.c_str());
748 :
749 15 : CPLHTTPResult* psResult = HTTPFetch( osURL, NULL);
750 15 : if (psResult == NULL)
751 : {
752 0 : return NULL;
753 : }
754 :
755 15 : if (strstr((const char*)psResult->pabyData,
756 : "<ServiceExceptionReport") != NULL ||
757 : strstr((const char*)psResult->pabyData,
758 : "<ows:ExceptionReport") != NULL)
759 : {
760 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
761 0 : psResult->pabyData);
762 0 : CPLHTTPDestroyResult(psResult);
763 0 : return NULL;
764 : }
765 :
766 15 : return psResult;
767 : }
768 :
769 : /************************************************************************/
770 : /* Open() */
771 : /************************************************************************/
772 :
773 84 : int OGRWFSDataSource::Open( const char * pszFilename, int bUpdateIn)
774 :
775 : {
776 84 : bUpdate = bUpdateIn;
777 84 : CPLFree(pszName);
778 84 : pszName = CPLStrdup(pszFilename);
779 :
780 84 : CPLXMLNode* psWFSCapabilities = NULL;
781 84 : CPLXMLNode* psXML = LoadFromFile(pszFilename);
782 84 : CPLString osTypeName;
783 84 : const char* pszBaseURL = NULL;
784 :
785 84 : if (psXML == NULL)
786 : {
787 83 : if (!EQUALN(pszFilename, "WFS:", 4) &&
788 : FindSubStringInsensitive(pszFilename, "SERVICE=WFS") == NULL)
789 : {
790 68 : return FALSE;
791 : }
792 :
793 15 : pszBaseURL = pszFilename;
794 15 : if (EQUALN(pszFilename, "WFS:", 4))
795 15 : pszBaseURL += 4;
796 :
797 15 : osBaseURL = pszBaseURL;
798 :
799 15 : if (strncmp(pszBaseURL, "http://", 7) != 0 &&
800 : strncmp(pszBaseURL, "https://", 8) != 0)
801 0 : return FALSE;
802 :
803 : CPLHTTPResult* psResult = SendGetCapabilities(pszBaseURL,
804 15 : osTypeName);
805 15 : if (psResult == NULL)
806 : {
807 0 : return FALSE;
808 : }
809 :
810 15 : if (strstr((const char*) psResult->pabyData, "CubeWerx"))
811 : {
812 : /* At least true for CubeWerx Suite 4.15.1 */
813 0 : bUseFeatureId = TRUE;
814 : }
815 15 : else if (strstr((const char*) psResult->pabyData, "deegree"))
816 : {
817 4 : bGmlObjectIdNeedsGMLPrefix = TRUE;
818 : }
819 :
820 15 : psXML = CPLParseXMLString( (const char*) psResult->pabyData );
821 15 : if (psXML == NULL)
822 : {
823 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
824 0 : psResult->pabyData);
825 0 : CPLHTTPDestroyResult(psResult);
826 0 : return FALSE;
827 : }
828 15 : osGetCapabilities = (const char*) psResult->pabyData;
829 :
830 15 : CPLHTTPDestroyResult(psResult);
831 : }
832 1 : else if ( WFSFindNode( psXML, "OGRWFSDataSource" ) == NULL &&
833 : WFSFindNode( psXML, "WFS_Capabilities" ) != NULL )
834 : {
835 : /* This is directly the Capabilities document */
836 0 : char* pszXML = CPLSerializeXMLTree(WFSFindNode( psXML, "WFS_Capabilities" ));
837 0 : osGetCapabilities = pszXML;
838 0 : CPLFree(pszXML);
839 : }
840 : else
841 : {
842 1 : CPLXMLNode* psRoot = WFSFindNode( psXML, "OGRWFSDataSource" );
843 1 : if (psRoot == NULL)
844 : {
845 : CPLError(CE_Failure, CPLE_AppDefined,
846 0 : "Cannot find <OGRWFSDataSource>");
847 0 : CPLDestroyXMLNode( psXML );
848 0 : return FALSE;
849 : }
850 :
851 1 : pszBaseURL = CPLGetXMLValue(psRoot, "URL", NULL);
852 1 : if (pszBaseURL == NULL)
853 : {
854 : CPLError(CE_Failure, CPLE_AppDefined,
855 0 : "Cannot find <URL>");
856 0 : CPLDestroyXMLNode( psXML );
857 0 : return FALSE;
858 : }
859 1 : osBaseURL = pszBaseURL;
860 :
861 : /* -------------------------------------------------------------------- */
862 : /* Capture other parameters. */
863 : /* -------------------------------------------------------------------- */
864 : const char *pszParm;
865 :
866 1 : pszParm = CPLGetXMLValue( psRoot, "Timeout", NULL );
867 1 : if( pszParm )
868 : papszHttpOptions =
869 : CSLSetNameValue(papszHttpOptions,
870 0 : "TIMEOUT", pszParm );
871 :
872 1 : pszParm = CPLGetXMLValue( psRoot, "HTTPAUTH", NULL );
873 1 : if( pszParm )
874 : papszHttpOptions =
875 : CSLSetNameValue( papszHttpOptions,
876 0 : "HTTPAUTH", pszParm );
877 :
878 1 : pszParm = CPLGetXMLValue( psRoot, "USERPWD", NULL );
879 1 : if( pszParm )
880 : papszHttpOptions =
881 : CSLSetNameValue( papszHttpOptions,
882 0 : "USERPWD", pszParm );
883 :
884 1 : pszParm = CPLGetXMLValue( psRoot, "Version", NULL );
885 1 : if( pszParm )
886 0 : osVersion = pszParm;
887 :
888 1 : pszParm = CPLGetXMLValue( psRoot, "PagingAllowed", NULL );
889 1 : if( pszParm )
890 0 : bPagingAllowed = CSLTestBoolean(pszParm);
891 :
892 1 : pszParm = CPLGetXMLValue( psRoot, "PageSize", NULL );
893 1 : if( pszParm )
894 : {
895 0 : nPageSize = atoi(pszParm);
896 0 : if (nPageSize <= 0)
897 0 : nPageSize = 100;
898 : }
899 :
900 1 : osTypeName = CPLURLGetValue(pszBaseURL, "TYPENAME");
901 :
902 1 : psWFSCapabilities = WFSFindNode( psRoot, "WFS_Capabilities" );
903 1 : if (psWFSCapabilities == NULL)
904 : {
905 : CPLHTTPResult* psResult = SendGetCapabilities(pszBaseURL,
906 0 : osTypeName);
907 0 : if (psResult == NULL)
908 : {
909 0 : CPLDestroyXMLNode( psXML );
910 0 : return FALSE;
911 : }
912 :
913 0 : CPLXMLNode* psXML2 = CPLParseXMLString( (const char*) psResult->pabyData );
914 0 : if (psXML2 == NULL)
915 : {
916 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
917 0 : psResult->pabyData);
918 0 : CPLHTTPDestroyResult(psResult);
919 0 : CPLDestroyXMLNode( psXML );
920 0 : return FALSE;
921 : }
922 :
923 0 : CPLHTTPDestroyResult(psResult);
924 :
925 0 : psWFSCapabilities = WFSFindNode( psXML2, "WFS_Capabilities" );
926 0 : if (psWFSCapabilities == NULL )
927 : {
928 : CPLError(CE_Failure, CPLE_AppDefined,
929 0 : "Cannot find <WFS_Capabilities>");
930 0 : CPLDestroyXMLNode( psXML );
931 0 : CPLDestroyXMLNode( psXML2 );
932 0 : return FALSE;
933 : }
934 :
935 0 : CPLAddXMLChild(psXML, CPLCloneXMLTree(psWFSCapabilities));
936 :
937 0 : int bOK = CPLSerializeXMLTreeToFile(psXML, pszFilename);
938 :
939 0 : CPLDestroyXMLNode( psXML );
940 0 : CPLDestroyXMLNode( psXML2 );
941 :
942 0 : if (bOK)
943 0 : return Open(pszFilename, bUpdate);
944 : else
945 0 : return FALSE;
946 : }
947 : else
948 : {
949 1 : psFileXML = psXML;
950 :
951 : /* To avoid to have nodes after WFSCapabilities */
952 1 : CPLXMLNode* psAfterWFSCapabilities = psWFSCapabilities->psNext;
953 1 : psWFSCapabilities->psNext = NULL;
954 1 : char* pszXML = CPLSerializeXMLTree(psWFSCapabilities);
955 1 : psWFSCapabilities->psNext = psAfterWFSCapabilities;
956 1 : osGetCapabilities = pszXML;
957 1 : CPLFree(pszXML);
958 : }
959 : }
960 :
961 : int bInvertAxisOrderIfLatLong = CSLTestBoolean(CPLGetConfigOption(
962 16 : "GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
963 :
964 16 : CPLXMLNode* psStrippedXML = CPLCloneXMLTree(psXML);
965 16 : CPLStripXMLNamespace( psStrippedXML, NULL, TRUE );
966 16 : psWFSCapabilities = CPLGetXMLNode( psStrippedXML, "=WFS_Capabilities" );
967 16 : if (psWFSCapabilities == NULL)
968 : {
969 1 : psWFSCapabilities = CPLGetXMLNode( psStrippedXML, "=OGRWFSDataSource.WFS_Capabilities" );
970 : }
971 16 : if (psWFSCapabilities == NULL)
972 : {
973 : CPLError(CE_Failure, CPLE_AppDefined,
974 0 : "Cannot find <WFS_Capabilities>");
975 0 : if (!psFileXML) CPLDestroyXMLNode( psXML );
976 0 : CPLDestroyXMLNode( psStrippedXML );
977 0 : return FALSE;
978 : }
979 :
980 16 : if (pszBaseURL == NULL)
981 : {
982 : /* This is directly the Capabilities document */
983 0 : pszBaseURL = CPLGetXMLValue( psWFSCapabilities, "OperationsMetadata.Operation.DCP.HTTP.Get.href", NULL );
984 0 : if (pszBaseURL == NULL) /* WFS 1.0.0 variant */
985 0 : pszBaseURL = CPLGetXMLValue( psWFSCapabilities, "Capability.Request.GetCapabilities.DCPType.HTTP.Get.onlineResource", NULL );
986 :
987 0 : if (pszBaseURL == NULL)
988 : {
989 : CPLError(CE_Failure, CPLE_AppDefined,
990 0 : "Cannot find base URL");
991 0 : CPLDestroyXMLNode( psStrippedXML );
992 0 : return FALSE;
993 : }
994 :
995 0 : osBaseURL = pszBaseURL;
996 : }
997 :
998 16 : if (osVersion.size() == 0)
999 16 : osVersion = CPLGetXMLValue(psWFSCapabilities, "version", "1.0.0");
1000 16 : if (strcmp(osVersion.c_str(), "1.0.0") == 0)
1001 3 : bUseFeatureId = TRUE;
1002 : else
1003 : {
1004 : /* Some servers happen to support RESULTTYPE=hits in 1.0.0, but there */
1005 : /* is no way to advertisze this */
1006 13 : if (atoi(osVersion) >= 2)
1007 1 : bGetFeatureSupportHits = TRUE; /* WFS >= 2.0.0 supports hits */
1008 : else
1009 12 : bGetFeatureSupportHits = DetectIfGetFeatureSupportHits(psWFSCapabilities);
1010 13 : osRequiredOutputFormat = DetectRequiredOutputFormat(psWFSCapabilities);
1011 13 : bRequiresEnvelopeSpatialFilter = DetectRequiresEnvelopeSpatialFilter(psWFSCapabilities);
1012 : }
1013 :
1014 16 : DetectTransactionSupport(psWFSCapabilities);
1015 :
1016 : /* Detect if server is GEOSERVER */
1017 16 : CPLXMLNode* psKeywords = CPLGetXMLNode(psWFSCapabilities, "ServiceIdentification.Keywords");
1018 16 : if (psKeywords)
1019 : {
1020 9 : CPLXMLNode* psKeyword = psKeywords->psChild;
1021 36 : for(;psKeyword != NULL;psKeyword=psKeyword->psNext)
1022 : {
1023 33 : if (psKeyword->eType == CXT_Element &&
1024 : psKeyword->pszValue != NULL &&
1025 : EQUAL(psKeyword->pszValue, "Keyword") &&
1026 : psKeyword->psChild != NULL &&
1027 : psKeyword->psChild->pszValue != NULL &&
1028 : EQUALN(psKeyword->psChild->pszValue, "GEOSERVER", 9))
1029 : {
1030 6 : bIsGEOSERVER = TRUE;
1031 6 : break;
1032 : }
1033 : }
1034 : }
1035 :
1036 16 : if (bUpdate && !bTransactionSupport)
1037 : {
1038 : CPLError(CE_Failure, CPLE_AppDefined,
1039 0 : "Server is read-only WFS; no WFS-T feature advertized");
1040 0 : if (!psFileXML) CPLDestroyXMLNode( psXML );
1041 0 : CPLDestroyXMLNode( psStrippedXML );
1042 0 : return FALSE;
1043 : }
1044 :
1045 16 : CPLXMLNode* psFilterCap = CPLGetXMLNode(psWFSCapabilities, "Filter_Capabilities.Scalar_Capabilities");
1046 16 : if (psFilterCap)
1047 : {
1048 : bHasMinOperators = CPLGetXMLNode(psFilterCap, "LogicalOperators") != NULL ||
1049 16 : CPLGetXMLNode(psFilterCap, "Logical_Operators") != NULL;
1050 16 : if (CPLGetXMLNode(psFilterCap, "ComparisonOperators"))
1051 13 : psFilterCap = CPLGetXMLNode(psFilterCap, "ComparisonOperators");
1052 3 : else if (CPLGetXMLNode(psFilterCap, "Comparison_Operators"))
1053 3 : psFilterCap = CPLGetXMLNode(psFilterCap, "Comparison_Operators");
1054 : else
1055 0 : psFilterCap = NULL;
1056 16 : if (psFilterCap)
1057 : {
1058 16 : if (CPLGetXMLNode(psFilterCap, "Simple_Comparisons") == NULL)
1059 : {
1060 13 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThan");
1061 13 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThan");
1062 13 : if (atoi(osVersion) >= 2)
1063 : {
1064 1 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThanOrEqualTo");
1065 1 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThanOrEqualTo");
1066 : }
1067 : else
1068 : {
1069 12 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThanEqualTo");
1070 12 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThanEqualTo");
1071 : }
1072 13 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "EqualTo");
1073 13 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "NotEqualTo");
1074 13 : bHasMinOperators &= FindComparisonOperator(psFilterCap, "Like");
1075 : }
1076 : else
1077 : {
1078 : bHasMinOperators &= CPLGetXMLNode(psFilterCap, "Simple_Comparisons") != NULL &&
1079 3 : CPLGetXMLNode(psFilterCap, "Like") != NULL;
1080 : }
1081 : bHasNullCheck = FindComparisonOperator(psFilterCap, "NullCheck") ||
1082 : FindComparisonOperator(psFilterCap, "Null") || /* WFS 2.0.0 */
1083 16 : CPLGetXMLNode(psFilterCap, "NullCheck") != NULL;
1084 : }
1085 : else
1086 : {
1087 0 : bHasMinOperators = FALSE;
1088 : }
1089 : }
1090 :
1091 16 : CPLXMLNode* psChild = CPLGetXMLNode(psWFSCapabilities, "FeatureTypeList");
1092 16 : if (psChild == NULL)
1093 : {
1094 : CPLError(CE_Failure, CPLE_AppDefined,
1095 0 : "Cannot find <FeatureTypeList>");
1096 0 : if (!psFileXML) CPLDestroyXMLNode( psXML );
1097 0 : CPLDestroyXMLNode( psStrippedXML );
1098 0 : return FALSE;
1099 : }
1100 :
1101 : CPLXMLNode* psChildIter;
1102 :
1103 : /* Check if there are layer names whose identical except their prefix */
1104 16 : std::set<CPLString> aosSetLayerNames;
1105 247 : for(psChildIter = psChild->psChild;
1106 : psChildIter != NULL;
1107 : psChildIter = psChildIter->psNext)
1108 : {
1109 234 : if (psChildIter->eType == CXT_Element &&
1110 : strcmp(psChildIter->pszValue, "FeatureType") == 0)
1111 : {
1112 224 : const char* pszName = CPLGetXMLValue(psChildIter, "Name", NULL);
1113 224 : if (pszName != NULL)
1114 : {
1115 224 : const char* pszShortName = strchr(pszName, ':');
1116 224 : if (pszShortName)
1117 218 : pszName = pszShortName + 1;
1118 224 : if (aosSetLayerNames.find(pszName) != aosSetLayerNames.end())
1119 : {
1120 3 : bKeepLayerNamePrefix = TRUE;
1121 3 : CPLDebug("WFS", "At least 2 layers have names that are only distinguishable by keeping the prefix");
1122 3 : break;
1123 : }
1124 221 : aosSetLayerNames.insert(pszName);
1125 : }
1126 : }
1127 : }
1128 :
1129 283 : for(psChildIter = psChild->psChild;
1130 : psChildIter != NULL;
1131 : psChildIter = psChildIter->psNext)
1132 : {
1133 267 : if (psChildIter->eType == CXT_Element &&
1134 : strcmp(psChildIter->pszValue, "FeatureType") == 0)
1135 : {
1136 257 : const char* pszNS = NULL;
1137 257 : const char* pszNSVal = NULL;
1138 257 : CPLXMLNode* psFeatureTypeIter = psChildIter->psChild;
1139 2287 : while(psFeatureTypeIter != NULL)
1140 : {
1141 1773 : if (psFeatureTypeIter->eType == CXT_Attribute)
1142 : {
1143 209 : pszNS = psFeatureTypeIter->pszValue;
1144 209 : pszNSVal = psFeatureTypeIter->psChild->pszValue;
1145 : }
1146 1773 : psFeatureTypeIter = psFeatureTypeIter->psNext;
1147 : }
1148 :
1149 257 : const char* pszName = CPLGetXMLValue(psChildIter, "Name", NULL);
1150 257 : const char* pszTitle = CPLGetXMLValue(psChildIter, "Title", NULL);
1151 257 : const char* pszAbstract = CPLGetXMLValue(psChildIter, "Abstract", NULL);
1152 257 : if (pszName != NULL &&
1153 : (osTypeName.size() == 0 ||
1154 : strcmp(osTypeName.c_str(), pszName) == 0))
1155 : {
1156 : const char* pszDefaultSRS =
1157 119 : CPLGetXMLValue(psChildIter, "DefaultSRS", NULL);
1158 119 : if (pszDefaultSRS == NULL)
1159 33 : pszDefaultSRS = CPLGetXMLValue(psChildIter, "SRS", NULL);
1160 119 : if (pszDefaultSRS == NULL)
1161 18 : pszDefaultSRS = CPLGetXMLValue(psChildIter, "DefaultCRS", NULL); /* WFS 2.0.0 */
1162 :
1163 119 : OGRSpatialReference* poSRS = NULL;
1164 119 : int bAxisOrderAlreadyInverted = FALSE;
1165 :
1166 : /* If a SRSNAME parameter has been encoded in the URL, use it as the SRS */
1167 119 : CPLString osSRSName = CPLURLGetValue(pszBaseURL, "SRSNAME");
1168 119 : if (osSRSName.size() != 0)
1169 : {
1170 0 : pszDefaultSRS = osSRSName.c_str();
1171 : }
1172 :
1173 119 : if (pszDefaultSRS)
1174 : {
1175 119 : OGRSpatialReference oSRS;
1176 119 : if (oSRS.SetFromUserInput(pszDefaultSRS) == OGRERR_NONE)
1177 : {
1178 119 : poSRS = oSRS.Clone();
1179 119 : if (bInvertAxisOrderIfLatLong &&
1180 : GML_IsSRSLatLongOrder(pszDefaultSRS))
1181 : {
1182 25 : bAxisOrderAlreadyInverted = TRUE;
1183 :
1184 : OGR_SRSNode *poGEOGCS =
1185 25 : poSRS->GetAttrNode( "GEOGCS" );
1186 25 : if( poGEOGCS != NULL )
1187 : {
1188 25 : poGEOGCS->StripNodes( "AXIS" );
1189 : }
1190 : }
1191 119 : }
1192 : }
1193 :
1194 119 : CPLXMLNode* psBBox = NULL;
1195 119 : CPLXMLNode* psLatLongBBox = NULL;
1196 119 : int bFoundBBox = FALSE;
1197 119 : double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0;
1198 119 : if ((psBBox = CPLGetXMLNode(psChildIter, "WGS84BoundingBox")) != NULL)
1199 : {
1200 104 : const char* pszLC = CPLGetXMLValue(psBBox, "LowerCorner", NULL);
1201 104 : const char* pszUC = CPLGetXMLValue(psBBox, "UpperCorner", NULL);
1202 104 : if (pszLC != NULL && pszUC != NULL)
1203 : {
1204 104 : CPLString osConcat(pszLC);
1205 104 : osConcat += " ";
1206 104 : osConcat += pszUC;
1207 : char** papszTokens;
1208 : papszTokens = CSLTokenizeStringComplex(
1209 104 : osConcat, " ,", FALSE, FALSE );
1210 104 : if (CSLCount(papszTokens) == 4)
1211 : {
1212 104 : bFoundBBox = TRUE;
1213 104 : dfMinX = CPLAtof(papszTokens[0]);
1214 104 : dfMinY = CPLAtof(papszTokens[1]);
1215 104 : dfMaxX = CPLAtof(papszTokens[2]);
1216 104 : dfMaxY = CPLAtof(papszTokens[3]);
1217 : }
1218 104 : CSLDestroy(papszTokens);
1219 : }
1220 : }
1221 15 : else if ((psLatLongBBox = CPLGetXMLNode(psChildIter,
1222 : "LatLongBoundingBox")) != NULL)
1223 : {
1224 : const char* pszMinX =
1225 15 : CPLGetXMLValue(psLatLongBBox, "minx", NULL);
1226 : const char* pszMinY =
1227 15 : CPLGetXMLValue(psLatLongBBox, "miny", NULL);
1228 : const char* pszMaxX =
1229 15 : CPLGetXMLValue(psLatLongBBox, "maxx", NULL);
1230 : const char* pszMaxY =
1231 15 : CPLGetXMLValue(psLatLongBBox, "maxy", NULL);
1232 15 : if (pszMinX != NULL && pszMinY != NULL &&
1233 : pszMaxX != NULL && pszMaxY != NULL)
1234 : {
1235 15 : bFoundBBox = TRUE;
1236 15 : dfMinX = CPLAtof(pszMinX);
1237 15 : dfMinY = CPLAtof(pszMinY);
1238 15 : dfMaxX = CPLAtof(pszMaxX);
1239 15 : dfMaxY = CPLAtof(pszMaxY);
1240 : }
1241 : }
1242 :
1243 119 : osLayerMetadataCSV += "\"";
1244 119 : osLayerMetadataCSV += pszName;
1245 119 : osLayerMetadataCSV += "\"";
1246 119 : osLayerMetadataCSV += ",";
1247 119 : if (pszTitle)
1248 : {
1249 103 : osLayerMetadataCSV += "\"";
1250 103 : osLayerMetadataCSV += pszTitle;
1251 103 : osLayerMetadataCSV += "\"";
1252 : }
1253 119 : osLayerMetadataCSV += ",";
1254 119 : if (pszAbstract)
1255 : {
1256 35 : osLayerMetadataCSV += "\"";
1257 35 : osLayerMetadataCSV += pszAbstract;
1258 35 : osLayerMetadataCSV += "\"";
1259 : }
1260 119 : osLayerMetadataCSV += "\n";
1261 :
1262 : OGRWFSLayer* poLayer = new OGRWFSLayer(
1263 : this, poSRS, bAxisOrderAlreadyInverted,
1264 119 : pszBaseURL, pszName, pszNS, pszNSVal);
1265 :
1266 119 : if (poSRS)
1267 : {
1268 119 : char* pszProj4 = NULL;
1269 119 : if (poSRS->exportToProj4(&pszProj4) == OGRERR_NONE)
1270 : {
1271 : /* See http://trac.osgeo.org/gdal/ticket/4041 */
1272 : /* For now, we restrict to GEOSERVER as apparently the order is always longitude,latitude */
1273 : /* other servers might also qualify, so this should be relaxed */
1274 : /* Also accept when <wfs:DefaultCRS>urn:ogc:def:crs:OGC:1.3:CRS84</wfs:DefaultCRS> */
1275 119 : if ((bIsGEOSERVER &&
1276 : (strcmp(pszProj4, "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ") == 0 ||
1277 : strcmp(pszProj4, "+proj=longlat +datum=WGS84 +no_defs ") == 0)) ||
1278 : strcmp(pszDefaultSRS, "urn:ogc:def:crs:OGC:1.3:CRS84") == 0)
1279 : {
1280 24 : poLayer->SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
1281 : }
1282 : #if 0
1283 : else
1284 : {
1285 : OGRSpatialReference oWGS84;
1286 : oWGS84.SetWellKnownGeogCS("WGS84");
1287 : OGRCoordinateTransformation* poCT;
1288 : poCT = OGRCreateCoordinateTransformation(&oWGS84, poSRS);
1289 : if (poCT)
1290 : {
1291 : double dfULX = dfMinX;
1292 : double dfULY = dfMaxY;
1293 : double dfURX = dfMaxX;
1294 : double dfURY = dfMaxY;
1295 : double dfLLX = dfMinX;
1296 : double dfLLY = dfMinY;
1297 : double dfLRX = dfMaxX;
1298 : double dfLRY = dfMinY;
1299 : if (poCT->Transform(1, &dfULX, &dfULY, NULL) &&
1300 : poCT->Transform(1, &dfURX, &dfURY, NULL) &&
1301 : poCT->Transform(1, &dfLLX, &dfLLY, NULL) &&
1302 : poCT->Transform(1, &dfLRX, &dfLRY, NULL))
1303 : {
1304 : dfMinX = dfULX;
1305 : dfMinX = MIN(dfMinX, dfURX);
1306 : dfMinX = MIN(dfMinX, dfLLX);
1307 : dfMinX = MIN(dfMinX, dfLRX);
1308 :
1309 : dfMinY = dfULY;
1310 : dfMinY = MIN(dfMinY, dfURY);
1311 : dfMinY = MIN(dfMinY, dfLLY);
1312 : dfMinY = MIN(dfMinY, dfLRY);
1313 :
1314 : dfMaxX = dfULX;
1315 : dfMaxX = MAX(dfMaxX, dfURX);
1316 : dfMaxX = MAX(dfMaxX, dfLLX);
1317 : dfMaxX = MAX(dfMaxX, dfLRX);
1318 :
1319 : dfMaxY = dfULY;
1320 : dfMaxY = MAX(dfMaxY, dfURY);
1321 : dfMaxY = MAX(dfMaxY, dfLLY);
1322 : dfMaxY = MAX(dfMaxY, dfLRY);
1323 :
1324 : poLayer->SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
1325 : }
1326 : }
1327 : delete poCT;
1328 : }
1329 : #endif
1330 : }
1331 119 : CPLFree(pszProj4);
1332 : }
1333 :
1334 : papoLayers = (OGRWFSLayer **)CPLRealloc(papoLayers,
1335 119 : sizeof(OGRWFSLayer*) * (nLayers + 1));
1336 119 : papoLayers[nLayers ++] = poLayer;
1337 :
1338 119 : if (psFileXML != NULL)
1339 : {
1340 3 : CPLXMLNode* psIter = psXML->psChild;
1341 15 : while(psIter)
1342 : {
1343 12 : if (psIter->eType == CXT_Element &&
1344 : EQUAL(psIter->pszValue, "OGRWFSLayer") &&
1345 : strcmp(CPLGetXMLValue(psIter, "name", ""), pszName) == 0)
1346 : {
1347 3 : CPLXMLNode* psSchema = WFSFindNode( psIter->psChild, "schema" );
1348 3 : if (psSchema)
1349 : {
1350 3 : OGRFeatureDefn* poSrcFDefn = poLayer->ParseSchema(psSchema);
1351 3 : if (poSrcFDefn)
1352 3 : poLayer->BuildLayerDefn(poSrcFDefn);
1353 : }
1354 3 : break;
1355 : }
1356 9 : psIter = psIter->psNext;
1357 : }
1358 119 : }
1359 : }
1360 : }
1361 : }
1362 :
1363 16 : if (!psFileXML) CPLDestroyXMLNode( psXML );
1364 16 : CPLDestroyXMLNode( psStrippedXML );
1365 :
1366 16 : return TRUE;
1367 : }
1368 :
1369 : /************************************************************************/
1370 : /* LoadMultipleLayerDefn() */
1371 : /************************************************************************/
1372 :
1373 : /* TinyOWS doesn't support POST, but MapServer, GeoServer and Deegree do */
1374 : #define USE_GET_FOR_DESCRIBE_FEATURE_TYPE 1
1375 :
1376 15 : void OGRWFSDataSource::LoadMultipleLayerDefn(const char* pszLayerName,
1377 : char* pszNS, char* pszNSVal)
1378 : {
1379 : /*if (!bIsGEOSERVER)
1380 : return;*/
1381 :
1382 15 : if (!bLoadMultipleLayerDefn)
1383 1 : return;
1384 :
1385 14 : if (aoSetAlreadyTriedLayers.find(pszLayerName) != aoSetAlreadyTriedLayers.end())
1386 0 : return;
1387 :
1388 14 : char* pszPrefix = CPLStrdup(pszLayerName);
1389 14 : char* pszColumn = strchr(pszPrefix, ':');
1390 14 : if (pszColumn)
1391 13 : *pszColumn = 0;
1392 : else
1393 1 : *pszPrefix = 0;
1394 :
1395 : #if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1
1396 14 : CPLString osLayerToFetch(pszLayerName);
1397 : #else
1398 : CPLString osTypeNameToPost;
1399 : osTypeNameToPost += " <TypeName>";
1400 : osTypeNameToPost += pszLayerName;
1401 : osTypeNameToPost += "</TypeName>\n";
1402 : #endif
1403 :
1404 14 : int nLayersToFetch = 1;
1405 14 : aoSetAlreadyTriedLayers.insert(pszLayerName);
1406 :
1407 126 : for(int i=0;i<nLayers;i++)
1408 : {
1409 112 : if (!papoLayers[i]->HasLayerDefn())
1410 : {
1411 : /* We must be careful to requests only layers with the same prefix/namespace */
1412 112 : const char* pszName = papoLayers[i]->GetName();
1413 303 : if ((pszPrefix[0] == 0 && strchr(pszName, ':') == NULL) ||
1414 110 : (pszPrefix[0] != 0 && strncmp(pszName, pszPrefix, strlen(pszPrefix)) == 0 &&
1415 81 : pszName[strlen(pszPrefix)] == ':'))
1416 : {
1417 83 : if (aoSetAlreadyTriedLayers.find(pszName) != aoSetAlreadyTriedLayers.end())
1418 14 : continue;
1419 69 : aoSetAlreadyTriedLayers.insert(pszName);
1420 :
1421 : #if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1
1422 69 : if (nLayersToFetch > 0)
1423 69 : osLayerToFetch += ",";
1424 69 : osLayerToFetch += papoLayers[i]->GetName();
1425 : #else
1426 : osTypeNameToPost += " <TypeName>";
1427 : osTypeNameToPost += pszName;
1428 : osTypeNameToPost += "</TypeName>\n";
1429 : #endif
1430 69 : nLayersToFetch ++;
1431 :
1432 : /* Avoid fetching to many layer definition at a time */
1433 69 : if (nLayersToFetch >= 50)
1434 0 : break;
1435 : }
1436 : }
1437 : }
1438 :
1439 14 : CPLFree(pszPrefix);
1440 14 : pszPrefix = NULL;
1441 :
1442 : #if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1
1443 14 : CPLString osURL(osBaseURL);
1444 14 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
1445 14 : osURL = CPLURLAddKVP(osURL, "VERSION", GetVersion());
1446 14 : osURL = CPLURLAddKVP(osURL, "REQUEST", "DescribeFeatureType");
1447 14 : osURL = CPLURLAddKVP(osURL, "TYPENAME", osLayerToFetch);
1448 14 : osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", NULL);
1449 14 : osURL = CPLURLAddKVP(osURL, "MAXFEATURES", NULL);
1450 14 : osURL = CPLURLAddKVP(osURL, "FILTER", NULL);
1451 14 : osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", GetRequiredOutputFormat());
1452 :
1453 14 : if (pszNS && GetNeedNAMESPACE())
1454 : {
1455 : /* Older Deegree version require NAMESPACE */
1456 : /* This has been now corrected */
1457 0 : CPLString osValue("xmlns(");
1458 0 : osValue += pszNS;
1459 0 : osValue += "=";
1460 0 : osValue += pszNSVal;
1461 0 : osValue += ")";
1462 0 : osURL = CPLURLAddKVP(osURL, "NAMESPACE", osValue);
1463 : }
1464 :
1465 14 : CPLHTTPResult* psResult = HTTPFetch( osURL, NULL);
1466 : #else
1467 : CPLString osPost;
1468 : osPost += "<?xml version=\"1.0\"?>\n";
1469 : osPost += "<wfs:DescribeFeatureType xmlns:wfs=\"http://www.opengis.net/wfs\"\n";
1470 : osPost += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
1471 : osPost += " service=\"WFS\" version=\""; osPost += GetVersion(); osPost += "\"\n";
1472 : osPost += " xmlns:gml=\"http://www.opengis.net/gml\"\n";
1473 : osPost += " xmlns:ogc=\"http://www.opengis.net/ogc\"\n";
1474 : if (pszNS && pszNSVal)
1475 : {
1476 : osPost += " xmlns:";
1477 : osPost += pszNS;
1478 : osPost += "=\"";
1479 : osPost += pszNSVal;
1480 : osPost += "\"\n";
1481 : }
1482 : osPost += " xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/";
1483 : osPost += GetVersion();
1484 : osPost += "/wfs.xsd\"";
1485 : if (osRequiredOutputFormat.size())
1486 : {
1487 : osPost += "\n";
1488 : osPost += " outputFormat=\"";
1489 : osPost += osRequiredOutputFormat;
1490 : osPost += "\"";
1491 : }
1492 : osPost += ">\n";
1493 : osPost += osTypeNameToPost;
1494 : osPost += "</wfs:DescribeFeatureType>\n";
1495 :
1496 : //CPLDebug("WFS", "%s", osPost.c_str());
1497 :
1498 : char** papszOptions = NULL;
1499 : papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
1500 : papszOptions = CSLAddNameValue(papszOptions, "HEADERS",
1501 : "Content-Type: application/xml; charset=UTF-8");
1502 :
1503 : CPLHTTPResult* psResult = HTTPFetch(GetPostTransactionURL(), papszOptions);
1504 : CSLDestroy(papszOptions);
1505 : #endif
1506 :
1507 14 : if (psResult == NULL)
1508 : {
1509 0 : bLoadMultipleLayerDefn = FALSE;
1510 : return;
1511 : }
1512 :
1513 14 : if (strstr((const char*)psResult->pabyData, "<ServiceExceptionReport") != NULL)
1514 : {
1515 0 : if (IsOldDeegree((const char*)psResult->pabyData))
1516 : {
1517 : /* just silently forgive */
1518 : }
1519 : else
1520 : {
1521 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
1522 0 : psResult->pabyData);
1523 : }
1524 0 : CPLHTTPDestroyResult(psResult);
1525 0 : bLoadMultipleLayerDefn = FALSE;
1526 : return;
1527 : }
1528 :
1529 14 : CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData );
1530 14 : if (psXML == NULL)
1531 : {
1532 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
1533 0 : psResult->pabyData);
1534 0 : CPLHTTPDestroyResult(psResult);
1535 0 : bLoadMultipleLayerDefn = FALSE;
1536 : return;
1537 : }
1538 14 : CPLHTTPDestroyResult(psResult);
1539 :
1540 14 : CPLXMLNode* psSchema = WFSFindNode(psXML, "schema");
1541 14 : if (psSchema == NULL)
1542 : {
1543 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find <Schema>");
1544 0 : CPLDestroyXMLNode( psXML );
1545 0 : bLoadMultipleLayerDefn = FALSE;
1546 : return;
1547 : }
1548 :
1549 14 : CPLString osTmpFileName;
1550 :
1551 14 : osTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this);
1552 14 : CPLSerializeXMLTreeToFile(psSchema, osTmpFileName);
1553 :
1554 14 : std::vector<GMLFeatureClass*> aosClasses;
1555 14 : GMLParseXSD( osTmpFileName, aosClasses );
1556 :
1557 14 : if ((int)aosClasses.size() == nLayersToFetch)
1558 : {
1559 9 : std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
1560 9 : std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
1561 48 : while (iter != eiter)
1562 : {
1563 30 : GMLFeatureClass* poClass = *iter;
1564 30 : iter ++;
1565 :
1566 : OGRWFSLayer* poLayer;
1567 :
1568 30 : if (bKeepLayerNamePrefix && pszNS != NULL && strchr(poClass->GetName(), ':') == NULL)
1569 : {
1570 0 : CPLString osWithPrefix(pszNS);
1571 0 : osWithPrefix += ":";
1572 0 : osWithPrefix += poClass->GetName();
1573 0 : poLayer = (OGRWFSLayer* )GetLayerByName(osWithPrefix);
1574 : }
1575 : else
1576 30 : poLayer = (OGRWFSLayer* )GetLayerByName(poClass->GetName());
1577 :
1578 30 : if (poLayer)
1579 : {
1580 30 : if (!poLayer->HasLayerDefn())
1581 : {
1582 30 : CPLXMLNode* psSchemaForLayer = CPLCloneXMLTree(psSchema);
1583 30 : CPLStripXMLNamespace( psSchemaForLayer, NULL, TRUE );
1584 30 : CPLXMLNode* psIter = psSchemaForLayer->psChild;
1585 30 : int bHasAlreadyImportedGML = FALSE;
1586 30 : int bFoundComplexType = FALSE;
1587 30 : int bFoundElement = FALSE;
1588 758 : while(psIter != NULL)
1589 : {
1590 698 : CPLXMLNode* psIterNext = psIter->psNext;
1591 724 : if (psIter->eType == CXT_Element &&
1592 : strcmp(psIter->pszValue,"complexType") == 0)
1593 : {
1594 26 : const char* pszName = CPLGetXMLValue(psIter, "name", "");
1595 26 : CPLString osExpectedName(poLayer->GetShortName());
1596 26 : osExpectedName += "Type";
1597 26 : CPLString osExpectedName2(poLayer->GetShortName());
1598 26 : osExpectedName2 += "_Type";
1599 26 : if (strcmp(pszName, osExpectedName) == 0 ||
1600 : strcmp(pszName, osExpectedName2) == 0 ||
1601 : strcmp(pszName, poLayer->GetShortName()) == 0)
1602 : {
1603 12 : bFoundComplexType = TRUE;
1604 : }
1605 : else
1606 : {
1607 14 : CPLRemoveXMLChild( psSchemaForLayer, psIter );
1608 14 : CPLDestroyXMLNode(psIter);
1609 26 : }
1610 : }
1611 1022 : else if (psIter->eType == CXT_Element &&
1612 : strcmp(psIter->pszValue,"element") == 0)
1613 : {
1614 350 : const char* pszName = CPLGetXMLValue(psIter, "name", "");
1615 350 : CPLString osExpectedName(poLayer->GetShortName());
1616 350 : osExpectedName += "Type";
1617 350 : CPLString osExpectedName2(poLayer->GetShortName());
1618 350 : osExpectedName2 += "_Type";
1619 :
1620 350 : const char* pszType = CPLGetXMLValue(psIter, "type", "");
1621 350 : CPLString osExpectedType(poLayer->GetName());
1622 350 : osExpectedType += "Type";
1623 350 : CPLString osExpectedType2(poLayer->GetName());
1624 350 : osExpectedType2 += "_Type";
1625 690 : if (strcmp(pszType, osExpectedType) == 0 ||
1626 : strcmp(pszType, osExpectedType2) == 0 ||
1627 340 : strcmp(pszType, poLayer->GetName()) == 0 ||
1628 : (strchr(pszType, ':') &&
1629 : (strcmp(strchr(pszType, ':') + 1, osExpectedType) == 0 ||
1630 : strcmp(strchr(pszType, ':') + 1, osExpectedType2) == 0)))
1631 : {
1632 12 : bFoundElement = TRUE;
1633 : }
1634 338 : else if (*pszType == '\0' &&
1635 : CPLGetXMLNode(psIter, "complexType") != NULL &&
1636 : (strcmp(pszName, osExpectedName) == 0 ||
1637 : strcmp(pszName, osExpectedName2) == 0 ||
1638 : strcmp(pszName, poLayer->GetShortName()) == 0) )
1639 : {
1640 18 : bFoundElement = TRUE;
1641 18 : bFoundComplexType = TRUE;
1642 : }
1643 : else
1644 : {
1645 320 : CPLRemoveXMLChild( psSchemaForLayer, psIter );
1646 320 : CPLDestroyXMLNode(psIter);
1647 350 : }
1648 : }
1649 322 : else if (psIter->eType == CXT_Element &&
1650 : strcmp(psIter->pszValue,"import") == 0 &&
1651 : strcmp(CPLGetXMLValue(psIter, "namespace", ""),
1652 : "http://www.opengis.net/gml") == 0)
1653 : {
1654 12 : if (bHasAlreadyImportedGML)
1655 : {
1656 0 : CPLRemoveXMLChild( psSchemaForLayer, psIter );
1657 0 : CPLDestroyXMLNode(psIter);
1658 : }
1659 : else
1660 12 : bHasAlreadyImportedGML = TRUE;
1661 : }
1662 698 : psIter = psIterNext;
1663 : }
1664 :
1665 30 : if (bFoundComplexType && bFoundElement)
1666 : {
1667 30 : OGRFeatureDefn* poSrcFDefn = poLayer->ParseSchema(psSchemaForLayer);
1668 30 : if (poSrcFDefn)
1669 : {
1670 30 : poLayer->BuildLayerDefn(poSrcFDefn);
1671 30 : SaveLayerSchema(poLayer->GetName(), psSchemaForLayer);
1672 : }
1673 : }
1674 :
1675 30 : CPLDestroyXMLNode(psSchemaForLayer);
1676 : }
1677 : else
1678 : {
1679 : CPLDebug("WFS", "Found several time schema for layer %s in server response. Shouldn't happen",
1680 0 : poClass->GetName());
1681 : }
1682 : }
1683 : else
1684 : {
1685 : CPLDebug("WFS", "Cannot find layer %s. Shouldn't happen",
1686 0 : poClass->GetName());
1687 : }
1688 30 : delete poClass;
1689 : }
1690 : }
1691 5 : else if (aosClasses.size() > 0)
1692 : {
1693 2 : std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
1694 2 : std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
1695 16 : while (iter != eiter)
1696 : {
1697 12 : GMLFeatureClass* poClass = *iter;
1698 12 : iter ++;
1699 12 : delete poClass;
1700 : }
1701 : }
1702 : else
1703 : {
1704 3 : CPLDebug("WFS", "Turn off loading of multiple layer definitions at a single time");
1705 3 : bLoadMultipleLayerDefn = FALSE;
1706 : }
1707 :
1708 14 : VSIUnlink(osTmpFileName);
1709 :
1710 14 : CPLDestroyXMLNode( psXML );
1711 : }
1712 :
1713 : /************************************************************************/
1714 : /* SaveLayerSchema() */
1715 : /************************************************************************/
1716 :
1717 36 : void OGRWFSDataSource::SaveLayerSchema(const char* pszLayerName, CPLXMLNode* psSchema)
1718 : {
1719 36 : if (psFileXML != NULL)
1720 : {
1721 0 : bRewriteFile = TRUE;
1722 0 : CPLXMLNode* psLayerNode = CPLCreateXMLNode(NULL, CXT_Element, "OGRWFSLayer");
1723 0 : CPLSetXMLValue(psLayerNode, "#name", pszLayerName);
1724 0 : CPLAddXMLChild(psLayerNode, CPLCloneXMLTree(psSchema));
1725 0 : CPLAddXMLChild(psFileXML, psLayerNode);
1726 : }
1727 36 : }
1728 :
1729 : /************************************************************************/
1730 : /* IsOldDeegree() */
1731 : /************************************************************************/
1732 :
1733 0 : int OGRWFSDataSource::IsOldDeegree(const char* pszErrorString)
1734 : {
1735 0 : if (!bNeedNAMESPACE &&
1736 : strstr(pszErrorString, "Invalid \"TYPENAME\" parameter. No binding for prefix") != NULL)
1737 : {
1738 0 : bNeedNAMESPACE = TRUE;
1739 0 : return TRUE;
1740 : }
1741 0 : return FALSE;
1742 : }
1743 :
1744 : /************************************************************************/
1745 : /* WFS_EscapeURL() */
1746 : /************************************************************************/
1747 :
1748 69 : static CPLString WFS_EscapeURL(CPLString osURL)
1749 : {
1750 69 : CPLString osNewURL;
1751 : size_t i;
1752 :
1753 69 : int bNeedsEscaping = FALSE;
1754 9249 : for(i=0;i<osURL.size();i++)
1755 : {
1756 9193 : char ch = osURL[i];
1757 9193 : if (ch == '<' || ch == '>' || ch == ' ' || ch == '"')
1758 : {
1759 13 : bNeedsEscaping = TRUE;
1760 13 : break;
1761 : }
1762 : }
1763 :
1764 69 : if (!bNeedsEscaping)
1765 56 : return osURL;
1766 :
1767 6151 : for(i=0;i<osURL.size();i++)
1768 : {
1769 6138 : char ch = osURL[i];
1770 6138 : if (ch == '<')
1771 180 : osNewURL += "%3C";
1772 5958 : else if (ch == '>')
1773 180 : osNewURL += "%3E";
1774 5778 : else if (ch == ' ')
1775 47 : osNewURL += "%20";
1776 5731 : else if (ch == '"')
1777 80 : osNewURL += "%22";
1778 5651 : else if (ch == '%')
1779 0 : osNewURL += "%25";
1780 : else
1781 5651 : osNewURL += ch;
1782 : }
1783 13 : return osNewURL;
1784 : }
1785 :
1786 : /************************************************************************/
1787 : /* HTTPFetch() */
1788 : /************************************************************************/
1789 :
1790 69 : CPLHTTPResult* OGRWFSDataSource::HTTPFetch( const char* pszURL, char** papszOptions )
1791 : {
1792 69 : char** papszNewOptions = CSLDuplicate(papszOptions);
1793 69 : if (bUseHttp10)
1794 0 : papszNewOptions = CSLAddNameValue(papszNewOptions, "HTTP_VERSION", "1.0");
1795 69 : if (papszHttpOptions)
1796 0 : papszNewOptions = CSLMerge(papszNewOptions, papszHttpOptions);
1797 69 : CPLHTTPResult* psResult = CPLHTTPFetch( WFS_EscapeURL(pszURL), papszNewOptions );
1798 69 : CSLDestroy(papszNewOptions);
1799 :
1800 69 : if (psResult == NULL)
1801 : {
1802 0 : return NULL;
1803 : }
1804 69 : if (psResult->nStatus != 0 || psResult->pszErrBuf != NULL)
1805 : {
1806 : /* A few buggy servers return chunked data with errouneous remaining bytes value */
1807 : /* curl doesn't like this. Retry with HTTP 1.0 protocol instead that doesn't support */
1808 : /* chunked data */
1809 0 : if (psResult->pszErrBuf &&
1810 : strstr(psResult->pszErrBuf, "transfer closed with outstanding read data remaining") &&
1811 : !bUseHttp10)
1812 : {
1813 0 : CPLDebug("WFS", "Probably buggy remote server. Retrying with HTTP 1.0 protocol");
1814 0 : bUseHttp10 = TRUE;
1815 0 : return HTTPFetch(pszURL, papszOptions);
1816 : }
1817 :
1818 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s (%d)",
1819 0 : (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown", psResult->nStatus);
1820 0 : CPLHTTPDestroyResult(psResult);
1821 0 : return NULL;
1822 : }
1823 69 : if (psResult->pabyData == NULL)
1824 : {
1825 0 : CPLError(CE_Failure, CPLE_AppDefined, "Empty content returned by server");
1826 0 : CPLHTTPDestroyResult(psResult);
1827 0 : return NULL;
1828 : }
1829 69 : return psResult;
1830 : }
1831 :
1832 : /************************************************************************/
1833 : /* ExecuteSQL() */
1834 : /************************************************************************/
1835 :
1836 5 : OGRLayer * OGRWFSDataSource::ExecuteSQL( const char *pszSQLCommand,
1837 : OGRGeometry *poSpatialFilter,
1838 : const char *pszDialect )
1839 :
1840 : {
1841 : /* -------------------------------------------------------------------- */
1842 : /* Use generic implementation for OGRSQL dialect. */
1843 : /* -------------------------------------------------------------------- */
1844 5 : if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") )
1845 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
1846 : poSpatialFilter,
1847 0 : pszDialect );
1848 :
1849 : /* -------------------------------------------------------------------- */
1850 : /* Deal with "SELECT _LAST_INSERTED_FIDS_ FROM layername" statement */
1851 : /* -------------------------------------------------------------------- */
1852 5 : if( EQUALN(pszSQLCommand, "SELECT _LAST_INSERTED_FIDS_ FROM ", 33) )
1853 : {
1854 1 : const char* pszIter = pszSQLCommand + 33;
1855 14 : while(*pszIter && *pszIter != ' ')
1856 12 : pszIter ++;
1857 :
1858 1 : CPLString osName = pszSQLCommand + 33;
1859 1 : osName.resize(pszIter - (pszSQLCommand + 33));
1860 1 : OGRWFSLayer* poLayer = (OGRWFSLayer*)GetLayerByName(osName);
1861 1 : if (poLayer == NULL)
1862 : {
1863 : CPLError(CE_Failure, CPLE_AppDefined,
1864 0 : "Unknown layer : %s", osName.c_str());
1865 0 : return NULL;
1866 : }
1867 :
1868 1 : OGRSFDriver* poMEMDrv = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory");
1869 1 : if (poMEMDrv == NULL)
1870 : {
1871 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot load 'Memory' driver");
1872 0 : return NULL;
1873 : }
1874 :
1875 1 : OGRDataSource* poMEMDS = poMEMDrv->CreateDataSource("dummy_name", NULL);
1876 1 : OGRLayer* poMEMLayer = poMEMDS->CreateLayer("FID_LIST", NULL, wkbNone, NULL);
1877 1 : OGRFieldDefn oFDefn("gml_id", OFTString);
1878 1 : poMEMLayer->CreateField(&oFDefn);
1879 :
1880 1 : const std::vector<CPLString>& aosFIDList = poLayer->GetLastInsertedFIDList();
1881 1 : std::vector<CPLString>::const_iterator iter = aosFIDList.begin();
1882 1 : std::vector<CPLString>::const_iterator eiter = aosFIDList.end();
1883 4 : while (iter != eiter)
1884 : {
1885 2 : const CPLString& osFID = *iter;
1886 2 : OGRFeature* poFeature = new OGRFeature(poMEMLayer->GetLayerDefn());
1887 4 : poFeature->SetField(0, osFID);
1888 2 : poMEMLayer->CreateFeature(poFeature);
1889 2 : delete poFeature;
1890 2 : iter ++;
1891 : }
1892 :
1893 1 : return new OGRWFSWrappedResultLayer(poMEMDS, poMEMLayer);
1894 : }
1895 :
1896 : /* -------------------------------------------------------------------- */
1897 : /* Deal with "DELETE FROM layer_name WHERE expression" statement */
1898 : /* -------------------------------------------------------------------- */
1899 4 : if( EQUALN(pszSQLCommand, "DELETE FROM ", 12) )
1900 : {
1901 2 : const char* pszIter = pszSQLCommand + 12;
1902 28 : while(*pszIter && *pszIter != ' ')
1903 24 : pszIter ++;
1904 2 : if (*pszIter == 0)
1905 : {
1906 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid statement");
1907 0 : return NULL;
1908 : }
1909 :
1910 2 : CPLString osName = pszSQLCommand + 12;
1911 2 : osName.resize(pszIter - (pszSQLCommand + 12));
1912 2 : OGRWFSLayer* poLayer = (OGRWFSLayer*)GetLayerByName(osName);
1913 2 : if (poLayer == NULL)
1914 : {
1915 : CPLError(CE_Failure, CPLE_AppDefined,
1916 0 : "Unknown layer : %s", osName.c_str());
1917 0 : return NULL;
1918 : }
1919 :
1920 6 : while(*pszIter && *pszIter == ' ')
1921 2 : pszIter ++;
1922 2 : if (!EQUALN(pszIter, "WHERE ", 5))
1923 : {
1924 0 : CPLError(CE_Failure, CPLE_AppDefined, "WHERE clause missing");
1925 0 : return NULL;
1926 : }
1927 2 : pszIter += 5;
1928 :
1929 2 : const char* pszQuery = pszIter;
1930 :
1931 : /* Check with the generic SQL engine that this is a valid WHERE clause */
1932 2 : OGRFeatureQuery oQuery;
1933 2 : OGRErr eErr = oQuery.Compile( poLayer->GetLayerDefn(), pszQuery );
1934 2 : if( eErr != OGRERR_NONE )
1935 : {
1936 0 : return NULL;
1937 : }
1938 :
1939 : /* Now turn this into OGC Filter language if possible */
1940 2 : int bNeedsNullCheck = FALSE;
1941 2 : int nVersion = (strcmp(GetVersion(),"1.0.0") == 0) ? 100 : 110;
1942 : CPLString osOGCFilter = WFS_TurnSQLFilterToOGCFilter(pszQuery,
1943 : nVersion,
1944 : bPropertyIsNotEqualToSupported,
1945 : bUseFeatureId,
1946 : bGmlObjectIdNeedsGMLPrefix,
1947 2 : &bNeedsNullCheck);
1948 2 : if (bNeedsNullCheck && !HasNullCheck())
1949 0 : osOGCFilter = "";
1950 :
1951 2 : if (osOGCFilter.size() == 0)
1952 : {
1953 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot convert WHERE clause into a OGC filter");
1954 0 : return NULL;
1955 : }
1956 :
1957 2 : poLayer->DeleteFromFilter(osOGCFilter);
1958 :
1959 2 : return NULL;
1960 : }
1961 :
1962 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
1963 : poSpatialFilter,
1964 2 : pszDialect );
1965 :
1966 : }
1967 :
1968 : /************************************************************************/
1969 : /* ReleaseResultSet() */
1970 : /************************************************************************/
1971 :
1972 4 : void OGRWFSDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
1973 : {
1974 4 : delete poResultsSet;
1975 4 : }
|