[geos-commits] r2628 - in trunk: source/headers/geos/operation/buffer source/operation/buffer tests/xmltester

svn_geos at osgeo.org svn_geos at osgeo.org
Sun Sep 27 16:43:06 EDT 2009


Author: strk
Date: 2009-09-27 16:43:03 -0400 (Sun, 27 Sep 2009)
New Revision: 2628

Modified:
   trunk/source/headers/geos/operation/buffer/BufferBuilder.h
   trunk/source/headers/geos/operation/buffer/OffsetCurveBuilder.h
   trunk/source/operation/buffer/BufferBuilder.cpp
   trunk/source/operation/buffer/OffsetCurveBuilder.cpp
   trunk/source/operation/buffer/OffsetCurveVertexList.h
   trunk/tests/xmltester/XMLTester.cpp
Log:
Re-introduce the singlesided buffer patch. Tests still need to be worked on.


Modified: trunk/source/headers/geos/operation/buffer/BufferBuilder.h
===================================================================
--- trunk/source/headers/geos/operation/buffer/BufferBuilder.h	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/source/headers/geos/operation/buffer/BufferBuilder.h	2009-09-27 20:43:03 UTC (rev 2628)
@@ -206,6 +206,11 @@
 	geom::Geometry* buffer(const geom::Geometry *g, double distance);
 		// throw (GEOSException);
 
+	/// Not in JTS: this is a GEOS extension
+	geom::Geometry* bufferLineSingleSided( const geom::Geometry* g,
+	                                double distance, bool leftSide ) ;
+		// throw (GEOSException);
+
 };
 
 } // namespace geos::operation::buffer

Modified: trunk/source/headers/geos/operation/buffer/OffsetCurveBuilder.h
===================================================================
--- trunk/source/headers/geos/operation/buffer/OffsetCurveBuilder.h	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/source/headers/geos/operation/buffer/OffsetCurveBuilder.h	2009-09-27 20:43:03 UTC (rev 2628)
@@ -91,6 +91,25 @@
 	                  std::vector<geom::CoordinateSequence*>& lineList);
 
 	/**
+	 * This method handles single points as well as lines.
+	 *
+	 * Lines are assumed to <b>not</b> be closed (the function will not
+	 * fail for closed lines, but will generate superfluous line caps).
+	 *
+	 * @param lineList the std::vector to which CoordinateSequences will
+	 *                 be pushed_back
+	 * @param leftSide indicates that the left side buffer will be
+	 *                 obtained/skipped
+	 * @param rightSide indicates that the right side buffer will
+	 *                  be obtained/skipped
+	 *
+	 * NOTE: this is a GEOS extension
+	 */
+	void getSingleSidedLineCurve(const geom::CoordinateSequence* inputPts, 
+	     double distance, std::vector<geom::CoordinateSequence*>& lineList,
+	     bool leftSide, bool rightSide ) ;
+
+	/**
 	 * This method handles the degenerate cases of single points and lines,
 	 * as well as rings.
 	 *
@@ -229,6 +248,9 @@
 
 	int side;
 
+	// Not in JTS, used for single-sided buffers
+	int endCapIndex;
+
 	void init(double newDistance);
 
 	/**

Modified: trunk/source/operation/buffer/BufferBuilder.cpp
===================================================================
--- trunk/source/operation/buffer/BufferBuilder.cpp	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/source/operation/buffer/BufferBuilder.cpp	2009-09-27 20:43:03 UTC (rev 2628)
@@ -26,11 +26,15 @@
 #include <geos/geom/Geometry.h>
 #include <geos/geom/Polygon.h>
 #include <geos/geom/GeometryCollection.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/MultiLineString.h>
 #include <geos/operation/buffer/BufferBuilder.h>
 #include <geos/operation/buffer/OffsetCurveBuilder.h>
 #include <geos/operation/buffer/OffsetCurveSetBuilder.h>
 #include <geos/operation/buffer/BufferSubgraph.h>
 #include <geos/operation/buffer/SubgraphDepthLocater.h>
+#include <geos/operation/overlay/OverlayOp.h>
+#include <geos/operation/linemerge/LineMerger.h>
 #include <geos/algorithm/LineIntersector.h>
 #include <geos/noding/IntersectionAdder.h>
 #include <geos/noding/SegmentString.h>
@@ -64,6 +68,7 @@
 using namespace geos::noding;
 using namespace geos::algorithm;
 using namespace geos::operation::overlay;
+using namespace geos::operation::linemerge;
 
 namespace geos {
 namespace operation { // geos.operation
@@ -102,6 +107,166 @@
 
 /*public*/
 Geometry*
