Skip to content
Permalink
Browse files

[tracer] Disable tracing if there are too many features displayed

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 2c414ce058a1e32608ee4121044cd9930e0843f2
@@ -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()
@@ -17,6 +17,7 @@

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

#include <queue>
@@ -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() ) )
@@ -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;
@@ -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 );
@@ -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();
@@ -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()
@@ -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();
}


@@ -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();
@@ -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 );

@@ -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;
@@ -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,
@@ -73,7 +78,7 @@ class CORE_EXPORT QgsTracer : public QObject
bool isPointSnapped( const QgsPoint& pt );

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

private slots:
@@ -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;
};


@@ -3,6 +3,7 @@
#include "qgsapplication.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerregistry.h"
#include "qgsmessagebar.h"
#include "qgssnappingutils.h"
#include "qgsvectorlayer.h"

@@ -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();
}
@@ -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 );
@@ -1,6 +1,7 @@
#ifndef QGSMAPCANVASTRACER_H
#define QGSMAPCANVASTRACER_H

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

class QAction;
@@ -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();
@@ -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() );
@@ -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)
@@ -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;
@@ -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
}
}
@@ -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 */

0 comments on commit 2c414ce

Please sign in to comment.
You can’t perform that action at this time.