Skip to content

Commit

Permalink
[tracer] Disable tracing if there are too many features displayed
Browse files Browse the repository at this point in the history
This ensures that building of tracing graph never takes too long.
(The most expensive part is noding of linestrings to ensure they
only touch at the ends)
  • Loading branch information
wonder-sk committed Jan 11, 2016
1 parent 3e48393 commit 2c414ce
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -1976,6 +1976,8 @@ void QgisApp::createToolBars()

mTracer = new QgsMapCanvasTracer( mMapCanvas );
mAdvancedDigitizeToolBar->insertAction( mActionUndo, mTracer->actionEnableTracing() );
connect( mTracer, SIGNAL( messageEmitted( QString, QgsMessageBar::MessageLevel ) ),
this, SLOT( displayMapToolMessage( QString, QgsMessageBar::MessageLevel ) ) );
}

void QgisApp::createStatusBar()
Expand Down
40 changes: 20 additions & 20 deletions src/core/qgstracer.cpp
Expand Up @@ -17,6 +17,7 @@

#include "qgsgeometry.h"
#include "qgsgeometryutils.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"

#include <queue>
Expand Down Expand Up @@ -402,19 +403,6 @@ void reset_graph( QgsTracerGraph& g )
}


void print_shortest_path( const QgsTracerGraph& g, int v1, int v2 )
{
qDebug( "from %d to %d", v1, v2 );
QVector<QgsPoint> points = shortest_path( g, v1, v2 );

if ( points.isEmpty() )
qDebug( "no path!" );

foreach ( QgsPoint p, points )
qDebug( "p: %f %f", p.x(), p.y() );
}


void extract_linework( QgsGeometry* g, QgsMultiPolyline& mpl )
{
switch ( QgsWKBTypes::flatType( g->geometry()->wkbType() ) )
Expand Down Expand Up @@ -449,14 +437,15 @@ void extract_linework( QgsGeometry* g, QgsMultiPolyline& mpl )

QgsTracer::QgsTracer()
: mGraph( 0 )
, mMaxFeatureCount( 0 )
{
}


void QgsTracer::initGraph()
bool QgsTracer::initGraph()
{
if ( mGraph )
return; // already initialized
return true; // already initialized

QgsFeature f;
QgsMultiPolyline mpl;
Expand All @@ -468,6 +457,7 @@ void QgsTracer::initGraph()
QTime t1, t2, t2a, t3;

t1.start();
int featuresCounted = 0;
foreach ( QgsVectorLayer* vl, mLayers )
{
QgsCoordinateTransform ct( vl->crs(), mCRS );
Expand Down Expand Up @@ -496,10 +486,14 @@ void QgsTracer::initGraph()
}

extract_linework( f.geometry(), mpl );
++featuresCounted;
}
}
int timeExtract = t1.elapsed();

if ( mMaxFeatureCount != 0 && featuresCounted >= mMaxFeatureCount )
return false;

// resolve intersections

t2.start();
Expand Down Expand Up @@ -531,7 +525,9 @@ void QgsTracer::initGraph()

int timeMake = t3.elapsed();

qDebug( "tracer extract %d ms, noding %d ms (call %d ms), make %d ms", timeExtract, timeNoding, timeNodingCall, timeMake );
QgsDebugMsg( QString( "tracer extract %1 ms, noding %2 ms (call %3 ms), make %4 ms" )
.arg ( timeExtract ).arg( timeNoding ).arg ( timeNodingCall ).arg( timeMake ) );
return true;
}

QgsTracer::~QgsTracer()
Expand Down Expand Up @@ -581,12 +577,12 @@ void QgsTracer::setExtent( const QgsRectangle& extent )
invalidateGraph();
}

void QgsTracer::init()
bool QgsTracer::init()
{
if ( mGraph )
return;
return true;

initGraph();
return initGraph();
}


