incremental-wsf.patch
src/app/qgisapp.cpp | ||
---|---|---|
2437 | 2437 |
return; |
2438 | 2438 |
} |
2439 | 2439 | |
2440 |
// Fudge for now |
|
2441 | 2440 |
QgsDebugMsg( "about to addWfsLayer" ); |
2442 | 2441 | |
2443 | 2442 |
// TODO: QDialog for now, switch to QWidget in future |
... | ... | |
2450 | 2449 |
connect( wfss , SIGNAL( addWfsLayer( QString, QString ) ), |
2451 | 2450 |
this , SLOT( addWfsLayer( QString, QString ) ) ); |
2452 | 2451 | |
2453 |
wfss->setProperty( "MapExtent", mMapCanvas->extent().toString() ); //hack to reenable wfs with extent setting |
|
2452 |
//reenable wfs with extent setting: pass canvas info to source select |
|
2453 |
wfss->setProperty( "MapExtent", mMapCanvas->extent().toString() ); |
|
2454 |
if ( mMapCanvas->mapRenderer()->hasCrsTransformEnabled() ) |
|
2455 |
{ //if "on the fly" reprojection is active, pass canvas CRS |
|
2456 |
wfss->setProperty( "MapCRS", mMapCanvas->mapRenderer()->destinationCrs().authid() ); |
|
2457 |
} |
|
2454 | 2458 | |
2455 | 2459 |
bool bkRenderFlag = mMapCanvas->renderFlag(); |
2456 | 2460 |
mMapCanvas->setRenderFlag( false ); |
src/providers/wfs/qgswfsdata.cpp | ||
---|---|---|
101 | 101 | |
102 | 102 |
if ( mainWindow ) |
103 | 103 |
{ |
104 |
progressDialog = new QProgressDialog( tr( "Loading WFS data" ), tr( "Abort" ), 0, 0, mainWindow );
|
|
104 |
progressDialog = new QProgressDialog( tr( "Loading WFS data\n%1" ).arg( mTypeName ), tr( "Abort" ), 0, 0, mainWindow );
|
|
105 | 105 |
progressDialog->setWindowModality( Qt::ApplicationModal ); |
106 | 106 |
connect( this, SIGNAL( dataReadProgress( int ) ), progressDialog, SLOT( setValue( int ) ) ); |
107 | 107 |
connect( this, SIGNAL( totalStepsUpdate( int ) ), progressDialog, SLOT( setMaximum( int ) ) ); |
src/providers/wfs/qgswfsprovider.cpp | ||
---|---|---|
18 | 18 |
#define WFS_THRESHOLD 200 |
19 | 19 | |
20 | 20 |
#include "qgsapplication.h" |
21 |
#include "qgsmaplayerregistry.h" |
|
21 | 22 |
#include "qgsfeature.h" |
22 | 23 |
#include "qgsfield.h" |
23 | 24 |
#include "qgsgeometry.h" |
... | ... | |
51 | 52 |
mUseIntersect( false ), |
52 | 53 |
mSourceCRS( 0 ), |
53 | 54 |
mFeatureCount( 0 ), |
54 |
mValid( true ) |
|
55 |
mValid( true ), |
|
56 |
mLayer( 0 ), |
|
57 |
mGetRenderedOnly( false ), |
|
58 |
mInitGro( false ) |
|
55 | 59 |
{ |
56 | 60 |
mSpatialIndex = 0; |
57 | 61 |
if ( uri.isEmpty() ) |
... | ... | |
231 | 235 |
mAttributesToFetch = fetchAttributes; |
232 | 236 |
mFetchGeom = fetchGeometry; |
233 | 237 | |
234 |
QString dsURI = dataSourceUri(); |
|
235 |
if ( dsURI.contains( "BBOX" ) ) |
|
236 |
{ |
|
237 |
QUrl url( dsURI ); |
|
238 |
url.removeQueryItem( "BBOX" ); |
|
239 |
url.addQueryItem( "BBOX", QString::number( rect.xMinimum() ) + "," + QString::number( rect.yMinimum() ) + "," |
|
240 |
+ QString::number( rect.xMaximum() ) + "," + QString::number( rect.yMaximum() ) ); |
|
241 |
setDataSourceUri( url.toString() ); |
|
242 |
reloadData(); |
|
238 |
if ( rect.isEmpty() ) |
|
239 |
{ //select all features |
|
240 |
mSpatialFilter = mExtent; |
|
243 | 241 |
mSelectedFeatures = mFeatures.keys(); |
244 |
mSpatialFilter = rect; |
|
245 | 242 |
} |
246 | 243 |
else |
247 |
{ |
|
248 |
if ( rect.isEmpty() ) |
|
249 |
{ |
|
250 |
mSpatialFilter = mExtent; |
|
251 |
mSelectedFeatures = mFeatures.keys(); |
|
252 |
} |
|
253 |
else |
|
254 |
{ |
|
255 |
mSpatialFilter = rect; |
|
256 |
mSelectedFeatures = mSpatialIndex->intersects( mSpatialFilter ); |
|
244 |
{ //select features intersecting caller's extent |
|
245 |
QString dsURI = dataSourceUri(); |
|
246 |
//first time through, initialize GetRenderedOnly args |
|
247 |
//ctor cannot initialize because layer object not available then |
|
248 |
if ( ! mInitGro ) |
|
249 |
{ //did user check "Cache Features" in WFS layer source selection?* |
|
250 |
if ( dsURI.contains( "BBOX" ) ) |
|
251 |
{ //no: initialize incremental getFeature |
|
252 |
if ( initGetRenderedOnly() ) |
|
253 |
{ |
|
254 |
mGetRenderedOnly = true; |
|
255 |
} |
|
256 |
else |
|
257 |
{ //initialization failed; |
|
258 |
QgsDebugMsg( QString( "GetRenderOnly aborted; \"Cache Features\" in effect" ) ); |
|
259 |
} |
|
260 |
} |
|
261 |
mInitGro = true; |
|
262 |
} |
|
263 | ||
264 |
if ( mGetRenderedOnly ) |
|
265 |
{ //"Cache Features" was not selected for this layer |
|
266 |
//has rendered extent expanded beyond last-retrieved WFS extent? |
|
267 |
//NB: "intersect" instead of "contains" to evade small rounding errors; avoids |
|
268 |
// unnecessary double fetch on initial rendering or zoom-in/zoom-out sequences |
|
269 |
QgsRectangle olap( rect ); |
|
270 |
olap = olap.intersect( &mGetExtent ); |
|
271 |
double carea = rect.width() * rect.height(); |
|
272 |
double oarea = olap.width() * olap.height(); |
|
273 |
if ( .9999 < oarea/carea ) |
|
274 |
{ //do not re-fetch if previous get extent covers at least 99.99% of canvas extent |
|
275 |
QgsDebugMsg( QString( "Layer %1 GetRenderedOnly: no fetch required" ).arg( mLayer->name() ) ); |
|
276 |
} |
|
277 |
else |
|
278 |
{ |
|
279 |
mGetExtent.combineExtentWith( &rect ); |
|
280 |
QgsDebugMsg( QString( "Layer %1 GetRenderedOnly: fetching extent %2" ) |
|
281 |
.arg( mLayer->name(), mGetExtent.asWktCoordinates() ) ); |
|
282 |
dsURI = dsURI.replace( QRegExp( "BBOX=[.,0-9]*" ), |
|
283 |
QString( "BBOX=%1,%2,%3,%4" ) |
|
284 |
.arg( mGetExtent.xMinimum(), 0, 'f' ) |
|
285 |
.arg( mGetExtent.yMinimum(), 0, 'f' ) |
|
286 |
.arg( mGetExtent.xMaximum(), 0, 'f' ) |
|
287 |
.arg( mGetExtent.yMaximum(), 0, 'f' ) ); |
|
288 |
setDataSourceUri( dsURI ); |
|
289 |
reloadData(); |
|
290 |
mLayer->updateExtents(); |
|
291 |
} |
|
257 | 292 |
} |
293 | ||
294 |
mSpatialFilter = rect; |
|
295 |
mSelectedFeatures = mSpatialIndex->intersects( mSpatialFilter ); |
|
258 | 296 |
} |
259 | 297 | |
260 | 298 |
mFeatureIterator = mSelectedFeatures.begin(); |
... | ... | |
2288 | 2326 |
} |
2289 | 2327 |
} |
2290 | 2328 | |
2329 |
//initialization for getRenderedOnly option |
|
2330 |
//(formerly "Only request features overlapping the current view extent") |
|
2331 |
bool QgsWFSProvider::initGetRenderedOnly( ) |
|
2332 |
{ //find our layer |
|
2333 |
QMap<QString, QgsMapLayer*> layers = QgsMapLayerRegistry::instance()->mapLayers(); |
|
2334 |
QMap<QString, QgsMapLayer*>::const_iterator layersIt = layers.begin(); |
|
2335 |
for ( ; layersIt != layers.end() ; ++layersIt ) |
|
2336 |
{ |
|
2337 |
if ( ( mLayer = dynamic_cast<QgsVectorLayer*>( layersIt.value() ) ) ) |
|
2338 |
{ |
|
2339 |
if ( mLayer->dataProvider() == this ) |
|
2340 |
{ |
|
2341 |
QgsDebugMsg( QString( "found layer %1" ).arg( mLayer->name() ) ); |
|
2342 |
break; |
|
2343 |
} |
|
2344 |
} |
|
2345 |
} |
|
2346 |
if ( layersIt == layers.end() ) |
|
2347 |
{ |
|
2348 |
QgsDebugMsg( "SHOULD NOT OCCUR: initialize() did not find layer." ); |
|
2349 |
return false; |
|
2350 |
} |
|
2351 |
//set initial getFeatures extent from URI BBOX |
|
2352 |
double m[4]; |
|
2353 |
bool ok = false; //toDouble failure flag |
|
2354 |
QRegExp rebbox( "BBOX=(([0-9.]+,?){4})" ); |
|
2355 |
QString dsURI = dataSourceUri(); |
|
2356 |
if ( dsURI.indexOf( rebbox ) >! 0 ) |
|
2357 |
{ |
|
2358 |
QStringList mlist = rebbox.cap( 1 ).split( "," ); |
|
2359 |
QStringList::const_iterator mlistIt = mlist.begin(); |
|
2360 |
ok = true; |
|
2361 |
for (int i = 0 ; mlistIt != mlist.end() && ok ; ++mlistIt ) |
|
2362 |
{ |
|
2363 |
m[ i++ ] = ( *mlistIt ).toDouble( &ok ); |
|
2364 |
} |
|
2365 |
} |
|
2366 |
if ( ok ) |
|
2367 |
{ //initial getFeatures extent |
|
2368 |
mGetExtent = QgsRectangle( m[0], m[1], m[2], m[3] ); |
|
2369 |
} |
|
2370 |
else |
|
2371 |
{ |
|
2372 |
QgsDebugMsg( QString( "SHOULD NOT OCCUR: missing or malformed URI BBOX args \"%1\"" ).arg( dataSourceUri() ) ); |
|
2373 |
return false; |
|
2374 |
} |
|
2375 |
return true; |
|
2376 |
} |
|
2377 | ||
2291 | 2378 |
void QgsWFSProvider::handleException( const QDomDocument& serverResponse ) const |
2292 | 2379 |
{ |
2293 | 2380 |
QDomElement exceptionElem = serverResponse.documentElement(); |
src/providers/wfs/qgswfsprovider.h | ||
---|---|---|
23 | 23 |
#include "qgsrectangle.h" |
24 | 24 |
#include "qgscoordinatereferencesystem.h" |
25 | 25 |
#include "qgsvectordataprovider.h" |
26 |
#include "qgsmaplayer.h" |
|
27 |
#include "qgsvectorlayer.h" |
|
26 | 28 | |
27 | 29 |
class QgsRectangle; |
28 | 30 |
class QgsSpatialIndex; |
... | ... | |
191 | 193 |
QString mWfsNamespace; |
192 | 194 |
/**Server capabilities for this layer (generated from capabilities document)*/ |
193 | 195 |
int mCapabilities; |
196 |
/**GetRenderedOnly: layer asociated with this provider*/ |
|
197 |
QgsVectorLayer *mLayer; |
|
198 |
/**GetRenderedOnly: fetch only features within canvas extent to be rendered*/ |
|
199 |
bool mGetRenderedOnly; |
|
200 |
/**GetRenderedOnly initializaiton flat*/ |
|
201 |
bool mInitGro; |
|
202 |
/**if GetRenderedOnly, extent specified in WFS getFeatures; else empty (no constraint)*/ |
|
203 |
QgsRectangle mGetExtent; |
|
194 | 204 | |
195 | 205 |
//encoding specific methods of getFeature |
196 | 206 |
int getFeatureGET( const QString& uri, const QString& geometryAttribute ); |
... | ... | |
284 | 294 |
void appendSupportedOperations( const QDomElement& operationsElem, int& capabilities ) const; |
285 | 295 |
/**Shows a message box with the exception string (or does nothing if the xml document is not an exception)*/ |
286 | 296 |
void handleException( const QDomDocument& serverResponse ) const; |
297 |
/**Initializes "Cache Features" inactive processing*/ |
|
298 |
bool initGetRenderedOnly(); |
|
287 | 299 | |
288 | 300 |
void deleteData(); |
289 | 301 |
}; |
src/providers/wfs/qgswfssourceselect.cpp | ||
---|---|---|
15 | 15 |
* * |
16 | 16 |
***************************************************************************/ |
17 | 17 | |
18 |
#include "qgisinterface.h" |
|
19 | 18 |
#include "qgswfssourceselect.h" |
20 | 19 |
#include "qgswfsconnection.h" |
21 | 20 |
#include "qgswfsprovider.h" |
... | ... | |
25 | 24 |
#include "qgscontexthelp.h" |
26 | 25 |
#include "qgsproject.h" |
27 | 26 |
#include "qgscoordinatereferencesystem.h" |
27 |
#include "qgscoordinatetransform.h" |
|
28 | 28 |
#include "qgslogger.h" |
29 | 29 |
#include "qgsmapcanvas.h" //for current view extent |
30 | 30 |
#include "qgsmanageconnectionsdialog.h" |
... | ... | |
263 | 263 | |
264 | 264 |
QList<QTreeWidgetItem*> selectedItems = treeWidget->selectedItems(); |
265 | 265 |
QList<QTreeWidgetItem*>::const_iterator sIt = selectedItems.constBegin(); |
266 |
for ( ; sIt != selectedItems.constEnd(); ++sIt ) |
|
266 |
QgsWFSConnection conn( cmbConnections->currentText() ); |
|
267 |
QString pCrsString( labelCoordRefSys->text() ); |
|
268 |
QgsCoordinateReferenceSystem pCrs( pCrsString ); |
|
269 |
//prepare canvas extent info for layers with "cache features" option not set |
|
270 |
QgsRectangle extent; |
|
271 |
QVariant extentVariant = property( "MapExtent" ); |
|
272 |
if ( extentVariant.isValid() ) |
|
267 | 273 |
{ |
268 |
QString typeName = ( *sIt )->text( 1 ); |
|
269 |
QString crs = labelCoordRefSys->text(); |
|
270 |
QString filter = ( *sIt )->text( 4 ); |
|
271 | ||
272 |
QgsRectangle currentRectangle; |
|
273 |
if (( *sIt )->checkState( 3 ) == Qt::Unchecked ) |
|
274 |
QString crs; |
|
275 |
QgsCoordinateTransform xform; |
|
276 |
QString extentString = extentVariant.toString(); |
|
277 |
QStringList minMaxSplit = extentString.split( ":" ); |
|
278 |
if ( minMaxSplit.size() > 1 ) |
|
274 | 279 |
{ |
275 |
currentRectangle = mExtent; |
|
280 |
QStringList xyMinSplit = minMaxSplit[0].split( "," ); |
|
281 |
QStringList xyMaxSplit = minMaxSplit[1].split( "," ); |
|
282 |
if ( xyMinSplit.size() > 1 && xyMaxSplit.size() > 1 ) |
|
283 |
{ |
|
284 |
extent.set( xyMinSplit[0].toDouble(), xyMinSplit[1].toDouble(), |
|
285 |
xyMaxSplit[0].toDouble(), xyMaxSplit[1].toDouble() ); |
|
286 |
} |
|
276 | 287 |
} |
277 | ||
278 |
//add a wfs layer to the map |
|
279 |
QgsWFSConnection conn( cmbConnections->currentText() ); |
|
280 |
QString uri = conn.uriGetFeature( typeName, crs, filter, currentRectangle ); |
|
281 |
emit addWfsLayer( uri, typeName ); |
|
288 |
//does canvas have "on the fly" reprojection set? |
|
289 |
QVariant crsVariant = property( "MapCRS" ); |
|
290 |
if ( crsVariant.isValid() ) |
|
291 |
{ //transform between provider CRS set in source select dialog and canvas CRS |
|
292 |
QgsCoordinateReferenceSystem cCrs( crsVariant.toString() ); |
|
293 |
if ( pCrs.isValid() && cCrs.isValid() ) |
|
294 |
{ |
|
295 |
QgsCoordinateTransform xform( pCrs, cCrs ); |
|
296 |
extent = xform.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); |
|
297 |
QgsDebugMsg( QString("canvas transform: Canvas CRS=%1, Provider CRS=%2, BBOX=%3") |
|
298 |
.arg(cCrs.authid(), pCrs.authid(), extent.asWktCoordinates() ) ); |
|
299 |
} |
|
300 |
} |
|
301 |
} |
|
302 |
//create layers that user selected from this WFS source |
|
303 |
for ( ; sIt != selectedItems.constEnd(); ++sIt ) |
|
304 |
{ //add a wfs layer to the map |
|
305 |
QString typeName = ( *sIt )->text( 1 ); //WFS repository's name for layer |
|
306 |
QString filter = ( *sIt )->text( 4 ); //optional filter specified by user |
|
307 |
//is "cache features" checked? |
|
308 |
if ( ( *sIt )->checkState( 3 ) == Qt::Checked ) |
|
309 |
{ //yes: entire WFS layer will be retrieved and cached |
|
310 |
mUri = conn.uriGetFeature( typeName, pCrsString, filter ); |
|
311 |
} |
|
312 |
else |
|
313 |
{ //no: include BBOX of current canvas extent in URI |
|
314 |
mUri = conn.uriGetFeature( typeName, pCrsString, filter, extent ); |
|
315 |
} |
|
316 |
emit addWfsLayer( mUri, typeName ); |
|
282 | 317 |
} |
283 | 318 |
accept(); |
284 | 319 |
} |
... | ... | |
399 | 434 |
} |
400 | 435 |
} |
401 | 436 |
} |
402 | ||
403 |
void QgsWFSSourceSelect::showEvent( QShowEvent* event ) |
|
404 |
{ |
|
405 |
Q_UNUSED( event ); |
|
406 |
QVariant extentVariant = property( "MapExtent" ); |
|
407 |
if ( extentVariant.isValid() ) |
|
408 |
{ |
|
409 |
QString extentString = extentVariant.toString(); |
|
410 |
QStringList minMaxSplit = extentString.split( ":" ); |
|
411 |
if ( minMaxSplit.size() > 1 ) |
|
412 |
{ |
|
413 |
QStringList xyMinSplit = minMaxSplit[0].split( "," ); |
|
414 |
QStringList xyMaxSplit = minMaxSplit[1].split( "," ); |
|
415 |
if ( xyMinSplit.size() > 1 && xyMaxSplit.size() > 1 ) |
|
416 |
{ |
|
417 |
mExtent.set( xyMinSplit[0].toDouble(), xyMinSplit[1].toDouble(), xyMaxSplit[0].toDouble(), xyMaxSplit[1].toDouble() ); |
|
418 |
return; |
|
419 |
} |
|
420 |
} |
|
421 |
} |
|
422 |
} |
src/providers/wfs/qgswfssourceselect.h | ||
---|---|---|
20 | 20 | |
21 | 21 |
#include "ui_qgswfssourceselectbase.h" |
22 | 22 |
#include "qgscontexthelp.h" |
23 |
#include "qgsrectangle.h" |
|
24 | 23 | |
25 | 24 |
class QgsGenericProjectionSelector; |
26 | 25 |
class QgsWFSConnection; |
... | ... | |
47 | 46 |
std::map<QString, std::list<QString> > mAvailableCRS; |
48 | 47 |
QAbstractButton* btnAdd; |
49 | 48 |
QgsWFSConnection* mConn; |
50 |
QgsRectangle mExtent;
|
|
49 |
QString mUri; // data source URI
|
|
51 | 50 | |
52 | 51 |
void populateConnectionList(); |
53 | 52 | |
... | ... | |
74 | 73 | |
75 | 74 |
void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); } |
76 | 75 | |
77 |
protected: |
|
78 |
void showEvent( QShowEvent* event ); |
|
79 | 76 |
}; |
80 | 77 | |
81 | 78 |
#endif |
src/ui/qgswfssourceselectbase.ui | ||
---|---|---|
6 | 6 |
<rect> |
7 | 7 |
<x>0</x> |
8 | 8 |
<y>0</y> |
9 |
<width>552</width>
|
|
9 |
<width>592</width>
|
|
10 | 10 |
<height>439</height> |
11 | 11 |
</rect> |
12 | 12 |
</property> |
... | ... | |
114 | 114 |
<property name="columnCount"> |
115 | 115 |
<number>5</number> |
116 | 116 |
</property> |
117 |
<attribute name="headerMinimumSectionSize"> |
|
118 |
<number>27</number> |
|
119 |
</attribute> |
|
120 |
<attribute name="headerMinimumSectionSize"> |
|
121 |
<number>27</number> |
|
122 |
</attribute> |
|
117 | 123 |
<column> |
118 | 124 |
<property name="text"> |
119 | 125 |
<string>Title</string> |
... | ... | |
131 | 137 |
</column> |
132 | 138 |
<column> |
133 | 139 |
<property name="text"> |
134 |
<string>cache features</string> |
|
140 |
<string>Cache |
|
141 |
Features</string> |
|
142 |
</property> |
|
143 |
<property name="textAlignment"> |
|
144 |
<set>AlignHCenter|AlignVCenter|AlignCenter</set> |
|
135 | 145 |
</property> |
136 | 146 |
</column> |
137 | 147 |
<column> |