incremental-wsf.patch

Bill Clay, 2011-12-19 08:26 AM

Download (15.7 KB)

View differences:

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>