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