bool QgsMapToolReshape::isBindingLine( QgsVectorLayer *vlayer, const QgsRectangle &bbox ) const { if ( vlayer->geometryType() != QgsWkbTypes::LineGeometry ) return false; bool begin = false; bool end = false; const QgsPointXY beginPoint = points().first(); const QgsPointXY endPoint = points().last(); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; // check that extremities of the new line are contained by features while ( fit.nextFeature( f ) ) { const QgsGeometry geom = f.geometry(); if ( !geom.isNull() ) { const QgsPolylineXY line = geom.asPolyline(); if ( line.contains( beginPoint ) ) begin = true; else if ( line.contains( endPoint ) ) end = true; } } return end && begin; }
std::unique_ptr<QgsLineString> QgsGeometryFactory::linestringFromPolyline( const QgsPolylineXY &polyline ) { QVector< double > x; x.reserve( polyline.size() ); QVector< double > y; y.reserve( polyline.size() ); QgsPolylineXY::const_iterator it = polyline.constBegin(); for ( ; it != polyline.constEnd(); ++it ) { x << it->x(); y << it->y(); } std::unique_ptr< QgsLineString > line = qgis::make_unique< QgsLineString >( x, y ); return line; }
bool QgsGeometryValidator::ringInRing( const QgsPolylineXY &inside, const QgsPolylineXY &outside ) { for ( int i = 0; !mStop && i < inside.size(); i++ ) { if ( !pointInRing( outside, inside[i] ) ) return false; } return true; }
bool QgsGeometryValidator::pointInRing( const QgsPolylineXY &ring, const QgsPointXY &p ) { bool inside = false; int j = ring.size() - 1; for ( int i = 0; !mStop && i < ring.size(); i++ ) { if ( qgsDoubleNear( ring[i].x(), p.x() ) && qgsDoubleNear( ring[i].y(), p.y() ) ) return true; if ( ( ring[i].y() < p.y() && ring[j].y() >= p.y() ) || ( ring[j].y() < p.y() && ring[i].y() >= p.y() ) ) { if ( ring[i].x() + ( p.y() - ring[i].y() ) / ( ring[j].y() - ring[i].y() ) * ( ring[j].x() - ring[i].x() ) <= p.x() ) inside = !inside; } j = i; } return inside; }
// TODO: move to geometry utils double closestSegment( const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon ) { double sqrDist = std::numeric_limits<double>::max(); const QgsPointXY *pldata = pl.constData(); int plcount = pl.count(); double prevX = pldata[0].x(), prevY = pldata[0].y(); double segmentPtX, segmentPtY; for ( int i = 1; i < plcount; ++i ) { double currentX = pldata[i].x(); double currentY = pldata[i].y(); double testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon ); if ( testDist < sqrDist ) { sqrDist = testDist; vertexAfter = i; } prevX = currentX; prevY = currentY; } return sqrDist; }
bool TopolError::fixSnap() { bool ok; QgsFeature f1, f2; FeatureLayer fl = mFeaturePairs.at( 1 ); ok = fl.layer->getFeatures( ( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ) ).nextFeature( f2 ); fl = mFeaturePairs.first(); ok = ok && fl.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ).nextFeature( f1 ); if ( !ok ) return false; QgsGeometry ge = f1.geometry(); QgsPolylineXY line = ge.asPolyline(); QgsPolylineXY conflictLine = mConflict.asPolyline(); line.last() = conflictLine.last(); QgsGeometry newG = QgsGeometry::fromPolylineXY( line ); bool ret = fl.layer->changeGeometry( f1.id(), newG ); return ret; }
void QgsGeometryValidator::checkRingIntersections( int p0, int i0, const QgsPolylineXY &ring0, int p1, int i1, const QgsPolylineXY &ring1 ) { for ( int i = 0; !mStop && i < ring0.size() - 1; i++ ) { QgsVector v = ring0[i + 1] - ring0[i]; for ( int j = 0; !mStop && j < ring1.size() - 1; j++ ) { QgsVector w = ring1[j + 1] - ring1[j]; QgsPointXY s; if ( intersectLines( ring0[i], v, ring1[j], w, s ) ) { double d = -distLine2Point( ring0[i], v.perpVector(), s ); if ( d >= 0 && d <= v.length() ) { d = -distLine2Point( ring1[j], w.perpVector(), s ); if ( d > 0 && d < w.length() && ring0[i + 1] != ring1[j + 1] && ring0[i + 1] != ring1[j] && ring0[i + 0] != ring1[j + 1] && ring0[i + 0] != ring1[j] ) { QString msg = QObject::tr( "segment %1 of ring %2 of polygon %3 intersects segment %4 of ring %5 of polygon %6 at %7" ) .arg( i0 ).arg( i ).arg( p0 ) .arg( i1 ).arg( j ).arg( p1 ) .arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } } } } }
// TODO: move to geometry utils double distance2D( const QgsPolylineXY &coords ) { int np = coords.count(); if ( np == 0 ) return 0; double x0 = coords[0].x(), y0 = coords[0].y(); double x1, y1; double dist = 0; for ( int i = 1; i < np; ++i ) { x1 = coords[i].x(); y1 = coords[i].y(); dist += std::sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) ); x0 = x1; y0 = y1; } return dist; }
ErrorList topolTest::checkPseudos( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; QgsFeature f; if ( layer1->geometryType() != QgsWkbTypes::LineGeometry ) { return errorList; } QList<FeatureLayer>::iterator it; qDebug() << mFeatureList1.count(); QgsPointXY startPoint; QgsPointXY endPoint; std::multimap<QgsPointXY, QgsFeatureId, PointComparer> endVerticesMap; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); if ( g1.isNull() ) { QgsMessageLog::logMessage( tr( "Skipping invalid first geometry in pseudo line test." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g1 ) ) { QgsMessageLog::logMessage( tr( "Failed to import first geometry into GEOS in pseudo line test." ), tr( "Topology plugin" ) ); continue; } if ( g1.isMultipart() ) { QgsMultiPolylineXY lines = g1.asMultiPolyline(); for ( int m = 0; m < lines.count(); m++ ) { QgsPolylineXY line = lines[m]; startPoint = line[0]; endPoint = line[line.size() - 1]; endVerticesMap.insert( std::pair<QgsPointXY, QgsFeatureId>( startPoint, it->feature.id() ) ); endVerticesMap.insert( std::pair<QgsPointXY, QgsFeatureId>( endPoint, it->feature.id() ) ); } } else { QgsPolylineXY polyline = g1.asPolyline(); startPoint = polyline[0]; endPoint = polyline[polyline.size() - 1]; endVerticesMap.insert( std::pair<QgsPointXY, QgsFeatureId>( startPoint, it->feature.id() ) ); endVerticesMap.insert( std::pair<QgsPointXY, QgsFeatureId>( endPoint, it->feature.id() ) ); } } QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( std::multimap<QgsPointXY, QgsFeatureId, PointComparer>::iterator pointIt = endVerticesMap.begin(), end = endVerticesMap.end(); pointIt != end; pointIt = endVerticesMap.upper_bound( pointIt->first ) ) { QgsPointXY p = pointIt->first; QgsFeatureId k = pointIt->second; size_t repetitions = endVerticesMap.count( p ); if ( repetitions == 2 ) { QgsGeometry conflictGeom = QgsGeometry::fromPointXY( p ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } } QgsRectangle bBox = conflictGeom.boundingBox(); QgsFeature feat; FeatureLayer ftrLayer1; //need to fetch attributes?? being safe side by fetching.. layer1->getFeatures( QgsFeatureRequest().setFilterFid( k ) ).nextFeature( feat ); ftrLayer1.feature = feat; ftrLayer1.layer = layer1; QList<FeatureLayer> errorFtrLayers; errorFtrLayers << ftrLayer1 << ftrLayer1; TopolErrorPseudos *err = new TopolErrorPseudos( bBox, conflictGeom, errorFtrLayers ); errorList << err; } } return errorList; }
void QgsGeometryValidator::validatePolyline( int i, QgsPolylineXY line, bool ring ) { if ( ring ) { if ( line.size() < 4 ) { QString msg = QObject::tr( "ring %1 with less than four points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } if ( line[0] != line[ line.size() - 1 ] ) { QString msg = QObject::tr( "ring %1 not closed" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } } else if ( line.size() < 2 ) { QString msg = QObject::tr( "line %1 with less than two points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } int j = 0; while ( j < line.size() - 1 ) { int n = 0; while ( j < line.size() - 1 && line[j] == line[j + 1] ) { line.remove( j ); n++; } if ( n > 0 ) { QString msg = QObject::tr( "line %1 contains %n duplicate node(s) at %2", "number of duplicate nodes", n ).arg( i ).arg( j ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, line[j] ) ); mErrorCount++; } j++; } for ( j = 0; !mStop && j < line.size() - 3; j++ ) { QgsVector v = line[j + 1] - line[j]; double vl = v.length(); int n = ( j == 0 && ring ) ? line.size() - 2 : line.size() - 1; for ( int k = j + 2; !mStop && k < n; k++ ) { QgsVector w = line[k + 1] - line[k]; QgsPointXY s; if ( !intersectLines( line[j], v, line[k], w, s ) ) continue; double d = 0.0; try { d = -distLine2Point( line[j], v.perpVector(), s ); } catch ( QgsException &e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d < 0 || d > vl ) continue; try { d = -distLine2Point( line[k], w.perpVector(), s ); } catch ( QgsException &e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d <= 0 || d >= w.length() ) continue; QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4" ).arg( j ).arg( k ).arg( i ).arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } }
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom ) { if ( !mLayer->isSpatial() ) return 1; if ( geom.isNull() ) { return 1; } int returnVal = 0; QgsWkbTypes::Type wkbType = geom.wkbType(); switch ( QgsWkbTypes::geometryType( wkbType ) ) { //line case QgsWkbTypes::LineGeometry: { if ( !QgsWkbTypes::isMultiType( wkbType ) ) { QgsPolylineXY line = geom.asPolyline(); QgsPolylineXY::const_iterator line_it = line.constBegin(); for ( ; line_it != line.constEnd(); ++line_it ) { if ( addTopologicalPoints( *line_it ) != 0 ) { returnVal = 2; } } } else { QgsMultiPolylineXY multiLine = geom.asMultiPolyline(); QgsPolylineXY currentPolyline; for ( int i = 0; i < multiLine.size(); ++i ) { QgsPolylineXY::const_iterator line_it = currentPolyline.constBegin(); for ( ; line_it != currentPolyline.constEnd(); ++line_it ) { if ( addTopologicalPoints( *line_it ) != 0 ) { returnVal = 2; } } } } break; } case QgsWkbTypes::PolygonGeometry: { if ( !QgsWkbTypes::isMultiType( wkbType ) ) { QgsPolygonXY polygon = geom.asPolygon(); QgsPolylineXY currentRing; for ( int i = 0; i < polygon.size(); ++i ) { currentRing = polygon.at( i ); QgsPolylineXY::const_iterator line_it = currentRing.constBegin(); for ( ; line_it != currentRing.constEnd(); ++line_it ) { if ( addTopologicalPoints( *line_it ) != 0 ) { returnVal = 2; } } } } else { QgsMultiPolygonXY multiPolygon = geom.asMultiPolygon(); QgsPolygonXY currentPolygon; QgsPolylineXY currentRing; for ( int i = 0; i < multiPolygon.size(); ++i ) { currentPolygon = multiPolygon.at( i ); for ( int j = 0; j < currentPolygon.size(); ++j ) { currentRing = currentPolygon.at( j ); QgsPolylineXY::const_iterator line_it = currentRing.constBegin(); for ( ; line_it != currentRing.constEnd(); ++line_it ) { if ( addTopologicalPoints( *line_it ) != 0 ) { returnVal = 2; } } } } } break; } case QgsWkbTypes::PointGeometry: case QgsWkbTypes::UnknownGeometry: case QgsWkbTypes::NullGeometry: break; } return returnVal; }
ErrorList topolTest::checkSegmentLength( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer1 ); Q_UNUSED( layer2 ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; QgsFeature f; QList<FeatureLayer>::iterator it; QgsPolygonXY pol; QgsMultiPolygonXY mpol; QgsPolylineXY segm; QgsPolylineXY ls; QgsMultiPolylineXY mls; QList<FeatureLayer> fls; TopolErrorShort *err = nullptr; double distance; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) { emit progress( i ); } if ( testCanceled() ) { break; } QgsGeometry g1 = it->feature.geometry(); // switching by type here, because layer can contain both single and multi version geometries switch ( g1.wkbType() ) { case QgsWkbTypes::LineString: case QgsWkbTypes::LineString25D: ls = g1.asPolyline(); for ( int i = 1; i < ls.size(); ++i ) { distance = std::sqrt( ls[i - 1].sqrDist( ls[i] ) ); if ( distance < tolerance ) { fls.clear(); fls << *it << *it; segm.clear(); segm << ls[i - 1] << ls[i]; QgsGeometry conflict = QgsGeometry::fromPolylineXY( segm ); err = new TopolErrorShort( g1.boundingBox(), conflict, fls ); //err = new TopolErrorShort(g1->boundingBox(), QgsGeometry::fromPolyline(segm), fls); errorList << err; //break on getting the first error break; } } break; case QgsWkbTypes::Polygon: case QgsWkbTypes::Polygon25D: pol = g1.asPolygon(); for ( int i = 0; i < pol.size(); ++i ) { for ( int j = 1; j < pol[i].size(); ++j ) { distance = std::sqrt( pol[i][j - 1].sqrDist( pol[i][j] ) ); if ( distance < tolerance ) { fls.clear(); fls << *it << *it; segm.clear(); segm << pol[i][j - 1] << pol[i][j]; QgsGeometry conflict = QgsGeometry::fromPolylineXY( segm ); err = new TopolErrorShort( g1.boundingBox(), conflict, fls ); errorList << err; //break on getting the first error break; } } } break; case QgsWkbTypes::MultiLineString: case QgsWkbTypes::MultiLineString25D: mls = g1.asMultiPolyline(); for ( int k = 0; k < mls.size(); ++k ) { QgsPolylineXY &ls = mls[k]; for ( int i = 1; i < ls.size(); ++i ) { distance = std::sqrt( ls[i - 1].sqrDist( ls[i] ) ); if ( distance < tolerance ) { fls.clear(); fls << *it << *it; segm.clear(); segm << ls[i - 1] << ls[i]; QgsGeometry conflict = QgsGeometry::fromPolylineXY( segm ); err = new TopolErrorShort( g1.boundingBox(), conflict, fls ); errorList << err; //break on getting the first error break; } } } break; case QgsWkbTypes::MultiPolygon: case QgsWkbTypes::MultiPolygon25D: mpol = g1.asMultiPolygon(); for ( int k = 0; k < mpol.size(); ++k ) { QgsPolygonXY &pol = mpol[k]; for ( int i = 0; i < pol.size(); ++i ) { for ( int j = 1; j < pol[i].size(); ++j ) { distance = pol[i][j - 1].sqrDist( pol[i][j] ); if ( distance < tolerance ) { fls.clear(); fls << *it << *it; segm.clear(); segm << pol[i][j - 1] << pol[i][j]; QgsGeometry conflict = QgsGeometry::fromPolylineXY( segm ); err = new TopolErrorShort( g1.boundingBox(), conflict, fls ); errorList << err; //break on getting the first error break; } } } } break; default: continue; } } return errorList; }