Add QgsSnappingUtils::snapToCurrentLayer() + utility methods in QgsTolerance

Also added missing SIP wrappers for new classes QgsPointLocator and QgsSnappingUtils
This commit is contained in:
Martin Dobias 2015-01-04 13:04:49 +07:00
parent 488e7f11a9
commit 4b8ea28262
9 changed files with 309 additions and 57 deletions

View File

@ -0,0 +1,112 @@
class QgsPointLocator : QObject
{
%TypeHeaderCode
#include <qgspointlocator.h>
%End
public:
explicit QgsPointLocator( QgsVectorLayer* layer, const QgsCoordinateReferenceSystem* destCRS = 0 );
~QgsPointLocator();
enum Type { Invalid, Vertex, Edge, Area, All };
/** Prepare the indexes for given or-ed combination of query types (Vertex, Edge, Area).
* If not initialized explicitly, index of particular type will be inited when first such query is issued.
*/
void init( int types = All, bool force = false );
//! check whether index for given query type exists
bool hasIndex( Type t ) const;
struct Match
{
//! consruct invalid match
Match();
Match( QgsPointLocator::Type t, QgsVectorLayer* vl, QgsFeatureId fid, double dist, const QgsPoint& pt, int vertexIndex = 0 );
QgsPointLocator::Type type() const;
bool isValid() const;
bool hasVertex() const;
bool hasEdge() const;
bool hasArea() const;
//! for vertex / edge match
//! units depending on what class returns it (geom.cache: layer units, map canvas snapper: dest crs units)
double distance() const;
//! for vertex / edge match
//! coords depending on what class returns it (geom.cache: layer coords, map canvas snapper: dest coords)
QgsPoint point() const;
//! for vertex / edge match (first vertex of the edge)
int vertexIndex();
//! reference vector layer
QgsVectorLayer* layer();
QgsFeatureId featureId();
void replaceIfBetter( const QgsPointLocator::Match& m, double maxDistance );
//! Only for a valid edge match - obtain endpoints of the edge
void edgePoints( QgsPoint& pt1 /Out/, QgsPoint& pt2 /Out/ ) const;
};
typedef QList<QgsPointLocator::Match> MatchList;
//! Interface that allows rejection of some matches in intersection queries
//! (e.g. a match can only belong to a particular feature / match must not be a particular point).
//! Implement the interface and pass its instance to QgsPointLocator or QgsSnappingUtils methods.
struct MatchFilter
{
virtual bool acceptMatch( const QgsPointLocator::Match& match ) = 0;
};
// 1-NN queries
//! find nearest vertex to the specified point
QgsPointLocator::Match nearestVertex( const QgsPoint& point );
//! find nearest edge to the specified point
QgsPointLocator::Match nearestEdge( const QgsPoint& point );
// k-NN queries
//! find nearest vertices to the specified point - sorted by distance
//! will return up to maxMatches matches
MatchList nearestVertices( const QgsPoint& point, int maxMatches );
//! find nearest edges to the specified point - sorted by distance
MatchList nearestEdges( const QgsPoint& point, int maxMatches );
// intersection queries
//! Find nearest vertices to the specified point - sorted by distance.
//! Will return matches up to distance given by tolerance.
//! Optional filter may discard unwanted matches.
MatchList verticesInTolerance( const QgsPoint& point, double tolerance, QgsPointLocator::MatchFilter* filter = 0 );
//! Find nearest edges to the specified point - sorted by distance.
//! Will return matches up to distance given by tolerance.
//! Optional filter may discard unwanted matches.
MatchList edgesInTolerance( const QgsPoint& point, double tolerance, QgsPointLocator::MatchFilter* filter = 0 );
//! Find vertices within given rectangle.
//! If distToPoint is given, the matches will be sorted by distance to that point.
//! Optional filter may discard unwanted matches.
MatchList verticesInRect( const QgsRectangle& rect, const QgsPoint* distToPoint = 0, QgsPointLocator::MatchFilter* filter = 0 );
//! Find edges within given rectangle.
//! If distToPoint is given, the matches will be sorted by distance to that point.
//! Optional filter may discard unwanted matches.
MatchList edgesInRect( const QgsRectangle& rect, const QgsPoint* distToPoint = 0, QgsPointLocator::MatchFilter* filter = 0 );
// point-in-polygon query
// TODO: function to return just the first match?
//! find out if the point is in any polygons
MatchList pointInPolygon( const QgsPoint& point );
};

View File

