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