GEOS 3.14.0dev
operation/overlayng/Edge.h
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
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#pragma once
16
17#include <geos/geom/Coordinate.h>
18#include <geos/geom/CoordinateSequence.h>
19#include <geos/geom/Dimension.h>
20#include <geos/operation/overlayng/OverlayEdge.h>
21#include <geos/operation/overlayng/OverlayLabel.h>
22#include <geos/operation/overlayng/EdgeSourceInfo.h>
23#include <geos/util/GEOSException.h>
24#include <geos/export.h>
25
26
27#include <memory>
28
29
30namespace geos { // geos.
31namespace operation { // geos.operation
32namespace overlayng { // geos.operation.overlayng
33
55class GEOS_DLL Edge {
57
58private:
59
60 // Members
61 int aDim = OverlayLabel::DIM_UNKNOWN;
62 int aDepthDelta = 0;
63 bool aIsHole = false;
64 int bDim = OverlayLabel::DIM_UNKNOWN;
65 int bDepthDelta = 0;
66 bool bIsHole = false;
67 std::unique_ptr<geom::CoordinateSequence> pts;
68
69 // Methods
70
86 static void initLabel(OverlayLabel& lbl, uint8_t geomIndex, int dim, int depthDelta, bool p_isHole);
87
88 static int labelDim(int dim, int depthDelta)
89 {
90 if (dim == geom::Dimension::False)
91 return OverlayLabel::DIM_NOT_PART;
92
93 if (dim == geom::Dimension::L)
94 return OverlayLabel::DIM_LINE;
95
96 // assert: dim is A
97 bool isCollapse = (depthDelta == 0);
98 if (isCollapse)
99 return OverlayLabel::DIM_COLLAPSE;
100
101 return OverlayLabel::DIM_BOUNDARY;
102 };
103
104 bool isHole(int index) const
105 {
106 if (index == 0)
107 return aIsHole;
108 return bIsHole;
109 };
110
111 bool isBoundary(int geomIndex) const
112 {
113 if (geomIndex == 0)
114 return aDim == OverlayLabel::DIM_BOUNDARY;
115 return bDim == OverlayLabel::DIM_BOUNDARY;
116 };
117
122 bool isShell(int geomIndex) const
123 {
124 if (geomIndex == 0) {
125 return (aDim == OverlayLabel::DIM_BOUNDARY && ! aIsHole);
126 }
127 return (bDim == OverlayLabel::DIM_BOUNDARY && ! bIsHole);
128 };
129
130 static Location locationRight(int depthDelta)
131 {
132 int sgn = delSign(depthDelta);
133 switch (sgn) {
134 case 0: return Location::NONE;
135 case 1: return Location::INTERIOR;
136 case -1: return Location::EXTERIOR;
137 }
138 return Location::NONE;
139 };
140
141 static Location locationLeft(int depthDelta)
142 {
143 // TODO: is it always safe to ignore larger depth deltas?
144 int sgn = delSign(depthDelta);
145 switch (sgn) {
146 case 0: return Location::NONE;
147 case 1: return Location::EXTERIOR;
148 case -1: return Location::INTERIOR;
149 }
150 return Location::NONE;
151 };
152
153 static int delSign(int depthDel)
154 {
155 if (depthDel > 0) return 1;
156 if (depthDel < 0) return -1;
157 return 0;
158 };
159
160 void copyInfo(const EdgeSourceInfo* info)
161 {
162 if (info->getIndex() == 0) {
163 aDim = info->getDimension();
164 aIsHole = info->isHole();
165 aDepthDelta = info->getDepthDelta();
166 }
167 else {
168 bDim = info->getDimension();
169 bIsHole = info->isHole();
170 bDepthDelta = info->getDepthDelta();
171 }
172 };
173
174 static bool isHoleMerged(int geomIndex, const Edge* edge1, const Edge* edge2)
175 {
176 // TOD: this might be clearer with tri-state logic for isHole?
177 bool isShell1 = edge1->isShell(geomIndex);
178 bool isShell2 = edge2->isShell(geomIndex);
179 bool isShellMerged = isShell1 || isShell2;
180 // flip since isHole is stored
181 return !isShellMerged;
182 };
183
184
185public:
186
187 Edge()
188 : aDim(OverlayLabel::DIM_UNKNOWN)
189 , aDepthDelta(0)
190 , aIsHole(false)
191 , bDim(OverlayLabel::DIM_UNKNOWN)
192 , bDepthDelta(0)
193 , bIsHole(false)
194 , pts(nullptr)
195 {};
196
197 friend std::ostream& operator<<(std::ostream& os, const Edge& e);
198
199 static bool isCollapsed(const geom::CoordinateSequence* pts);
200
201 // takes ownership of pts from caller
202 Edge(std::unique_ptr<geom::CoordinateSequence>&& p_pts, const EdgeSourceInfo* info);
203
204 // return a clone of the underlying points
205 std::unique_ptr<geom::CoordinateSequence> getCoordinates()
206 {
207 return pts->clone();
208 };
209
210 // return a read-only pointer to the underlying points
211 const geom::CoordinateSequence* getCoordinatesRO() const
212 {
213 return pts.get();
214 };
215
216 // release the underlying points to the caller
217 geom::CoordinateSequence* releaseCoordinates()
218 {
219 geom::CoordinateSequence* cs = pts.release();
220 pts.reset(nullptr);
221 return cs;
222 };
223
224 const geom::Coordinate& getCoordinate(std::size_t index) const
225 {
226 return pts->getAt(index);
227 };
228
229 std::size_t size() const
230 {
231 return pts->size();
232 };
233
234 bool direction() const
235 {
236 if (pts->size() < 2) {
237 throw util::GEOSException("Edge must have >= 2 points");
238 }
239
240 const geom::Coordinate& p0 = pts->getAt(0);
241 const geom::Coordinate& p1 = pts->getAt(1);
242 const geom::Coordinate& pn0 = pts->getAt(pts->size() - 1);
243 const geom::Coordinate& pn1 = pts->getAt(pts->size() - 2);
244
245 int cmp = 0;
246 int cmp0 = p0.compareTo(pn0);
247 if (cmp0 != 0) cmp = cmp0;
248
249 if (cmp == 0) {
250 int cmp1 = p1.compareTo(pn1);
251 if (cmp1 != 0) cmp = cmp1;
252 }
253
254 if (cmp == 0) {
255 throw util::GEOSException("Edge direction cannot be determined because endpoints are equal");
256 }
257
258 return cmp == -1;
259 };
260
265 bool relativeDirection(const Edge* edge2) const
266 {
267 // assert: the edges match (have the same coordinates up to direction)
268 if (!getCoordinate(0).equals2D(edge2->getCoordinate(0))) {
269 return false;
270 }
271 if (!getCoordinate(1).equals2D(edge2->getCoordinate(1))) {
272 return false;
273 }
274 return true;
275 };
276
277 int dimension(int geomIndex) const
278 {
279 if (geomIndex == 0) return aDim;
280 return bDim;
281 };
282
287 void merge(const Edge* edge)
288 {
294 aIsHole = isHoleMerged(0, this, edge);
295 bIsHole = isHoleMerged(1, this, edge);
296
297 if (edge->aDim > aDim) aDim = edge->aDim;
298 if (edge->bDim > bDim) bDim = edge->bDim;
299
300 bool relDir = relativeDirection(edge);
301 int flipFactor = relDir ? 1 : -1;
302 aDepthDelta += flipFactor * edge->aDepthDelta;
303 bDepthDelta += flipFactor * edge->bDepthDelta;
304 };
305
306 void populateLabel(OverlayLabel &lbl) const
307 {
308 initLabel(lbl, 0, aDim, aDepthDelta, aIsHole);
309 initLabel(lbl, 1, bDim, bDepthDelta, bIsHole);
310 };
311
312 bool compareTo(const Edge& e) const
313 {
314 const geom::Coordinate& ca = getCoordinate(0);
315 const geom::Coordinate& cb = e.getCoordinate(0);
316 if(ca.compareTo(cb) < 0) {
317 return true;
318 }
319 else if (ca.compareTo(cb) > 0) {
320 return false;
321 }
322 else {
323 const geom::Coordinate& cca = getCoordinate(1);
324 const geom::Coordinate& ccb = e.getCoordinate(1);
325 if(cca.compareTo(ccb) < 0) {
326 return true;
327 }
328 else if (cca.compareTo(ccb) > 0) {
329 return false;
330 }
331 else {
332 return false;
333 }
334 }
335 }
336
337};
338
339bool EdgeComparator(const Edge* a, const Edge* b);
340
341
342
343} // namespace geos.operation.overlayng
344} // namespace geos.operation
345} // namespace geos
346
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:56
Coordinate is the lightweight class used to store coordinates.
Definition Coordinate.h:217
Definition EdgeSourceInfo.h:38
Definition operation/overlayng/Edge.h:55
bool relativeDirection(const Edge *edge2) const
Definition operation/overlayng/Edge.h:265
void merge(const Edge *edge)
Definition operation/overlayng/Edge.h:287
Definition OverlayLabel.h:86
Base class for all GEOS exceptions.
Definition GEOSException.h:37
Location
Constants representing the location of a point relative to a geometry.
Definition Location.h:32
Basic namespace for all GEOS functionalities.
Definition geos.h:39