@ -0,0 +1,79 @@
class QgsSnappingUtils : QObject
{
%TypeHeaderCode
#include <qgssnappingutils.h>
%End
public:
QgsSnappingUtils( QObject* parent /TransferThis/ = 0 );
~QgsSnappingUtils();
// main actions
/** get a point locator for the given layer. If such locator does not exist, it will be created */
QgsPointLocator* locatorForLayer( QgsVectorLayer* vl );
/** snap to map according to the current configuration (mode). Optional filter allows to discard unwanted matches. */
QgsPointLocator::Match snapToMap( const QPoint& point, QgsPointLocator::MatchFilter* filter = 0 );
QgsPointLocator::Match snapToMap( const QgsPoint& pointMap, QgsPointLocator::MatchFilter* filter = 0 );
// TODO: multi-variant
/** snap to current layer */
QgsPointLocator::Match snapToCurrentLayer( const QPoint& point, int type, QgsPointLocator::MatchFilter* filter = 0 );
// environment setup
/** assign current map settings to the utils - used for conversion between screen coords to map coords */
void setMapSettings( const QgsMapSettings& settings );
const QgsMapSettings& mapSettings() const;
/** set current layer so that if mode is SnapCurrentLayer we know which layer to use */
void setCurrentLayer( QgsVectorLayer* layer );
QgsVectorLayer* currentLayer() const;
// configuration
//! modes for "snap to background"
enum SnapToMapMode
{
SnapCurrentLayer, //!< snap just to current layer (tolerance+type from QSettings)
SnapPerLayerConfig, //!< snap according to the configuration set in setLayers()
};
/** Set how the snapping to map is done */
void setSnapToMapMode( SnapToMapMode mode );
/** Find out how the snapping to map is done */
SnapToMapMode snapToMapMode() const;
/** configure options used when the mode is snap to current layer */
void setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit );
/** query options used when the mode is snap to current layer */
void defaultSettings( int& type /Out/, double& tolerance /Out/, QgsTolerance::UnitType& unit /Out/ );
struct LayerConfig
{
LayerConfig( QgsVectorLayer* l, int t, double tol, QgsTolerance::UnitType u );
QgsVectorLayer* layer;
int type;
double tolerance;
QgsTolerance::UnitType unit;
};
/** Set layers which will be used for snapping */
void setLayers( const QList<QgsSnappingUtils::LayerConfig>& layers );
/** Query layers used for snapping */
QList<QgsSnappingUtils::LayerConfig> layers() const;
/** Set whether to consider intersections of nearby segments for snapping */
void setSnapOnIntersections( bool enabled );
/** Query whether to consider intersections of nearby segments for snapping */
bool snapOnIntersections() const;
public slots:
/** Read snapping configuration from the project */
void readConfigFromProject();
};

View File

@ -15,17 +15,25 @@ class QgsTolerance
Pixels
};
/**
* Static function to get vertex tolerance value.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units (not layer units)
* @note added in 2.8
*/
static double vertexSearchRadius( const QgsMapSettings& mapSettings );
/**
* Static function to get vertex tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units
* @return value of vertex tolerance in layer units
*/
static double vertexSearchRadius( QgsMapLayer* layer, const QgsMapSettings& mapSettings );
/**
* Static function to get vertex tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units
* @return value of vertex tolerance in layer units
*/
//! @deprecated since 2.4 - use override with QgsMapSettings
static double vertexSearchRadius( QgsMapLayer* layer, QgsMapRenderer* renderer ) /Deprecated/;
@ -33,35 +41,45 @@ class QgsTolerance
/**
* Static function to get default tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of default tolerance in map units
* @return value of vertex tolerance in layer units
*/
static double defaultTolerance( QgsMapLayer* layer, const QgsMapSettings& mapSettings );
/**
* Static function to get default tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of default tolerance in map units
* @return value of vertex tolerance in layer units
*/
//! @deprecated since 2.4 - use override with QgsMapSettings
static double defaultTolerance( QgsMapLayer* layer, QgsMapRenderer* renderer ) /Deprecated/;
/**
* Static function to translate tolerance value into current map unit value
* Static function to translate tolerance value into map units
* @param tolerance tolerance value to be translated
* @param mapSettings settings of the map
* @param units type of units to be translated
* @return value of tolerance in map units
* @note added in 2.8
*/
static double toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
/**
* Static function to translate tolerance value into layer units
* @param tolerance tolerance value to be translated
* @param layer reference layer
* @param mapSettings settings of the map
* @param units type of units to be translated
* @return value of tolerance in map units
* @return value of tolerance in layer units
*/
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = MapUnits );
/**
* Static function to translate tolerance value into current map unit value
* Static function to translate tolerance value into layer units
* @param tolerance tolerance value to be translated
* @param layer reference layer
* @param renderer renderer
* @param units type of units to be translated
* @return value of tolerance in map units
* @return value of tolerance in layer units
*/
//! @deprecated since 2.4 - use the override with QgsMapSettings
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = MapUnits ) /Deprecated/;

