EVOLUTION-MANAGER
Edit File: ogrcompoundcurve.cpp
/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRCompoundCurve geometry class. * Author: Even Rouault, even dot rouault at spatialys dot com * ****************************************************************************** * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "ogr_geometry.h" #include <cmath> #include <cstddef> #include "cpl_error.h" #include "ogr_core.h" #include "ogr_geometry.h" #include "ogr_p.h" #include "ogr_spatialref.h" CPL_CVSID("$Id: ogrcompoundcurve.cpp 39408 2017-06-30 19:01:20Z jef $"); /************************************************************************/ /* OGRCompoundCurve() */ /************************************************************************/ /** * \brief Create an empty compound curve. */ OGRCompoundCurve::OGRCompoundCurve() {} /************************************************************************/ /* OGRCompoundCurve( const OGRCompoundCurve& ) */ /************************************************************************/ /** * \brief Copy constructor. * * Note: before GDAL 2.1, only the default implementation of the constructor * existed, which could be unsafe to use. * * @since GDAL 2.1 */ OGRCompoundCurve::OGRCompoundCurve( const OGRCompoundCurve& other ) : OGRCurve(other), oCC(other.oCC) {} /************************************************************************/ /* ~OGRCompoundCurve() */ /************************************************************************/ OGRCompoundCurve::~OGRCompoundCurve() {} /************************************************************************/ /* operator=( const OGRCompoundCurve&) */ /************************************************************************/ /** * \brief Assignment operator. * * Note: before GDAL 2.1, only the default implementation of the operator * existed, which could be unsafe to use. * * @since GDAL 2.1 */ OGRCompoundCurve& OGRCompoundCurve::operator=( const OGRCompoundCurve& other ) { if( this != &other) { OGRCurve::operator=( other ); oCC = other.oCC; } return *this; } /************************************************************************/ /* getGeometryType() */ /************************************************************************/ OGRwkbGeometryType OGRCompoundCurve::getGeometryType() const { if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) ) return wkbCompoundCurveZM; else if( flags & OGR_G_MEASURED ) return wkbCompoundCurveM; else if( flags & OGR_G_3D ) return wkbCompoundCurveZ; else return wkbCompoundCurve; } /************************************************************************/ /* getGeometryName() */ /************************************************************************/ const char * OGRCompoundCurve::getGeometryName() const { return "COMPOUNDCURVE"; } /************************************************************************/ /* WkbSize() */ /************************************************************************/ int OGRCompoundCurve::WkbSize() const { return oCC.WkbSize(); } /************************************************************************/ /* addCurveDirectlyFromWkt() */ /************************************************************************/ OGRErr OGRCompoundCurve::addCurveDirectlyFromWkb( OGRGeometry* poSelf, OGRCurve* poCurve ) { OGRCompoundCurve* poCC = (OGRCompoundCurve*)poSelf; return poCC->addCurveDirectlyInternal( poCurve, 1e-14, FALSE ); } /************************************************************************/ /* importFromWkb() */ /************************************************************************/ OGRErr OGRCompoundCurve::importFromWkb( unsigned char * pabyData, int nSize, OGRwkbVariant eWkbVariant ) { OGRwkbByteOrder eByteOrder = wkbNDR; int nDataOffset = 0; // coverity[tainted_data] OGRErr eErr = oCC.importPreambuleFromWkb(this, pabyData, nSize, nDataOffset, eByteOrder, 9, eWkbVariant); if( eErr != OGRERR_NONE ) return eErr; return oCC.importBodyFromWkb(this, pabyData, nSize, nDataOffset, FALSE, // bAcceptCompoundCurve addCurveDirectlyFromWkb, eWkbVariant); } /************************************************************************/ /* exportToWkb() */ /************************************************************************/ OGRErr OGRCompoundCurve::exportToWkb( OGRwkbByteOrder eByteOrder, unsigned char * pabyData, OGRwkbVariant eWkbVariant ) const { // Does not make sense for new geometries, so patch it. if( eWkbVariant == wkbVariantOldOgc ) eWkbVariant = wkbVariantIso; return oCC.exportToWkb(this, eByteOrder, pabyData, eWkbVariant); } /************************************************************************/ /* addCurveDirectlyFromWkt() */ /************************************************************************/ OGRErr OGRCompoundCurve::addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ) { return ((OGRCompoundCurve*)poSelf)->addCurveDirectly(poCurve); } /************************************************************************/ /* importFromWkt() */ /************************************************************************/ OGRErr OGRCompoundCurve::importFromWkt( char ** ppszInput ) { return importCurveCollectionFromWkt( ppszInput, FALSE, // bAllowEmptyComponent TRUE, // bAllowLineString TRUE, // bAllowCurve FALSE, // bAllowCompoundCurve addCurveDirectlyFromWkt ); } /************************************************************************/ /* exportToWkt() */ /************************************************************************/ OGRErr OGRCompoundCurve::exportToWkt( char ** ppszDstText, OGRwkbVariant /* eWkbVariant */ ) const { return oCC.exportToWkt(this, ppszDstText); } /************************************************************************/ /* clone() */ /************************************************************************/ OGRGeometry *OGRCompoundCurve::clone() const { OGRCompoundCurve *poNewCC = new OGRCompoundCurve; poNewCC->assignSpatialReference( getSpatialReference() ); poNewCC->flags = flags; for( int i = 0; i < oCC.nCurveCount; i++ ) { poNewCC->addCurve( oCC.papoCurves[i] ); } return poNewCC; } /************************************************************************/ /* empty() */ /************************************************************************/ void OGRCompoundCurve::empty() { oCC.empty(this); } /************************************************************************/ /* getEnvelope() */ /************************************************************************/ void OGRCompoundCurve::getEnvelope( OGREnvelope * psEnvelope ) const { oCC.getEnvelope(psEnvelope); } /************************************************************************/ /* getEnvelope() */ /************************************************************************/ void OGRCompoundCurve::getEnvelope( OGREnvelope3D * psEnvelope ) const { oCC.getEnvelope(psEnvelope); } /************************************************************************/ /* IsEmpty() */ /************************************************************************/ OGRBoolean OGRCompoundCurve::IsEmpty() const { return oCC.IsEmpty(); } /************************************************************************/ /* get_Length() */ /* */ /* For now we return a simple euclidean 2D distance. */ /************************************************************************/ double OGRCompoundCurve::get_Length() const { double dfLength = 0.0; for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) dfLength += oCC.papoCurves[iGeom]->get_Length(); return dfLength; } /************************************************************************/ /* StartPoint() */ /************************************************************************/ void OGRCompoundCurve::StartPoint(OGRPoint *p) const { CPLAssert(oCC.nCurveCount > 0); oCC.papoCurves[0]->StartPoint(p); } /************************************************************************/ /* EndPoint() */ /************************************************************************/ void OGRCompoundCurve::EndPoint(OGRPoint *p) const { CPLAssert(oCC.nCurveCount > 0); oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(p); } /************************************************************************/ /* Value() */ /************************************************************************/ void OGRCompoundCurve::Value( double dfDistance, OGRPoint *poPoint ) const { if( dfDistance < 0 ) { StartPoint( poPoint ); return; } double dfLength = 0.0; for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) { const double dfSegLength = oCC.papoCurves[iGeom]->get_Length(); if( dfSegLength > 0 ) { if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= dfDistance) ) { oCC.papoCurves[iGeom]->Value(dfDistance - dfLength, poPoint); return; } dfLength += dfSegLength; } } EndPoint( poPoint ); } /************************************************************************/ /* CurveToLineInternal() */ /************************************************************************/ OGRLineString * OGRCompoundCurve::CurveToLineInternal( double dfMaxAngleStepSizeDegrees, const char* const* papszOptions, int bIsLinearRing ) const { OGRLineString* const poLine = bIsLinearRing ? new OGRLinearRing() : new OGRLineString(); poLine->assignSpatialReference(getSpatialReference()); for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) { OGRLineString* poSubLS = oCC.papoCurves[iGeom]->CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); poLine->addSubLineString(poSubLS, (iGeom == 0) ? 0 : 1); delete poSubLS; } return poLine; } /************************************************************************/ /* CurveToLine() */ /************************************************************************/ OGRLineString * OGRCompoundCurve::CurveToLine( double dfMaxAngleStepSizeDegrees, const char* const* papszOptions ) const { return CurveToLineInternal(dfMaxAngleStepSizeDegrees, papszOptions, FALSE); } /************************************************************************/ /* Equals() */ /************************************************************************/ OGRBoolean OGRCompoundCurve::Equals( OGRGeometry *poOther ) const { if( poOther == this ) return TRUE; if( poOther->getGeometryType() != getGeometryType() ) return FALSE; OGRCompoundCurve *poOCC = (OGRCompoundCurve *) poOther; return oCC.Equals(&(poOCC->oCC)); } /************************************************************************/ /* setCoordinateDimension() */ /************************************************************************/ void OGRCompoundCurve::setCoordinateDimension( int nNewDimension ) { oCC.setCoordinateDimension( this, nNewDimension ); } void OGRCompoundCurve::set3D( OGRBoolean bIs3D ) { oCC.set3D(this, bIs3D); } void OGRCompoundCurve::setMeasured( OGRBoolean bIsMeasured ) { oCC.setMeasured(this, bIsMeasured); } /************************************************************************/ /* getNumCurves() */ /************************************************************************/ /** * \brief Return the number of curves. * * Note that the number of curves making this compound curve. * * Relates to the ISO SQL/MM ST_NumCurves() function. * * @return number of curves. */ int OGRCompoundCurve::getNumCurves() const { return oCC.nCurveCount; } /************************************************************************/ /* getCurve() */ /************************************************************************/ /** * \brief Fetch reference to indicated internal ring. * * Note that the returned curve pointer is to an internal data object of the * OGRCompoundCurve. It should not be modified or deleted by the application, * and the pointer is only valid till the polygon is next modified. Use the * OGRGeometry::clone() method to make a separate copy within the application. * * Relates to the ISO SQL/MM ST_CurveN() function. * * @param iRing curve index from 0 to getNumCurves() - 1. * * @return pointer to curve. May be NULL. */ OGRCurve *OGRCompoundCurve::getCurve( int iRing ) { return oCC.getCurve(iRing); } /************************************************************************/ /* getCurve() */ /************************************************************************/ /** * \brief Fetch reference to indicated internal ring. * * Note that the returned curve pointer is to an internal data object of the * OGRCompoundCurve. It should not be modified or deleted by the application, * and the pointer is only valid till the polygon is next modified. Use the * OGRGeometry::clone() method to make a separate copy within the application. * * Relates to the ISO SQL/MM ST_CurveN() function. * * @param iCurve curve index from 0 to getNumCurves() - 1. * * @return pointer to curve. May be NULL. */ const OGRCurve *OGRCompoundCurve::getCurve( int iCurve ) const { return oCC.getCurve(iCurve); } /************************************************************************/ /* stealCurve() */ /************************************************************************/ /** * \brief "Steal" reference to curve. * * @param iCurve curve index from 0 to getNumCurves() - 1. * * @return pointer to curve. May be NULL. */ OGRCurve* OGRCompoundCurve::stealCurve( int iCurve ) { return oCC.stealCurve(iCurve); } /************************************************************************/ /* addCurve() */ /************************************************************************/ /** * \brief Add a curve to the container. * * The passed geometry is cloned to make an internal copy. * * There is no ISO SQL/MM analog to this method. * * This method is the same as the C function OGR_G_AddGeometry(). * * @param poCurve geometry to add to the container. * @param dfToleranceEps tolerance when checking that the first point of a * segment matches then end point of the previous one. * Default value: 1e-14. * * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error * (for example if curves are not contiguous) */ OGRErr OGRCompoundCurve::addCurve( OGRCurve* poCurve, double dfToleranceEps ) { OGRCurve* poClonedCurve = (OGRCurve*)poCurve->clone(); const OGRErr eErr = addCurveDirectly( poClonedCurve, dfToleranceEps ); if( eErr != OGRERR_NONE ) delete poClonedCurve; return eErr; } /************************************************************************/ /* addCurveDirectly() */ /************************************************************************/ /** * \brief Add a curve directly to the container. * * Ownership of the passed geometry is taken by the container rather than * cloning as addCurve() does. * * There is no ISO SQL/MM analog to this method. * * This method is the same as the C function OGR_G_AddGeometryDirectly(). * * @param poCurve geometry to add to the container. * @param dfToleranceEps tolerance when checking that the first point of a * segment matches then end point of the previous one. * Default value: 1e-14. * * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error * (for example if curves are not contiguous) */ OGRErr OGRCompoundCurve::addCurveDirectly( OGRCurve* poCurve, double dfToleranceEps ) { return addCurveDirectlyInternal(poCurve, dfToleranceEps, TRUE ); } OGRErr OGRCompoundCurve::addCurveDirectlyInternal( OGRCurve* poCurve, double dfToleranceEps, int bNeedRealloc ) { if( poCurve->getNumPoints() == 1 ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid curve: not enough points"); return OGRERR_FAILURE; } const OGRwkbGeometryType eCurveType = wkbFlatten(poCurve->getGeometryType()); if( EQUAL(poCurve->getGeometryName(), "LINEARRING") ) { CPLError(CE_Failure, CPLE_AppDefined, "Linearring not allowed."); return OGRERR_FAILURE; } else if( eCurveType == wkbCompoundCurve ) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot add a compound curve inside a compound curve"); return OGRERR_FAILURE; } if( oCC.nCurveCount > 0 ) { if( oCC.papoCurves[oCC.nCurveCount-1]->IsEmpty() || poCurve->IsEmpty() ) { CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves"); return OGRERR_FAILURE; } OGRPoint end; OGRPoint start; oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(&end); poCurve->StartPoint(&start); if( fabs(end.getX() - start.getX()) > dfToleranceEps || fabs(end.getY() - start.getY()) > dfToleranceEps || fabs(end.getZ() - start.getZ()) > dfToleranceEps ) { poCurve->EndPoint(&start); if( fabs(end.getX() - start.getX()) > dfToleranceEps || fabs(end.getY() - start.getY()) > dfToleranceEps || fabs(end.getZ() - start.getZ()) > dfToleranceEps ) { CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves"); return OGRERR_FAILURE; } CPLDebug("GML", "reversing curve"); ((OGRSimpleCurve*)poCurve)->reversePoints(); } // Patch so that it matches exactly. ((OGRSimpleCurve*)poCurve)->setPoint(0, &end); } return oCC.addCurveDirectly(this, poCurve, bNeedRealloc); } /************************************************************************/ /* transform() */ /************************************************************************/ OGRErr OGRCompoundCurve::transform( OGRCoordinateTransformation *poCT ) { return oCC.transform(this, poCT); } /************************************************************************/ /* flattenTo2D() */ /************************************************************************/ void OGRCompoundCurve::flattenTo2D() { oCC.flattenTo2D(this); } /************************************************************************/ /* segmentize() */ /************************************************************************/ void OGRCompoundCurve::segmentize( double dfMaxLength ) { oCC.segmentize(dfMaxLength); } /************************************************************************/ /* swapXY() */ /************************************************************************/ void OGRCompoundCurve::swapXY() { oCC.swapXY(); } /************************************************************************/ /* hasCurveGeometry() */ /************************************************************************/ OGRBoolean OGRCompoundCurve::hasCurveGeometry( int bLookForNonLinear ) const { if( bLookForNonLinear ) { return oCC.hasCurveGeometry(bLookForNonLinear); } return TRUE; } /************************************************************************/ /* getLinearGeometry() */ /************************************************************************/ OGRGeometry * OGRCompoundCurve::getLinearGeometry( double dfMaxAngleStepSizeDegrees, const char* const* papszOptions ) const { return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); } /************************************************************************/ /* getNumPoints() */ /************************************************************************/ int OGRCompoundCurve::getNumPoints() const { int nPoints = 0; for( int i = 0; i < oCC.nCurveCount; i++ ) { nPoints += oCC.papoCurves[i]->getNumPoints(); if( i != 0 ) nPoints--; } return nPoints; } /************************************************************************/ /* OGRCompoundCurvePointIterator */ /************************************************************************/ class OGRCompoundCurvePointIterator: public OGRPointIterator { const OGRCompoundCurve *poCC; int iCurCurve; OGRPointIterator *poCurveIter; public: explicit OGRCompoundCurvePointIterator( const OGRCompoundCurve* poCCIn ) : poCC(poCCIn), iCurCurve(0), poCurveIter(NULL) {} virtual ~OGRCompoundCurvePointIterator() { delete poCurveIter; } virtual OGRBoolean getNextPoint( OGRPoint* p ) override; }; /************************************************************************/ /* getNextPoint() */ /************************************************************************/ OGRBoolean OGRCompoundCurvePointIterator::getNextPoint( OGRPoint* p ) { if( iCurCurve == poCC->getNumCurves() ) return FALSE; if( poCurveIter == NULL ) poCurveIter = poCC->getCurve(0)->getPointIterator(); if( !poCurveIter->getNextPoint(p) ) { iCurCurve++; if( iCurCurve == poCC->getNumCurves() ) return FALSE; delete poCurveIter; poCurveIter = poCC->getCurve(iCurCurve)->getPointIterator(); // Skip first point. return poCurveIter->getNextPoint(p) && poCurveIter->getNextPoint(p); } return TRUE; } /************************************************************************/ /* getPointIterator() */ /************************************************************************/ OGRPointIterator* OGRCompoundCurve::getPointIterator() const { return new OGRCompoundCurvePointIterator(this); } /************************************************************************/ /* CastToLineString() */ /************************************************************************/ //! @cond Doxygen_Suppress OGRLineString* OGRCompoundCurve::CastToLineString( OGRCompoundCurve* poCC ) { for( int i = 0; i < poCC->oCC.nCurveCount; i++ ) { poCC->oCC.papoCurves[i] = OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]); if( poCC->oCC.papoCurves[i] == NULL ) { delete poCC; return NULL; } } if( poCC->oCC.nCurveCount == 1 ) { OGRLineString* poLS = (OGRLineString*) poCC->oCC.papoCurves[0]; poLS->assignSpatialReference(poCC->getSpatialReference()); poCC->oCC.papoCurves[0] = NULL; delete poCC; return poLS; } OGRLineString* poLS = poCC->CurveToLineInternal(0, NULL, FALSE); delete poCC; return poLS; } /************************************************************************/ /* CastToLinearRing() */ /************************************************************************/ /** * \brief Cast to linear ring. * * The passed in geometry is consumed and a new one returned (or NULL in case * of failure) * * @param poCC the input geometry - ownership is passed to the method. * @return new geometry. */ OGRLinearRing* OGRCompoundCurve::CastToLinearRing( OGRCompoundCurve* poCC ) { for( int i = 0; i < poCC->oCC.nCurveCount; i++ ) { poCC->oCC.papoCurves[i] = OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]); if( poCC->oCC.papoCurves[i] == NULL ) { delete poCC; return NULL; } } if( poCC->oCC.nCurveCount == 1 ) { OGRLinearRing* poLR = OGRCurve::CastToLinearRing( poCC->oCC.papoCurves[0] ); if( poLR != NULL ) { poLR->assignSpatialReference(poCC->getSpatialReference()); } poCC->oCC.papoCurves[0] = NULL; delete poCC; return poLR; } OGRLinearRing* poLR = (OGRLinearRing *) poCC->CurveToLineInternal(0, NULL, TRUE); delete poCC; return poLR; } /************************************************************************/ /* GetCasterToLineString() */ /************************************************************************/ OGRCurveCasterToLineString OGRCompoundCurve::GetCasterToLineString() const { return (OGRCurveCasterToLineString) OGRCompoundCurve::CastToLineString; } /************************************************************************/ /* GetCasterToLinearRing() */ /************************************************************************/ OGRCurveCasterToLinearRing OGRCompoundCurve::GetCasterToLinearRing() const { return (OGRCurveCasterToLinearRing) OGRCompoundCurve::CastToLinearRing; } //! @endcond /************************************************************************/ /* get_Area() */ /************************************************************************/ double OGRCompoundCurve::get_Area() const { if( IsEmpty() || !get_IsClosed() ) return 0; // Optimization for convex rings. if( IsConvex() ) { // Compute area of shape without the circular segments. OGRPointIterator* poIter = getPointIterator(); OGRLineString oLS; oLS.setNumPoints( getNumPoints() ); OGRPoint p; for( int i = 0; poIter->getNextPoint(&p); i++ ) { oLS.setPoint( i, p.getX(), p.getY() ); } double dfArea = oLS.get_Area(); delete poIter; // Add the area of the spherical segments. dfArea += get_AreaOfCurveSegments(); return dfArea; } OGRLineString* poLS = CurveToLine(); double dfArea = poLS->get_Area(); delete poLS; return dfArea; } /************************************************************************/ /* get_AreaOfCurveSegments() */ /************************************************************************/ /** Return area of curve segments * @return area. */ double OGRCompoundCurve::get_AreaOfCurveSegments() const { double dfArea = 0; for( int i = 0; i < getNumCurves(); i++ ) { const OGRCurve* poPart = getCurve(i); dfArea += poPart->get_AreaOfCurveSegments(); } return dfArea; }