1 /**********************************************************************
2  *
3  * GEOS - Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2006 Refractions Research Inc.
7  *
8  * This is free software; you can redistribute and/or modify it under
9  * the terms of the GNU Lesser General Public Licence as published
10  * by the Free Software Foundation.
11  * See the COPYING file for more information.
12  *
13  ***********************************************************************
14  *
15  * Last port: operation/overlay/validate/OverlayResultValidator.java rev. 1.4 (JTS-1.10)
16  *
17  **********************************************************************/
18 
19 #include <geos/operation/overlay/validate/OverlayResultValidator.h>
20 #include <geos/operation/overlay/validate/FuzzyPointLocator.h>
21 #include <geos/operation/overlay/validate/OffsetPointGenerator.h>
22 #include <geos/operation/overlay/snap/GeometrySnapper.h>
23 #include <geos/geom/CoordinateSequence.h>
24 #include <geos/geom/MultiPoint.h>
25 #include <geos/geom/GeometryFactory.h>
26 #include <geos/geom/CoordinateSequenceFactory.h>
27 
28 #include <cassert>
29 #include <functional>
30 #include <vector>
31 #include <memory> // for unique_ptr
32 #include <algorithm> // for std::min etc.
33 
34 #ifndef GEOS_DEBUG
35 #define GEOS_DEBUG 0
36 #endif
37 
38 #if GEOS_DEBUG
39 #include <iomanip> // for setprecision
40 #endif
41 
42 #define COMPUTE_Z 1
43 #define USE_ELEVATION_MATRIX 1
44 #define USE_INPUT_AVGZ 0
45 
46 using namespace std;
47 using namespace geos::geom;
48 using namespace geos::geomgraph;
49 using namespace geos::algorithm;
50 
51 namespace geos {
52 namespace operation { // geos.operation
53 namespace overlay { // geos.operation.overlay
54 namespace validate { // geos.operation.overlay.validate
55 
56 namespace { // anonymous namespace
57 
58 #if GEOS_DEBUG
59 unique_ptr<MultiPoint>
toMultiPoint(vector<Coordinate> & coords)60 toMultiPoint(vector<Coordinate>& coords)
61 {
62     const GeometryFactory& gf = *(GeometryFactory::getDefaultInstance());
63     const CoordinateSequenceFactory& csf =
64         *(gf.getCoordinateSequenceFactory());
65 
66     unique_ptr< vector<Coordinate> > nc(new vector<Coordinate>(coords));
67     unique_ptr<CoordinateSequence> cs(csf.create(nc.release()));
68 
69     unique_ptr<MultiPoint> mp(gf.createMultiPoint(*cs));
70 
71     return mp;
72 }
73 #endif
74 
75 } // anonymous namespace
76 
77 
78 /* static public */
79 bool
isValid(const Geometry & geom0,const Geometry & geom1,OverlayOp::OpCode opCode,const Geometry & result)80 OverlayResultValidator::isValid(const Geometry& geom0, const Geometry& geom1,
81                                 OverlayOp::OpCode opCode,
82                                 const Geometry& result)
83 {
84     OverlayResultValidator validator(geom0, geom1, result);
85     return validator.isValid(opCode);
86 }
87 
88 /*public*/
OverlayResultValidator(const Geometry & geom0,const Geometry & geom1,const Geometry & result)89 OverlayResultValidator::OverlayResultValidator(
90     const Geometry& geom0,
91     const Geometry& geom1,
92     const Geometry& result)
93     :
94     boundaryDistanceTolerance(
95         computeBoundaryDistanceTolerance(geom0, geom1)
96     ),
97     g0(geom0),
98     g1(geom1),
99     gres(result),
100     fpl0(g0, boundaryDistanceTolerance),
101     fpl1(g1, boundaryDistanceTolerance),
102     fplres(gres, boundaryDistanceTolerance),
103     invalidLocation()
104 {
105 }
106 
107 /*public*/
108 bool
isValid(OverlayOp::OpCode overlayOp)109 OverlayResultValidator::isValid(OverlayOp::OpCode overlayOp)
110 {
111 
112     addTestPts(g0);
113     addTestPts(g1);
114     addTestPts(gres);
115 
116     if(! testValid(overlayOp)) {
117 #if GEOS_DEBUG
118         cerr << "OverlayResultValidator:" << endl
119              << "Points:" << *toMultiPoint(testCoords) << endl
120              << "Geom0: " << g0 << endl
121              << "Geom1: " << g1 << endl
122              << "Reslt: " << gres << endl
123              << "Locat: " << getInvalidLocation()
124              << endl;
125 #endif
126         return false;
127     }
128 
129 
130     return true;
131 }
132 
133 /*private*/
134 void
addTestPts(const Geometry & g)135 OverlayResultValidator::addTestPts(const Geometry& g)
136 {
137     OffsetPointGenerator ptGen(g, 5 * boundaryDistanceTolerance);
138     unique_ptr< vector<geom::Coordinate> > pts = ptGen.getPoints();
139     testCoords.insert(testCoords.end(), pts->begin(), pts->end());
140 }
141 
142 /*private*/
143 void
addVertices(const Geometry & g)144 OverlayResultValidator::addVertices(const Geometry& g)
145 {
146     // TODO: optimize this by not copying coordinates
147     //       and pre-allocating memory
148     unique_ptr<CoordinateSequence> cs(g.getCoordinates());
149 
150     testCoords.reserve(testCoords.size() + cs->size());
151     for (size_t i = 0; i < cs->size(); i++) {
152         testCoords.push_back(cs->getAt(i));
153     }
154 }
155 
156 /*private*/
157 bool
testValid(OverlayOp::OpCode overlayOp)158 OverlayResultValidator::testValid(OverlayOp::OpCode overlayOp)
159 {
160     for(size_t i = 0, n = testCoords.size(); i < n; ++i) {
161         Coordinate& pt = testCoords[i];
162         if(! testValid(overlayOp, pt)) {
163             invalidLocation = pt;
164             return false;
165         }
166     }
167     return true;
168 }
169 
170 /*private*/
171 bool
testValid(OverlayOp::OpCode overlayOp,const Coordinate & pt)172 OverlayResultValidator::testValid(OverlayOp::OpCode overlayOp,
173                                   const Coordinate& pt)
174 {
175     // TODO use std::array<geom::Location, 3> ?
176     std::vector<geom::Location> location(3);
177 
178     location[0] = fpl0.getLocation(pt);
179     location[1] = fpl1.getLocation(pt);
180     location[2] = fplres.getLocation(pt);
181 
182 #if GEOS_DEBUG
183     cerr << setprecision(10) << "Point " << pt << endl
184          << "Loc0: " << location[0] << endl
185          << "Loc1: " << location[1] << endl
186          << "Locr: " << location[2] << endl;
187 #endif
188 
189     /*
190      * If any location is on the Boundary, can't deduce anything,
191      * so just return true
192      */
193     if(find(location.begin(), location.end(), Location::BOUNDARY) != location.end()) {
194 #if GEOS_DEBUG
195         cerr << "OverlayResultValidator: testpoint " << pt <<
196              " is on the boundary, blindly returning a positive answer (is valid)" << endl;
197 #endif
198         return true;
199     }
200 
201     return isValidResult(overlayOp, location);
202 }
203 
204 /* private */
205 bool
isValidResult(OverlayOp::OpCode overlayOp,std::vector<geom::Location> & location)206 OverlayResultValidator::isValidResult(OverlayOp::OpCode overlayOp,
207                                       std::vector<geom::Location>& location)
208 {
209     bool expectedInterior = OverlayOp::isResultOfOp(location[0],
210                             location[1], overlayOp);
211 
212     bool resultInInterior = (location[2] == Location::INTERIOR);
213 
214     bool p_isValid = !(expectedInterior ^ resultInInterior);
215 
216     return p_isValid;
217 }
218 
219 /*private static*/
220 double
computeBoundaryDistanceTolerance(const geom::Geometry & g0,const geom::Geometry & g1)221 OverlayResultValidator::computeBoundaryDistanceTolerance(
222     const geom::Geometry& g0, const geom::Geometry& g1)
223 {
224     using geos::operation::overlay::snap::GeometrySnapper;
225 
226     return std::min(GeometrySnapper::computeSizeBasedSnapTolerance(g0),
227                     GeometrySnapper::computeSizeBasedSnapTolerance(g1));
228 }
229 
230 } // namespace geos.operation.overlay.validate
231 } // namespace geos.operation.overlay
232 } // namespace geos.operation
233 } // namespace geos
234 
235