void QgsMapToolOffsetCurve::canvasReleaseEvent( QMouseEvent * e ) { Q_UNUSED( e ); QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { deleteRubberBandAndGeometry(); return; } if ( !mGeometryModified ) { deleteRubberBandAndGeometry(); vlayer->destroyEditCommand(); return; } if ( mMultiPartGeometry ) { mModifiedGeometry.convertToMultiType(); } vlayer->beginEditCommand( tr( "Offset curve" ) ); bool editOk; if ( mSourceLayerId == vlayer->id() && !mForceCopy ) { editOk = vlayer->changeGeometry( mModifiedFeature, &mModifiedGeometry ); } else { QgsFeature f; f.setGeometry( mModifiedGeometry ); //add empty values for all fields (allows inserting attribute values via the feature form in the same session) QgsAttributes attrs( vlayer->pendingFields().count() ); const QgsFields& fields = vlayer->pendingFields(); for ( int idx = 0; idx < fields.count(); ++idx ) { attrs[idx] = QVariant(); } f.setAttributes( attrs ); editOk = vlayer->addFeature( f ); } if ( editOk ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } deleteRubberBandAndGeometry(); deleteDistanceItem(); delete mSnapVertexMarker; mSnapVertexMarker = 0; mForceCopy = false; mCanvas->refresh(); }
void QgsMapToolOffsetCurve::canvasPressEvent( QMouseEvent* e ) { deleteRubberBandAndGeometry(); mGeometryModified = false; mForceCopy = false; if ( !mCanvas ) { return; } //get selected features or snap to nearest feature if no selection QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { notifyNotVectorLayer(); return; } QgsSnappingUtils* snapping = mCanvas->snappingUtils(); // store previous settings int oldType; double oldSearchRadius; QgsTolerance::UnitType oldSearchRadiusUnit; QgsSnappingUtils::SnapToMapMode oldMode = snapping->snapToMapMode(); snapping->defaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); // setup new settings (temporary) QSettings settings; snapping->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers ); snapping->setDefaultSettings( QgsPointLocator::Edge, settings.value( "/Qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble(), ( QgsTolerance::UnitType ) settings.value( "/Qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() ); QgsPointLocator::Match match = snapping->snapToMap( e->pos() ); // restore old settings snapping->setSnapToMapMode( oldMode ); snapping->setDefaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); if ( match.hasEdge() && match.layer() ) { mSourceLayerId = match.layer()->id(); QgsFeature fet; if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) ) { mForceCopy = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed mOriginalGeometry = createOriginGeometry( match.layer(), match, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mOriginalGeometry, layer ); } mModifiedFeature = fet.id(); createDistanceItem(); } } }
void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset, bool leftSide ) { // need at least geos 3.3 for OffsetCurve tool #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \ ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3))) if ( !mRubberBand || !mOriginalGeometry ) { return; } QgsVectorLayer* sourceLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mSourceLayerId ) ); if ( !sourceLayer ) { return; } QgsGeometry geomCopy( *mOriginalGeometry ); const GEOSGeometry* geosGeom = geomCopy.asGeos(); if ( geosGeom ) { QSettings s; int joinStyle = s.value( "/Qgis/digitizing/offset_join_style", 0 ).toInt(); int quadSegments = s.value( "/Qgis/digitizing/offset_quad_seg", 8 ).toInt(); double mitreLimit = s.value( "/Qgis/digitizing/offset_miter_limit", 5.0 ).toDouble(); GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( QgsGeometry::getGEOSHandler(), geosGeom, leftSide ? offset : -offset, quadSegments, joinStyle, mitreLimit ); if ( !offsetGeom ) { deleteRubberBandAndGeometry(); deleteDistanceItem(); delete mSnapVertexMarker; mSnapVertexMarker = 0; mForceCopy = false; mGeometryModified = false; deleteDistanceItem(); emit messageEmitted( tr( "Creating offset geometry failed" ), QgsMessageBar::CRITICAL ); return; } if ( offsetGeom ) { mModifiedGeometry.fromGeos( offsetGeom ); mRubberBand->setToGeometry( &mModifiedGeometry, sourceLayer ); } } #else //GEOS_VERSION>=3.3 Q_UNUSED( offset ); Q_UNUSED( leftSide ); #endif //GEOS_VERSION>=3.3 }
void QgsMapToolOffsetCurve::canvasPressEvent( QMouseEvent* e ) { deleteRubberBandAndGeometry(); mGeometryModified = false; mForceCopy = false; if ( !mCanvas ) { return; } //get selected features or snap to nearest feature if no selection QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { notifyNotVectorLayer(); return; } QgsMapRenderer* renderer = mCanvas->mapRenderer(); QgsSnapper snapper( renderer ); configureSnapper( snapper ); QList<QgsSnappingResult> snapResults; snapper.snapPoint( e->pos(), snapResults ); if ( snapResults.size() > 0 ) { QgsFeature fet; const QgsSnappingResult& snapResult = snapResults.at( 0 ); if ( snapResult.layer ) { mSourceLayerId = snapResult.layer->id(); QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mSourceLayerId ) ); if ( vl && vl->getFeatures( QgsFeatureRequest().setFilterFid( snapResult.snappedAtGeometry ) ).nextFeature( fet ) ) { mForceCopy = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed mOriginalGeometry = createOriginGeometry( vl, snapResult, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mOriginalGeometry, layer ); } mModifiedFeature = fet.id(); createDistanceItem(); } } } }
void QgsMapToolOffsetCurve::updateGeometryAndRubberBand( double offset ) { if ( !mRubberBand || mOriginalGeometry.isNull() ) { return; } if ( !mLayer ) { return; } QgsGeometry offsetGeom; QgsSettings s; QgsGeometry::JoinStyle joinStyle = s.enumValue( QStringLiteral( "/qgis/digitizing/offset_join_style" ), QgsGeometry::JoinStyleRound ); int quadSegments = s.value( QStringLiteral( "/qgis/digitizing/offset_quad_seg" ), 8 ).toInt(); double miterLimit = s.value( QStringLiteral( "/qgis/digitizing/offset_miter_limit" ), 5.0 ).toDouble(); QgsGeometry::EndCapStyle capStyle = s.enumValue( QStringLiteral( "/qgis/digitizing/offset_cap_style" ), QgsGeometry::CapRound ); if ( QgsWkbTypes::geometryType( mOriginalGeometry.wkbType() ) == QgsWkbTypes::LineGeometry ) { offsetGeom = mManipulatedGeometry.offsetCurve( offset, quadSegments, joinStyle, miterLimit ); } else { offsetGeom = mManipulatedGeometry.buffer( offset, quadSegments, capStyle, joinStyle, miterLimit ); } if ( !offsetGeom ) { deleteRubberBandAndGeometry(); deleteUserInputWidget(); mLayer = nullptr; mGeometryModified = false; emit messageDiscarded(); emit messageEmitted( tr( "Creating offset geometry failed: %1" ).arg( offsetGeom.lastError() ), Qgis::Critical ); } else { mModifiedGeometry = offsetGeom; mRubberBand->setToGeometry( mModifiedGeometry, mLayer ); } }
QgsMapToolOffsetCurve::~QgsMapToolOffsetCurve() { deleteRubberBandAndGeometry(); deleteDistanceItem(); delete mSnapVertexMarker; }
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e ) { mCtrlHeldOnFirstClick = false; if ( e->button() == Qt::RightButton ) { cancel(); return; } if ( mOriginalGeometry.isNull() ) { // first click, get feature to modify deleteRubberBandAndGeometry(); mGeometryModified = false; QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Types( QgsPointLocator::Edge | QgsPointLocator::Area ) ); if ( ( match.hasEdge() || match.hasArea() ) && match.layer() ) { mLayer = match.layer(); QgsFeature fet; if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) ) { mCtrlHeldOnFirstClick = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed prepareGeometry( match, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mManipulatedGeometry, match.layer() ); } mModifiedFeature = fet.id(); createUserInputWidget(); bool hasZ = QgsWkbTypes::hasZ( mLayer->wkbType() ); bool hasM = QgsWkbTypes::hasZ( mLayer->wkbType() ); if ( hasZ || hasM ) { emit messageEmitted( QStringLiteral( "layer %1 has %2%3%4 geometry. %2%3%4 values be set to 0 when using offset tool." ) .arg( mLayer->name() ) .arg( hasZ ? "Z" : "" ) .arg( hasZ && hasM ? "/" : "" ) .arg( hasM ? "M" : "" ) , Qgis::Warning ); } } } if ( mOriginalGeometry.isNull() ) { emit messageEmitted( tr( "Could not find a nearby feature in any vector layer." ) ); cancel(); notifyNotVectorLayer(); } } else { // second click - apply changes double offset = calculateOffset( e->snapPoint() ); applyOffset( offset, e->modifiers() ); } }
void QgsMapToolOffsetCurve::cancel() { deleteUserInputWidget(); deleteRubberBandAndGeometry(); mLayer = nullptr; }
void QgsMapToolOffsetCurve::applyOffset( double offset, Qt::KeyboardModifiers modifiers ) { if ( !mLayer || offset == 0.0 ) { cancel(); notifyNotVectorLayer(); return; } updateGeometryAndRubberBand( offset ); // no modification if ( !mGeometryModified ) { mLayer->destroyEditCommand(); cancel(); return; } if ( mModifiedPart >= 0 ) { QgsGeometry geometry; int partIndex = 0; QgsWkbTypes::Type geomType = mOriginalGeometry.wkbType(); if ( QgsWkbTypes::geometryType( geomType ) == QgsWkbTypes::LineGeometry ) { QgsMultiPolylineXY newMultiLine; QgsMultiPolylineXY multiLine = mOriginalGeometry.asMultiPolyline(); QgsMultiPolylineXY::const_iterator it = multiLine.constBegin(); for ( ; it != multiLine.constEnd(); ++it ) { if ( partIndex == mModifiedPart ) { newMultiLine.append( mModifiedGeometry.asPolyline() ); } else { newMultiLine.append( *it ); } partIndex++; } geometry = QgsGeometry::fromMultiPolylineXY( newMultiLine ); } else { QgsMultiPolygonXY newMultiPoly; const QgsMultiPolygonXY multiPoly = mOriginalGeometry.asMultiPolygon(); QgsMultiPolygonXY::const_iterator multiPolyIt = multiPoly.constBegin(); for ( ; multiPolyIt != multiPoly.constEnd(); ++multiPolyIt ) { if ( partIndex == mModifiedPart ) { if ( mModifiedGeometry.isMultipart() ) { // not a ring if ( mModifiedRing <= 0 ) { // part became mulitpolygon, that means discard original rings from the part newMultiPoly += mModifiedGeometry.asMultiPolygon(); } else { // ring became multipolygon, oh boy! QgsPolygonXY newPoly; int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = multiPolyIt->constBegin(); for ( ; polyIt != multiPolyIt->constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { const QgsMultiPolygonXY ringParts = mModifiedGeometry.asMultiPolygon(); QgsPolygonXY newRings; QgsMultiPolygonXY::const_iterator ringIt = ringParts.constBegin(); for ( ; ringIt != ringParts.constEnd(); ++ringIt ) { // the different parts of the new rings cannot have rings themselves newRings.append( ringIt->at( 0 ) ); } newPoly += newRings; } else { newPoly.append( *polyIt ); } ringIndex++; } newMultiPoly.append( newPoly ); } } else { // original part had no ring if ( mModifiedRing == -1 ) { newMultiPoly.append( mModifiedGeometry.asPolygon() ); } else { QgsPolygonXY newPoly; int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = multiPolyIt->constBegin(); for ( ; polyIt != multiPolyIt->constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { newPoly.append( mModifiedGeometry.asPolygon().at( 0 ) ); } else { newPoly.append( *polyIt ); } ringIndex++; } newMultiPoly.append( newPoly ); } } } else { newMultiPoly.append( *multiPolyIt ); } partIndex++; } geometry = QgsGeometry::fromMultiPolygonXY( newMultiPoly ); } geometry.convertToMultiType(); mModifiedGeometry = geometry; } else if ( mModifiedRing >= 0 ) { // original geometry had some rings if ( mModifiedGeometry.isMultipart() ) { // not a ring if ( mModifiedRing == 0 ) { // polygon became mulitpolygon, that means discard original rings from the part // keep the modified geometry as is } else { QgsPolygonXY newPoly; const QgsPolygonXY poly = mOriginalGeometry.asPolygon(); // ring became multipolygon, oh boy! int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = poly.constBegin(); for ( ; polyIt != poly.constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { QgsMultiPolygonXY ringParts = mModifiedGeometry.asMultiPolygon(); QgsPolygonXY newRings; QgsMultiPolygonXY::const_iterator ringIt = ringParts.constBegin(); for ( ; ringIt != ringParts.constEnd(); ++ringIt ) { // the different parts of the new rings cannot have rings themselves newRings.append( ringIt->at( 0 ) ); } newPoly += newRings; } else { newPoly.append( *polyIt ); } ringIndex++; } mModifiedGeometry = QgsGeometry::fromPolygonXY( newPoly ); } } else { // simple case where modified geom is a polygon (not multi) QgsPolygonXY newPoly; const QgsPolygonXY poly = mOriginalGeometry.asPolygon(); int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = poly.constBegin(); for ( ; polyIt != poly.constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { newPoly.append( mModifiedGeometry.asPolygon().at( 0 ) ); } else { newPoly.append( *polyIt ); } ringIndex++; } mModifiedGeometry = QgsGeometry::fromPolygonXY( newPoly ); } } if ( !mModifiedGeometry.isGeosValid() ) { emit messageEmitted( tr( "Generated geometry is not valid." ), Qgis::Critical ); // no cancel, continue editing. return; } mLayer->beginEditCommand( tr( "Offset curve" ) ); bool editOk; if ( !mCtrlHeldOnFirstClick && !( modifiers & Qt::ControlModifier ) ) { editOk = mLayer->changeGeometry( mModifiedFeature, mModifiedGeometry ); } else { QgsFeature f; f.setGeometry( mModifiedGeometry ); //add empty values for all fields (allows inserting attribute values via the feature form in the same session) QgsAttributes attrs( mLayer->fields().count() ); const QgsFields &fields = mLayer->fields(); for ( int idx = 0; idx < fields.count(); ++idx ) { attrs[idx] = QVariant(); } f.setAttributes( attrs ); editOk = mLayer->addFeature( f ); } if ( editOk ) { mLayer->endEditCommand(); } else { mLayer->destroyEditCommand(); emit messageEmitted( QStringLiteral( "Could not apply offset" ), Qgis::Critical ); } deleteRubberBandAndGeometry(); deleteUserInputWidget(); mLayer->triggerRepaint(); mLayer = nullptr; }
void QgsMapToolOffsetCurve::applyOffset() { QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { deleteRubberBandAndGeometry(); notifyNotVectorLayer(); return; } // no modification if ( !mGeometryModified ) { deleteRubberBandAndGeometry(); layer->destroyEditCommand(); deleteDistanceWidget(); return; } if ( mMultiPartGeometry ) { mModifiedGeometry.convertToMultiType(); } layer->beginEditCommand( tr( "Offset curve" ) ); bool editOk; if ( mSourceLayerId == layer->id() && !mForceCopy ) { editOk = layer->changeGeometry( mModifiedFeature, &mModifiedGeometry ); } else { QgsFeature f; f.setGeometry( mModifiedGeometry ); //add empty values for all fields (allows inserting attribute values via the feature form in the same session) QgsAttributes attrs( layer->fields().count() ); const QgsFields& fields = layer->fields(); for ( int idx = 0; idx < fields.count(); ++idx ) { attrs[idx] = QVariant(); } f.setAttributes( attrs ); editOk = layer->addFeature( f ); } if ( editOk ) { layer->endEditCommand(); } else { layer->destroyEditCommand(); } deleteRubberBandAndGeometry(); deleteDistanceWidget(); delete mSnapVertexMarker; mSnapVertexMarker = nullptr; mForceCopy = false; mCanvas->refresh(); }