mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #8429 from lbartoletti/extendMapTool
[needs-docs][FEATURE] Trim/extend
This commit is contained in:
commit
d1d3a51efb
@ -732,6 +732,7 @@
|
||||
<file>themes/default/mIconExteriorRing.svg</file>
|
||||
<file>themes/default/mIconInteriorRings.svg</file>
|
||||
<file>themes/default/mIconFieldBinary.svg</file>
|
||||
<file>themes/default/mActionTrimExtendFeature.svg</file>
|
||||
<file>themes/default/mActionTerminal.svg</file>
|
||||
<file>themes/default/mIconFolder24.svg</file>
|
||||
<file>themes/default/mActionNewFolder.svg</file>
|
||||
|
196
images/themes/default/mActionTrimExtendFeature.svg
Normal file
196
images/themes/default/mActionTrimExtendFeature.svg
Normal file
@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="24"
|
||||
width="24"
|
||||
version="1.1"
|
||||
id="svg14115"
|
||||
sodipodi:docname="trim_extend_final.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
inkscape:export-filename="/home/sbe/icon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<metadata
|
||||
id="metadata14121">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs14119">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1108">
|
||||
<stop
|
||||
style="stop-color:#9e9e9e;stop-opacity:0.56849313"
|
||||
offset="0"
|
||||
id="stop1104" />
|
||||
<stop
|
||||
id="stop1112"
|
||||
offset="0.5"
|
||||
style="stop-color:#9e9e9e;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#9e9e9e;stop-opacity:0.57191783"
|
||||
offset="1"
|
||||
id="stop1106" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1108"
|
||||
id="linearGradient1110"
|
||||
x1="0.41561731"
|
||||
y1="10.903339"
|
||||
x2="23.584382"
|
||||
y2="10.903339"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1171"
|
||||
id="namedview14117"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.479178"
|
||||
inkscape:cx="7.4047109"
|
||||
inkscape:cy="16.874197"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg14115"
|
||||
inkscape:showpageshadow="false" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#498049;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.74823844;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 14.892578,19.973633 V 24 h 2 v -4.026367 z"
|
||||
id="path1524-6-5-67-9-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:url(#linearGradient1110);stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 1.4298598,10.903339 H 22.57014"
|
||||
id="path981"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#498049;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.81594944;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 9.3339844,-0.02945891 V 0.79495923 9.8474569 10.671875 H 11.333984 V 9.8474569 0.79495923 -0.02945891 Z"
|
||||
id="path1524-9-1-9-5-7-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffe6d5;stroke:#ffcba8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 10.333256,19.623725 V 11.584457"
|
||||
id="path1524-9-3-8-2-3-3-1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#8cbe8c;fill-opacity:1;stroke:#cee3ce;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 15.892872,19.999884 V 10.422903"
|
||||
id="path1524-62-9-2-5-6-0"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#5ba15b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.06707275;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 15.607917,10.423828 V 20 h 0.569322 v -9.576172 z"
|
||||
id="path1524-62-9-2-5-6-0-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e80000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.88558096;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 10.100419,11.583984 v 8.040229 h 0.467131 v -8.040229 z"
|
||||
id="path1524-9-3-8-2-3-3-1-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:#7e7e7e;stroke-width:1.18957305;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect919-6-5-6-7-4-7"
|
||||
width="2.810427"
|
||||
height="2.810427"
|
||||
x="8.9280491"
|
||||
y="9.5409317" />
|
||||
<rect
|
||||
style="opacity:0.997;fill:#ffffff;fill-opacity:1;stroke:#7e7e7e;stroke-width:1.18957305;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect919-6-5-6-7-4-5-6"
|
||||
width="2.810427"
|
||||
height="2.810427"
|
||||
x="14.487658"
|
||||
y="9.5409317" />
|
||||
<rect
|
||||
style="opacity:0.53600003;fill:#ffffff;fill-opacity:1;stroke:#7e7e7e;stroke-width:0.95076692;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect919-6-5-6-7-4-5-6-2"
|
||||
width="2.2462351"
|
||||
height="2.2462351"
|
||||
x="14.778381"
|
||||
y="19.052471" />
|
||||
<rect
|
||||
style="opacity:0.53600003;fill:#ffffff;fill-opacity:1;stroke:#7e7e7e;stroke-width:0.95076692;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect919-6-5-6-7-4-5-6-2-6"
|
||||
width="2.2462351"
|
||||
height="2.2462351"
|
||||
x="9.2237778"
|
||||
y="17.994656" />
|
||||
<g
|
||||
id="g1368"
|
||||
transform="matrix(0.70000007,0,0,0.69999993,-32.671712,30.638281)">
|
||||
<g
|
||||
id="g1379">
|
||||
<rect
|
||||
style="fill:#d26464"
|
||||
height="10.000001"
|
||||
rx="2.0114901"
|
||||
width="9.999999"
|
||||
x="47.889584"
|
||||
y="-20.177546"
|
||||
id="rect1223" />
|
||||
<path
|
||||
style="opacity:0.3;fill:#fcffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 48.889584,-15.177545 h 7.999999 c 0,0 0,0 0,-2 0,-2 -0.5,-2 -4,-2 -3.499999,0 -3.999999,0 -3.999999,2 0,2 0,2 0,2 z"
|
||||
id="path1225" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 50.768263,-13.056225 4.24264,-4.24264"
|
||||
style="overflow:visible;fill:#ffffff;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="path1227-9" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 50.768263,-17.298865 4.24264,4.24264"
|
||||
style="overflow:visible;fill:#ffffff;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="path1229" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g1373"
|
||||
transform="matrix(0.63636364,0,0,0.63636364,30.841475,21.941358)">
|
||||
<rect
|
||||
style="fill:#c4a000"
|
||||
height="11"
|
||||
rx="2.0114901"
|
||||
width="11"
|
||||
x="-23.785124"
|
||||
y="-32.72242"
|
||||
id="rect1320" />
|
||||
<path
|
||||
style="fill:#fcffff"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -18.785123,-31.722418 v 2.0625 c -0.537663,0.111041 -1.024662,0.383291 -1.375,0.78125 l -1.78125,-1.03125 -0.5,0.875 1.78125,1.03125 c -0.08206,0.247432 -0.125,0.506395 -0.125,0.78125 0,0.274855 0.04294,0.533818 0.125,0.78125 l -1.78125,1.03125 0.5,0.875 1.78125,-1.03125 c 0.352503,0.40042 0.832682,0.670182 1.375,0.78125 v 2.0625 h 1 v -2.0625 c 0.537663,-0.111041 1.024662,-0.383291 1.375,-0.78125 l 1.78125,1.03125 0.5,-0.875 -1.78125,-1.03125 c 0.08206,-0.247432 0.125,-0.506395 0.125,-0.78125 0,-0.274855 -0.04294,-0.533818 -0.125,-0.78125 l 1.78125,-1.03125 -0.5,-0.875 -1.78125,1.03125 c -0.352503,-0.40042 -0.832682,-0.670182 -1.375,-0.78125 v -2.0625 z m 0.5,3.5 c 0.552,0 1,0.448 1,1 0,0.552 -0.448,1 -1,1 -0.552,0 -1,-0.448 -1,-1 0,-0.552 0.448,-1 1,-1 z"
|
||||
id="path1322" />
|
||||
<path
|
||||
style="opacity:0.3;fill:#fcffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -22.785123,-26.722418 9,-0.0096 c 0,0 0,0 0,-2 0,-2.9904 -1,-2.9904 -4.5,-2.9904 -3.5,0 -4.5,0 -4.5,3 0,2 0,2 0,2 z"
|
||||
id="path1324" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 13 KiB |
@ -217,13 +217,24 @@ Project the point on a segment
|
||||
|
||||
|
||||
|
||||
static int leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
|
||||
static int leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 );
|
||||
%Docstring
|
||||
Returns a value < 0 if the point (``x``, ``y``) is left of the line from (``x1``, ``y1``) -> ( ``x2``, ``y2``).
|
||||
A positive return value indicates the point is to the right of the line.
|
||||
|
||||
If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
|
||||
on the line, or exactly in line with the segment) and the result is undefined.
|
||||
%End
|
||||
|
||||
static int leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 );
|
||||
%Docstring
|
||||
Returns a value < 0 if the point ``point`` is left of the line from ``p1`` -> ``p2``.
|
||||
A positive return value indicates the point is to the right of the line.
|
||||
|
||||
If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
|
||||
on the line, or exactly in line with the segment) and the result is undefined.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance );
|
||||
|
@ -81,6 +81,7 @@ SET(QGIS_APP_SRCS
|
||||
qgsmaptoolchangelabelproperties.cpp
|
||||
qgsmaptooldeletering.cpp
|
||||
qgsmaptooldeletepart.cpp
|
||||
qgsmaptooltrimextendfeature.cpp
|
||||
qgsmaptoolfeatureaction.cpp
|
||||
qgsmaptoolformannotation.cpp
|
||||
qgsmaptoolhtmlannotation.cpp
|
||||
@ -317,6 +318,7 @@ SET (QGIS_APP_MOC_HDRS
|
||||
qgsmaptoolchangelabelproperties.h
|
||||
qgsmaptooldeletepart.h
|
||||
qgsmaptooldeletering.h
|
||||
qgsmaptooltrimextendfeature.h
|
||||
qgsmaptoolfeatureaction.h
|
||||
qgsmaptoolformannotation.h
|
||||
qgsmaptoolhtmlannotation.h
|
||||
|
@ -418,6 +418,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
|
||||
#include "qgsmaptoolreverseline.h"
|
||||
#include "qgsgeometryvalidationmodel.h"
|
||||
#include "qgsgeometryvalidationdock.h"
|
||||
#include "qgsmaptooltrimextendfeature.h"
|
||||
|
||||
#include "vertextool/qgsvertextool.h"
|
||||
|
||||
@ -1488,6 +1489,7 @@ QgisApp::~QgisApp()
|
||||
delete mMapTools.mChangeLabelProperties;
|
||||
delete mMapTools.mDeletePart;
|
||||
delete mMapTools.mDeleteRing;
|
||||
delete mMapTools.mTrimExtendFeature;
|
||||
delete mMapTools.mFeatureAction;
|
||||
delete mMapTools.mFormAnnotation;
|
||||
delete mMapTools.mHtmlAnnotation;
|
||||
@ -2121,6 +2123,7 @@ void QgisApp::createActions()
|
||||
connect( mActionSnappingOptions, &QAction::triggered, this, &QgisApp::snappingOptions );
|
||||
connect( mActionOffsetCurve, &QAction::triggered, this, &QgisApp::offsetCurve );
|
||||
connect( mActionReverseLine, &QAction::triggered, this, &QgisApp::reverseLine );
|
||||
connect( mActionTrimExtendFeature, &QAction::triggered, this, [ = ] { mMapCanvas->setMapTool( mMapTools.mTrimExtendFeature ); } );
|
||||
|
||||
// View Menu Items
|
||||
connect( mActionPan, &QAction::triggered, this, &QgisApp::pan );
|
||||
@ -2409,6 +2412,7 @@ void QgisApp::createActionGroups()
|
||||
mMapToolGroup->addAction( mActionRotateLabel );
|
||||
mMapToolGroup->addAction( mActionChangeLabelProperties );
|
||||
mMapToolGroup->addAction( mActionReverseLine );
|
||||
mMapToolGroup->addAction( mActionTrimExtendFeature );
|
||||
|
||||
//
|
||||
// Preview Modes Group
|
||||
@ -3387,6 +3391,7 @@ void QgisApp::setTheme( const QString &themeName )
|
||||
mActionDecorationScaleBar->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionScaleBar.svg" ) ) );
|
||||
mActionDecorationGrid->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/grid.svg" ) ) );
|
||||
mActionReverseLine->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionReverseLine.svg" ) ) );
|
||||
mActionTrimExtendFeature->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionTrimExtendFeature.svg" ) ) );
|
||||
|
||||
emit currentThemeChanged( themeName );
|
||||
}
|
||||
@ -3638,6 +3643,8 @@ void QgisApp::createCanvasTools()
|
||||
mMapTools.mRotatePointSymbolsTool->setAction( mActionRotatePointSymbols );
|
||||
mMapTools.mOffsetPointSymbolTool = new QgsMapToolOffsetPointSymbol( mMapCanvas );
|
||||
mMapTools.mOffsetPointSymbolTool->setAction( mActionOffsetPointSymbol );
|
||||
mMapTools.mTrimExtendFeature = new QgsMapToolTrimExtendFeature( mMapCanvas );
|
||||
mMapTools.mTrimExtendFeature->setAction( mActionTrimExtendFeature );
|
||||
|
||||
mMapTools.mPinLabels = new QgsMapToolPinLabels( mMapCanvas );
|
||||
mMapTools.mPinLabels->setAction( mActionPinLabels );
|
||||
@ -12407,6 +12414,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
|
||||
mActionCopyLayer->setEnabled( false );
|
||||
mActionPasteLayer->setEnabled( false );
|
||||
mActionReverseLine->setEnabled( false );
|
||||
mActionTrimExtendFeature->setEnabled( false );
|
||||
|
||||
mUndoDock->widget()->setEnabled( false );
|
||||
mActionUndo->setEnabled( false );
|
||||
@ -12482,6 +12490,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
|
||||
mActionLabeling->setEnabled( isSpatial );
|
||||
mActionDiagramProperties->setEnabled( isSpatial );
|
||||
mActionReverseLine->setEnabled( false );
|
||||
mActionTrimExtendFeature->setEnabled( false );
|
||||
|
||||
mActionSelectFeatures->setEnabled( isSpatial );
|
||||
mActionSelectPolygon->setEnabled( isSpatial );
|
||||
@ -12627,6 +12636,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
|
||||
mActionSimplifyFeature->setEnabled( isEditable && canChangeGeometry );
|
||||
mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes );
|
||||
mActionReverseLine->setEnabled( isEditable && canChangeGeometry );
|
||||
mActionTrimExtendFeature->setEnabled( isEditable && canChangeGeometry );
|
||||
|
||||
mActionAddRing->setEnabled( false );
|
||||
mActionFillRing->setEnabled( false );
|
||||
@ -12647,6 +12657,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
|
||||
mActionSimplifyFeature->setEnabled( isEditable && canChangeGeometry );
|
||||
mActionDeleteRing->setEnabled( isEditable && canChangeGeometry );
|
||||
mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes );
|
||||
mActionTrimExtendFeature->setEnabled( isEditable && canChangeGeometry );
|
||||
}
|
||||
else if ( vlayer->geometryType() == QgsWkbTypes::NullGeometry )
|
||||
{
|
||||
|
@ -2084,6 +2084,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
QgsMapTool *mRotateLabel = nullptr;
|
||||
QgsMapTool *mChangeLabelProperties = nullptr;
|
||||
QgsMapTool *mReverseLine = nullptr ;
|
||||
QgsMapTool *mTrimExtendFeature = nullptr ;
|
||||
} mMapTools;
|
||||
|
||||
QgsMapTool *mNonEditMapTool = nullptr;
|
||||
|
279
src/app/qgsmaptooltrimextendfeature.cpp
Normal file
279
src/app/qgsmaptooltrimextendfeature.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
/***************************************************************************
|
||||
qgmaptooltrimextendfeature.cpp - map tool to trim or extend feature
|
||||
---------------------
|
||||
begin : October 2018
|
||||
copyright : (C) 2018 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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 "qgsmaptooltrimextendfeature.h"
|
||||
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsvertexmarker.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgstolerance.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsgeometryutils.h"
|
||||
#include "qgsmapmouseevent.h"
|
||||
#include "qgssnapindicator.h"
|
||||
|
||||
class QgsRubberBand;
|
||||
|
||||
class FeatureFilter : public QgsPointLocator::MatchFilter
|
||||
{
|
||||
public:
|
||||
FeatureFilter()
|
||||
{}
|
||||
|
||||
bool acceptMatch( const QgsPointLocator::Match &match ) override
|
||||
{
|
||||
if ( mLayer )
|
||||
return match.layer() == mLayer && match.hasEdge();
|
||||
|
||||
return match.hasEdge();
|
||||
}
|
||||
// We only want to modify the current layer. When geometries are overlapped, this makes it possible to snap onto the current layer.
|
||||
void setLayer( QgsVectorLayer *layer ) { mLayer = layer; }
|
||||
|
||||
private:
|
||||
const QgsVectorLayer *mLayer = nullptr;
|
||||
};
|
||||
|
||||
QgsMapToolTrimExtendFeature::QgsMapToolTrimExtendFeature( QgsMapCanvas *canvas )
|
||||
: QgsMapToolEdit( canvas )
|
||||
{
|
||||
mToolName = tr( "Trim/Extend feature" );
|
||||
}
|
||||
|
||||
static void getPoints( const QgsPointLocator::Match &match, QgsPoint &p1, QgsPoint &p2 )
|
||||
{
|
||||
const QgsFeatureId fid = match.featureId();
|
||||
const int vertex = match.vertexIndex();
|
||||
|
||||
const QgsGeometry geom = match.layer()->getGeometry( fid );
|
||||
|
||||
if ( !( geom.isNull() || geom.isEmpty() ) )
|
||||
{
|
||||
p1 = geom.vertexAt( vertex );
|
||||
p2 = geom.vertexAt( vertex + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMapToolTrimExtendFeature::canvasMoveEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
mMapPoint = e->mapPoint();
|
||||
|
||||
FeatureFilter filter;
|
||||
QgsPointLocator::Match match;
|
||||
|
||||
switch ( mStep )
|
||||
{
|
||||
case StepLimit:
|
||||
|
||||
match = mCanvas->snappingUtils()->snapToMap( mMapPoint, &filter );
|
||||
if ( match.isValid() )
|
||||
{
|
||||
mIs3DLayer = QgsWkbTypes::hasZ( match.layer()->wkbType() );
|
||||
|
||||
QgsPointXY p1, p2;
|
||||
match.edgePoints( p1, p2 );
|
||||
|
||||
mRubberBandLimit.reset( createRubberBand( QgsWkbTypes::LineGeometry ) );
|
||||
mRubberBandLimit->addPoint( p1 );
|
||||
mRubberBandLimit->addPoint( p2 );
|
||||
mRubberBandLimit->show();
|
||||
|
||||
}
|
||||
else if ( mRubberBandLimit )
|
||||
{
|
||||
mRubberBandLimit->hide();
|
||||
}
|
||||
break;
|
||||
case StepExtend:
|
||||
|
||||
QgsMapLayer *currentLayer = mCanvas->currentLayer();
|
||||
if ( !currentLayer )
|
||||
break;
|
||||
|
||||
mVlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
|
||||
if ( !mVlayer )
|
||||
break;
|
||||
|
||||
if ( !mVlayer->isEditable() )
|
||||
break;
|
||||
|
||||
filter.setLayer( mVlayer );
|
||||
match = mCanvas->snappingUtils()->snapToMap( mMapPoint, &filter );
|
||||
|
||||
if ( match.isValid() )
|
||||
{
|
||||
if ( match.layer() != mVlayer )
|
||||
break;
|
||||
|
||||
QgsPointXY p1, p2;
|
||||
match.edgePoints( p1, p2 );
|
||||
|
||||
getPoints( match, pExtend1, pExtend2 );
|
||||
|
||||
// No need to trim/extend if segments are continuous
|
||||
if ( ( ( pLimit1 == pExtend1 ) || ( pLimit1 == pExtend2 ) ) || ( ( pLimit2 == pExtend1 ) || ( pLimit2 == pExtend2 ) ) )
|
||||
break;
|
||||
|
||||
mSegmentIntersects = QgsGeometryUtils::segmentIntersection( pLimit1, pLimit2, pExtend1, pExtend2, mIntersection, mIsIntersection, 1e-8, true );
|
||||
|
||||
if ( mIs3DLayer && QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
|
||||
{
|
||||
/* Z Interpolation */
|
||||
QgsLineString line( pLimit1, pLimit2 );
|
||||
|
||||
mIntersection = QgsGeometryUtils::closestPoint( line, QgsPoint( mIntersection ) );
|
||||
}
|
||||
|
||||
if ( mIsIntersection )
|
||||
{
|
||||
mRubberBandIntersection.reset( createRubberBand( QgsWkbTypes::PointGeometry ) );
|
||||
mRubberBandIntersection->addPoint( QgsPointXY( mIntersection ) );
|
||||
mRubberBandIntersection->show();
|
||||
|
||||
mRubberBandExtend.reset( createRubberBand( match.layer()->geometryType() ) );
|
||||
|
||||
mGeom = match.layer()->getGeometry( match.featureId() );
|
||||
int index = match.vertexIndex();
|
||||
|
||||
if ( !mSegmentIntersects )
|
||||
{
|
||||
QgsPoint ptInter( mIntersection.x(), mIntersection.y() );
|
||||
if ( pExtend2.distance( ptInter ) < pExtend1.distance( ptInter ) )
|
||||
index += 1;
|
||||
}
|
||||
// TRIM PART
|
||||
else if ( QgsGeometryUtils::leftOfLine( QgsPoint( mMapPoint ), pLimit1, pLimit2 ) != QgsGeometryUtils::leftOfLine( pExtend1, pLimit1, pLimit2 ) )
|
||||
{
|
||||
// Part where the mouse is (+) will be trimed
|
||||
/* |
|
||||
* +
|
||||
* |
|
||||
* ----- --> -----
|
||||
* | |
|
||||
* | |
|
||||
*/
|
||||
|
||||
/* | |
|
||||
* | |
|
||||
* ----- --> -----
|
||||
* |
|
||||
* +
|
||||
* |
|
||||
*/
|
||||
index += 1;
|
||||
}
|
||||
|
||||
mIsModified = mGeom.moveVertex( mIntersection, index );
|
||||
|
||||
if ( mIsModified )
|
||||
{
|
||||
mRubberBandExtend->setToGeometry( mGeom );
|
||||
mRubberBandExtend->show();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mRubberBandExtend )
|
||||
mRubberBandExtend->hide();
|
||||
if ( mRubberBandIntersection )
|
||||
mRubberBandIntersection->hide();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mRubberBandExtend )
|
||||
mRubberBandExtend->hide();
|
||||
if ( mRubberBandIntersection )
|
||||
mRubberBandIntersection->hide();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMapToolTrimExtendFeature::canvasReleaseEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
mMapPoint = e->mapPoint();
|
||||
|
||||
FeatureFilter filter;
|
||||
QgsPointLocator::Match match;
|
||||
|
||||
if ( e->button() == Qt::LeftButton )
|
||||
{
|
||||
switch ( mStep )
|
||||
{
|
||||
case StepLimit:
|
||||
match = mCanvas->snappingUtils()->snapToMap( mMapPoint, &filter );
|
||||
if ( mRubberBandLimit && mRubberBandLimit->isVisible() )
|
||||
{
|
||||
getPoints( match, pLimit1, pLimit2 );
|
||||
mStep = StepExtend;
|
||||
}
|
||||
break;
|
||||
case StepExtend:
|
||||
if ( mIsModified )
|
||||
{
|
||||
filter.setLayer( mVlayer );
|
||||
match = mCanvas->snappingUtils()->snapToMap( mMapPoint, &filter );
|
||||
|
||||
match.layer()->beginEditCommand( tr( "Trim/Extend feature" ) );
|
||||
match.layer()->changeGeometry( match.featureId(), mGeom );
|
||||
match.layer()->endEditCommand();
|
||||
match.layer()->triggerRepaint();
|
||||
|
||||
emit messageEmitted( tr( "Feature trimed/extended." ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
emit messageEmitted( tr( "Couldn't trim or extend the feature." ) );
|
||||
}
|
||||
deactivate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( e->button() == Qt::RightButton )
|
||||
{
|
||||
deactivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void QgsMapToolTrimExtendFeature::keyPressEvent( QKeyEvent *e )
|
||||
{
|
||||
if ( e && e->isAutoRepeat() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( e && e->key() == Qt::Key_Escape )
|
||||
{
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMapToolTrimExtendFeature::deactivate()
|
||||
{
|
||||
mStep = StepLimit;
|
||||
mIsModified = false;
|
||||
mIs3DLayer = false;
|
||||
mIsIntersection = false;
|
||||
mSegmentIntersects = false;
|
||||
mRubberBandLimit.reset();
|
||||
mRubberBandExtend.reset();
|
||||
mRubberBandIntersection.reset();
|
||||
QgsMapTool::deactivate();
|
||||
}
|
||||
|
77
src/app/qgsmaptooltrimextendfeature.h
Normal file
77
src/app/qgsmaptooltrimextendfeature.h
Normal file
@ -0,0 +1,77 @@
|
||||
/***************************************************************************
|
||||
qgmaptooltrimextendfeature.h - map tool to trim or extend feature
|
||||
---------------------
|
||||
begin : October 2018
|
||||
copyright : (C) 2018 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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 QGSMAPTOOLTRIMEXTENDFEATURE_H
|
||||
#define QGSMAPTOOLTRIMEXTENDFEATURE_H
|
||||
|
||||
#include "qgsmaptooledit.h"
|
||||
#include "qgis_app.h"
|
||||
#include "qgsrubberband.h"
|
||||
|
||||
class APP_EXPORT QgsMapToolTrimExtendFeature : public QgsMapToolEdit
|
||||
{
|
||||
public:
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QgsMapToolTrimExtendFeature( QgsMapCanvas *canvas );
|
||||
~QgsMapToolTrimExtendFeature() override = default;
|
||||
|
||||
void canvasMoveEvent( QgsMapMouseEvent *e ) override;
|
||||
|
||||
void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
|
||||
|
||||
void keyPressEvent( QKeyEvent *e ) override;
|
||||
|
||||
//! called when map tool is being deactivated
|
||||
void deactivate() override;
|
||||
|
||||
private:
|
||||
//! Rubberband that shows the limit
|
||||
std::unique_ptr<QgsRubberBand>mRubberBandLimit;
|
||||
//! Rubberband that shows the feature being extended
|
||||
std::unique_ptr<QgsRubberBand>mRubberBandExtend;
|
||||
//! Rubberband that shows the intersection point
|
||||
std::unique_ptr<QgsRubberBand>mRubberBandIntersection;
|
||||
//! Points for the limit
|
||||
QgsPoint pLimit1, pLimit2;
|
||||
//! Points for extend
|
||||
QgsPoint pExtend1, pExtend2;
|
||||
//! intersection point between the projection of [pExtend1 - pExtend2] on [pLimit1 - pLimit2]
|
||||
QgsPoint mIntersection;
|
||||
//! map point used to determine which edges will be used for trim the feature
|
||||
QgsPointXY mMapPoint;
|
||||
//! geometry that will be returned
|
||||
QgsGeometry mGeom;
|
||||
//! Current layer which will be modified
|
||||
QgsVectorLayer *mVlayer = nullptr;
|
||||
//! Keep information about the state of the intersection
|
||||
bool mIsIntersection = false;
|
||||
//! Keep information of the first layer snapped is 3D or not
|
||||
bool mIs3DLayer = false;
|
||||
//! if feature is modified
|
||||
bool mIsModified = false;
|
||||
//! if the segments are intersected = trim
|
||||
bool mSegmentIntersects = false;
|
||||
enum Step
|
||||
{
|
||||
StepLimit,
|
||||
StepExtend,
|
||||
};
|
||||
//! The first step (0): choose the limit. The second step (1): choose the segment to trim/extend
|
||||
Step mStep = StepLimit;
|
||||
};
|
||||
|
||||
#endif // QGSMAPTOOLTRIMEXTENDFEATURE_H
|
@ -529,7 +529,12 @@ QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::selfIntersections(
|
||||
return intersections;
|
||||
}
|
||||
|
||||
int QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
|
||||
int QgsGeometryUtils::leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 )
|
||||
{
|
||||
return leftOfLine( point.x(), point.y(), p1.x(), p1.y(), p2.x(), p2.y() );
|
||||
}
|
||||
|
||||
int QgsGeometryUtils::leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 )
|
||||
{
|
||||
double f1 = x - x1;
|
||||
double f2 = y2 - y1;
|
||||
|
@ -244,7 +244,18 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
|
||||
* on the line, or exactly in line with the segment) and the result is undefined.
|
||||
*/
|
||||
static int leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
|
||||
static int leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 );
|
||||
|
||||
/**
|
||||
* Returns a value < 0 if the point \a point is left of the line from \a p1 -> \a p2.
|
||||
* A positive return value indicates the point is to the right of the line.
|
||||
*
|
||||
* If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
|
||||
* on the line, or exactly in line with the segment) and the result is undefined.
|
||||
*
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
static int leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 );
|
||||
|
||||
/**
|
||||
* Returns a point a specified \a distance toward a second point.
|
||||
|
@ -365,6 +365,7 @@
|
||||
<addaction name="mActionRotatePointSymbols"/>
|
||||
<addaction name="mActionOffsetPointSymbol"/>
|
||||
<addaction name="mActionReverseLine"/>
|
||||
<addaction name="mActionTrimExtendFeature"/>
|
||||
</widget>
|
||||
<addaction name="mProjectMenu"/>
|
||||
<addaction name="mEditMenu"/>
|
||||
@ -472,6 +473,7 @@
|
||||
<addaction name="mActionReshapeFeatures"/>
|
||||
<addaction name="mActionOffsetCurve"/>
|
||||
<addaction name="mActionReverseLine"/>
|
||||
<addaction name="mActionTrimExtendFeature"/>
|
||||
<addaction name="mActionSplitFeatures"/>
|
||||
<addaction name="mActionSplitParts"/>
|
||||
<addaction name="mActionMergeFeatures"/>
|
||||
@ -1063,6 +1065,18 @@
|
||||
<string>Reverse line</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="mActionTrimExtendFeature">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionTrimExtendFeature.svg</normaloff>:/images/themes/default/mActionTrimExtendFeature.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Trim/Extend Feature</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="mActionSnappingOptions">
|
||||
<property name="text">
|
||||
<string>&Snapping Options…</string>
|
||||
|
@ -111,4 +111,5 @@ ADD_QGIS_TEST(measuretool testqgsmeasuretool.cpp)
|
||||
ADD_QGIS_TEST(vertextool testqgsvertextool.cpp)
|
||||
ADD_QGIS_TEST(vectorlayersaveasdialogtest testqgsvectorlayersaveasdialog.cpp)
|
||||
ADD_QGIS_TEST(maptoolreverselinetest testqgsmaptoolreverseline.cpp)
|
||||
ADD_QGIS_TEST(maptooltrimextendfeaturetest testqgsmaptooltrimextendfeature.cpp)
|
||||
|
||||
|
448
tests/src/app/testqgsmaptooltrimextendfeature.cpp
Normal file
448
tests/src/app/testqgsmaptooltrimextendfeature.cpp
Normal file
@ -0,0 +1,448 @@
|
||||
/***************************************************************************
|
||||
TestQgsMapToolTrimExtendFeature.cpp
|
||||
--------------------------------
|
||||
Date : October 2018
|
||||
Copyright : (C) 2018 by Loïc Bartoletti
|
||||
Email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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 "qgstest.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
#include "qgscategorizedsymbolrenderer.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmapmouseevent.h"
|
||||
#include "qgsmapcanvassnappingutils.h"
|
||||
|
||||
#include "qgsmaptooltrimextendfeature.h"
|
||||
|
||||
|
||||
class TestQgsMapToolTrimExtendFeature : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestQgsMapToolTrimExtendFeature() = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QgsVectorLayer> vlPolygon, vlMultiLine, vlLineZ;
|
||||
QgsFeature f1, f2;
|
||||
QgsMapCanvas *mCanvas = nullptr;
|
||||
|
||||
private slots:
|
||||
|
||||
void initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
// Will make sure the settings dir with the style file for color ramp is created
|
||||
QgsApplication::createDatabase();
|
||||
QgsApplication::showSettings();
|
||||
|
||||
|
||||
// vector layer with a triangle in a rectangle:
|
||||
// (0,3) +-------------------+ (3,3)
|
||||
// | (1,2) +---+ (2,2) |
|
||||
// | \ | |
|
||||
// | \ | |
|
||||
// | \| |
|
||||
// | + (2,1) |
|
||||
// (0,0) +-------------------+ (3,0)
|
||||
vlPolygon.reset( new QgsVectorLayer( QStringLiteral( "MultiPolygon?field=fld:int" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ) );
|
||||
int idx = vlPolygon->fields().indexFromName( QStringLiteral( "fld" ) );
|
||||
QVERIFY( idx != -1 );
|
||||
f1.initAttributes( 1 );
|
||||
f2.initAttributes( 1 );
|
||||
|
||||
QgsPolygonXY polygon;
|
||||
QgsPolylineXY polyline;
|
||||
polyline << QgsPointXY( 1, 2 ) << QgsPointXY( 2, 1 ) << QgsPointXY( 2, 2 ) << QgsPointXY( 1, 2 );
|
||||
polygon << polyline;
|
||||
QgsGeometry polygonGeom = QgsGeometry::fromPolygonXY( polygon );
|
||||
f1.setGeometry( polygonGeom );
|
||||
f1.setAttribute( idx, QVariant( 1 ) );
|
||||
QgsFeatureList flist;
|
||||
flist << f1;
|
||||
vlPolygon->dataProvider()->addFeatures( flist );
|
||||
|
||||
QgsPolygonXY polygon2;
|
||||
QgsPolylineXY polyline2;
|
||||
polyline2 << QgsPointXY( 0, 0 ) << QgsPointXY( 3, 0 ) << QgsPointXY( 3, 3 ) << QgsPointXY( 0, 3 ) << QgsPointXY( 0, 0 );
|
||||
polygon2 << polyline2;
|
||||
QgsGeometry polygonGeom2 = QgsGeometry::fromPolygonXY( polygon2 );
|
||||
f2.setGeometry( polygonGeom2 );
|
||||
f2.setAttribute( idx, QVariant( 2 ) );
|
||||
QgsFeatureList flist2;
|
||||
flist2 << f2;
|
||||
vlPolygon->dataProvider()->addFeatures( flist2 );
|
||||
|
||||
|
||||
/*
|
||||
* |
|
||||
* | |
|
||||
* |
|
||||
* -----|
|
||||
* |
|
||||
*/
|
||||
|
||||
vlMultiLine.reset( new QgsVectorLayer( QStringLiteral( "MultiLineString?crs=EPSG:3946&field=pk:int" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
|
||||
QVERIFY( vlMultiLine->isValid() );
|
||||
QgsFeature multi( vlMultiLine->dataProvider()->fields(), 1 );
|
||||
multi.setAttribute( QStringLiteral( "pk" ), 1 );
|
||||
multi.setGeometry( QgsGeometry::fromWkt( QStringLiteral(
|
||||
"MultiLineString ((10 0, 14 0),(11 1, 11 0.5),(14 -2, 14 2))" ) ) );
|
||||
|
||||
vlMultiLine->dataProvider()->addFeatures( QgsFeatureList() << multi );
|
||||
|
||||
|
||||
/* (3 8 200)
|
||||
/
|
||||
/ (2 6 10)
|
||||
/ \
|
||||
(0 5 100) (3 5 5)
|
||||
*/
|
||||
vlLineZ.reset( new QgsVectorLayer( QStringLiteral( "LineStringZ?crs=EPSG:3946&field=pk:int" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
|
||||
QVERIFY( vlLineZ->isValid() );
|
||||
QgsFeature linez( vlLineZ->dataProvider()->fields(), 1 );
|
||||
linez.setAttribute( QStringLiteral( "pk" ), 1 );
|
||||
linez.setGeometry( QgsGeometry::fromWkt( QStringLiteral(
|
||||
"LineStringZ (3 5 5, 2 6 10)" ) ) );
|
||||
QgsFeature linez2( vlLineZ->dataProvider()->fields(), 2 );
|
||||
linez2.setAttribute( QStringLiteral( "pk" ), 2 );
|
||||
linez2.setGeometry( QgsGeometry::fromWkt( QStringLiteral(
|
||||
"LineStringZ (0 5 100, 3 8 200)" ) ) );
|
||||
|
||||
vlLineZ->dataProvider()->addFeatures( QgsFeatureList() << linez << linez2 );
|
||||
|
||||
|
||||
mCanvas = new QgsMapCanvas();
|
||||
mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
|
||||
mCanvas->setLayers( QList<QgsMapLayer *>() << vlPolygon.get() << vlMultiLine.get() << vlLineZ.get() );
|
||||
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( QSize( 50, 50 ) );
|
||||
mapSettings.setExtent( QgsRectangle( -1, -1, 4, 4 ) );
|
||||
QVERIFY( mapSettings.hasValidSettings() );
|
||||
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vlPolygon.get() << vlMultiLine.get() << vlLineZ.get() );
|
||||
|
||||
QgsSnappingUtils *mSnappingUtils = new QgsMapCanvasSnappingUtils( mCanvas, this );
|
||||
QgsSnappingConfig snappingConfig = mSnappingUtils->config();
|
||||
mSnappingUtils->setMapSettings( mapSettings );
|
||||
snappingConfig.setEnabled( true );
|
||||
snappingConfig.setTolerance( 100 );
|
||||
snappingConfig.setType( QgsSnappingConfig::VertexAndSegment );
|
||||
snappingConfig.setUnits( QgsTolerance::Pixels );
|
||||
snappingConfig.setMode( QgsSnappingConfig::AllLayers );
|
||||
mSnappingUtils->setConfig( snappingConfig );
|
||||
|
||||
mCanvas->setSnappingUtils( mSnappingUtils );
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void testPolygon()
|
||||
{
|
||||
|
||||
// vector layer with a triangle in a rectangle:
|
||||
// (0,3) +-------------------+ (3,3)
|
||||
// | (1,2) +---+ (2,2) |
|
||||
// | \ | |
|
||||
// | \ | |
|
||||
// | \| |
|
||||
// | + (2,1) |
|
||||
// (0,0) +-------------------+ (3,0)
|
||||
mCanvas->setCurrentLayer( vlPolygon.get() );
|
||||
std::unique_ptr< QgsMapToolTrimExtendFeature > tool( new QgsMapToolTrimExtendFeature( mCanvas ) );
|
||||
|
||||
vlPolygon->startEditing();
|
||||
// Limit
|
||||
QgsPointXY pt;
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 0, 0 );
|
||||
std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
// Extend
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1.5 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
|
||||
// vector layer with a trianglev in a rectangle:
|
||||
// (0,3) +-------------------+ (3,3)
|
||||
// | (1,2) +---+ (2,2) |
|
||||
// | \ \ |
|
||||
// | \ \ |
|
||||
// | \ \ |
|
||||
// | \|
|
||||
// (0,0) +-------------------+ (3,0)
|
||||
QgsFeature f = vlPolygon->getFeature( 1 );
|
||||
|
||||
QString wkt = "Polygon ((1 2, 3 0, 2 2, 1 2))";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
|
||||
vlPolygon->rollBack();
|
||||
}
|
||||
|
||||
|
||||
void testMultiLine()
|
||||
{
|
||||
/*
|
||||
* |
|
||||
* | |
|
||||
* |
|
||||
* -----|
|
||||
* |
|
||||
*/
|
||||
mCanvas->setCurrentLayer( vlMultiLine.get() );
|
||||
std::unique_ptr< QgsMapToolTrimExtendFeature > tool( new QgsMapToolTrimExtendFeature( mCanvas ) );
|
||||
|
||||
vlMultiLine->startEditing();
|
||||
// Limit
|
||||
QgsPointXY pt;
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 12, 0 );
|
||||
std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
// Extend
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 11, 0.8 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
/*
|
||||
*(11 1)|
|
||||
* | |
|
||||
* | |
|
||||
* -----|
|
||||
*(11 0)|
|
||||
*/
|
||||
QgsFeature f = vlMultiLine->getFeature( 1 );
|
||||
|
||||
QString wkt = "MultiLineString ((10 0, 14 0),(11 1, 11 0),(14 -2, 14 2))";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
|
||||
|
||||
// Limit
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 12, 0 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
// Extend
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 14, 1 );
|
||||
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
/*
|
||||
*
|
||||
* |
|
||||
* |
|
||||
* ------ (14 0)
|
||||
* |
|
||||
* (14 -2)
|
||||
*/
|
||||
f = vlMultiLine->getFeature( 1 );
|
||||
wkt = "MultiLineString ((10 0, 14 0),(11 1, 11 0),(14 -2, 14 0))";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
|
||||
vlMultiLine->rollBack();
|
||||
|
||||
|
||||
vlMultiLine->startEditing();
|
||||
// Limit
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 12, 0 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
// Extend
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 14, -1 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
/*
|
||||
* | (14 2)
|
||||
* | |
|
||||
* |
|
||||
* ------ (14 0)
|
||||
*(10 0)
|
||||
*/
|
||||
f = vlMultiLine->getFeature( 1 );
|
||||
|
||||
wkt = "MultiLineString ((10 0, 14 0),(11 1, 11 0.5),(14 0, 14 2))";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
}
|
||||
|
||||
void testLineZ()
|
||||
{
|
||||
|
||||
/* (3 8 200)
|
||||
/
|
||||
/ (2 6 10)
|
||||
/ \
|
||||
(0 5 100) (3 5 5)
|
||||
*/
|
||||
mCanvas->setCurrentLayer( vlLineZ.get() );
|
||||
std::unique_ptr< QgsMapToolTrimExtendFeature > tool( new QgsMapToolTrimExtendFeature( mCanvas ) );
|
||||
|
||||
vlLineZ->startEditing();
|
||||
// Limit
|
||||
QgsPointXY pt;
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 0, 5 );
|
||||
std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
// Extend
|
||||
pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 5 );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseMove,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) )
|
||||
) );
|
||||
tool->canvasMoveEvent( event.get() );
|
||||
event.reset( new QgsMapMouseEvent(
|
||||
mCanvas,
|
||||
QEvent::MouseButtonRelease,
|
||||
QPoint( std::round( pt.x() ), std::round( pt.y() ) ),
|
||||
Qt::LeftButton
|
||||
) );
|
||||
tool->canvasReleaseEvent( event.get() );
|
||||
|
||||
|
||||
|
||||
/* (3 8 200)
|
||||
/
|
||||
/\ (1.5 6.5 150)
|
||||
/ \
|
||||
(0 5 100) (3 5 5)
|
||||
*/
|
||||
QgsFeature f = vlLineZ->getFeature( 1 );
|
||||
|
||||
QString wkt = "LineStringZ (3 5 5, 1.5 6.5 150)";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
|
||||
vlLineZ->rollBack();
|
||||
}
|
||||
};
|
||||
|
||||
QGSTEST_MAIN( TestQgsMapToolTrimExtendFeature )
|
||||
#include "testqgsmaptooltrimextendfeature.moc"
|
Loading…
x
Reference in New Issue
Block a user