/****************************************************************************** * $Id: ogrcircularstring.cpp 28005 2014-11-26 10:04:43Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRCircularString geometry class. * Author: Even Rouault, even dot rouault at spatialys dot com * ****************************************************************************** * Copyright (c) 2010, 2014, Even Rouault * * 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 "ogr_geometry.h" #include "ogr_p.h" #include #include CPL_CVSID("$Id"); /************************************************************************/ /* OGRCircularString() */ /************************************************************************/ /** * \brief Create an empty circular string. */ OGRCircularString::OGRCircularString() { } /************************************************************************/ /* ~OGRCircularString() */ /************************************************************************/ OGRCircularString::~OGRCircularString() { } /************************************************************************/ /* getGeometryType() */ /************************************************************************/ OGRwkbGeometryType OGRCircularString::getGeometryType() const { if( getCoordinateDimension() == 3 ) return wkbCircularStringZ; else return wkbCircularString; } /************************************************************************/ /* getGeometryName() */ /************************************************************************/ const char * OGRCircularString::getGeometryName() const { return "CIRCULARSTRING"; } /************************************************************************/ /* importFromWkb() */ /* */ /* Initialize from serialized stream in well known binary */ /* format. */ /************************************************************************/ OGRErr OGRCircularString::importFromWkb( unsigned char * pabyData, int nSize, OGRwkbVariant eWkbVariant ) { OGRErr eErr = OGRSimpleCurve::importFromWkb(pabyData, nSize, eWkbVariant); if (eErr == OGRERR_NONE) { if (!IsValidFast()) { empty(); return OGRERR_CORRUPT_DATA; } } return eErr; } /************************************************************************/ /* exportToWkb() */ /* */ /* Build a well known binary representation of this object. */ /************************************************************************/ OGRErr OGRCircularString::exportToWkb( OGRwkbByteOrder eByteOrder, unsigned char * pabyData, OGRwkbVariant eWkbVariant ) const { if (!IsValidFast()) { return OGRERR_FAILURE; } if( eWkbVariant == wkbVariantOldOgc ) /* does not make sense for new geometries, so patch it */ eWkbVariant = wkbVariantIso; return OGRSimpleCurve::exportToWkb(eByteOrder, pabyData, eWkbVariant); } /************************************************************************/ /* importFromWkt() */ /* */ /* Instantiate from well known text format. Currently this is */ /* `CIRCULARSTRING [Z] ( x y [z], x y [z], ...)', */ /************************************************************************/ OGRErr OGRCircularString::importFromWkt( char ** ppszInput ) { OGRErr eErr = OGRSimpleCurve::importFromWkt(ppszInput); if (eErr == OGRERR_NONE) { if (!IsValidFast()) { empty(); return OGRERR_CORRUPT_DATA; } } return eErr; } /************************************************************************/ /* exportToWkt() */ /************************************************************************/ OGRErr OGRCircularString::exportToWkt( char ** ppszDstText, CPL_UNUSED OGRwkbVariant eWkbVariant ) const { if (!IsValidFast()) { return OGRERR_FAILURE; } return OGRSimpleCurve::exportToWkt(ppszDstText, wkbVariantIso); } /************************************************************************/ /* get_Length() */ /* */ /* For now we return a simple euclidian 2D distance. */ /************************************************************************/ double OGRCircularString::get_Length() const { double dfLength = 0.0; double R, cx, cy, alpha0, alpha1, alpha2; int i; for(i=0;i quadrantEnd ) { int tmp = quadrantStart; quadrantStart = quadrantEnd; quadrantEnd = tmp; } /* Transition trough quadrants in counter-clock wise direction */ for( int j=quadrantStart+1; j<=quadrantEnd; j++) { switch( ((j+8)%4) ) { case 0: psEnvelope->MaxX = MAX(psEnvelope->MaxX, cx + R); break; case 1: psEnvelope->MaxY = MAX(psEnvelope->MaxY, cy + R); break; case 2: psEnvelope->MinX = MIN(psEnvelope->MinX, cx - R); break; case 3: psEnvelope->MinY = MIN(psEnvelope->MaxY, cy - R); break; default: CPLAssert(FALSE); break; } } } } } /************************************************************************/ /* getEnvelope() */ /************************************************************************/ void OGRCircularString::getEnvelope( OGREnvelope * psEnvelope ) const { OGRSimpleCurve::getEnvelope(psEnvelope); ExtendEnvelopeWithCircular(psEnvelope); } /************************************************************************/ /* getEnvelope() */ /************************************************************************/ void OGRCircularString::getEnvelope( OGREnvelope3D * psEnvelope ) const { OGRSimpleCurve::getEnvelope(psEnvelope); ExtendEnvelopeWithCircular(psEnvelope); } /************************************************************************/ /* OGRCircularString::segmentize() */ /************************************************************************/ void OGRCircularString::segmentize( double dfMaxLength ) { if( !IsValidFast() || nPointCount == 0 ) return; /* So as to make sure that the same line followed in both directions */ /* result in the same segmentized line */ if ( paoPoints[0].x < paoPoints[nPointCount - 1].x || (paoPoints[0].x == paoPoints[nPointCount - 1].x && paoPoints[0].y < paoPoints[nPointCount - 1].y) ) { reversePoints(); segmentize(dfMaxLength); reversePoints(); } std::vector aoRawPoint; std::vector adfZ; for(int i=0;i dfMaxLength || dfSegmentLength2 > dfMaxLength ) { int nIntermediatePoints = 1 + 2 * (int)floor(dfSegmentLength1 / dfMaxLength / 2); double dfStep = (alpha1 - alpha0) / (nIntermediatePoints + 1); for(int j=1;j<=nIntermediatePoints;j++) { double alpha = alpha0 + dfStep * j; double x = cx + R * cos(alpha), y = cy + R * sin(alpha); aoRawPoint.push_back(OGRRawPoint(x,y)); if( padfZ ) { double z = padfZ[i] + (padfZ[i+1] - padfZ[i]) * (alpha - alpha0) / (alpha1 - alpha0); adfZ.push_back(z); } } } aoRawPoint.push_back(OGRRawPoint(x1,y1)); if( padfZ ) adfZ.push_back(padfZ[i+1]); if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) { int nIntermediatePoints = 1 + 2 * (int)floor(dfSegmentLength2 / dfMaxLength / 2); double dfStep = (alpha2 - alpha1) / (nIntermediatePoints + 1); for(int j=1;j<=nIntermediatePoints;j++) { double alpha = alpha1 + dfStep * j; double x = cx + R * cos(alpha), y = cy + R * sin(alpha); aoRawPoint.push_back(OGRRawPoint(x,y)); if( padfZ ) { double z = padfZ[i+1] + (padfZ[i+2] - padfZ[i+1]) * (alpha - alpha1) / (alpha2 - alpha1); adfZ.push_back(z); } } } } else { /* It is a straight line */ double dfSegmentLength1 = sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)); double dfSegmentLength2 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) { int nIntermediatePoints = 1 + 2 * (int)ceil(dfSegmentLength1 / dfMaxLength / 2); for(int j=1;j<=nIntermediatePoints;j++) { aoRawPoint.push_back(OGRRawPoint( x0 + j * (x1-x0) / (nIntermediatePoints + 1), y0 + j * (y1-y0) / (nIntermediatePoints + 1))); if( padfZ ) adfZ.push_back(padfZ[i] + j * (padfZ[i+1]-padfZ[i]) / (nIntermediatePoints + 1)); } } aoRawPoint.push_back(OGRRawPoint(x1,y1)); if( padfZ ) adfZ.push_back(padfZ[i+1]); if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) { int nIntermediatePoints = 1 + 2 * (int)ceil(dfSegmentLength2 / dfMaxLength / 2); for(int j=1;j<=nIntermediatePoints;j++) { aoRawPoint.push_back(OGRRawPoint( x1 + j * (x2-x1) / (nIntermediatePoints + 1), y1 + j * (y2-y1) / (nIntermediatePoints + 1))); if( padfZ ) adfZ.push_back(padfZ[i+1] + j * (padfZ[i+2]-padfZ[i+1]) / (nIntermediatePoints + 1)); } } } } aoRawPoint.push_back(paoPoints[nPointCount-1]); if( padfZ ) adfZ.push_back(padfZ[nPointCount-1]); CPLAssert(aoRawPoint.size() == 0 || (aoRawPoint.size() >= 3 && (aoRawPoint.size() % 2) == 1)); if( padfZ ) { CPLAssert(adfZ.size() == aoRawPoint.size()); } /* Is there actually something to modify ? */ if( nPointCount < (int)aoRawPoint.size() ) { nPointCount = (int)aoRawPoint.size(); paoPoints = (OGRRawPoint *) OGRRealloc(paoPoints, sizeof(OGRRawPoint) * nPointCount); memcpy(paoPoints, &aoRawPoint[0], sizeof(OGRRawPoint) * nPointCount); if( padfZ ) { padfZ = (double*) OGRRealloc(padfZ, sizeof(double) * aoRawPoint.size()); memcpy(padfZ, &adfZ[0], sizeof(double) * nPointCount); } } } /************************************************************************/ /* Value() */ /* */ /* Get an interpolated point at some distance along the curve. */ /************************************************************************/ void OGRCircularString::Value( double dfDistance, OGRPoint * poPoint ) const { double dfLength = 0; int i; if( dfDistance < 0 ) { StartPoint( poPoint ); return; } for(i=0;i 0) { if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= dfDistance) ) { double dfRatio; dfRatio = (dfDistance - dfLength) / dfSegLength; double alpha = alpha0 * (1 - dfRatio) + alpha2 * dfRatio; double x = cx + R * cos(alpha), y = cy + R * sin(alpha); poPoint->setX( x ); poPoint->setY( y ); if( getCoordinateDimension() == 3 ) poPoint->setZ( padfZ[i] * (1 - dfRatio) + padfZ[i+2] * dfRatio ); return; } dfLength += dfSegLength; } } else { /* It is a straight line */ double dfSegLength = sqrt((x2-x0)*(x2-x0)+(y2-y0)*(y2-y0)); if (dfSegLength > 0) { if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= dfDistance) ) { double dfRatio; dfRatio = (dfDistance - dfLength) / dfSegLength; poPoint->setX( paoPoints[i].x * (1 - dfRatio) + paoPoints[i+2].x * dfRatio ); poPoint->setY( paoPoints[i].y * (1 - dfRatio) + paoPoints[i+2].y * dfRatio ); if( getCoordinateDimension() == 3 ) poPoint->setZ( padfZ[i] * (1 - dfRatio) + padfZ[i+2] * dfRatio ); return; } dfLength += dfSegLength; } } } EndPoint( poPoint ); } /************************************************************************/ /* CurveToLine() */ /************************************************************************/ OGRLineString* OGRCircularString::CurveToLine(double dfMaxAngleStepSizeDegrees, const char* const* papszOptions) const { OGRLineString* poLine = new OGRLineString(); poLine->assignSpatialReference(getSpatialReference()); int bHasZ = (getCoordinateDimension() == 3); int i; for(i=0;iaddSubLineString(poArc, (i == 0) ? 0 : 1); delete poArc; } return poLine; } /************************************************************************/ /* IsValidFast() */ /************************************************************************/ OGRBoolean OGRCircularString::IsValidFast( ) const { if (nPointCount == 1 || nPointCount == 2 || (nPointCount >= 3 && (nPointCount % 2) == 0)) { CPLError(CE_Failure, CPLE_NotSupported, "Bad number of points in circular string : %d", nPointCount); return FALSE; } return TRUE; } /************************************************************************/ /* IsValid() */ /************************************************************************/ OGRBoolean OGRCircularString::IsValid( ) const { return IsValidFast() && OGRGeometry::IsValid(); } /************************************************************************/ /* hasCurveGeometry() */ /************************************************************************/ OGRBoolean OGRCircularString::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const { return TRUE; } /************************************************************************/ /* getLinearGeometry() */ /************************************************************************/ OGRGeometry* OGRCircularString::getLinearGeometry(double dfMaxAngleStepSizeDegrees, const char* const* papszOptions) const { return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); } /************************************************************************/ /* GetCasterToLineString() */ /************************************************************************/ OGRCurveCasterToLineString OGRCircularString::GetCasterToLineString() const { return (OGRCurveCasterToLineString) OGRGeometry::CastToError; } /************************************************************************/ /* GetCasterToLinearRing() */ /************************************************************************/ OGRCurveCasterToLinearRing OGRCircularString::GetCasterToLinearRing() const { return (OGRCurveCasterToLinearRing) OGRGeometry::CastToError; } /************************************************************************/ /* IsFullCircle() */ /************************************************************************/ int OGRCircularString::IsFullCircle( double& cx, double& cy, double& square_R ) const { if( getNumPoints() == 3 && get_IsClosed() ) { double x0 = getX(0); double y0 = getY(0); double x1 = getX(1); double y1 = getY(1); cx = (x0 + x1) / 2; cy = (y0 + y1) / 2; square_R = (x1-cx)*(x1-cx)+(y1-cy)*(y1-cy); return TRUE; } /* Full circle defined by 2 arcs ? */ else if( getNumPoints() == 5 && get_IsClosed() ) { double R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1; double R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2; if( OGRGeometryFactory::GetCurveParmeters( getX(0), getY(0), getX(1), getY(1), getX(2), getY(2), R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1) && OGRGeometryFactory::GetCurveParmeters( getX(2), getY(2), getX(3), getY(3), getX(4), getY(4), R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2) && fabs(R_1-R_2) < 1e-10 && fabs(cx_1-cx_2) < 1e-10 && fabs(cy_1-cy_2) < 1e-10 && (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0 ) { cx = cx_1; cy = cy_1; square_R = R_1 * R_1; return TRUE; } } return FALSE; } /************************************************************************/ /* get_AreaOfCurveSegments() */ /************************************************************************/ double OGRCircularString::get_AreaOfCurveSegments() const { double dfArea = 0; for( int i=0; i < getNumPoints() - 2; i += 2 ) { double x0 = getX(i), y0 = getY(i), x1 = getX(i+1), y1 = getY(i+1), x2 = getX(i+2), y2 = getY(i+2); double R, cx, cy, alpha0, alpha1, alpha2; if( OGRGeometryFactory::GetCurveParmeters(x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) { double delta_alpha01 = alpha1 - alpha0; /* should be <= PI in absolute value */ double delta_alpha12 = alpha2 - alpha1; /* same */ /* This is my maths, but wikipedia confirms it... */ /* Cf http://en.wikipedia.org/wiki/Circular_segment */ dfArea += 0.5 * R * R * fabs( delta_alpha01 - sin(delta_alpha01) + delta_alpha12 - sin(delta_alpha12) ); } } return dfArea; } /************************************************************************/ /* get_Area() */ /************************************************************************/ double OGRCircularString::get_Area() const { double cx, cy, square_R; if( IsEmpty() || !get_IsClosed() ) return 0; if( IsFullCircle(cx, cy, square_R) ) { return M_PI * square_R; } /* Optimization for convex rings */ if( IsConvex() ) { /* Compute area of shape without the circular segments */ double dfArea = get_LinearArea(); /* Add the area of the spherical segments */ dfArea += get_AreaOfCurveSegments(); return dfArea; } else { OGRLineString* poLS = CurveToLine(); double dfArea = poLS->get_Area(); delete poLS; return dfArea; } } /************************************************************************/ /* ContainsPoint() */ /************************************************************************/ int OGRCircularString::ContainsPoint( const OGRPoint* p ) const { double cx, cy, square_R; if( IsFullCircle(cx, cy, square_R) ) { double square_dist = (p->getX()- cx)*(p->getX()- cx)+ (p->getY()- cy)*(p->getY()- cy); return square_dist <= square_R; } return -1; }