+BufferBuilder::bufferLineSingleSided( const Geometry* g, double distance,
+                                      bool leftSide )
+{
+   // Returns the line used to create a single-sided buffer.
+   // Input requirement: Must be a LineString.
+   const LineString* l = dynamic_cast< const LineString* >( g );
+   if ( !l ) return NULL;
+
+   // Get geometry factory and precision model.
+   const PrecisionModel* precisionModel = workingPrecisionModel;
+   if ( !precisionModel ) precisionModel = l->getPrecisionModel();
+
+   assert( precisionModel );
+   assert( l );
+
+   geomFact = l->getFactory();
+
+   // First, generate the two-sided buffer using a butt-cap.
+   BufferParameters modParams = bufParams;
+   modParams.setEndCapStyle(BufferParameters::CAP_FLAT); 
+   Geometry* buf = buffer( l, distance );
+
+   // Create MultiLineStrings from this polygon.
+   Geometry* bufLineString = buf->getBoundary();
+
+   // Then, get the raw (i.e. unnoded) single sided offset curve.
+   OffsetCurveBuilder curveBuilder( precisionModel, modParams );
+   std::vector< CoordinateSequence* > lineList;
+   curveBuilder.getSingleSidedLineCurve( g->getCoordinates(), distance,
+                                         lineList, leftSide, !leftSide );
+
+   // Create a SegmentString from these coordinates.
+   SegmentString::NonConstVect curveList;
+   for ( unsigned int i = 0; i < lineList.size(); ++i )
+   {
+      CoordinateSequence* seq = lineList[i];
+      curveList.push_back( new NodedSegmentString( seq, NULL ) );
+   }
+
+   // Node these SegmentStrings.
+   Noder* noder = getNoder( precisionModel );
+   noder->computeNodes( &curveList );
+   SegmentString::NonConstVect* nodedEdges = noder->getNodedSubstrings();
+
+   // Create a geometry out of the noded substrings.
+   std::vector< Geometry* >* singleSidedNodedEdges =
+      new std::vector< Geometry * >();
+   for ( unsigned int i = 0; i < nodedEdges->size(); ++i )
+   {
+      singleSidedNodedEdges->push_back( geomFact->createLineString(
+         ( *nodedEdges )[i]->getCoordinates() ) );
+   }
+   Geometry* singleSided = geomFact->createMultiLineString(
+      singleSidedNodedEdges );
+
+   // Use the boolean operation intersect to obtain the line segments lying
+   // on both the butt-cap buffer and this multi-line.
+   Geometry* intersectedLines = singleSided->intersection( bufLineString );
+
+   // Merge result lines together.
+   LineMerger lineMerge;
+   lineMerge.add( intersectedLines );
+   std::vector< LineString* >* mergedLines = lineMerge.getMergedLineStrings();
+
+   // Convert the result into a std::vector< Geometry* >.
+   std::vector< Geometry* >* mergedLinesGeom = new std::vector< Geometry* >();
+   const Coordinate& startPoint = l->getCoordinatesRO()->front();
+   const Coordinate& endPoint = l->getCoordinatesRO()->back();
+   while( !mergedLines->empty() )
+   {
+      // Remove end points if they are a part of the original line to be
+      // buffered.
+      CoordinateSequence::AutoPtr coords(mergedLines->back()->getCoordinates());
+      if ( NULL != coords.get() )
+      {
+         // Use 98% of the buffer width as the point-distance requirement - this
+         // is to ensure that the point that is "distance" +/- epsilon is not
+         // included.
+         const double ptDistAllowance = 0.98 * distance;
+         // Use 102% of the buffer width as the line-length requirement - this
+         // is to ensure that line segments that is length "distance" +/-
+         // epsilon is removed.
+         const double segLengthAllowance = 1.02 * distance;
+
+         // Clean up the front of the list.
+         // Loop until the line's end is not inside the buffer width from
+         // the startPoint.
+         while ( coords->size() > 1 && 
+                 coords->front().distance( startPoint ) < ptDistAllowance )
+         {
+            // Record the end segment length.
+            double segLength = coords->front().distance( ( *coords )[1] );
+            // Stop looping if there are no more points, or if the segment
+            // length is larger than the buffer width.
+            if ( coords->size() <= 1 || segLength > segLengthAllowance )
+            {
+               break;
+            }
+            // If the first point is less than buffer width away from the
+            // reference point, then delete the point.
+            coords->deleteAt( 0 );
+         }
+         while ( coords->size() > 1 && 
+                 coords->front().distance( endPoint ) < ptDistAllowance )
+         {
+            double segLength = coords->front().distance( ( *coords )[1] );
+            if ( coords->size() <= 1 || segLength > segLengthAllowance )
+            {
+               break;
+            }
+            coords->deleteAt( 0 );
+         }
+
+         // Clean up the back of the list.
+         while ( coords->size() > 1 &&
+                 coords->back().distance( startPoint ) < ptDistAllowance )
+         {
+            double segLength = coords->back().distance(
+               ( *coords )[coords->size()-2] );
+            if ( coords->size() <= 1 || segLength > segLengthAllowance )
+            {
+               break;
+            }
+            coords->deleteAt( coords->size()-1 );
+         }
+         while ( coords->size() > 1 &&
+                 coords->back().distance( endPoint ) < ptDistAllowance )
+         {
+            double segLength = coords->back().distance(
+               ( *coords )[coords->size()-2] );
+            if ( coords->size() <= 1 || segLength > segLengthAllowance )
+            {
+               break;
+            }
+            coords->deleteAt( coords->size()-1 );
+         }
+
+         // Add the coordinates to the resultant line string.
+         if ( coords->size() > 1 )
+         {
+            mergedLinesGeom->push_back( geomFact->createLineString( coords.release() ) );
+         }
+      }
+
+      geomFact->destroyGeometry( mergedLines->back() );
+      mergedLines->pop_back();
+   }
+
+   // Clean up.
+   if ( noder != workingNoder ) delete noder;
+   geomFact->destroyGeometry( buf );
+   geomFact->destroyGeometry( bufLineString );
+   geomFact->destroyGeometry( singleSided );
+   geomFact->destroyGeometry( intersectedLines );
+
+   return geomFact->createMultiLineString( mergedLinesGeom );
+}
+
+/*public*/
+Geometry*
 BufferBuilder::buffer(const Geometry *g, double distance)
 	// throw(GEOSException *)
 {

Modified: trunk/source/operation/buffer/OffsetCurveBuilder.cpp
===================================================================
--- trunk/source/operation/buffer/OffsetCurveBuilder.cpp	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/source/operation/buffer/OffsetCurveBuilder.cpp	2009-09-27 20:43:03 UTC (rev 2628)
@@ -82,6 +82,7 @@
 		offset0(),
 		offset1(),
 		side(0),
+		endCapIndex(0),
 		vertexLists()
 {
 	// compute intersections in full precision, to provide accuracy
@@ -144,6 +145,62 @@
 
 /*public*/
 void
+OffsetCurveBuilder::getSingleSidedLineCurve(const CoordinateSequence* inputPts, 
+   double distance, vector<CoordinateSequence*>& lineList, bool leftSide,
+   bool rightSide)
+{
+   // A zero or negative width buffer of a line/point is empty.
+   if ( distance <= 0.0 ) return ;
+
+   init( distance ) ;
+
+   if ( inputPts->getSize() < 2 )
+   {
+      // No cap, so just return.
+      return ;
+   }
+   else
+   {
+      computeLineBufferCurve( *inputPts ) ;
+   }
+
+   // NOTE: we take ownership of lineCoord here ...
+   CoordinateSequence* lineCoord = vertexList->getCoordinates() ;
+
+   // [swong] April 24, 2008
+   // Left side:  index [n-2] to [endCapIndex]
+   // Right side: index [endCapIndex] to [n-2]
+   // Where n is the last index (size-1).
+   int n = lineCoord->size() - 1 ;
+
+   // Add the left side curve to the line list.
+   if ( leftSide )
+   {
+      CoordinateArraySequence* coordSeq = new CoordinateArraySequence() ;
+      coordSeq->add( ( *lineCoord )[n-2] ) ;
+      coordSeq->add( ( *lineCoord )[n-1] ) ;
+      for ( int i = 0 ; i <= endCapIndex ; ++i )
+      {
+         coordSeq->add( ( *lineCoord )[i] ) ;
+      }
+      lineList.push_back( coordSeq ) ;
+   }
+
+   // Add the right side curve to the line list.
+   if ( rightSide )
+   {
+      CoordinateArraySequence* coordSeq = new CoordinateArraySequence() ;
+      for ( int i = endCapIndex ; i <= n-2 ; ++i )
+      {
+         coordSeq->add( ( *lineCoord )[i] ) ;
+      }
+
+      lineList.push_back( coordSeq ) ;
+   }
+}
+
+/*public*/
+void
 OffsetCurveBuilder::getRingCurve(const CoordinateSequence *inputPts,
 		int side, double distance,
 		vector<CoordinateSequence*>& lineList)
@@ -226,6 +283,10 @@
 	// add line cap for end of line
 	addLineEndCap(simp1[n1-1], simp1[n1]);
 
+	// Record the index of the end of line cap.
+	endCapIndex = vertexList->size() - 2 ;
+
+
 	//---------- compute points for right side of line
 #ifndef SKIP_INPUT_SIMPLIFICATION
 	// Simplify the appropriate side of the line before generating

Modified: trunk/source/operation/buffer/OffsetCurveVertexList.h
===================================================================
--- trunk/source/operation/buffer/OffsetCurveVertexList.h	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/source/operation/buffer/OffsetCurveVertexList.h	2009-09-27 20:43:03 UTC (rev 2628)
@@ -157,6 +157,8 @@
 		return ret;
 	}
 
+	inline int size() const { return ptList ? ptList->size() : 0 ; }
+
 };
 
 std::ostream& operator<< (std::ostream& os, const OffsetCurveVertexList& lst)

Modified: trunk/tests/xmltester/XMLTester.cpp
===================================================================
--- trunk/tests/xmltester/XMLTester.cpp	2009-09-21 19:47:45 UTC (rev 2627)
+++ trunk/tests/xmltester/XMLTester.cpp	2009-09-27 20:43:03 UTC (rev 2628)
@@ -852,9 +852,6 @@
 			expected_result=printGeom(gRes.get());
 		}
 
-// disabled till we have an actual api exposed for that
-// (and probably a different name)
-#if 0
 		else if (opName=="buffersinglesided")
 		{
 			using namespace operation::buffer;
@@ -921,7 +918,6 @@
 			actual_result=printGeom(gRealRes.get());
 			expected_result=printGeom(gRes.get());
 		}
-#endif
 
 		else if (opName=="buffermitredjoin")
 		{



More information about the geos-commits mailing list