mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-01 22:03:40 -06:00
369 lines
14 KiB
C++
369 lines
14 KiB
C++
/******************************************************************************
|
|
* $Id: ograssemblepolygon.cpp 27044 2014-03-16 23:41:27Z rouault $
|
|
*
|
|
* Project: S-57 Reader
|
|
* Purpose: Implements polygon assembly from a bunch of arcs.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 1999, Frank Warmerdam
|
|
* Copyright (c) 2009-2011, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* 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 <vector>
|
|
#include "ogr_geometry.h"
|
|
#include "ogr_api.h"
|
|
#include "cpl_conv.h"
|
|
|
|
CPL_CVSID("$Id: ograssemblepolygon.cpp 27044 2014-03-16 23:41:27Z rouault $");
|
|
|
|
/************************************************************************/
|
|
/* CheckPoints() */
|
|
/* */
|
|
/* Check if two points are closer than the current best */
|
|
/* distance. Update the current best distance if they are. */
|
|
/************************************************************************/
|
|
|
|
static int CheckPoints( OGRLineString *poLine1, int iPoint1,
|
|
OGRLineString *poLine2, int iPoint2,
|
|
double *pdfDistance )
|
|
|
|
{
|
|
double dfDeltaX, dfDeltaY, dfDistance;
|
|
|
|
if( pdfDistance == NULL || *pdfDistance == 0 )
|
|
return poLine1->getX(iPoint1) == poLine2->getX(iPoint2)
|
|
&& poLine1->getY(iPoint1) == poLine2->getY(iPoint2);
|
|
|
|
dfDeltaX = poLine1->getX(iPoint1) - poLine2->getX(iPoint2);
|
|
dfDeltaY = poLine1->getY(iPoint1) - poLine2->getY(iPoint2);
|
|
|
|
dfDeltaX = ABS(dfDeltaX);
|
|
dfDeltaY = ABS(dfDeltaY);
|
|
|
|
if( dfDeltaX > *pdfDistance || dfDeltaY > *pdfDistance )
|
|
return FALSE;
|
|
|
|
dfDistance = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY);
|
|
|
|
if( dfDistance < *pdfDistance )
|
|
{
|
|
*pdfDistance = dfDistance;
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* AddEdgeToRing() */
|
|
/************************************************************************/
|
|
|
|
static void AddEdgeToRing( OGRLinearRing * poRing, OGRLineString * poLine,
|
|
int bReverse )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Establish order and range of traverse. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iStart=0, iEnd=0, iStep=0;
|
|
int nVertToAdd = poLine->getNumPoints();
|
|
|
|
if( !bReverse )
|
|
{
|
|
iStart = 0;
|
|
iEnd = nVertToAdd - 1;
|
|
iStep = 1;
|
|
}
|
|
else
|
|
{
|
|
iStart = nVertToAdd - 1;
|
|
iEnd = 0;
|
|
iStep = -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we skip a repeating vertex? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poRing->getNumPoints() > 0
|
|
&& CheckPoints( poRing, poRing->getNumPoints()-1,
|
|
poLine, iStart, NULL ) )
|
|
{
|
|
iStart += iStep;
|
|
}
|
|
|
|
poRing->addSubLineString( poLine, iStart, iEnd );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OGRBuildPolygonFromEdges() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Build a ring from a bunch of arcs.
|
|
*
|
|
* @param hLines handle to an OGRGeometryCollection (or OGRMultiLineString) containing the line string geometries to be built into rings.
|
|
* @param bBestEffort not yet implemented???.
|
|
* @param bAutoClose indicates if the ring should be close when first and
|
|
* last points of the ring are the same.
|
|
* @param dfTolerance tolerance into which two arcs are considered
|
|
* close enough to be joined.
|
|
* @param peErr OGRERR_NONE on success, or OGRERR_FAILURE on failure.
|
|
* @return an handle to the new geometry, a polygon.
|
|
*
|
|
*/
|
|
|
|
OGRGeometryH OGRBuildPolygonFromEdges( OGRGeometryH hLines,
|
|
int bBestEffort,
|
|
int bAutoClose,
|
|
double dfTolerance,
|
|
OGRErr * peErr )
|
|
|
|
{
|
|
if( hLines == NULL )
|
|
{
|
|
if (peErr != NULL)
|
|
*peErr = OGRERR_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for the case of a geometrycollection that can be */
|
|
/* promoted to MultiLineString. */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRGeometry* poGeom = (OGRGeometry*) hLines;
|
|
if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection )
|
|
{
|
|
int iGeom;
|
|
OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
|
|
|
|
for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
|
|
{
|
|
if( wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType())
|
|
!= wkbLineString )
|
|
{
|
|
if (peErr != NULL)
|
|
*peErr = OGRERR_FAILURE;
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"The geometry collection contains non line string geometries");
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else if (wkbFlatten(poGeom->getGeometryType()) != wkbMultiLineString )
|
|
{
|
|
if (peErr != NULL)
|
|
*peErr = OGRERR_FAILURE;
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"The passed geometry is not an OGRGeometryCollection (or OGRMultiLineString) "
|
|
"containing line string geometries");
|
|
return NULL;
|
|
}
|
|
|
|
int bSuccess = TRUE;
|
|
OGRGeometryCollection *poLines = (OGRGeometryCollection *) hLines;
|
|
std::vector<OGRLinearRing*> aoRings;
|
|
|
|
(void) bBestEffort;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup array of line markers indicating if they have been */
|
|
/* added to a ring yet. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nEdges = poLines->getNumGeometries();
|
|
int *panEdgeConsumed, nRemainingEdges = nEdges;
|
|
|
|
panEdgeConsumed = (int *) CPLCalloc(sizeof(int),nEdges);
|
|
|
|
/* ==================================================================== */
|
|
/* Loop generating rings. */
|
|
/* ==================================================================== */
|
|
while( nRemainingEdges > 0 )
|
|
{
|
|
int iEdge;
|
|
OGRLineString *poLine;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Find the first unconsumed edge. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iEdge = 0; panEdgeConsumed[iEdge]; iEdge++ ) {}
|
|
|
|
poLine = (OGRLineString *) poLines->getGeometryRef(iEdge);
|
|
|
|
panEdgeConsumed[iEdge] = TRUE;
|
|
nRemainingEdges--;
|
|
|
|
if (poLine->getNumPoints() < 2)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Start a new ring, copying in the current line directly */
|
|
/* -------------------------------------------------------------------- */
|
|
OGRLinearRing *poRing = new OGRLinearRing();
|
|
|
|
AddEdgeToRing( poRing, poLine, FALSE );
|
|
|
|
/* ==================================================================== */
|
|
/* Loop adding edges to this ring until we make a whole pass */
|
|
/* within finding anything to add. */
|
|
/* ==================================================================== */
|
|
int bWorkDone = TRUE;
|
|
double dfBestDist = dfTolerance;
|
|
|
|
while( !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,NULL)
|
|
&& nRemainingEdges > 0
|
|
&& bWorkDone )
|
|
{
|
|
int iBestEdge = -1, bReverse = FALSE;
|
|
|
|
bWorkDone = FALSE;
|
|
dfBestDist = dfTolerance;
|
|
|
|
// We consider linking the end to the beginning. If this is
|
|
// closer than any other option we will just close the loop.
|
|
|
|
//CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,&dfBestDist);
|
|
|
|
// Find unused edge with end point closest to our loose end.
|
|
for( iEdge = 0; iEdge < nEdges; iEdge++ )
|
|
{
|
|
if( panEdgeConsumed[iEdge] )
|
|
continue;
|
|
|
|
poLine = (OGRLineString *) poLines->getGeometryRef(iEdge);
|
|
if (poLine->getNumPoints() < 2)
|
|
continue;
|
|
|
|
if( CheckPoints(poLine,0,poRing,poRing->getNumPoints()-1,
|
|
&dfBestDist) )
|
|
{
|
|
iBestEdge = iEdge;
|
|
bReverse = FALSE;
|
|
}
|
|
if( CheckPoints(poLine,poLine->getNumPoints()-1,
|
|
poRing,poRing->getNumPoints()-1,
|
|
&dfBestDist) )
|
|
{
|
|
iBestEdge = iEdge;
|
|
bReverse = TRUE;
|
|
}
|
|
|
|
// if we use exact comparison, jump now
|
|
if (dfTolerance == 0.0 && iBestEdge != -1) break;
|
|
}
|
|
|
|
// We found one within tolerance - add it.
|
|
if( iBestEdge != -1 )
|
|
{
|
|
poLine = (OGRLineString *)
|
|
poLines->getGeometryRef(iBestEdge);
|
|
|
|
AddEdgeToRing( poRing, poLine, bReverse );
|
|
|
|
panEdgeConsumed[iBestEdge] = TRUE;
|
|
nRemainingEdges--;
|
|
bWorkDone = TRUE;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Did we fail to complete the ring? */
|
|
/* -------------------------------------------------------------------- */
|
|
dfBestDist = dfTolerance;
|
|
|
|
if( !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,
|
|
&dfBestDist) )
|
|
{
|
|
CPLDebug( "OGR",
|
|
"Failed to close ring %d.\n"
|
|
"End Points are: (%.8f,%.7f) and (%.7f,%.7f)\n",
|
|
(int)aoRings.size(),
|
|
poRing->getX(0), poRing->getY(0),
|
|
poRing->getX(poRing->getNumPoints()-1),
|
|
poRing->getY(poRing->getNumPoints()-1) );
|
|
|
|
bSuccess = FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we need to auto-close this ring? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bAutoClose
|
|
&& !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,NULL) )
|
|
{
|
|
poRing->addPoint( poRing->getX(0),
|
|
poRing->getY(0),
|
|
poRing->getZ(0));
|
|
}
|
|
|
|
aoRings.push_back(poRing);
|
|
} /* next ring */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLFree( panEdgeConsumed );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify exterior ring - it will be the largest. #3610 */
|
|
/* -------------------------------------------------------------------- */
|
|
double maxarea = 0.0;
|
|
int maxring = -1, rn;
|
|
OGREnvelope tenv;
|
|
|
|
for (rn = 0; rn < (int)aoRings.size(); ++rn)
|
|
{
|
|
aoRings[rn]->getEnvelope(&tenv);
|
|
double tarea = (tenv.MaxX - tenv.MinX) * (tenv.MaxY - tenv.MinY);
|
|
if (tarea > maxarea)
|
|
{
|
|
maxarea = tarea;
|
|
maxring = rn;
|
|
}
|
|
}
|
|
|
|
OGRPolygon *poPolygon = new OGRPolygon();
|
|
|
|
if (maxring != -1)
|
|
{
|
|
poPolygon->addRingDirectly(aoRings[maxring]);
|
|
for (rn = 0; rn < (int)aoRings.size(); ++rn)
|
|
{
|
|
if (rn == maxring) continue;
|
|
poPolygon->addRingDirectly(aoRings[rn]);
|
|
}
|
|
}
|
|
|
|
if( peErr != NULL )
|
|
{
|
|
if( bSuccess )
|
|
*peErr = OGRERR_NONE;
|
|
else
|
|
*peErr = OGRERR_FAILURE;
|
|
}
|
|
|
|
return (OGRGeometryH) poPolygon;
|
|
}
|
|
|
|
|
|
|