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