int SDTSRawPolygon::AssembleRings() { int iEdge; int bSuccess = TRUE; if( nRings > 0 ) return TRUE; if( nEdges == 0 ) return FALSE; /* -------------------------------------------------------------------- */ /* Allocate ring arrays. */ /* -------------------------------------------------------------------- */ panRingStart = (int *) CPLMalloc(sizeof(int) * nEdges); nVertices = 0; for( iEdge = 0; iEdge < nEdges; iEdge++ ) { nVertices += papoEdges[iEdge]->nVertices; } padfX = (double *) CPLMalloc(sizeof(double) * nVertices); padfY = (double *) CPLMalloc(sizeof(double) * nVertices); padfZ = (double *) CPLMalloc(sizeof(double) * nVertices); nVertices = 0; /* -------------------------------------------------------------------- */ /* Setup array of line markers indicating if they have been */ /* added to a ring yet. */ /* -------------------------------------------------------------------- */ int *panEdgeConsumed, nRemainingEdges = nEdges; panEdgeConsumed = (int *) CPLCalloc(sizeof(int),nEdges); /* ==================================================================== */ /* Loop generating rings. */ /* ==================================================================== */ while( nRemainingEdges > 0 ) { int nStartNode, nLinkNode; /* -------------------------------------------------------------------- */ /* Find the first unconsumed edge. */ /* -------------------------------------------------------------------- */ SDTSRawLine *poEdge; for( iEdge = 0; panEdgeConsumed[iEdge]; iEdge++ ) {} poEdge = papoEdges[iEdge]; /* -------------------------------------------------------------------- */ /* Start a new ring, copying in the current line directly */ /* -------------------------------------------------------------------- */ panRingStart[nRings++] = nVertices; AddEdgeToRing( poEdge->nVertices, poEdge->padfX, poEdge->padfY, poEdge->padfZ, FALSE, FALSE ); panEdgeConsumed[iEdge] = TRUE; nRemainingEdges--; nStartNode = poEdge->oStartNode.nRecord; nLinkNode = poEdge->oEndNode.nRecord; /* ==================================================================== */ /* Loop adding edges to this ring until we make a whole pass */ /* within finding anything to add. */ /* ==================================================================== */ int bWorkDone = TRUE; while( nLinkNode != nStartNode && nRemainingEdges > 0 && bWorkDone ) { bWorkDone = FALSE; for( iEdge = 0; iEdge < nEdges; iEdge++ ) { if( panEdgeConsumed[iEdge] ) continue; poEdge = papoEdges[iEdge]; if( poEdge->oStartNode.nRecord == nLinkNode ) { AddEdgeToRing( poEdge->nVertices, poEdge->padfX, poEdge->padfY, poEdge->padfZ, FALSE, TRUE ); nLinkNode = poEdge->oEndNode.nRecord; } else if( poEdge->oEndNode.nRecord == nLinkNode ) { AddEdgeToRing( poEdge->nVertices, poEdge->padfX, poEdge->padfY, poEdge->padfZ, TRUE, TRUE ); nLinkNode = poEdge->oStartNode.nRecord; } else { continue; } panEdgeConsumed[iEdge] = TRUE; nRemainingEdges--; bWorkDone = TRUE; } } /* -------------------------------------------------------------------- */ /* Did we fail to complete the ring? */ /* -------------------------------------------------------------------- */ if( nLinkNode != nStartNode ) bSuccess = FALSE; } /* next ring */ CPLFree( panEdgeConsumed ); if( !bSuccess ) return bSuccess; /* ==================================================================== */ /* Compute the area of each ring. The sign will be positive */ /* for counter clockwise rings, otherwise negative. */ /* */ /* The algorithm used in this function was taken from _Graphics */ /* Gems II_, James Arvo, 1991, Academic Press, Inc., section 1.1, */ /* "The Area of a Simple Polygon", Jon Rokne, pp. 5-6. */ /* ==================================================================== */ double *padfRingArea, dfMaxArea = 0.0; int iRing, iBiggestRing = -1; padfRingArea = (double *) CPLCalloc(sizeof(double),nRings); for( iRing = 0; iRing < nRings; iRing++ ) { double dfSum1 = 0.0, dfSum2 = 0.0; int i, nRingVertices; if( iRing == nRings - 1 ) nRingVertices = nVertices - panRingStart[iRing]; else nRingVertices = panRingStart[iRing+1] - panRingStart[iRing]; for( i = panRingStart[iRing]; i < panRingStart[iRing] + nRingVertices - 1; i++) { dfSum1 += padfX[i] * padfY[i+1]; dfSum2 += padfY[i] * padfX[i+1]; } padfRingArea[iRing] = (dfSum1 - dfSum2) / 2; if( ABS(padfRingArea[iRing]) > dfMaxArea ) { dfMaxArea = ABS(padfRingArea[iRing]); iBiggestRing = iRing; } } /* ==================================================================== */ /* Make a new set of vertices, and copy the largest ring into */ /* it, adjusting the direction if necessary to ensure that this */ /* outer ring is counter clockwise. */ /* ==================================================================== */ double *padfXRaw = padfX; double *padfYRaw = padfY; double *padfZRaw = padfZ; int *panRawRingStart = panRingStart; int nRawVertices = nVertices; int nRawRings = nRings; int nRingVertices; padfX = (double *) CPLMalloc(sizeof(double) * nVertices); padfY = (double *) CPLMalloc(sizeof(double) * nVertices); padfZ = (double *) CPLMalloc(sizeof(double) * nVertices); panRingStart = (int *) CPLMalloc(sizeof(int) * nRawRings); nVertices = 0; nRings = 0; if( iBiggestRing == nRawRings - 1 ) nRingVertices = nRawVertices - panRawRingStart[iBiggestRing]; else nRingVertices = panRawRingStart[iBiggestRing+1] - panRawRingStart[iBiggestRing]; panRingStart[nRings++] = 0; AddEdgeToRing( nRingVertices, padfXRaw + panRawRingStart[iBiggestRing], padfYRaw + panRawRingStart[iBiggestRing], padfZRaw + panRawRingStart[iBiggestRing], padfRingArea[iBiggestRing] < 0.0, FALSE ); /* ==================================================================== */ /* Add the rest of the rings, which must be holes, in clockwise */ /* order. */ /* ==================================================================== */ for( iRing = 0; iRing < nRawRings; iRing++ ) { if( iRing == iBiggestRing ) continue; if( iRing == nRawRings - 1 ) nRingVertices = nRawVertices - panRawRingStart[iRing]; else nRingVertices = panRawRingStart[iRing+1] - panRawRingStart[iRing]; panRingStart[nRings++] = nVertices; AddEdgeToRing( nRingVertices, padfXRaw + panRawRingStart[iRing], padfYRaw + panRawRingStart[iRing], padfZRaw + panRawRingStart[iRing], padfRingArea[iRing] > 0.0, FALSE ); } /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ CPLFree( padfXRaw ); CPLFree( padfYRaw ); CPLFree( padfZRaw ); CPLFree( padfRingArea ); CPLFree( panRawRingStart ); CPLFree( papoEdges ); papoEdges = NULL; nEdges = 0; return TRUE; }
OGRGeometryH OGRBuildPolygonFromEdges( OGRGeometryH hLines, int bBestEffort, int bAutoClose, double dfTolerance, OGRErr * peErr ) { int bSuccess = TRUE; OGRGeometryCollection *poLines = (OGRGeometryCollection *) hLines; OGRPolygon *poPolygon = new OGRPolygon(); (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); /* -------------------------------------------------------------------- */ /* Start a new ring, copying in the current line directly */ /* -------------------------------------------------------------------- */ OGRLinearRing *poRing = new OGRLinearRing(); AddEdgeToRing( poRing, poLine, FALSE ); panEdgeConsumed[iEdge] = TRUE; nRemainingEdges--; /* ==================================================================== */ /* 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( 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; } } // 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", poPolygon->getNumInteriorRings()+1, 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)); } poPolygon->addRingDirectly( poRing ); } /* next ring */ /* -------------------------------------------------------------------- */ /* Cleanup. */ /* -------------------------------------------------------------------- */ CPLFree( panEdgeConsumed ); // Eventually we should at least identify the external ring properly, // perhaps even ordering the direction of rings, though this isn't // required by the OGC geometry model. if( peErr != NULL ) { if( bSuccess ) *peErr = OGRERR_NONE; else *peErr = OGRERR_FAILURE; } return (OGRGeometryH) poPolygon; }