1 /**********************************************************************
2  *
3  * GEOS - Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2006 Refractions Research Inc.
7  * Copyright (C) 2001-2002 Vivid Solutions Inc.
8  *
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.
13  *
14  **********************************************************************
15  *
16  * Last port: index/chain/MonotoneChain.java rev. 1.15 (JTS-1.10)
17  *
18  **********************************************************************/
19 
20 #include <geos/index/chain/MonotoneChain.h>
21 #include <geos/index/chain/MonotoneChainSelectAction.h>
22 #include <geos/index/chain/MonotoneChainOverlapAction.h>
23 #include <geos/geom/CoordinateSequence.h>
24 #include <geos/geom/LineSegment.h>
25 #include <geos/geom/Envelope.h>
26 #include <geos/util.h>
27 
28 using namespace geos::geom;
29 
30 namespace geos {
31 namespace index { // geos.index
32 namespace chain { // geos.index.chain
33 
MonotoneChain(const geom::CoordinateSequence & newPts,size_t nstart,size_t nend,void * nContext)34 MonotoneChain::MonotoneChain(const geom::CoordinateSequence& newPts,
35                              size_t nstart, size_t nend, void* nContext)
36     :
37     pts(newPts),
38     context(nContext),
39     start(nstart),
40     end(nend),
41     env(newPts[nstart], newPts[nend]),
42     envIsSet(false),
43     id(-1)
44 {
45 }
46 
47 const Envelope&
getEnvelope()48 MonotoneChain::getEnvelope()
49 {
50     return getEnvelope(0.0);
51 }
52 
53 const Envelope&
getEnvelope(double expansionDistance)54 MonotoneChain::getEnvelope(double expansionDistance)
55 {
56     if (!envIsSet) {
57         env.init(pts[start], pts[end]);
58         if (expansionDistance > 0.0) {
59             env.expandBy(expansionDistance);
60         }
61         envIsSet = true;
62     }
63     return env;
64 }
65 
66 void
getLineSegment(size_t index,LineSegment & ls) const67 MonotoneChain::getLineSegment(size_t index, LineSegment& ls) const
68 {
69     ls.p0 = pts[index];
70     ls.p1 = pts[index + 1];
71 }
72 
73 std::unique_ptr<CoordinateSequence>
getCoordinates() const74 MonotoneChain::getCoordinates() const
75 {
76     return std::unique_ptr<CoordinateSequence>(pts.clone());
77 }
78 
79 void
select(const Envelope & searchEnv,MonotoneChainSelectAction & mcs)80 MonotoneChain::select(const Envelope& searchEnv, MonotoneChainSelectAction& mcs)
81 {
82     computeSelect(searchEnv, start, end, mcs);
83 }
84 
85 void
computeSelect(const Envelope & searchEnv,size_t start0,size_t end0,MonotoneChainSelectAction & mcs)86 MonotoneChain::computeSelect(const Envelope& searchEnv,
87                              size_t start0, size_t end0,
88                              MonotoneChainSelectAction& mcs)
89 {
90     const Coordinate& p0 = pts[start0];
91     const Coordinate& p1 = pts[end0];
92 
93     // terminating condition for the recursion
94     if(end0 - start0 == 1) {
95         mcs.select(*this, start0);
96         return;
97     }
98     // nothing to do if the envelopes don't overlap
99     if(!searchEnv.intersects(p0, p1)) {
100         return;
101     }
102     // the chains overlap,so split each in half and iterate (binary search)
103     size_t mid = (start0 + end0) / 2;
104 
105     // Assert: mid != start or end (since we checked above for end-start <= 1)
106     // check terminating conditions before recursing
107     if(start0 < mid) {
108         computeSelect(searchEnv, start0, mid, mcs);
109     }
110 
111     if(mid < end0) {
112         computeSelect(searchEnv, mid, end0, mcs);
113     }
114 }
115 
116 /* public */
117 void
computeOverlaps(MonotoneChain * mc,MonotoneChainOverlapAction * mco)118 MonotoneChain::computeOverlaps(MonotoneChain* mc,
119                                MonotoneChainOverlapAction* mco)
120 {
121     computeOverlaps(start, end, *mc, mc->start, mc->end, 0.0, *mco);
122 }
123 
124 /* public */
125 void
computeOverlaps(MonotoneChain * mc,double overlapTolerance,MonotoneChainOverlapAction * mco)126 MonotoneChain::computeOverlaps(MonotoneChain* mc, double overlapTolerance,
127                                MonotoneChainOverlapAction* mco)
128 {
129     computeOverlaps(start, end, *mc, mc->start, mc->end, overlapTolerance, *mco);
130 }
131 
132 /*private*/
133 void
computeOverlaps(size_t start0,size_t end0,MonotoneChain & mc,size_t start1,size_t end1,double overlapTolerance,MonotoneChainOverlapAction & mco)134 MonotoneChain::computeOverlaps(size_t start0, size_t end0,
135                                MonotoneChain& mc,
136                                size_t start1, size_t end1,
137                                double overlapTolerance,
138                                MonotoneChainOverlapAction& mco)
139 {
140     // terminating condition for the recursion
141     if(end0 - start0 == 1 && end1 - start1 == 1) {
142         mco.overlap(*this, start0, mc, start1);
143         return;
144     }
145 
146     // nothing to do if the envelopes of these subchains don't overlap
147     if(!overlaps(start0, end0, mc, start1, end1, overlapTolerance)) {
148         return;
149     }
150 
151     // the chains overlap,so split each in half and iterate (binary search)
152     size_t mid0 = (start0 + end0) / 2;
153     size_t mid1 = (start1 + end1) / 2;
154 
155     // Assert: mid != start or end (since we checked above for
156     // end-start <= 1)
157     // check terminating conditions before recursing
158     if(start0 < mid0) {
159         if(start1 < mid1) {
160             computeOverlaps(start0, mid0, mc, start1, mid1, overlapTolerance, mco);
161         }
162         if(mid1 < end1) {
163             computeOverlaps(start0, mid0, mc, mid1, end1, overlapTolerance, mco);
164         }
165     }
166 
167     if(mid0 < end0) {
168         if(start1 < mid1) {
169             computeOverlaps(mid0, end0, mc, start1, mid1, overlapTolerance, mco);
170         }
171         if(mid1 < end1) {
172             computeOverlaps(mid0, end0, mc, mid1, end1, overlapTolerance, mco);
173         }
174     }
175 }
176 
177 /*private*/
178 bool
overlaps(size_t start0,size_t end0,const MonotoneChain & mc,size_t start1,size_t end1,double overlapTolerance) const179 MonotoneChain::overlaps(size_t start0, size_t end0, const MonotoneChain& mc,
180                         size_t start1, size_t end1, double overlapTolerance) const
181 {
182     if (overlapTolerance > 0.0) {
183         return overlaps(pts[start0], pts[end0], mc.pts[start1], mc.pts[end1], overlapTolerance);
184     }
185     return Envelope::intersects(pts.getAt(start0), pts.getAt(end0),
186                                 mc.pts.getAt(start1), mc.pts.getAt(end1));
187 }
188 
189 /*private*/
190 bool
overlaps(const Coordinate & p1,const Coordinate & p2,const Coordinate & q1,const Coordinate & q2,double overlapTolerance) const191 MonotoneChain::overlaps(const Coordinate& p1, const Coordinate& p2,
192                         const Coordinate& q1, const Coordinate& q2,
193                         double overlapTolerance) const
194 {
195     double minq = std::min(q1.x, q2.x);
196     double maxq = std::max(q1.x, q2.x);
197     double minp = std::min(p1.x, p2.x);
198     double maxp = std::max(p1.x, p2.x);
199 
200     if (minp > (maxq + overlapTolerance))
201         return false;
202     if (maxp < (minq - overlapTolerance))
203         return false;
204 
205     minq = std::min(q1.y, q2.y);
206     maxq = std::max(q1.y, q2.y);
207     minp = std::min(p1.y, p2.y);
208     maxp = std::max(p1.y, p2.y);
209 
210     if (minp > (maxq + overlapTolerance))
211         return false;
212     if (maxp < (minq - overlapTolerance))
213         return false;
214     return true;
215 }
216 
217 
218 } // namespace geos.index.chain
219 } // namespace geos.index
220 } // namespace geos
221