Skip to content
Permalink
Browse files
Add user interaction, snapping, etc. for offset curve tool
  • Loading branch information
mhugent committed Feb 15, 2012
1 parent 82e5f91 commit 313e0edde1f8cbe185d908ba7706ad19c0775c44
@@ -14,11 +14,12 @@
***************************************************************************/

#include "qgsmaptooloffsetcurve.h"
#include "qgsmapcanvas.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>

QgsMapToolOffsetCurve::QgsMapToolOffsetCurve( QgsMapCanvas* canvas ): QgsMapToolEdit( canvas ), mRubberBand( 0 ), mGeometry( 0 )
QgsMapToolOffsetCurve::QgsMapToolOffsetCurve( QgsMapCanvas* canvas ): QgsMapToolEdit( canvas ), mRubberBand( 0 ), mOriginalGeometry( 0 ), mGeometryModified( false )
{
}

@@ -30,6 +31,7 @@ QgsMapToolOffsetCurve::~QgsMapToolOffsetCurve()
void QgsMapToolOffsetCurve::canvasPressEvent( QMouseEvent * e )
{
deleteRubberBandAndGeometry();
mGeometryModified = false;

//get selected features or snap to nearest feature if no selection
QgsVectorLayer* layer = currentVectorLayer();
@@ -38,31 +40,61 @@ void QgsMapToolOffsetCurve::canvasPressEvent( QMouseEvent * e )
return;
}

//selection or take closest feature
//QgsPoint layerCoords = toLayerCoordinates( layer, e->pos() );

//optionally merge the features together

//create rubberband from feature(s)

//for now, just take the first selected feature
QgsFeatureList selectedFeatures = layer->selectedFeatures();
if ( selectedFeatures.size() > 0 )
{
mGeometry = selectedFeatures[0].geometryAndOwnership();
//take the first selected feature
mOriginalGeometry = selectedFeatures[0].geometryAndOwnership();
mRubberBand = createRubberBand();
mRubberBand->setToGeometry( mGeometry, layer );
mRubberBand->setToGeometry( mOriginalGeometry, layer );
mModifiedFeature = selectedFeatures[0].id();
}
else //do a snap to the closest feature
{
QList<QgsSnappingResult> snapResults;
QgsMapCanvasSnapper snapper( mCanvas );
snapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToSegment );
if ( snapResults.size() > 0 )
{
QgsFeature fet;
if ( layer->featureAtId( snapResults.at( 0 ).snappedAtGeometry, fet ) )
{
mOriginalGeometry = fet.geometryAndOwnership();
mRubberBand = createRubberBand();
mRubberBand->setToGeometry( mOriginalGeometry, layer );
mModifiedFeature = fet.id();
}
}
}
}

void QgsMapToolOffsetCurve::canvasReleaseEvent( QMouseEvent * e )
{
deleteRubberBandAndGeometry();
QgsVectorLayer* vlayer = currentVectorLayer();
if ( !vlayer || !mGeometryModified )
{
deleteRubberBandAndGeometry();
return;
}

vlayer->beginEditCommand( tr( "Offset curve" ) );
if ( vlayer->changeGeometry( mModifiedFeature, &mModifiedGeometry ) )
{
vlayer->endEditCommand();
}
else
{
vlayer->destroyEditCommand();
}

delete mRubberBand;
mRubberBand = 0;
mCanvas->refresh();
}

void QgsMapToolOffsetCurve::canvasMoveEvent( QMouseEvent * e )
{
if ( !mGeometry || !mRubberBand )
if ( !mOriginalGeometry || !mRubberBand )
{
return;
}
@@ -73,25 +105,26 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QMouseEvent * e )
return;
}

mGeometryModified = true;

//get offset from current position rectangular to feature
QgsPoint layerCoords = toLayerCoordinates( layer, e->pos() );
QgsPoint minDistPoint;
int beforeVertex;
double offset = sqrt( mGeometry->closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex ) );
double leftOf;
double offset = sqrt( mOriginalGeometry->closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
qWarning( QString::number( offset ).toLocal8Bit().data() );

//create offset geometry using geos
QgsGeometry geomCopy( *mGeometry );
QgsGeometry geomCopy( *mOriginalGeometry );
GEOSGeometry* geosGeom = geomCopy.asGeos();
if ( geosGeom )
{
//GEOSGeometry* offsetGeom = GEOSOffsetCurve( geosGeom, offset, 8, 1, 1 );
GEOSGeometry* offsetGeom = GEOSSingleSidedBuffer( geosGeom, offset, 8, 1, 1, 0 );
GEOSGeometry* offsetGeom = GEOSSingleSidedBuffer( geosGeom, offset, 8, 1, 1, ( leftOf < 0 ) ? 1 : 0 );
if ( offsetGeom )
{
QgsGeometry rubberBandGeometry;
rubberBandGeometry.fromGeos( offsetGeom );
mRubberBand->setToGeometry( &rubberBandGeometry, layer );
mModifiedGeometry.fromGeos( offsetGeom );
mRubberBand->setToGeometry( &mModifiedGeometry, layer );
}
}
}
@@ -100,6 +133,6 @@ void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry()
{
delete mRubberBand;
mRubberBand = 0;
delete mGeometry;
mGeometry = 0;
delete mOriginalGeometry;
mOriginalGeometry = 0;
}
@@ -17,7 +17,7 @@
#define QGSMAPTOOLOFFSETCURVE_H

