1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2005-2006 Refractions Research Inc.
7 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
14 **********************************************************************/
17 # if defined(GEOS_DEBUG_MSVC_USE_VLD) && !defined(GEOS_TEST_USE_STACKWALKER)
20 //#define _CRTDBG_MAP_ALLOC
24 #pragma warning(disable : 4127)
27 #include <geos/geom/Point.h>
28 #include <geos/geom/LineString.h>
29 #include <geos/geom/LinearRing.h>
30 #include <geos/geom/Polygon.h>
31 #include <geos/geom/GeometryCollection.h>
32 #include <geos/geom/GeometryFactory.h>
33 #include <geos/geom/IntersectionMatrix.h>
34 #include <geos/geom/PrecisionModel.h>
35 #include <geos/geom/BinaryOp.h>
36 #include <geos/operation/overlay/OverlayOp.h>
37 #include <geos/operation/buffer/BufferBuilder.h>
38 #include <geos/operation/buffer/BufferParameters.h>
39 #include <geos/operation/buffer/BufferOp.h>
40 #include <geos/util.h>
41 #include <geos/util/Interrupt.h>
42 //#include <geos/geomgraph.h>
43 #include <geos/io/WKBReader.h>
44 #include <geos/io/WKBWriter.h>
45 #include <geos/io/WKTReader.h>
46 #include <geos/io/WKTWriter.h>
47 #include <geos/opRelate.h>
48 #include <geos/opPolygonize.h>
49 #include <geos/opLinemerge.h>
50 #include <geos/profiler.h>
51 #include <geos/unload.h>
52 #include <geos/opValid.h>
53 #include "XMLTester.h"
54 #include "BufferResultMatcher.h"
55 #include "SingleSidedBufferResultMatcher.h"
75 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
77 #include "Stackwalker.h"
80 // Geometry methods do use BinaryOp internally
84 using namespace geos::operation::polygonize
;
85 using namespace geos::operation::linemerge
;
86 using std::runtime_error
;
90 // a utility function defining a very simple method to indent a line of text
91 const char * getIndent( unsigned int numIndents
)
93 static const char * pINDENT
= " + ";
94 static const unsigned int LENGTH
= strlen( pINDENT
);
96 if ( numIndents
> LENGTH
) numIndents
= LENGTH
;
98 return &pINDENT
[ LENGTH
-numIndents
];
103 void dump_to_stdout( const TiXmlNode
* pParent
, unsigned int indent
= 0 )
105 if ( !pParent
) return;
107 const TiXmlText
*pText
;
108 int t
= pParent
->Type();
109 printf( "%s", getIndent( indent
));
113 case TiXmlNode::DOCUMENT
:
114 printf( "Document" );
117 case TiXmlNode::ELEMENT
:
118 printf( "Element \"%s\"", pParent
->Value() );
121 case TiXmlNode::COMMENT
:
122 printf( "Comment: \"%s\"", pParent
->Value());
125 case TiXmlNode::UNKNOWN
:
129 case TiXmlNode::TEXT
:
130 pText
= pParent
->ToText();
131 printf( "Text: [%s]", pText
->Value() );
134 case TiXmlNode::DECLARATION
:
135 printf( "Declaration" );
142 const TiXmlNode
* pChild
;
144 for ( pChild
= pParent
->FirstChild(); pChild
!= 0; pChild
= pChild
->NextSibling())
146 dump_to_stdout( pChild
, indent
+2 );
153 tolower(std::string
& str
)
155 std::transform(str
.begin(), str
.end(), str
.begin(), (int(*)(int))std::tolower
);
159 normalize_filename(const std::string
& str
)
161 std::string newstring
;
163 std::string::size_type last_slash
= str
.find_last_of('/', str
.size());
164 if ( last_slash
== std::string::npos
) newstring
= str
;
165 else newstring
= str
.substr(last_slash
+1);
167 for (std::string::iterator i
=newstring
.begin(), e
=newstring
.end(); i
!=e
; ++i
)
169 if ( *i
== '.' ) *i
= '_';
177 /* Could be an XMLTester class private but oh well.. */
179 checkBufferSuccess(geom::Geometry
const& gRes
, geom::Geometry
const& gRealRes
, double dist
)
185 if ( gRes
.getGeometryTypeId() != gRealRes
.getGeometryTypeId() )
187 std::cerr
<< "Expected result is of type "
188 << gRes
.getGeometryType()
189 << "; obtained result is of type "
190 << gRealRes
.getGeometryType()
196 // Is a buffer always an area ?
197 if ( gRes
.getDimension() != 2 )
199 std::cerr
<< "Don't know how to validate "
200 << "result of buffer operation "
201 << "when expected result is not an "
207 geos::xmltester::BufferResultMatcher matcher
;
208 if ( ! matcher
.isBufferResultMatch(gRealRes
,
212 std::cerr
<< "BufferResultMatcher FAILED" << std::endl
;
224 checkSingleSidedBufferSuccess(geom::Geometry
& gRes
,
225 geom::Geometry
& gRealRes
, double dist
)
231 if ( gRes
.getGeometryTypeId() != gRealRes
.getGeometryTypeId() )
233 std::cerr
<< "Expected result is of type "
234 << gRes
.getGeometryType()
235 << "; obtained result is of type "
236 << gRealRes
.getGeometryType()
242 geos::xmltester::SingleSidedBufferResultMatcher matcher
;
243 if ( ! matcher
.isBufferResultMatch(gRealRes
,
247 std::cerr
<< "SingleSidedBufferResultMatcher FAILED" << std::endl
;
258 XMLTester::XMLTester()
277 testValidOutput(false),
278 testValidInput(false),
282 setVerbosityLevel(0);
286 XMLTester::setVerbosityLevel(int value
)
288 int old_value
=verbose
;
297 XMLTester::printTest(bool success
, const std::string
& expected_result
, const std::string
& actual_result
, const util::Profile
&prof
)
301 std::cout
<< "INSERT INTO \"" << normalize_filename(*curr_file
) << "\" VALUES ("
304 << "'" << opSignature
<< "', "
305 << "'" << curr_case_desc
<< "', ";
310 std::cout
<< "'" << printGeom(gA
) << "', ";
312 std::cout
<< "NULL, ";
316 std::cout
<< "'" << printGeom(gB
) << "', ";
318 std::cout
<< "NULL, ";
321 std::cout
<< "'" << expected_result
<< "', "
322 << "'" << actual_result
<< "', ";
324 if ( success
) std::cout
<< "'t'";
325 else std::cout
<< "'f'";
327 std::cout
<< ");" << std::endl
;
332 std::cout
<< *curr_file
<<":";
333 std::cout
<< " case" << caseCount
<< ":";
334 std::cout
<< " test" << testCount
<< ": "
336 std::cout
<< ": " << (success
?"ok.":"failed.");
337 std::cout
<< " (" << std::setprecision(15) << round(prof
.getTot()/1000) << " ms)" << std::endl
;
339 std::cout
<< "\tDescription: " << curr_case_desc
<< std::endl
;
343 std::cout
<< "\tGeometry A: ";
344 printGeom(std::cout
, gA
);
345 std::cout
<< std::endl
;
349 std::cout
<< "\tGeometry B: ";
350 printGeom(std::cout
, gB
);
351 std::cout
<< std::endl
;
354 std::cout
<< "\tExpected result: "<<expected_result
<<std::endl
;
355 std::cout
<< "\tObtained result: "<<actual_result
<<std::endl
;
356 std::cout
<<std::endl
;
361 XMLTester::run(const std::string
&source
)
367 std::cout
<< "CREATE TABLE \"" << normalize_filename(*curr_file
) << "\""
368 << "( caseno integer, testno integer, "
369 << " operation varchar, description varchar, "
370 << " a geometry, b geometry, expected geometry, "
371 << " obtained geometry, result bool )"
373 // NOTE: qgis 0.7.4 require oids for proper operations.
374 // The 'WITH OIDS' parameter is supported back to
375 // PostgreSQL 7.2, so if you run an older version
376 // rebuild with the next line commented out.
386 if ( ! xml
.LoadFile(source
.c_str()) )
388 std::stringstream err
;
389 err
<< "Could not load " << source
<< ": " << xml
.ErrorDesc();
390 throw runtime_error(err
.str());
393 //dump_to_stdout(&xml);
395 const TiXmlNode
* node
= xml
.FirstChild("run");
398 throw(runtime_error("Document has no childs"));
405 XMLTester::resultSummary(std::ostream
&os
) const
407 os
<<"Files: "<<testFileCount
<<std::endl
;
408 os
<<"Tests: "<<totalTestCount
<<std::endl
;
409 os
<<"Failed: "<<failed
<<std::endl
;
410 os
<<"Succeeded: "<<succeeded
<<std::endl
;
414 XMLTester::resetCounters()
416 testFileCount
=totalTestCount
=failed
=succeeded
=0;
420 XMLTester::parseRun(const TiXmlNode
* node
)
422 using geos::geom::PrecisionModel
;
426 //dump_to_stdout(node);
428 // Look for precisionModel element
429 const TiXmlElement
* el
= node
->FirstChildElement("precisionModel");
430 if ( el
) parsePrecisionModel(el
);
431 else pm
.reset(new PrecisionModel());
435 std::cerr
<< *curr_file
<<": run: Precision Model: " << pm
->toString() <<std::endl
;
438 factory
= geom::GeometryFactory::create(pm
.get());
439 wktreader
.reset(new io::WKTReader(factory
.get()));
440 wktwriter
.reset(new io::WKTWriter());
441 wkbreader
.reset(new io::WKBReader(*factory
));
442 wkbwriter
.reset(new io::WKBWriter());
444 const TiXmlNode
* casenode
;
445 for ( casenode
= node
->FirstChild("case");
447 casenode
= casenode
->NextSibling("case") )
451 } catch (const std::exception
& exc
) {
452 std::cerr
<<exc
.what()<<std::endl
;
459 XMLTester::parsePrecisionModel(const TiXmlElement
* el
)
461 using geos::geom::PrecisionModel
;
463 //dump_to_stdout(el);
465 /* This does not seem to work... */
467 const char* typeStr
= el
->Attribute("type");
468 if ( typeStr
) type
= typeStr
;
470 const char* scaleStr
= el
->Attribute("scale");
473 if ( type
== "FLOATING_SINGLE" )
475 pm
.reset(new PrecisionModel(PrecisionModel::FLOATING_SINGLE
));
479 pm
.reset(new PrecisionModel());
485 double scale
= std::strtod(scaleStr
, &stopstring
);
489 if ( ! el
->QueryDoubleAttribute("offsetx", &offsetX
) )
490 {} // std::cerr << "No offsetx" << std::endl;
492 if ( ! el
->QueryDoubleAttribute("offsety", &offsetY
) )
493 {} // std::cerr << "No offsety" << std::endl;
495 // NOTE: PrecisionModel discards offsets anyway...
496 pm
.reset(new PrecisionModel(scale
, offsetX
, offsetY
));
502 XMLTester::testValid(const geom::Geometry
* g
, const std::string
& label
)
504 operation::valid::IsValidOp
ivo(g
);
505 bool valid
= ivo
.isValid();
508 operation::valid::TopologyValidationError
*err
= ivo
.getValidationError();
509 std::cerr
<< *curr_file
<< ":"
510 << " case" << caseCount
<< ":"
511 << " test" << testCount
<< ": "
512 << opSignature
<< ": "
513 << " invalid geometry (" << label
514 << "): " << err
->toString() << std::endl
;
520 * Parse WKT or HEXWKB
523 XMLTester::parseGeometry(const std::string
&in
, const char* label
)
525 if ( ( ! wkbreader
.get() ) || ( ! wktreader
.get() ) )
526 throw(runtime_error("No precision model specified"));
528 std::stringstream
is(in
, std::ios_base::in
);
531 // Remove leading spaces
532 while (is
.get(first_char
) && std::isspace(first_char
));
555 ret
= wkbreader
->readHEX(is
);
558 ret
= wktreader
->read(in
);
562 if ( testValidInput
) testValid(ret
, std::string(label
));
570 XMLTester::trimBlanks(const std::string
&in
)
573 std::string::size_type pos
= in
.find_first_not_of(" \t\n\r");
574 if (pos
!=std::string::npos
) out
=in
.substr(pos
);
575 pos
= out
.find_last_not_of(" \t\n\r");
576 if (pos
!=std::string::npos
) out
=out
.substr(0, pos
+1);
581 XMLTester::parseCase(const TiXmlNode
* node
)
587 std::string thrownException
;
593 //dump_to_stdout(node);
595 curr_case_desc
.clear();
596 const TiXmlNode
* txt
= node
->FirstChild("desc");
598 txt
= txt
->FirstChild();
599 if ( txt
) curr_case_desc
= trimBlanks(txt
->Value());
602 //std::cerr << "Desc: " << curr_case_desc << std::endl;
606 const TiXmlNode
*el
= node
->FirstChild("a");
607 geomAin
= el
->FirstChild()->Value();
608 geomAin
= trimBlanks(geomAin
);
609 gA
= parseGeometry(geomAin
, "Geometry A");
611 if ( 0 != (el
= node
->FirstChild("b")) )
613 geomBin
= el
->FirstChild()->Value();
614 geomBin
= trimBlanks(geomBin
);
615 gB
= parseGeometry(geomBin
, "Geometry B");
618 catch (const std::exception
&e
) {
619 thrownException
= e
.what();
622 thrownException
= "Unknown exception";
625 //std::cerr << "A: " << geomAin << std::endl;
626 //std::cerr << "B: " << geomBin << std::endl;
629 if ( thrownException
!= "" )
631 std::cout
<< *curr_file
<<":";
632 std::cout
<< " case" << caseCount
<< ":";
633 std::cout
<< " skipped ("<<thrownException
<<")."<<std::endl
;
640 const TiXmlNode
* testnode
;
641 for ( testnode
= node
->FirstChild("test");
643 testnode
= testnode
->NextSibling("test") )
648 totalTestCount
+=testCount
;
656 XMLTester::printGeom(std::ostream
& os
, const geom::Geometry
*g
)
662 XMLTester::printGeom(const geom::Geometry
*g
)
666 std::stringstream
s(std::ios_base::binary
|std::ios_base::in
|std::ios_base::out
);
667 wkbwriter
->write(*g
, s
);
668 std::stringstream s2
;
669 wkbreader
->printHEX(s
, s2
);
674 return wktwriter
->write(g
);
679 XMLTester::parseTest(const TiXmlNode
* node
)
681 using namespace operation::overlay
;
683 typedef std::auto_ptr
< geom::Geometry
> GeomAutoPtr
;
685 int success
=0; // no success by default
695 const TiXmlNode
* opnode
= node
->FirstChild("op");
696 if ( ! opnode
) throw(runtime_error("case has no op"));
698 //dump_to_stdout(opnode);
700 const TiXmlElement
* opel
= opnode
->ToElement();
702 const char* tmp
= opel
->Attribute("name");
703 if ( tmp
) opName
= tmp
;
705 tmp
= opel
->Attribute("arg1");
706 if ( tmp
) opArg1
= tmp
;
708 tmp
= opel
->Attribute("arg2");
709 if ( tmp
) opArg2
= tmp
;
711 tmp
= opel
->Attribute("arg3");
712 if ( tmp
) opArg3
= tmp
;
714 tmp
= opel
->Attribute("arg4");
715 if ( tmp
) opArg4
= tmp
;
717 const TiXmlNode
* resnode
= opnode
->FirstChild();
720 std::stringstream tmp
;
721 tmp
<< "op of test " << testCount
722 << " of case " << caseCount
723 << " has no expected result child";
724 throw(runtime_error(tmp
.str()));
726 opRes
= resnode
->Value();
729 opRes
=trimBlanks(opRes
);
730 opName
=trimBlanks(opName
);
733 std::string opSig
="";
735 if ( opArg1
!= "" ) opSig
=opArg1
;
736 if ( opArg2
!= "" ) {
737 if ( opSig
!= "" ) opSig
+= ", ";
740 if ( opArg3
!= "" ) {
741 if ( opSig
!= "" ) opSig
+= ", ";
744 if ( opArg4
!= "" ) {
745 if ( opSig
!= "" ) opSig
+= ", ";
749 opSignature
= opName
+ "(" + opSig
+ ")";
751 std::string actual_result
="NONE";
753 // expected_result will be modified by specific tests
754 // if needed (geometry normalization, for example)
755 std::string expected_result
=opRes
;
757 util::Profile
profile("op");
761 if (opName
=="relate")
763 std::auto_ptr
<geom::IntersectionMatrix
> im(gA
->relate(gB
));
766 if (im
->matches(opArg3
)) actual_result
="true";
767 else actual_result
="false";
769 if (actual_result
==opRes
) success
=1;
771 else if (opName
=="relatestring")
773 std::auto_ptr
<geom::IntersectionMatrix
> im(gA
->relate(gB
));
776 actual_result
=im
->toString();
778 if (actual_result
==opRes
) success
=1;
781 else if (opName
=="isvalid")
783 geom::Geometry
*gT
=gA
;
784 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) {
788 if (gT
->isValid()) actual_result
="true";
789 else actual_result
="false";
791 if (actual_result
==opRes
) success
=1;
795 else if (opName
=="intersection")
798 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
804 GeomAutoPtr
gRealRes(gA
->intersection(gB
));
806 GeomAutoPtr gRealRes
= BinaryOp(gA
, gB
, overlayOp(OverlayOp::opINTERSECTION
));
811 gRealRes
->normalize();
813 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
815 actual_result
=printGeom(gRealRes
.get());
816 expected_result
=printGeom(gRes
.get());
818 if ( testValidOutput
)
819 success
&= int(testValid(gRealRes
.get(), "result"));
822 else if (opName
=="union")
824 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
826 GeomAutoPtr gRealRes
;
829 gRealRes
.reset(gA
->Union(gB
));
831 gRealRes
= BinaryOp(gA
, gB
, overlayOp(OverlayOp::opUNION
));
834 gRealRes
= gA
->Union();
837 if (gRes
->equals(gRealRes
.get())) success
=1;
839 actual_result
=printGeom(gRealRes
.get());
840 expected_result
=printGeom(gRes
.get());
842 if ( testValidOutput
)
843 success
&= int(testValid(gRealRes
.get(), "result"));
846 else if (opName
=="difference")
849 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
853 GeomAutoPtr
gRealRes(gA
->difference(gB
));
855 GeomAutoPtr gRealRes
= BinaryOp(gA
, gB
, overlayOp(OverlayOp::opDIFFERENCE
));
858 gRealRes
->normalize();
860 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
862 actual_result
=printGeom(gRealRes
.get());
863 expected_result
=printGeom(gRes
.get());
865 if ( testValidOutput
)
866 success
&= int(testValid(gRealRes
.get(), "result"));
869 else if (opName
=="symdifference")
871 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
875 GeomAutoPtr
gRealRes(gA
->symDifference(gB
));
877 GeomAutoPtr gRealRes
= BinaryOp(gA
, gB
, overlayOp(OverlayOp::opSYMDIFFERENCE
));
880 gRealRes
->normalize();
882 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
884 actual_result
=printGeom(gRealRes
.get());
885 expected_result
=printGeom(gRes
.get());
887 if ( testValidOutput
)
888 success
&= int(testValid(gRealRes
.get(), "result"));
891 else if (opName
=="intersects")
893 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
894 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
896 if (g1
->intersects(g2
)) actual_result
="true";
897 else actual_result
="false";
899 if (actual_result
==opRes
) success
=1;
902 else if (opName
=="contains")
904 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
905 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
907 if (g1
->contains(g2
)) actual_result
="true";
908 else actual_result
="false";
910 if (actual_result
==opRes
) success
=1;
913 else if (opName
=="within")
915 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
916 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
918 if (g1
->within(g2
)) actual_result
="true";
919 else actual_result
="false";
921 if (actual_result
==opRes
) success
=1;
924 else if (opName
=="covers")
926 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
927 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
929 if (g1
->covers(g2
)) actual_result
="true";
930 else actual_result
="false";
932 if (actual_result
==opRes
) success
=1;
935 else if (opName
=="coveredby")
937 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
938 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
940 if (g1
->coveredBy(g2
)) actual_result
="true";
941 else actual_result
="false";
943 if (actual_result
==opRes
) success
=1;
946 else if (opName
=="getboundary")
948 geom::Geometry
*gT
=gA
;
949 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
951 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
954 GeomAutoPtr
gRealRes(gT
->getBoundary());
955 gRealRes
->normalize();
957 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
959 actual_result
=printGeom(gRealRes
.get());
960 expected_result
=printGeom(gRes
.get());
962 if ( testValidOutput
)
963 success
&= int(testValid(gRealRes
.get(), "result"));
966 else if (opName
=="getcentroid")
968 geom::Geometry
*gT
=gA
;
969 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
971 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
974 GeomAutoPtr
gRealRes(gT
->getCentroid());
976 if ( gRealRes
.get() ) gRealRes
->normalize();
977 else gRealRes
.reset(factory
->createPoint());
978 gRealRes
->normalize();
980 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
982 actual_result
=printGeom(gRealRes
.get());
983 expected_result
=printGeom(gRes
.get());
985 if ( testValidOutput
)
986 success
&= int(testValid(gRealRes
.get(), "result"));
989 else if (opName
=="issimple")
991 geom::Geometry
*gT
=gA
;
992 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
994 if (gT
->isSimple()) actual_result
="true";
995 else actual_result
="false";
997 if (actual_result
==opRes
) success
=1;
1001 else if (opName
=="convexhull")
1003 geom::Geometry
*gT
=gA
;
1004 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1006 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
1009 GeomAutoPtr
gRealRes(gT
->convexHull());
1010 gRealRes
->normalize();
1012 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
1014 actual_result
=printGeom(gRealRes
.get());
1015 expected_result
=printGeom(gRes
.get());
1017 if ( testValidOutput
)
1018 success
&= int(testValid(gRealRes
.get(), "result"));
1021 else if (opName
=="buffer")
1023 using namespace operation::buffer
;
1025 geom::Geometry
*gT
=gA
;
1026 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1028 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
1033 GeomAutoPtr gRealRes
;
1034 double dist
= std::atof(opArg2
.c_str());
1036 BufferParameters params
;
1037 if ( opArg3
!= "" ) {
1038 params
.setQuadrantSegments(std::atoi(opArg3
.c_str()));
1042 BufferOp
op(gT
, params
);
1043 gRealRes
.reset(op
.getResultGeometry(dist
));
1046 gRealRes
->normalize();
1048 // Validate the buffer operation
1049 success
= checkBufferSuccess(*gRes
, *gRealRes
, dist
);
1051 actual_result
=printGeom(gRealRes
.get());
1052 expected_result
=printGeom(gRes
.get());
1054 if ( testValidOutput
)
1055 success
&= int(testValid(gRealRes
.get(), "result"));
1058 else if (opName
=="buffersinglesided")
1060 using namespace operation::buffer
;
1062 geom::Geometry
*gT
=gA
;
1063 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1065 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
1070 GeomAutoPtr gRealRes
;
1071 double dist
= std::atof(opArg2
.c_str());
1073 BufferParameters params
;
1074 params
.setJoinStyle( BufferParameters::JOIN_ROUND
) ;
1075 if ( opArg3
!= "" ) {
1076 params
.setQuadrantSegments( std::atoi(opArg3
.c_str()));
1079 bool leftSide
= true ;
1080 if ( opArg4
== "right" )
1085 BufferBuilder
bufBuilder( params
) ;
1086 gRealRes
.reset( bufBuilder
.bufferLineSingleSided(
1087 gT
, dist
, leftSide
) ) ;
1090 gRealRes
->normalize();
1092 // Validate the single sided buffer operation
1093 success
= checkSingleSidedBufferSuccess(*gRes
,
1096 actual_result
=printGeom(gRealRes
.get());
1097 expected_result
=printGeom(gRes
.get());
1099 if ( testValidOutput
)
1100 success
&= int(testValid(gRealRes
.get(), "result"));
1103 else if (opName
=="buffermitredjoin")
1105 using namespace operation::buffer
;
1107 geom::Geometry
*gT
=gA
;
1108 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1110 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
1115 GeomAutoPtr gRealRes
;
1116 double dist
= std::atof(opArg2
.c_str());
1118 BufferParameters params
;
1119 params
.setJoinStyle(BufferParameters::JOIN_MITRE
);
1121 if ( opArg3
!= "" ) {
1122 params
.setQuadrantSegments(std::atoi(opArg3
.c_str()));
1125 BufferOp
op(gT
, params
);
1126 gRealRes
.reset(op
.getResultGeometry(dist
));
1129 gRealRes
->normalize();
1131 // Validate the buffer operation
1132 success
= checkBufferSuccess(*gRes
, *gRealRes
, dist
);
1134 actual_result
=printGeom(gRealRes
.get());
1135 expected_result
=printGeom(gRes
.get());
1137 if ( testValidOutput
)
1138 success
&= int(testValid(gRealRes
.get(), "result"));
1142 else if (opName
=="getinteriorpoint")
1144 geom::Geometry
*gT
=gA
;
1145 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1147 GeomAutoPtr
gRes(parseGeometry(opRes
, "expected"));
1150 GeomAutoPtr
gRealRes(gT
->getInteriorPoint());
1151 if ( gRealRes
.get() ) gRealRes
->normalize();
1152 else gRealRes
.reset(factory
->createPoint());
1154 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
1156 actual_result
=printGeom(gRealRes
.get());
1157 expected_result
=printGeom(gRes
.get());
1159 if ( testValidOutput
)
1160 success
&= int(testValid(gRealRes
.get(), "result"));
1163 else if (opName
=="iswithindistance")
1165 double dist
=std::atof(opArg3
.c_str());
1166 if (gA
->isWithinDistance(gB
, dist
)) {
1167 actual_result
="true";
1169 actual_result
="false";
1172 if (actual_result
==opRes
) success
=1;
1176 else if (opName
=="polygonize")
1179 GeomAutoPtr
gRes(wktreader
->read(opRes
));
1186 std::vector
<geos::geom::Polygon
*>*polys
= plgnzr
.getPolygons();
1187 std::vector
<geom::Geometry
*>*newgeoms
= new std::vector
<geom::Geometry
*>;
1188 for (unsigned int i
=0; i
<polys
->size(); i
++)
1189 newgeoms
->push_back((*polys
)[i
]);
1192 GeomAutoPtr
gRealRes(factory
->createGeometryCollection(newgeoms
));
1193 gRealRes
->normalize();
1196 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
1198 actual_result
=printGeom(gRealRes
.get());
1199 expected_result
=printGeom(gRes
.get());
1201 if ( testValidOutput
)
1202 success
&= int(testValid(gRealRes
.get(), "result"));
1205 else if (opName
=="linemerge")
1207 GeomAutoPtr
gRes(wktreader
->read(opRes
));
1210 geom::Geometry
*gT
=gA
;
1212 if ( ( opArg1
== "B" || opArg1
== "b" ) && gB
) gT
=gB
;
1216 std::auto_ptr
< std::vector
<geom::LineString
*> > lines ( merger
.getMergedLineStrings() );
1217 std::vector
<geom::Geometry
*>*newgeoms
= new std::vector
<geom::Geometry
*>(lines
->begin(),
1220 GeomAutoPtr
gRealRes(factory
->createGeometryCollection(newgeoms
));
1221 gRealRes
->normalize();
1223 if (gRes
->compareTo(gRealRes
.get())==0) success
=1;
1225 actual_result
=printGeom(gRealRes
.get());
1226 expected_result
=printGeom(gRes
.get());
1228 if ( testValidOutput
)
1229 success
&= int(testValid(gRealRes
.get(), "result"));
1232 else if (opName
=="areatest")
1235 double toleratedDiff
= std::strtod(opRes
.c_str(), &rest
);
1238 if ( rest
== opRes
.c_str() )
1240 throw std::runtime_error("malformed testcase: missing tolerated area difference in 'areatest' op");
1245 std::cerr
<< "Running intersection for areatest" << std::endl
;
1247 #ifndef USE_BINARYOP
1248 GeomAutoPtr
gI(gA
->intersection(gB
));
1250 GeomAutoPtr gI
= BinaryOp(gA
, gB
,
1251 overlayOp(OverlayOp::opINTERSECTION
));
1254 if ( testValidOutput
)
1256 validOut
&= int(testValid(gI
.get(), "areatest intersection"));
1261 std::cerr
<< "Running difference(A,B) for areatest" << std::endl
;
1264 #ifndef USE_BINARYOP
1265 GeomAutoPtr
gDab(gA
->difference(gB
));
1267 GeomAutoPtr gDab
= BinaryOp(gA
, gB
,
1268 overlayOp(OverlayOp::opDIFFERENCE
));
1271 if ( testValidOutput
)
1273 validOut
&= int(testValid(gI
.get(), "areatest difference(a,b)"));
1278 std::cerr
<< "Running difference(B,A) for areatest" << std::endl
;
1281 #ifndef USE_BINARYOP
1282 GeomAutoPtr
gDba(gB
->difference(gA
));
1284 GeomAutoPtr gDba
= BinaryOp(gB
, gA
,
1285 overlayOp(OverlayOp::opDIFFERENCE
));
1288 if ( testValidOutput
)
1290 validOut
&= int(testValid(gI
.get(), "areatest difference(b,a)"));
1295 std::cerr
<< "Running symdifference for areatest" << std::endl
;
1298 #ifndef USE_BINARYOP
1299 GeomAutoPtr
gSD(gA
->symDifference(gB
));
1301 GeomAutoPtr gSD
= BinaryOp(gA
, gB
,
1302 overlayOp(OverlayOp::opSYMDIFFERENCE
));
1305 if ( testValidOutput
)
1307 validOut
&= int(testValid(gI
.get(), "areatest symdifference"));
1312 std::cerr
<< "Running union for areatest" << std::endl
;
1315 #ifndef USE_BINARYOP
1316 GeomAutoPtr
gU(gA
->Union(gB
));
1318 GeomAutoPtr gU
= BinaryOp(gA
, gB
,
1319 overlayOp(OverlayOp::opUNION
));
1322 double areaA
= gA
->getArea();
1323 double areaB
= gB
->getArea();
1324 double areaI
= gI
->getArea();
1325 double areaDab
= gDab
->getArea();
1326 double areaDba
= gDba
->getArea();
1327 double areaSD
= gSD
->getArea();
1328 double areaU
= gU
->getArea();
1331 std::string maxdiffop
;
1333 // @ : symdifference
1338 // A == ( A ^ B ) + ( A - B )
1339 double diff
= std::fabs ( areaA
- areaI
- areaDab
);
1340 if ( diff
> maxdiff
) {
1341 maxdiffop
= "A == ( A ^ B ) + ( A - B )";
1345 // B == ( A ^ B ) + ( B - A )
1346 diff
= std::fabs ( areaB
- areaI
- areaDba
);
1347 if ( diff
> maxdiff
) {
1348 maxdiffop
= "B == ( A ^ B ) + ( B - A )";
1352 // ( A @ B ) == ( A - B ) + ( B - A )
1353 diff
= std::fabs ( areaDab
+ areaDba
- areaSD
);
1354 if ( diff
> maxdiff
) {
1355 maxdiffop
= "( A @ B ) == ( A - B ) + ( B - A )";
1359 // ( A u B ) == ( A ^ B ) + ( A @ B )
1360 diff
= std::fabs ( areaI
+ areaSD
- areaU
);
1361 if ( diff
> maxdiff
) {
1362 maxdiffop
= "( A u B ) == ( A ^ B ) + ( A @ B )";
1366 if ( maxdiff
<= toleratedDiff
)
1368 success
= 1 && validOut
;
1371 std::stringstream tmp
;
1372 tmp
<< maxdiffop
<< ": " << maxdiff
;
1373 actual_result
=tmp
.str();
1374 expected_result
=opRes
;
1377 else if (opName
=="distance")
1380 double distE
= std::strtod(opRes
.c_str(), &rest
);
1381 if ( rest
== opRes
.c_str() )
1383 throw std::runtime_error("malformed testcase: missing expected result in 'distance' op");
1386 geom::Geometry
*g1
= opArg1
== "B" ? gB
: gA
;
1387 geom::Geometry
*g2
= opArg2
== "B" ? gB
: gA
;
1388 double distO
= g1
->distance(g2
);
1389 std::stringstream ss
; ss
<< distO
;
1390 actual_result
= ss
.str();
1392 // TODO: Use a tolerance ?
1393 success
= ( distO
== distE
) ? 1 : 0;
1398 std::cerr
<< *curr_file
<< ":";
1399 std::cerr
<< " case" << caseCount
<< ":";
1400 std::cerr
<< " test" << testCount
<< ": "
1401 << opName
<< "(" << opSig
<< ")";
1402 std::cerr
<< ": skipped (unrecognized)." << std::endl
;
1407 catch (const std::exception
&e
)
1409 std::cerr
<<"EXCEPTION on case "<<caseCount
1410 <<" test "<<testCount
<<": "<<e
.what()
1412 actual_result
= e
.what();
1416 std::cerr
<< "Unknown EXEPTION on case "
1419 actual_result
= "Unknown exception thrown";
1422 if ( success
) ++succeeded
;
1425 if ((!success
&& verbose
) || verbose
> 1)
1427 printTest(!!success
, expected_result
, actual_result
, profile
);
1430 if (test_predicates
&& gB
&& gA
) {
1431 runPredicates(gA
, gB
);
1437 XMLTester::runPredicates(const geom::Geometry
*gA
, const geom::Geometry
*gB
)
1439 std::cout
<< "\t Equals:\tAB=" << (gA
->equals(gB
)?"T":"F") << ", BA=" << (gB
->equals(gA
)?"T":"F") << std::endl
;
1440 std::cout
<< "\t Disjoint:\tAB=" << (gA
->disjoint(gB
)?"T":"F") << ", BA=" << (gB
->disjoint(gA
)?"T":"F") << std::endl
;
1441 std::cout
<< "\tIntersects:\tAB=" << (gA
->intersects(gB
)?"T":"F") << ", BA=" << (gB
->intersects(gA
)?"T":"F") << std::endl
;
1442 std::cout
<< "\t Touches:\tAB=" << (gA
->touches(gB
)?"T":"F") << ", BA=" << (gB
->touches(gA
)?"T":"F") << std::endl
;
1443 std::cout
<< "\t Crosses:\tAB=" << (gA
->crosses(gB
)?"T":"F") << ", BA=" << (gB
->crosses(gA
)?"T":"F") << std::endl
;
1444 std::cout
<< "\t Within:\tAB=" << (gA
->within(gB
)?"T":"F") << ", BA=" << (gB
->within(gA
)?"T":"F") << std::endl
;
1445 std::cout
<< "\t Contains:\tAB=" << (gA
->contains(gB
)?"T":"F") << ", BA=" << (gB
->contains(gA
)?"T":"F") << std::endl
;
1446 std::cout
<< "\t Overlaps:\tAB=" << (gA
->overlaps(gB
)?"T":"F") << ", BA=" << (gB
->overlaps(gA
)?"T":"F") << std::endl
;
1449 XMLTester::~XMLTester()
1455 usage(char *me
, int exitcode
, std::ostream
&os
)
1457 os
<< "Usage: " << me
<< " [options] <test> [<test> ...]" << std::endl
;
1458 os
<< "Options: " << std::endl
;
1459 os
<< " -v Verbose mode "
1460 << "(multiple -v increment verbosity)" << std::endl
1461 << "--test-valid-output Test output validity" << std::endl
1462 << "--test-valid-input Test input validity" << std::endl
1463 << "--sql-output Produce SQL output" << std::endl
1464 << "--wkb-output Print Geometries as HEXWKB" << std::endl
;
1466 std::exit(exitcode
);
1470 request_interrupt(int sig
)
1472 geos::util::Interrupt::request();
1476 main(int argC
, char* argV
[])
1479 bool sql_output
=false;
1481 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
1486 if ( argC
< 2 ) usage(argV
[0], 1, std::cerr
);
1488 signal(15, request_interrupt
);
1491 tester
.setVerbosityLevel(verbose
);
1493 for (int i
=1; i
<argC
; ++i
)
1495 // increment verbosity level
1496 if ( ! std::strcmp(argV
[i
], "-v" ) )
1499 tester
.setVerbosityLevel(verbose
);
1502 if ( ! std::strcmp(argV
[i
], "--test-valid-output" ) )
1504 tester
.testOutputValidity(true);
1507 if ( ! std::strcmp(argV
[i
], "--sql-output" ) )
1510 tester
.setSQLOutput(sql_output
);
1513 if ( ! std::strcmp(argV
[i
], "--wkb-output" ) )
1516 tester
.setHEXWKBOutput(sql_output
);
1519 if ( ! std::strcmp(argV
[i
], "--test-valid-input" ) )
1521 tester
.testInputValidity(true);
1525 std::string source
= argV
[i
];
1529 } catch (const std::exception
& exc
) {
1530 std::cerr
<<exc
.what()<<std::endl
;
1534 if ( ! sql_output
) tester
.resultSummary(std::cout
);
1535 else tester
.resultSummary(std::cerr
);
1537 io::Unload::Release();
1539 return tester
.getFailuresCount();
1541 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
1548 /**********************************************************************
1549 * $Log: XMLTester.cpp,v $
1550 * Revision 1.38 2006/07/13 03:59:10 csavage
1551 * Changes to compile on VC++ - fully qualified polygon name. Should also work on MingW, will test next.
1553 * Revision 1.37 2006/06/19 20:48:35 strk
1554 * parseCase(): make sure to exit the <case> tag before returning
1556 * Revision 1.36 2006/06/14 19:19:10 strk
1557 * Added support for "AreaTest" operations.
1559 * Revision 1.35 2006/06/12 10:39:29 strk
1560 * don't print test file precision model if verbosity level < 2.
1562 * Revision 1.34 2006/06/05 15:36:34 strk
1563 * Given OverlayOp funx code enum a name and renamed values to have a lowercase prefix. Drop all of noding headers from installed header set.
1565 * Revision 1.33 2006/05/19 16:38:22 strk
1566 * * tests/xmltester/XMLTester.cpp: report
1567 * error on load of requested tests.
1569 * Revision 1.32 2006/04/14 14:57:15 strk
1570 * XMLTester binary ops invoked using the new BinaryOp template function.
1572 * Revision 1.31 2006/04/07 13:26:38 strk
1573 * Use of auto_ptr<> to prevent confusing leaks in tester
1575 * Revision 1.30 2006/03/22 16:01:33 strk
1576 * indexBintree.h header split, classes renamed to match JTS
1578 * Revision 1.29 2006/03/17 14:56:39 strk
1579 * Fixed filename normalizer for sql output
1580 **********************************************************************/