int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) { if ( !mLayer->isSpatial() ) return 1; double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8; //work with a tolerance because coordinate projection may introduce some rounding double threshold = 0.0000001; if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters ) { threshold = 0.001; } else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet ) { threshold = 0.0001; } QgsRectangle searchRect( p.x() - threshold, p.y() - threshold, p.x() + threshold, p.y() + threshold ); double sqrSnappingTolerance = threshold * threshold; QgsFeature f; QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest() .setFilterRect( searchRect ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QMap<QgsFeatureId, QgsGeometry> features; QMap<QgsFeatureId, int> segments; while ( fit.nextFeature( f ) ) { int afterVertex; QgsPointXY snappedPoint; double sqrDistSegmentSnap = f.geometry().closestSegmentWithContext( p, snappedPoint, afterVertex, nullptr, segmentSearchEpsilon ); if ( sqrDistSegmentSnap < sqrSnappingTolerance ) { segments[f.id()] = afterVertex; features[f.id()] = f.geometry(); } } if ( segments.isEmpty() ) return 2; for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it ) { QgsFeatureId fid = it.key(); int segmentAfterVertex = it.value(); QgsGeometry geom = features[fid]; int atVertex, beforeVertex, afterVertex; double sqrDistVertexSnap; geom.closestVertex( p, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap ); if ( sqrDistVertexSnap < sqrSnappingTolerance ) continue; // the vertex already exists - do not insert it if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) ) { QgsDebugMsg( "failed to insert topo point" ); } } return 0; }
bool QgsPointDistanceRenderer::renderFeature( QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker ) { Q_UNUSED( drawVertexMarker ); Q_UNUSED( context ); Q_UNUSED( layer ); /* * IMPORTANT: This algorithm is ported to Python in the processing "Points Displacement" algorithm. * Please port any changes/improvements to that algorithm too! */ //check if there is already a point at that position if ( !feature.hasGeometry() ) return false; QgsMarkerSymbol *symbol = firstSymbolForFeature( feature, context ); //if the feature has no symbol (e.g., no matching rule in a rule-based renderer), skip it if ( !symbol ) return false; //point position in screen coords QgsGeometry geom = feature.geometry(); QgsWkbTypes::Type geomType = geom.wkbType(); if ( QgsWkbTypes::flatType( geomType ) != QgsWkbTypes::Point ) { //can only render point type return false; } QString label; if ( mDrawLabels ) { label = getLabel( feature ); } QgsCoordinateTransform xform = context.coordinateTransform(); QgsFeature transformedFeature = feature; if ( xform.isValid() ) { geom.transform( xform ); transformedFeature.setGeometry( geom ); } double searchDistance = context.convertToMapUnits( mTolerance, mToleranceUnit, mToleranceMapUnitScale ); QgsPointXY point = transformedFeature.geometry().asPoint(); QList<QgsFeatureId> intersectList = mSpatialIndex->intersects( searchRect( point, searchDistance ) ); if ( intersectList.empty() ) { mSpatialIndex->insertFeature( transformedFeature ); // create new group ClusteredGroup newGroup; newGroup << GroupedFeature( transformedFeature, symbol->clone(), selected, label ); mClusteredGroups.push_back( newGroup ); // add to group index mGroupIndex.insert( transformedFeature.id(), mClusteredGroups.count() - 1 ); mGroupLocations.insert( transformedFeature.id(), point ); } else { // find group with closest location to this point (may be more than one within search tolerance) QgsFeatureId minDistFeatureId = intersectList.at( 0 ); double minDist = mGroupLocations.value( minDistFeatureId ).distance( point ); for ( int i = 1; i < intersectList.count(); ++i ) { QgsFeatureId candidateId = intersectList.at( i ); double newDist = mGroupLocations.value( candidateId ).distance( point ); if ( newDist < minDist ) { minDist = newDist; minDistFeatureId = candidateId; } } int groupIdx = mGroupIndex[ minDistFeatureId ]; ClusteredGroup &group = mClusteredGroups[groupIdx]; // calculate new centroid of group QgsPointXY oldCenter = mGroupLocations.value( minDistFeatureId ); mGroupLocations[ minDistFeatureId ] = QgsPointXY( ( oldCenter.x() * group.size() + point.x() ) / ( group.size() + 1.0 ), ( oldCenter.y() * group.size() + point.y() ) / ( group.size() + 1.0 ) ); // add to a group group << GroupedFeature( transformedFeature, symbol->clone(), selected, label ); // add to group index mGroupIndex.insert( transformedFeature.id(), groupIdx ); } return true; }
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent ) { if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) ) { return; } mDisplacementGroups.clear(); mDisplacementIds.clear(); //use a spatial index to check if there is already a point at a position QgsSpatialIndex spatialIndex; //attributes QgsAttributeList attList; QList<QString> attributeStrings = usedAttributes(); QList<QString>::const_iterator attStringIt = attributeStrings.constBegin(); for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt ) { attList.push_back( vlayer->fieldNameIndex( *attStringIt ) ); } QgsFeature f; QList<QgsFeatureId> intersectList; vlayer->select( attList, viewExtent, true, false ); while ( vlayer->nextFeature( f ) ) { intersectList.clear(); //check, if there is already a point at that position if ( f.geometry() ) { intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) ); if ( intersectList.empty() ) { spatialIndex.insertFeature( f ); } else { //go through all the displacement group maps and search an entry where the id equals the result of the spatial search QgsFeatureId existingEntry = intersectList.at( 0 ); bool found = false; QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { if ( it->size() > 0 && it->contains( existingEntry ) ) { found = true; QgsFeature feature; it->insert( f.id(), f ); mDisplacementIds.insert( f.id() ); break; } } if ( !found )//insert the already existing feature and the new one into a map { QMap<QgsFeatureId, QgsFeature> newMap; QgsFeature existingFeature; vlayer->featureAtId( existingEntry, existingFeature ); newMap.insert( existingEntry, existingFeature ); mDisplacementIds.insert( existingEntry ); newMap.insert( f.id(), f ); mDisplacementIds.insert( f.id() ); mDisplacementGroups.push_back( newMap ); } } } } //refresh the selection because the vector layer is going to step through all features now vlayer->select( attList, viewExtent, true, false ); }