void object::test<1>() { using geos::operation::buffer::BufferBuilder; using geos::operation::buffer::BufferParameters; using geos::algorithm::CGAlgorithms; using geos::geom::LineString; // Original input from test in ticket #633 //std::string wkt0("LINESTRING (" // "665.7317504882812500 133.0762634277343700," // "1774.4752197265625000 19.9391822814941410," // "756.2413940429687500 466.8306579589843700," // "626.1337890625000000 1898.0147705078125000," // "433.8007202148437500 404.6052856445312500)"); //double const distance = 57.164000837203; // Simplified equivalent input std::string wkt0("LINESTRING(0 0, 50 -10, 10 10, 0 50, -10 10)"); double const distance = 5; GeomPtr g0(wktreader.read(wkt0)); ensure(0 != g0.get()); ensure_equals(g0->getNumPoints(), std::size_t(5)); BufferParameters params; params.setEndCapStyle(BufferParameters::CAP_FLAT); params.setQuadrantSegments(8); params.setJoinStyle(BufferParameters::JOIN_MITRE); params.setMitreLimit(5.57F); //params.setSingleSided(true); // DO NOT switch for non-areal input, see ticket #633 BufferBuilder builder(params); ensure(distance > 0); // left-side { GeomPtr gB(builder.bufferLineSingleSided(g0.get(), distance, true)); ensure(0 != gB.get()); ensure_equals(gB->getGeometryTypeId(), geos::geom::GEOS_LINESTRING); // Left-side offset curve expected with 5+ vertices ensure(gB->getNumPoints() >= g0->getNumPoints()); // For left-side offset curve, the offset will be at the left side of the input line // and retain the same direction. ensure_equals( CGAlgorithms::isCCW(dynamic_cast<LineString*>(g0.get())->getCoordinatesRO()), CGAlgorithms::isCCW(dynamic_cast<LineString*>(gB.get())->getCoordinatesRO())); } // right-side { GeomPtr gB(builder.bufferLineSingleSided(g0.get(), distance, false)); ensure(0 != gB.get()); ensure_equals(gB->getGeometryTypeId(), geos::geom::GEOS_LINESTRING); // Right-side offset curve expected with 5+ vertices ensure(gB->getNumPoints() >= g0->getNumPoints()); // For right-side offset curve, it'll be at the right side // and in the opposite direction. ensure_equals( CGAlgorithms::isCCW(dynamic_cast<LineString*>(g0.get())->getCoordinatesRO()), !CGAlgorithms::isCCW(dynamic_cast<LineString*>(gB.get())->getCoordinatesRO())); } }
void XMLTester::parseTest(const TiXmlNode* node) { using namespace operation::overlay; typedef std::auto_ptr< geom::Geometry > GeomAutoPtr; int success=0; // no success by default std::string opName; std::string opArg1; std::string opArg2; std::string opArg3; std::string opArg4; std::string opRes; ++testCount; const TiXmlNode* opnode = node->FirstChild("op"); if ( ! opnode ) throw(runtime_error("case has no op")); //dump_to_stdout(opnode); const TiXmlElement* opel = opnode->ToElement(); const char* tmp = opel->Attribute("name"); if ( tmp ) opName = tmp; tmp = opel->Attribute("arg1"); if ( tmp ) opArg1 = tmp; tmp = opel->Attribute("arg2"); if ( tmp ) opArg2 = tmp; tmp = opel->Attribute("arg3"); if ( tmp ) opArg3 = tmp; tmp = opel->Attribute("arg4"); if ( tmp ) opArg4 = tmp; const TiXmlNode* resnode = opnode->FirstChild(); if ( ! resnode ) { std::stringstream tmp; tmp << "op of test " << testCount << " of case " << caseCount << " has no expected result child"; throw(runtime_error(tmp.str())); } opRes = resnode->Value(); // trim blanks opRes=trimBlanks(opRes); opName=trimBlanks(opName); tolower(opName); std::string opSig=""; if ( opArg1 != "" ) opSig=opArg1; if ( opArg2 != "" ) { if ( opSig != "" ) opSig += ", "; opSig += opArg2; } if ( opArg3 != "" ) { if ( opSig != "" ) opSig += ", "; opSig += opArg3; } if ( opArg4 != "" ) { if ( opSig != "" ) opSig += ", "; opSig += opArg4; } opSignature = opName + "(" + opSig + ")"; std::string actual_result="NONE"; // expected_result will be modified by specific tests // if needed (geometry normalization, for example) std::string expected_result=opRes; try { util::Profile profile("op"); if (opName=="relate") { std::auto_ptr<geom::IntersectionMatrix> im(gA->relate(gB)); assert(im.get()); if (im->matches(opArg3)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="isvalid") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) { gT=gB; } if (gT->isValid()) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="intersection") { GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); #ifndef USE_BINARYOP GeomAutoPtr gRealRes(gA->intersection(gB)); #else GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opINTERSECTION)); #endif gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="union") { GeomAutoPtr gRes(parseGeometry(opRes, "expected")); GeomAutoPtr gRealRes; if ( gB ) { #ifndef USE_BINARYOP gRealRes.reset(gA->Union(gB)); #else gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opUNION)); #endif } else { gRealRes = gA->Union(); } if (gRes->equals(gRealRes.get())) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="difference") { GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); #ifndef USE_BINARYOP GeomAutoPtr gRealRes(gA->difference(gB)); #else GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opDIFFERENCE)); #endif gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="symdifference") { GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); #ifndef USE_BINARYOP GeomAutoPtr gRealRes(gA->symDifference(gB)); #else GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opSYMDIFFERENCE)); #endif gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="intersects") { geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; if (g1->intersects(g2)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="contains") { geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; if (g1->contains(g2)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="within") { geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; if (g1->within(g2)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="covers") { geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; if (g1->covers(g2)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="coveredby") { geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; if (g1->coveredBy(g2)) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="getboundary") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); GeomAutoPtr gRealRes(gT->getBoundary()); gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="getcentroid") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); GeomAutoPtr gRealRes(gT->getCentroid()); if ( gRealRes.get() ) gRealRes->normalize(); else gRealRes.reset(factory->createPoint()); gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="issimple") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; if (gT->isSimple()) actual_result="true"; else actual_result="false"; if (actual_result==opRes) success=1; } else if (opName=="convexhull") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); GeomAutoPtr gRealRes(gT->convexHull()); gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="buffer") { using namespace operation::buffer; geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); profile.start(); GeomAutoPtr gRealRes; double dist = std::atof(opArg2.c_str()); BufferParameters params; if ( opArg3 != "" ) { params.setQuadrantSegments(std::atoi(opArg3.c_str())); } BufferOp op(gT, params); gRealRes.reset(op.getResultGeometry(dist)); profile.stop(); gRealRes->normalize(); // Validate the buffer operation success = checkBufferSuccess(*gRes, *gRealRes, dist); actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="buffersinglesided") { using namespace operation::buffer; geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); profile.start(); GeomAutoPtr gRealRes; double dist = std::atof(opArg2.c_str()); BufferParameters params ; params.setJoinStyle( BufferParameters::JOIN_ROUND ) ; if ( opArg3 != "" ) { params.setQuadrantSegments( std::atoi(opArg3.c_str())); } bool leftSide = true ; if ( opArg4 == "right" ) { leftSide = false ; } BufferBuilder bufBuilder( params ) ; gRealRes.reset( bufBuilder.bufferLineSingleSided( gT, dist, leftSide ) ) ; profile.stop(); gRealRes->normalize(); // Validate the single sided buffer operation success = checkSingleSidedBufferSuccess(*gRes, *gRealRes, dist); actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="buffermitredjoin") { using namespace operation::buffer; geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); profile.start(); GeomAutoPtr gRealRes; double dist = std::atof(opArg2.c_str()); BufferParameters params; params.setJoinStyle(BufferParameters::JOIN_MITRE); if ( opArg3 != "" ) { params.setQuadrantSegments(std::atoi(opArg3.c_str())); } BufferOp op(gT, params); gRealRes.reset(op.getResultGeometry(dist)); profile.stop(); gRealRes->normalize(); // Validate the buffer operation success = checkBufferSuccess(*gRes, *gRealRes, dist); actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="getinteriorpoint") { geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; GeomAutoPtr gRes(parseGeometry(opRes, "expected")); gRes->normalize(); GeomAutoPtr gRealRes(gT->getInteriorPoint()); if ( gRealRes.get() ) gRealRes->normalize(); else gRealRes.reset(factory->createPoint()); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="iswithindistance") { double dist=std::atof(opArg3.c_str()); if (gA->isWithinDistance(gB, dist)) { actual_result="true"; } else { actual_result="false"; } if (actual_result==opRes) success=1; } else if (opName=="polygonize") { GeomAutoPtr gRes(wktreader->read(opRes)); gRes->normalize(); Polygonizer plgnzr; plgnzr.add(gA); std::vector<geos::geom::Polygon *>*polys = plgnzr.getPolygons(); std::vector<geom::Geometry *>*newgeoms = new std::vector<geom::Geometry *>; for (unsigned int i=0; i<polys->size(); i++) newgeoms->push_back((*polys)[i]); delete polys; GeomAutoPtr gRealRes(factory->createGeometryCollection(newgeoms)); gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="linemerge") { GeomAutoPtr gRes(wktreader->read(opRes)); gRes->normalize(); geom::Geometry *gT=gA; if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB; LineMerger merger; merger.add(gT); std::auto_ptr< std::vector<geom::LineString *> > lines ( merger.getMergedLineStrings() ); std::vector<geom::Geometry *>*newgeoms = new std::vector<geom::Geometry *>(lines->begin(), lines->end()); GeomAutoPtr gRealRes(factory->createGeometryCollection(newgeoms)); gRealRes->normalize(); if (gRes->compareTo(gRealRes.get())==0) success=1; actual_result=printGeom(gRealRes.get()); expected_result=printGeom(gRes.get()); if ( testValidOutput ) success &= int(testValid(gRealRes.get(), "result")); } else if (opName=="areatest") { char* rest; double toleratedDiff = std::strtod(opRes.c_str(), &rest); int validOut = 1; if ( rest == opRes.c_str() ) { throw std::runtime_error("malformed testcase: missing tolerated area difference in 'areatest' op"); } if ( verbose > 1 ) { std::cerr << "Running intersection for areatest" << std::endl; } #ifndef USE_BINARYOP GeomAutoPtr gI(gA->intersection(gB)); #else GeomAutoPtr gI = BinaryOp(gA, gB, overlayOp(OverlayOp::opINTERSECTION)); #endif if ( testValidOutput ) { validOut &= int(testValid(gI.get(), "areatest intersection")); } if ( verbose > 1 ) { std::cerr << "Running difference(A,B) for areatest" << std::endl; } #ifndef USE_BINARYOP GeomAutoPtr gDab(gA->difference(gB)); #else GeomAutoPtr gDab = BinaryOp(gA, gB, overlayOp(OverlayOp::opDIFFERENCE)); #endif if ( testValidOutput ) { validOut &= int(testValid(gI.get(), "areatest difference(a,b)")); } if ( verbose > 1 ) { std::cerr << "Running difference(B,A) for areatest" << std::endl; } #ifndef USE_BINARYOP GeomAutoPtr gDba(gB->difference(gA)); #else GeomAutoPtr gDba = BinaryOp(gB, gA, overlayOp(OverlayOp::opDIFFERENCE)); #endif if ( testValidOutput ) { validOut &= int(testValid(gI.get(), "areatest difference(b,a)")); } if ( verbose > 1 ) { std::cerr << "Running symdifference for areatest" << std::endl; } #ifndef USE_BINARYOP GeomAutoPtr gSD(gA->symDifference(gB)); #else GeomAutoPtr gSD = BinaryOp(gA, gB, overlayOp(OverlayOp::opSYMDIFFERENCE)); #endif if ( testValidOutput ) { validOut &= int(testValid(gI.get(), "areatest symdifference")); } if ( verbose > 1 ) { std::cerr << "Running union for areatest" << std::endl; } #ifndef USE_BINARYOP GeomAutoPtr gU(gA->Union(gB)); #else GeomAutoPtr gU = BinaryOp(gA, gB, overlayOp(OverlayOp::opUNION)); #endif double areaA = gA->getArea(); double areaB = gB->getArea(); double areaI = gI->getArea(); double areaDab = gDab->getArea(); double areaDba = gDba->getArea(); double areaSD = gSD->getArea(); double areaU = gU->getArea(); double maxdiff = 0; std::string maxdiffop; // @ : symdifference // - : difference // + : union // ^ : intersection // A == ( A ^ B ) + ( A - B ) double diff = std::fabs ( areaA - areaI - areaDab ); if ( diff > maxdiff ) { maxdiffop = "A == ( A ^ B ) + ( A - B )"; maxdiff = diff; } // B == ( A ^ B ) + ( B - A ) diff = std::fabs ( areaB - areaI - areaDba ); if ( diff > maxdiff ) { maxdiffop = "B == ( A ^ B ) + ( B - A )"; maxdiff = diff; } // ( A @ B ) == ( A - B ) + ( B - A ) diff = std::fabs ( areaDab + areaDba - areaSD ); if ( diff > maxdiff ) { maxdiffop = "( A @ B ) == ( A - B ) + ( B - A )"; maxdiff = diff; } // ( A u B ) == ( A ^ B ) + ( A @ B ) diff = std::fabs ( areaI + areaSD - areaU ); if ( diff > maxdiff ) { maxdiffop = "( A u B ) == ( A ^ B ) + ( A @ B )"; maxdiff = diff; } if ( maxdiff <= toleratedDiff ) { success = 1 && validOut; } std::stringstream tmp; tmp << maxdiffop << ": " << maxdiff; actual_result=tmp.str(); expected_result=opRes; } else if (opName=="distance") { char* rest; double distE = std::strtod(opRes.c_str(), &rest); if ( rest == opRes.c_str() ) { throw std::runtime_error("malformed testcase: missing expected result in 'distance' op"); } geom::Geometry *g1 = opArg1 == "B" ? gB : gA; geom::Geometry *g2 = opArg2 == "B" ? gB : gA; double distO = g1->distance(g2); std::stringstream ss; ss << distO; actual_result = ss.str(); // TODO: Use a tolerance ? success = ( distO == distE ) ? 1 : 0; } else { std::cerr << *curr_file << ":"; std::cerr << " case" << caseCount << ":"; std::cerr << " test" << testCount << ": " << opName << "(" << opSig << ")"; std::cerr << ": skipped (unrecognized)." << std::endl; return; } } catch (const std::exception &e) { std::cerr<<"EXCEPTION on case "<<caseCount <<" test "<<testCount<<": "<<e.what() <<std::endl; actual_result = e.what(); } catch (...) { std::cerr << "Unknown EXEPTION on case " << caseCount << std::endl; actual_result = "Unknown exception thrown"; } if ( success ) ++succeeded; else ++failed; if ((!success && verbose) || verbose > 1) { printTest(!!success, expected_result, actual_result); } if (test_predicates && gB && gA) { runPredicates(gA, gB); } }
/*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 ) throw util::IllegalArgumentException("BufferBuilder::bufferLineSingleSided only accept linestrings"); // 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 = 0; // This is a (temp?) hack to workaround the fact that // BufferBuilder BufferParamaters are immutable after // construction, while we want to force the end cap // style to FLAT for single-sided buffering { BufferBuilder tmp(modParams); buf = tmp.buffer( l, distance ); } // Create MultiLineStrings from this polygon. Geometry* bufLineString = buf->getBoundary(); #ifdef GEOS_DEBUG_SSB std::cerr << "input|" << *l << std::endl; std::cerr << "buffer|" << *bufLineString << std::endl; #endif // Then, get the raw (i.e. unnoded) single sided offset curve. OffsetCurveBuilder curveBuilder( precisionModel, modParams ); std::vector< CoordinateSequence* > lineList; std::auto_ptr< CoordinateSequence > coords ( g->getCoordinates() ); curveBuilder.getSingleSidedLineCurve( coords.get(), distance, lineList, leftSide, !leftSide ); coords.reset(); // Create a SegmentString from these coordinates. SegmentString::NonConstVect curveList; for ( unsigned int i = 0; i < lineList.size(); ++i ) { CoordinateSequence* seq = lineList[i]; SegmentString* ss = new NodedSegmentString(seq, NULL); curveList.push_back( ss ); } // 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, n = nodedEdges->size(); i < n; ++i ) { SegmentString* ss = ( *nodedEdges )[i]; Geometry* tmp = geomFact->createLineString( ss->getCoordinates()->clone() ); singleSidedNodedEdges->push_back( tmp ); } if ( nodedEdges != &curveList ) delete nodedEdges; for (size_t i=0, n=curveList.size(); i<n; ++i) delete curveList[i]; curveList.clear(); for (size_t i=0, n=lineList.size(); i<n; ++i) delete lineList[i]; lineList.clear(); Geometry* singleSided = geomFact->createMultiLineString( singleSidedNodedEdges ); #ifdef GEOS_DEBUG_SSB std::cerr << "edges|" << *singleSided << std::endl; #endif // 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 ); // NOTE: we use Snapped overlay because the actual buffer boundary might // diverge from original offset curves due to the addition of // intersections with caps and joins curves using geos::operation::overlay::snap::SnapOverlayOp; Geometry* intersectedLines = SnapOverlayOp::overlayOp(*singleSided, *bufLineString, OverlayOp::opINTERSECTION).release(); #ifdef GEOS_DEBUG_SSB std::cerr << "intersection" << "|" << *intersectedLines << std::endl; #endif // Merge result lines together. LineMerger lineMerge; lineMerge.add( intersectedLines ); std::auto_ptr< 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 ); if ( mergedLinesGeom->size() > 1 ) return geomFact->createMultiLineString( mergedLinesGeom ); else { // Must be a single line Geometry* single = (*mergedLinesGeom)[0]; delete mergedLinesGeom; return single; } }