View File

@ -19,6 +19,7 @@
#include "qgsvertexmarker.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h"
#include "qgssnappingutils.h"
#include "qgstolerance.h"
#include <QMouseEvent>
@ -122,38 +123,33 @@ QgsGeometry* QgsMapToolDeletePart::partUnderPoint( QPoint point, QgsFeatureId& f
case QGis::Point:
case QGis::Line:
{
if ( mSnapper.snapToCurrentLayer( point, mRecentSnappingResults, QgsSnapper::SnapToVertexAndSegment ) == 0 )
QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( point, QgsPointLocator::Vertex | QgsPointLocator::Edge );
if ( !match.isValid() )
return geomPart;
int snapVertex = match.vertexIndex();
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( match.featureId() ) ).nextFeature( f );
QgsGeometry* g = f.geometry();
if ( !g->isMultipart() )
return geomPart;
if ( g->wkbType() == QGis::WKBMultiPoint || g->wkbType() == QGis::WKBMultiPoint25D )
{
if ( mRecentSnappingResults.length() > 0 )
fid = match.featureId();
partNum = snapVertex;
return QgsGeometry::fromPoint( match.point() );
}
if ( g->wkbType() == QGis::WKBMultiLineString || g->wkbType() == QGis::WKBMultiLineString25D )
{
QgsMultiPolyline mline = g->asMultiPolyline();
for ( int part = 0; part < mline.count(); part++ )
{
QgsSnappingResult sr = mRecentSnappingResults.first();
int snapVertex = sr.snappedVertexNr;
if ( snapVertex == -1 )
snapVertex = sr.beforeVertexNr;
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( sr.snappedAtGeometry ) ).nextFeature( f );
QgsGeometry* g = f.geometry();
if ( !g->isMultipart() )
return geomPart;
if ( g->wkbType() == QGis::WKBMultiPoint || g->wkbType() == QGis::WKBMultiPoint25D )
if ( snapVertex < mline[part].count() )
{
fid = sr.snappedAtGeometry;
partNum = snapVertex;
return QgsGeometry::fromPoint( sr.snappedVertex );
}
if ( g->wkbType() == QGis::WKBMultiLineString || g->wkbType() == QGis::WKBMultiLineString25D )
{
QgsMultiPolyline mline = g->asMultiPolyline();
for ( int part = 0; part < mline.count(); part++ )
{
if ( snapVertex < mline[part].count() )
{
fid = sr.snappedAtGeometry;
partNum = part;
return QgsGeometry::fromPolyline( mline[part] );
}
snapVertex -= mline[part].count();
}
fid = match.featureId();
partNum = part;
return QgsGeometry::fromPolyline( mline[part] );
}
snapVertex -= mline[part].count();
}
}
break;

View File

@ -41,7 +41,6 @@ class APP_EXPORT QgsMapToolDeletePart: public QgsMapToolEdit
private:
QgsVectorLayer* vlayer;
QList<QgsSnappingResult> mRecentSnappingResults;
QgsGeometry* partUnderPoint( QPoint p, QgsFeatureId &fid, int &partNum );

View File

