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()));
        }
    }
Exemplo n.º 2
0
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;
   }
}