Expand Down Expand Up @@ -618,6 +614,8 @@ void QgsTracer::onGeometryChanged( QgsFeatureId fid, QgsGeometry& geom )
QVector<QgsPoint> QgsTracer::findShortestPath( const QgsPoint& p1, const QgsPoint& p2 )
{
init(); // does nothing if the graph exists already
if ( !mGraph )
return QVector<QgsPoint>();

QTime t;
t.start();
Expand All @@ -630,7 +628,7 @@ QVector<QgsPoint> QgsTracer::findShortestPath( const QgsPoint& p1, const QgsPoin
QgsPolyline points = shortest_path( *mGraph, v1, v2 );
int tPath = t2.elapsed();

qDebug( "path timing: prep %d ms, path %d ms", tPrep, tPath );
QgsDebugMsg( QString( "path timing: prep %1 ms, path %2 ms" ).arg( tPrep ).arg( tPath ) );

reset_graph( *mGraph );

Expand All @@ -640,6 +638,8 @@ QVector<QgsPoint> QgsTracer::findShortestPath( const QgsPoint& p1, const QgsPoin
bool QgsTracer::isPointSnapped( const QgsPoint& pt )
{
init(); // does nothing if the graph exists already
if ( !mGraph )
return false;

if ( point2vertex( *mGraph, pt ) != -1 )
return true;
Expand Down
12 changes: 10 additions & 2 deletions src/core/qgstracer.h
Expand Up @@ -57,11 +57,16 @@ class CORE_EXPORT QgsTracer : public QObject
//! Set extent to which graph's features will be limited (empty extent means no limit)
void setExtent( const QgsRectangle& extent );

//! Get maximum possible number of features in graph. If the number is exceeded, graph is not created.
int maxFeatureCount() const { return mMaxFeatureCount; }
//! Get maximum possible number of features in graph. If the number is exceeded, graph is not created.
void setMaxFeatureCount( int count ) { mMaxFeatureCount = count; }

//! Build the internal data structures. This may take some time
//! depending on how big the input layers are. It is not necessary
//! to call this method explicitly - it will be called by findShortestPath()
//! if necessary.
void init();
virtual bool init();

//! Given two points, find the shortest path and return points on the way.
//! If the points are not located on existing vertices or edges,
Expand All @@ -73,7 +78,7 @@ class CORE_EXPORT QgsTracer : public QObject
bool isPointSnapped( const QgsPoint& pt );

private:
void initGraph();
bool initGraph();
void invalidateGraph();

private slots:
Expand All @@ -90,6 +95,9 @@ class CORE_EXPORT QgsTracer : public QObject
QgsCoordinateReferenceSystem mCRS;
//! Extent for graph building (empty extent means no limit)
QgsRectangle mExtent;
//! Limit of how many features can be in the graph (0 means no limit).
//! This is to avoid possibly long graph preparation for complicated layers
int mMaxFeatureCount;
};


Expand Down
15 changes: 15 additions & 0 deletions src/gui/qgsmapcanvastracer.cpp
Expand Up @@ -3,6 +3,7 @@
#include "qgsapplication.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerregistry.h"
#include "qgsmessagebar.h"
#include "qgssnappingutils.h"
#include "qgsvectorlayer.h"

Expand All @@ -26,6 +27,10 @@ QgsMapCanvasTracer::QgsMapCanvasTracer( QgsMapCanvas* canvas )
mActionEnableTracing->setShortcut( Qt::Key_T );
mActionEnableTracing->setCheckable( true );

// arbitrarily chosen limit that should allow for fairly fast initialization
// of the underlying graph structure
setMaxFeatureCount( 10000 );

updateSettings(); // initialize
updateLayerSettings();
}
Expand All @@ -35,6 +40,16 @@ QgsMapCanvasTracer::~QgsMapCanvasTracer()
sTracers.remove( mCanvas );
}

bool QgsMapCanvasTracer::init()
{
bool res = QgsTracer::init();

if ( !res )
emit messageEmitted( tr( "Tracing disabled because there are too many features displayed. Try zooming in or disable some layers." ) );

return res;
}

QgsMapCanvasTracer* QgsMapCanvasTracer::tracerForCanvas( QgsMapCanvas* canvas )
{
return sTracers.value( canvas, 0 );
Expand Down
10 changes: 10 additions & 0 deletions src/gui/qgsmapcanvastracer.h
@@ -1,6 +1,7 @@
#ifndef QGSMAPCANVASTRACER_H
#define QGSMAPCANVASTRACER_H

#include "qgsmessagebar.h"
#include "qgstracer.h"

class QAction;
Expand All @@ -16,11 +17,20 @@ class GUI_EXPORT QgsMapCanvasTracer : public QgsTracer

QAction* actionEnableTracing() { return mActionEnableTracing; }

virtual bool init();

//! Retrieve instance of this class associated with given canvas (if any).
//! The class keeps a simple registry of tracers associated with map canvas
//! instances for easier access to the common tracer by various map tools
static QgsMapCanvasTracer* tracerForCanvas( QgsMapCanvas* canvas );

signals:
//! emit a message
void messageEmitted( const QString& message, QgsMessageBar::MessageLevel = QgsMessageBar::INFO );

//! emit signal to clear previous message
//void messageDiscarded();

private slots:
void updateSettings();
void updateLayerSettings();
Expand Down
20 changes: 17 additions & 3 deletions src/gui/qgsmaptoolcapture.cpp
Expand Up @@ -174,6 +174,9 @@ void QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent* e )
if ( !tracer )
return; // this should not happen!

if ( !tracer->init() ) // normally no-op if the graph is built already
return; // graph would be too big

mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line );

