Skip to content

Commit

Permalink
Add snapping utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
m-kuhn committed Oct 8, 2016
1 parent eed95ae commit 7808ca5
Show file tree
Hide file tree
Showing 8 changed files with 460 additions and 41 deletions.
6 changes: 6 additions & 0 deletions src/qgismobileapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
#include "submodel.h"
#include "expressionvariablemodel.h"
#include "badlayerhandler.h"
#include "snappingutils.h"
#include "snappingresult.h"

QgisMobileapp::QgisMobileapp( QgsApplication* app, QWindow* parent )
: QQuickView( parent )
Expand Down Expand Up @@ -117,6 +119,9 @@ void QgisMobileapp::initDeclarative()
qRegisterMetaType<QgsWkbTypes::GeometryType>( "QgsWkbTypes::GeometryType" );
qRegisterMetaType<QgsFeatureId>( "QgsFeatureId" );
qRegisterMetaType<QgsAttributes>( "QgsAttributes" );
qRegisterMetaType<SnappingResult>( "SnappingResult" );
qRegisterMetaType<QgsPoint>( "QgsPoint" );
qRegisterMetaType<QgsSnappingConfig>( "QgsSnappingConfig" );

// Register QField QML types
qmlRegisterUncreatableType<AppInterface>( "org.qgis", 1, 0, "QgisInterface", "QgisInterface is only provided by the environment and cannot be created ad-hoc" );
Expand All @@ -142,6 +147,7 @@ void QgisMobileapp::initDeclarative()
qmlRegisterType<SubModel>( "org.qfield", 1, 0, "SubModel" );
qmlRegisterType<ExpressionVariableModel>( "org.qfield", 1, 0, "ExpressionVariableModel" );
qmlRegisterType<BadLayerHandler>( "org.qfield", 1, 0, "BadLayerHandler" );
qmlRegisterType<SnappingUtils>( "org.qfield", 1, 0, "SnappingUtils" );

// Calculate device pixels
int dpiX = QApplication::desktop()->physicalDpiX();
Expand Down
83 changes: 49 additions & 34 deletions src/qml/CoordinateLocator.qml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import QtQuick 2.5
import org.qgis 1.0
import org.qfield 1.0

import QtPositioning 5.3