#include "qgsmaptooledit.h"
class QgsGeometry;
#include "qgsgeometry.h"

class QgsMapToolOffsetCurve: public QgsMapToolEdit
{
@@ -34,7 +34,13 @@ class QgsMapToolOffsetCurve: public QgsMapToolEdit
/**Rubberband that shows the position of the offset curve*/
QgsRubberBand* mRubberBand;
/**Geometry to manipulate*/
QgsGeometry* mGeometry;
QgsGeometry* mOriginalGeometry;
/**Geometry after manipulation*/
QgsGeometry mModifiedGeometry;
/**ID of manipulated feature*/
QgsFeatureId mModifiedFeature;
/**Internal flag to distinguish move from click*/
bool mGeometryModified;

void deleteRubberBandAndGeometry();
};
@@ -2405,7 +2405,8 @@ double QgsGeometry::closestVertexWithContext( const QgsPoint& point, int& atVert
double QgsGeometry::closestSegmentWithContext(
const QgsPoint& point,
QgsPoint& minDistPoint,
int& beforeVertex )
int& beforeVertex,
double* leftOf )
{
QgsDebugMsg( "Entering." );
QgsPoint distPoint;
@@ -2471,6 +2472,10 @@ double QgsGeometry::closestSegmentWithContext(
closestSegmentIndex = index;
sqrDist = testdist;
minDistPoint = distPoint;
if ( leftOf )
{
*leftOf = QgsGeometry::leftOf( point.x(), point.y(), *prevx, *prevy, *thisx, *thisy );
}
}
}
ptr += sizeof( double );
@@ -2515,6 +2520,10 @@ double QgsGeometry::closestSegmentWithContext(
closestSegmentIndex = pointindex;
sqrDist = testdist;
minDistPoint = distPoint;
if ( leftOf )
{
*leftOf = QgsGeometry::leftOf( point.x(), point.y(), *prevx, *prevy, *thisx, *thisy );
}
}
}
prevx = thisx;
@@ -2557,6 +2566,10 @@ double QgsGeometry::closestSegmentWithContext(
closestSegmentIndex = index;
sqrDist = testdist;
minDistPoint = distPoint;
if ( leftOf )
{
*leftOf = QgsGeometry::leftOf( point.x(), point.y(), *prevx, *prevy, *thisx, *thisy );
}
}
}
prevx = thisx;
@@ -2605,6 +2618,10 @@ double QgsGeometry::closestSegmentWithContext(
closestSegmentIndex = pointindex;
sqrDist = testdist;
minDistPoint = distPoint;
if ( leftOf )
{
*leftOf = QgsGeometry::leftOf( point.x(), point.y(), *prevx, *prevy, *thisx, *thisy );
}
}
}
prevx = thisx;
@@ -6453,3 +6470,12 @@ bool QgsGeometry::isGeosEmpty()
return false;
}
}

double QgsGeometry::leftOf( double x, double y, double& x1, double& y1, double& x2, double& y2 )
{
double f1 = x - x1;
double f2 = y2 - y1;
double f3 = y - y1;
double f4 = x2 - x1;
return f1*f2 - f3*f4;
}
@@ -241,9 +241,10 @@ class CORE_EXPORT QgsGeometry
* @param minDistPoint Receives the nearest point on the segment
* @param beforeVertex Receives index of the vertex before the closest segment. The vertex
* after the closest segment is always beforeVertex + 1
* @param leftOf Out: Returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right )
* @return The squared cartesian distance is also returned in sqrDist, negative number on error
*/
double closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& beforeVertex );
double closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& beforeVertex, double* leftOf = 0 );

/**Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
@return 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
@@ -566,6 +567,9 @@ class CORE_EXPORT QgsGeometry
static bool geosRelOp( char( *op )( const GEOSGeometry*, const GEOSGeometry * ),
QgsGeometry *a, QgsGeometry *b );

/**Returns < 0 if point(x/y) is left of the line x1,y1 -> x1,y2*/
double leftOf( double x, double y, double& x1, double& y1, double& x2, double& y2 );


static int refcount;
}; // class QgsGeometry
@@ -18,12 +18,11 @@
#ifndef QGSMAPCANVASSNAPPER_H
#define QGSMAPCANVASSNAPPER_H

#include "qgssnapper.h"
#include <QList>
#include "qgssnapper.h"

class QgsMapCanvas;
class QPoint;
class QgsSnapper;

/** \ingroup gui
* This class reads the snapping properties from the current project and

0 comments on commit 313e0ed

Please sign in to comment.