QVector<QgsPoint> points = tracer->findShortestPath( pt0, e->mapPoint() );
Expand All @@ -189,12 +192,17 @@ void QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent* e )
}


bool QgsMapToolCapture::tracingAddVertex( const QgsPoint& point )
bool QgsMapToolCapture::tracingAddVertex( const QgsPoint& point, bool& pathNotFound )
{
pathNotFound = false;

QgsMapCanvasTracer* tracer = QgsMapCanvasTracer::tracerForCanvas( mCanvas );
if ( !tracer )
return false; // this should not happen!

if ( !tracer->init() ) // normally no-op if the graph is built already
return false; // graph would be too big

if ( mCaptureCurve.numPoints() == 0 )
{
// only accept first point if it is snapped to the graph (to vertex or edge)
Expand All @@ -216,7 +224,10 @@ bool QgsMapToolCapture::tracingAddVertex( const QgsPoint& point )

QVector<QgsPoint> points = tracer->findShortestPath( pt0, point );
if ( points.isEmpty() )
{
pathNotFound = true;
return false; // ignore the vertex - can't find path to the end point!
}

// transform points
QList<QgsPointV2> layerPoints;
Expand Down Expand Up @@ -358,9 +369,12 @@ int QgsMapToolCapture::addVertex( const QgsPoint& point )

if ( tracingEnabled() )
{
if ( !tracingAddVertex( point ) )
bool pathNotFound;
bool res = tracingAddVertex( point, pathNotFound );
if ( !res )
{
emit messageEmitted( tr( "Tracing: The point needs to be snapped to a geometry" ), QgsMessageBar::WARNING );
if ( pathNotFound )
emit messageEmitted( tr( "Tracing: The point needs to be snapped to a geometry" ), QgsMessageBar::WARNING );
return 1; // early exit if the point cannot be accepted
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgsmaptoolcapture.h
Expand Up @@ -148,7 +148,7 @@ class GUI_EXPORT QgsMapToolCapture : public QgsMapToolAdvancedDigitizing
//! handle of mouse movement when tracing enabled and capturing has started
void tracingMouseMove( QgsMapMouseEvent* e );
//! handle of addition of clicked point (with the rest of the trace) when tracing enabled
bool tracingAddVertex( const QgsPoint& point );
bool tracingAddVertex( const QgsPoint& point, bool& pathNotFound );

private:
/** Flag to indicate a map canvas capture operation is taking place */
Expand Down

0 comments on commit 2c414ce

Please sign in to comment.