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