static void _makeWalls( const QgsCurve &ring, bool ccw, float extrusionHeight, QVector<float> &data, bool addNormals, double originX, double originY ) { // we need to find out orientation of the ring so that the triangles we generate // face the right direction // (for exterior we want clockwise order, for holes we want counter-clockwise order) bool is_counter_clockwise = _isRingCounterClockWise( ring ); QgsVertexId::VertexType vt; QgsPoint pt; QgsPoint ptPrev; ring.pointAt( is_counter_clockwise == ccw ? 0 : ring.numPoints() - 1, ptPrev, vt ); for ( int i = 1; i < ring.numPoints(); ++i ) { ring.pointAt( is_counter_clockwise == ccw ? i : ring.numPoints() - i - 1, pt, vt ); float x0 = ptPrev.x() - originX, y0 = ptPrev.y() - originY; float x1 = pt.x() - originX, y1 = pt.y() - originY; float z0 = ptPrev.z(); float z1 = pt.z(); // make a quad make_quad( x0, y0, z0, x1, y1, z1, extrusionHeight, data, addNormals ); ptPrev = pt; } }
bool QgsCurvePolygon::insertVertex( QgsVertexId vId, const QgsPoint &vertex ) { if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() ) { return false; } QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 ); int n = ring->numPoints(); bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex ); if ( !success ) { return false; } // If first or last vertex is inserted, re-sync the last/first vertex if ( vId.vertex == 0 ) ring->moveVertex( QgsVertexId( 0, 0, n ), vertex ); else if ( vId.vertex == n ) ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex ); clearCache(); return true; }
bool QgsTriangle::moveVertex( QgsVertexId vId, const QgsPointV2 &newPos ) { if ( !mExteriorRing || vId.part != 0 || vId.ring != 0 || vId.vertex < 0 || vId.vertex > 4 ) { return false; } if ( vId.vertex == 4 ) { vId.vertex = 0; } QgsPointV2 p1( vId.vertex == 0 ? newPos : vertexAt( 0 ) ); QgsPointV2 p2( vId.vertex == 1 ? newPos : vertexAt( 1 ) ); QgsPointV2 p3( vId.vertex == 2 ? newPos : vertexAt( 2 ) ); if ( !validateGeom( p1, p2, p3 ) ) { return false; } QgsCurve *ring = mExteriorRing; int n = ring->numPoints(); bool success = ring->moveVertex( vId, newPos ); if ( success ) { // If first or last vertex is moved, also move the last/first vertex if ( vId.vertex == 0 ) ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos ); clearCache(); } return success; }
bool QgsCurvePolygon::nextVertex( QgsVertexId &vId, QgsPoint &vertex ) const { if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() ) { return false; } if ( vId.ring < 0 ) { vId.ring = 0; vId.vertex = -1; if ( vId.part < 0 ) { vId.part = 0; } return mExteriorRing->nextVertex( vId, vertex ); } else { QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1]; if ( ring->nextVertex( vId, vertex ) ) { return true; } ++vId.ring; vId.vertex = -1; if ( vId.ring >= 1 + mInteriorRings.size() ) { return false; } ring = mInteriorRings[ vId.ring - 1 ]; return ring->nextVertex( vId, vertex ); } }
double QgsCurvePolygon::vertexAngle( QgsVertexId vertex ) const { if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() ) { //makes no sense - conversion of false to double! return false; } QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1]; return ring->vertexAngle( vertex ); }
static bool _isRingCounterClockWise( const QgsCurve &ring ) { double a = 0; int count = ring.numPoints(); QgsVertexId::VertexType vt; QgsPoint pt, ptPrev; ring.pointAt( 0, ptPrev, vt ); for ( int i = 1; i < count + 1; ++i ) { ring.pointAt( i % count, pt, vt ); a += ptPrev.x() * pt.y() - ptPrev.y() * pt.x(); ptPrev = pt; } return a > 0; // clockwise if a is negative }
bool QgsCurvePolygon::moveVertex( QgsVertexId vId, const QgsPoint &newPos ) { if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() ) { return false; } QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 ); int n = ring->numPoints(); bool success = ring->moveVertex( vId, newPos ); if ( success ) { // If first or last vertex is moved, also move the last/first vertex if ( vId.vertex == 0 ) ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos ); else if ( vId.vertex == n - 1 ) ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos ); clearCache(); } return success; }
double QgsCompoundCurve::vertexAngle( QgsVertexId vertex ) const { QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex ); if ( curveIds.size() == 1 ) { QgsCurve *curve = mCurves[curveIds.at( 0 ).first]; return curve->vertexAngle( curveIds.at( 0 ).second ); } else if ( curveIds.size() > 1 ) { QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first]; QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first]; double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second ); double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second ); return QgsGeometryUtils::averageAngle( angle1, angle2 ); } else { return 0.0; } }
bool QgsCompoundCurve::fromWkb( QgsConstWkbPtr &wkbPtr ) { clear(); if ( !wkbPtr ) { return false; } QgsWkbTypes::Type type = wkbPtr.readHeader(); if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::CompoundCurve ) { return false; } mWkbType = type; int nCurves; wkbPtr >> nCurves; QgsCurve *currentCurve = nullptr; for ( int i = 0; i < nCurves; ++i ) { QgsWkbTypes::Type curveType = wkbPtr.readHeader(); wkbPtr -= 1 + sizeof( int ); if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString ) { currentCurve = new QgsLineString(); } else if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::CircularString ) { currentCurve = new QgsCircularString(); } else { return false; } currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr mCurves.append( currentCurve ); } return true; }
bool QgsCurvePolygon::deleteVertex( QgsVertexId vId ) { if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() ) { return false; } QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 ); int n = ring->numPoints(); if ( n <= 4 ) { //no points will be left in ring, so remove whole ring if ( vId.ring == 0 ) { mExteriorRing.reset(); if ( !mInteriorRings.isEmpty() ) { mExteriorRing.reset( mInteriorRings.takeFirst() ); } } else { removeInteriorRing( vId.ring - 1 ); } clearCache(); return true; } bool success = ring->deleteVertex( vId ); if ( success ) { // If first or last vertex is removed, re-sync the last/first vertex // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex // may have been deleted (e.g. with CircularString) if ( vId.vertex == 0 ) ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) ); else if ( vId.vertex == n - 1 ) ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) ); clearCache(); } return success; }
static bool ring_make_geos_friendly( QgsCurve &ring ) { if ( ring.nCoordinates() == 0 ) return false; // earlier we allowed in only geometries with straight segments QgsLineString &linestring = dynamic_cast<QgsLineString &>( ring ); // close the ring if not already closed (2d only) QgsPointV2 p1 = linestring.startPoint(), p2 = linestring.endPoint(); if ( p1.x() != p2.x() || p1.y() != p2.y() ) linestring.addVertex( p1 ); // must have at least 4 coordinates to be accepted by GEOS while ( linestring.nCoordinates() < 4 ) linestring.addVertex( p1 ); return true; }
QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part ) { if ( !geom ) { return QgsGeometry::InvalidBaseGeometry; } if ( !part ) { return QgsGeometry::InvalidInput; } //multitype? QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom ); if ( !geomCollection ) { return QgsGeometry::AddPartNotMultiGeometry; } bool added = false; if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface || QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ); if ( curve && curve->isClosed() && curve->numPoints() >= 4 ) { std::unique_ptr<QgsCurvePolygon> poly; if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString ) { poly = qgis::make_unique< QgsPolygonV2 >(); } else { poly = qgis::make_unique< QgsCurvePolygon >(); } // Ownership is still with part, curve points to the same object and is transferred // to poly here. part.release(); poly->setExteriorRing( curve ); added = geomCollection->addGeometry( poly.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon ) { added = geomCollection->addGeometry( part.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon ) { std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) ); int i; int n = geomCollection->numGeometries(); for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ ) ; added = i == parts->numGeometries(); if ( !added ) { while ( geomCollection->numGeometries() > n ) geomCollection->removeGeometry( n ); return QgsGeometry::InvalidInput; } } else { return QgsGeometry::InvalidInput; } } else { added = geomCollection->addGeometry( part.release() ); } return added ? QgsGeometry::Success : QgsGeometry::InvalidInput; }
void QgsCompoundCurve::addVertex( const QgsPoint &pt ) { if ( mCurves.isEmpty() || mWkbType == QgsWkbTypes::Unknown ) { setZMTypeFromSubGeometry( &pt, QgsWkbTypes::CompoundCurve ); } //is last curve QgsLineString QgsCurve *lastCurve = nullptr; if ( !mCurves.isEmpty() ) { lastCurve = mCurves.at( mCurves.size() - 1 ); } QgsLineString *line = nullptr; if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != QgsWkbTypes::LineString ) { line = new QgsLineString(); mCurves.append( line ); if ( lastCurve ) { line->addVertex( lastCurve->endPoint() ); } lastCurve = line; } else //create new QgsLineString* with point in it { line = static_cast<QgsLineString *>( lastCurve ); } line->addVertex( pt ); clearCache(); }