@ -60,16 +60,6 @@ void QgsSnappingUtils::clearAllLocators()
}
// return snap tolerance in map units (not in layer units as from QgsTolerance)
static double _snapTolerance( double tolerance, QgsTolerance::UnitType units, const QgsMapSettings& mapSettings )
{
if ( units == QgsTolerance::MapUnits )
return tolerance;
else // pixels
return tolerance * mapSettings.mapUnitsPerPixel();
}
static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPoint& pt, const QgsPointLocator::MatchList& segments )
{
QSet<QgsPoint> endpoints;
@ -180,7 +170,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
return QgsPointLocator::Match();
// data from project
double tolerance = _snapTolerance( mDefaultTolerance, mDefaultUnit, mMapSettings );
double tolerance = QgsTolerance::toleranceInMapUnits( mDefaultTolerance, mMapSettings, mDefaultUnit );
int type = mDefaultType;
// use ad-hoc locator
@ -208,7 +198,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
foreach ( const LayerConfig& layerConfig, mLayers )
{
double tolerance = _snapTolerance( layerConfig.tolerance, layerConfig.unit, mMapSettings );
double tolerance = QgsTolerance::toleranceInMapUnits( layerConfig.tolerance, mMapSettings, layerConfig.unit );
if ( QgsPointLocator* loc = locatorForLayer( layerConfig.layer ) )
{
loc->init( layerConfig.type );
@ -232,6 +222,25 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
return QgsPointLocator::Match();
}
QgsPointLocator::Match QgsSnappingUtils::snapToCurrentLayer( const QPoint& point, int type, QgsPointLocator::MatchFilter* filter )
{
if ( !mCurrentLayer )
return QgsPointLocator::Match();
QgsPointLocator* loc = locatorForLayer( mCurrentLayer );
loc->init( type );
if ( !loc )
return QgsPointLocator::Match();
QgsPoint pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
QgsPointLocator::Match bestMatch;
_updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
return bestMatch;
}
void QgsSnappingUtils::setMapSettings( const QgsMapSettings& settings )
{
QString oldDestCRS = mMapSettings.hasCrsTransformEnabled() ? mMapSettings.destinationCrs().authid() : QString();

View File

@ -44,6 +44,8 @@ class QgsSnappingUtils : public QObject
QgsPointLocator::Match snapToMap( const QgsPoint& pointMap, QgsPointLocator::MatchFilter* filter = 0 );
// TODO: multi-variant
/** snap to current layer */
QgsPointLocator::Match snapToCurrentLayer( const QPoint& point, int type, QgsPointLocator::MatchFilter* filter = 0 );
// environment setup

View File

@ -18,8 +18,19 @@
#include <QPoint>
#include <cmath>
double QgsTolerance::toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units )
{
// converts to map units
if ( units == MapUnits )
return tolerance;
else
return tolerance * mapSettings.mapUnitsPerPixel();
}
double QgsTolerance::toleranceInMapUnits( double tolerance, QgsMapLayer *layer, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units )
{
// converts to layer units
if ( units == MapUnits )
{
return tolerance;
@ -33,6 +44,14 @@ double QgsTolerance::toleranceInMapUnits( double tolerance, QgsMapLayer* layer,
return toleranceInMapUnits( tolerance, layer, renderer->mapSettings(), units );
}
double QgsTolerance::vertexSearchRadius( const QgsMapSettings& mapSettings )
{
QSettings settings;
double tolerance = settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble();
UnitType units = ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt();
return toleranceInMapUnits( tolerance, mapSettings, units );
}
double QgsTolerance::vertexSearchRadius( QgsMapLayer *layer, const QgsMapSettings &mapSettings )
{
QSettings settings;

View File

@ -36,17 +36,25 @@ class CORE_EXPORT QgsTolerance
Pixels
};
/**
* Static function to get vertex tolerance value.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units (not layer units)
* @note added in 2.8
*/
static double vertexSearchRadius( const QgsMapSettings& mapSettings );
/**
* Static function to get vertex tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units
* @return value of vertex tolerance in layer units
*/
static double vertexSearchRadius( QgsMapLayer* layer, const QgsMapSettings& mapSettings );
/**
* Static function to get vertex tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of vertex tolerance in map units
* @return value of vertex tolerance in layer units
*/
//! @deprecated since 2.4 - use override with QgsMapSettings
Q_DECL_DEPRECATED static double vertexSearchRadius( QgsMapLayer* layer, QgsMapRenderer* renderer );
@ -54,35 +62,45 @@ class CORE_EXPORT QgsTolerance
/**
* Static function to get default tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of default tolerance in map units
* @return value of default tolerance in layer units
*/
static double defaultTolerance( QgsMapLayer* layer, const QgsMapSettings& mapSettings );
/**
* Static function to get default tolerance value for a layer.
* The value is read from settings and transformed if necessary.
* @return value of default tolerance in map units
* @return value of default tolerance in layer units
*/
//! @deprecated since 2.4 - use override with QgsMapSettings
Q_DECL_DEPRECATED static double defaultTolerance( QgsMapLayer* layer, QgsMapRenderer* renderer );
/**
* Static function to translate tolerance value into current map unit value
* Static function to translate tolerance value into map units
* @param tolerance tolerance value to be translated
* @param mapSettings settings of the map
* @param units type of units to be translated
* @return value of tolerance in map units
* @note added in 2.8
*/
static double toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
/**
* Static function to translate tolerance value into layer units
* @param tolerance tolerance value to be translated
* @param layer reference layer
* @param mapSettings settings of the map
* @param units type of units to be translated
* @return value of tolerance in map units
* @return value of tolerance in layer units
*/
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = MapUnits );
/**
* Static function to translate tolerance value into current map unit value
* Static function to translate tolerance value into layer units
* @param tolerance tolerance value to be translated
* @param layer reference layer
* @param renderer renderer
* @param units type of units to be translated
* @return value of tolerance in map units
* @return value of tolerance in layer units
*/
//! @deprecated since 2.4 - use the override with QgsMapSettings
Q_DECL_DEPRECATED static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = MapUnits );