GEOS  3.13.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 
30 namespace geos { // geos.
31 namespace operation { // geos.operation
32 namespace overlayng { // geos.operation.overlayng
33 
55 class GEOS_DLL Edge {
56 
57 private:
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 
184 public:
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(std::unique_ptr<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 
338 bool 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:56
Coordinate is the lightweight class used to store coordinates.
Definition: Coordinate.h:216
@ 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:89
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: Angle.h:25