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 
30 namespace geos { // geos.
31 namespace operation { // geos.operation
32 namespace overlayng { // geos.operation.overlayng
33 
55 class GEOS_DLL Edge {
57 
58 private:
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 
185 public:
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 
339 bool 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
@ 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: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: Angle.h:25