Expand All @@ -9,10 +10,36 @@ Item {
property color color: "#263238"
property color highlightColor: "#CFD8DC"

property point coordinate
property point sourcePoint: Qt.point( width / 2, height / 2 ) // In screen coordinates
property alias currentLayer: snappingUtils.currentLayer

property bool __coordinateChangedByMapSettings: false
readonly property alias snappingResult: snappingUtils.snappingResult
readonly property alias snappedCoordinate: snappingUtils.snappedCoordinate // In map coordinates, derived from snappinResult
readonly property alias snappedPoint: snappingUtils.snappedPoint // In screen coordinates, derived from snappinResult

SnappingUtils {
id: snappingUtils

mapSettings: locator.mapSettings
inputCoordinate: sourcePoint
config: qgisProject.snappingConfig

property point snappedCoordinate
property point snappedPoint

onSnappingResultChanged: {
if ( snappingResult.isValid )
{
snappedCoordinate = Qt.point( snappingResult.point.x, snappingResult.point.y )
snappedPoint = mapSettings.coordinateToScreen( snappedCoordinate )
}
else
{
snappedPoint = sourcePoint
snappedCoordinate = mapSettings.screenToCoordinate( snappedPoint )
}
}
}

Rectangle {
id: crosshairCircleInnerBuffer
Expand All @@ -30,41 +57,41 @@ Item {
Rectangle {
id: crosshairCircle

x: parent.width / 2 - radius
y: parent.height / 2 - radius
x: snappedPoint.x - radius
y: snappedPoint.y - radius

border.color: parent.color

Connections {
target: snappingUtils
onSnappingResultChanged: crosshairCircle.border.color = snappingResult.isValid ? "#9b59b6" : locator.color
}

border.width: 1.2 * dp
color: "transparent"
antialiasing: true

width: 48 * dp
height: width
radius: width / 2
}

Rectangle {
anchors.centerIn: crosshairCircle

color: parent.color
Rectangle {
anchors.centerIn: parent

width: 1.2 * dp
height: crosshairCircle.height * 4 / 6
}
color: parent.border.color

Rectangle {
anchors.centerIn: crosshairCircle

color: parent.color
width: 1.2 * dp
height: parent.height * 4 / 6
}

width: crosshairCircle.width * 4 / 6
height: 1.2 * dp
}
Rectangle {
anchors.centerIn: parent

Connections {
target: mapSettings
color: parent.border.color

onExtentChanged: __updateCoordinate()
width: parent.width * 4 / 6
height: 1.2 * dp
}
}

ParallelAnimation {
Expand Down Expand Up @@ -113,16 +140,4 @@ Item {
{
flashAnimation.start()
}

function __updateCoordinate()
{
__coordinateChangedByMapSettings = true
coordinate = mapSettings.screenToCoordinate( Qt.point( crosshairCircle.x + crosshairCircle.radius, crosshairCircle.y + crosshairCircle.radius ) )
__coordinateChangedByMapSettings = false
}

onCoordinateChanged: {
if ( !__coordinateChangedByMapSettings )
mapSettings.setCenter( coordinate )
}
}
7 changes: 2 additions & 5 deletions src/qml/qgismobileapp.qml
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,9 @@ Rectangle {
mapSettings: mapCanvas.mapSettings

model: RubberbandModel {
currentCoordinate: coordinateLocator.coordinate
currentCoordinate: coordinateLocator.snappedCoordinate
vectorLayer: layerSelector.currentLayer
crs: mapCanvas.mapSettings.destinationCrs

onCurrentCoordinateChanged: {
coordinateLocator.coordinate = currentCoordinate
}
}

anchors.fill: parent
Expand All @@ -142,6 +138,7 @@ Rectangle {
visible: mainWindow.state === "digitize"
highlightColor: digitizingToolbar.isDigitizing ? digitizingRubberband.color : "#CFD8DC"
mapSettings: mapCanvas.mapSettings
currentLayer: layerSelector.currentLayer
}

/* GPS marker */
Expand Down
111 changes: 111 additions & 0 deletions src/snappingresult.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/***************************************************************************
snappingresult.cpp - SnappingResult
---------------------
begin : 8.10.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "snappingresult.h"

SnappingResult::SnappingResult()
: mType( Invalid )
, mDist( 0 )
, mPoint()
, mLayer( nullptr )
, mFid( 0 )
, mVertexIndex( 0 )
{}

SnappingResult::SnappingResult( SnappingResult::Type t, QgsVectorLayer* vl, QgsFeatureId fid, double dist, const QgsPoint& pt, int vertexIndex, QgsPoint* edgePoints )
: mType( t )
, mDist( dist )
, mPoint( pt )
, mLayer( vl )
, mFid( fid )
, mVertexIndex( vertexIndex )
{
if ( edgePoints )
{
mEdgePoints[0] = edgePoints[0];
mEdgePoints[1] = edgePoints[1];
}
}

SnappingResult::SnappingResult( const QgsPointLocator::Match& match )
: mType( matchTypeToSnappingResultType( match.type() ) )
, mDist( match.distance() )
, mPoint( match.point() )
, mLayer( match.layer() )
, mFid( match.featureId() )
, mVertexIndex( match.vertexIndex() )
{
}

SnappingResult::Type SnappingResult::type() const
{
return mType;
}

bool SnappingResult::isValid() const
{
return mType != Invalid;
}

bool SnappingResult::hasVertex() const
{
return mType == Vertex;
}

bool SnappingResult::hasEdge() const
{
return mType == Edge;
}

bool SnappingResult::hasArea() const
{
return mType == Area;
}

double SnappingResult::distance() const
{
return mDist;
}

QgsPoint SnappingResult::point() const
{
return mPoint;
}

int SnappingResult::vertexIndex() const
{
return mVertexIndex;
}

QgsVectorLayer*SnappingResult::layer() const
{
return mLayer;
}

QgsFeatureId SnappingResult::featureId() const
{
return mFid;
}

void SnappingResult::edgePoints( QgsPoint& pt1, QgsPoint& pt2 ) const
{
pt1 = mEdgePoints[0];
pt2 = mEdgePoints[1];
}

SnappingResult::Type SnappingResult::matchTypeToSnappingResultType( QgsPointLocator::Type type )
{
return static_cast<Type>( type );
}
97 changes: 97 additions & 0 deletions src/snappingresult.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/***************************************************************************
snappingresult.h - SnappingResult
---------------------
begin : 8.10.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef SNAPPINGRESULT_H
#define SNAPPINGRESULT_H

#include <qgspoint.h>
#include <qgsvectorlayer.h>
#include <qgspointlocator.h>

class SnappingResult
{
Q_GADGET
Q_PROPERTY( QgsPoint point READ point )
Q_PROPERTY( bool isValid READ isValid )

public:
/**
* The type of a snap result or the filter type for a snap request.
*/
enum Type
{
Invalid = 0, //!< Invalid
Vertex = 1, //!< Snapped to a vertex. Can be a vertex of the geometry or an intersection.
Edge = 2, //!< Snapped to an edge
Area = 4, //!< Snapped to an area
All = Vertex | Edge | Area //!< Combination of vertex, edge and area
};

Q_DECLARE_FLAGS( Types, Type )

//! construct invalid match
SnappingResult();

SnappingResult( Type t, QgsVectorLayer* vl, QgsFeatureId fid, double dist, const QgsPoint& pt, int vertexIndex = 0, QgsPoint* edgePoints = nullptr );

SnappingResult( const QgsPointLocator::Match& match );

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;

void setPoint( const QgsPoint& pt );

//! for vertex / edge match (first vertex of the edge)
int vertexIndex() const;

/**
* The vector layer where the snap occurred.
* Will be null if the snap happened on an intersection.
*/
QgsVectorLayer* layer() const;

/**
* The id of the feature to which the snapped geometry belongs.
*/
QgsFeatureId featureId() const;

//! Only for a valid edge match - obtain endpoints of the edge
void edgePoints( QgsPoint& pt1, QgsPoint& pt2 ) const;

private:
Type matchTypeToSnappingResultType( QgsPointLocator::Type type );

Type mType;
double mDist;
QgsPoint mPoint;
QgsVectorLayer* mLayer;
QgsFeatureId mFid;
int mVertexIndex; // e.g. vertex index
QgsPoint mEdgePoints[2];
};

#endif // SNAPPINGRESULT_H
Loading

0 comments on commit 7808ca5

Please sign in to comment.