Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add precision reducer classes along with associated and dependent classes and tests #24

Merged
merged 3 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/dart_jts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ part 'src/com/hydrologis/dart_jts/operation/overlay.dart';
part 'src/com/hydrologis/dart_jts/operation/predicate.dart';
part 'src/com/hydrologis/dart_jts/operation/buffer.dart';
part 'src/com/hydrologis/dart_jts/operation/buffer_validate.dart';
part 'src/com/hydrologis/dart_jts/edgegraph/half_edge.dart';
part 'src/com/hydrologis/dart_jts/edgegraph/edge_graph.dart';
part 'src/com/hydrologis/dart_jts/edgegraph/edge_graph_builder.dart';
part 'src/com/hydrologis/dart_jts/edgegraph/mark_half_edge.dart';
part 'src/com/hydrologis/dart_jts/noding/noding.dart';
part 'src/com/hydrologis/dart_jts/noding/fast_noding_validator.dart';
part 'src/com/hydrologis/dart_jts/noding/noding_intersection_finder.dart';
part 'src/com/hydrologis/dart_jts/noding/snap_round.dart';
part 'src/com/hydrologis/dart_jts/math/math.dart';
part 'src/com/hydrologis/dart_jts/geomgraph/geomgraph.dart';
part 'src/com/hydrologis/dart_jts/geomgraph/index.dart';
Expand All @@ -47,6 +54,7 @@ part 'src/com/hydrologis/dart_jts/index/index.dart';
part 'src/com/hydrologis/dart_jts/index/chain.dart';
part 'src/com/hydrologis/dart_jts/index/strtree.dart';
part 'src/com/hydrologis/dart_jts/index/quadtree.dart';
part 'src/com/hydrologis/dart_jts/index/kdtree.dart';
part 'src/com/hydrologis/dart_jts/util/avltree.dart';
part 'src/com/hydrologis/dart_jts/extra.dart';
part 'src/com/hydrologis/dart_jts/addons/geodesy.dart';
Expand All @@ -71,3 +79,25 @@ part 'src/com/hydrologis/dart_jts/simplify/line_segment_index.dart';
part 'src/com/hydrologis/dart_jts/simplify/tagged_line_string_simplifier.dart';
part 'src/com/hydrologis/dart_jts/simplify/vw_simplifier.dart';
part 'src/com/hydrologis/dart_jts/simplify/vw_line_simplifier.dart';
part 'src/com/hydrologis/dart_jts/precision/geometry_precision_reducer.dart';
part 'src/com/hydrologis/dart_jts/precision/pointwise_precision_reducer_transformer.dart';
part 'src/com/hydrologis/dart_jts/precision/precision_reducer_transformer.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_ng.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/intersection_point_builder.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_edge_ring.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_edge.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_mixed_points.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/indexed_point_on_line_locator.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/input_geometry.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_util.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/elevation_model.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_label.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_graph.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/overlay_points.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/line_builder_ng.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/edge_noding_builder.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/ring_clipper.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/line_limiter.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/edge_source_info.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/edge_merger.dart';
part 'src/com/hydrologis/dart_jts/operation/overlayng/maximal_edge_ring_ng.dart';
104 changes: 97 additions & 7 deletions lib/src/com/hydrologis/dart_jts/algorithm/algorithm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,41 @@ class CGAlgorithmsDD {
.signum();
}

/**
* Returns the index of the direction of the point {@code q} relative to
* a vector specified by {@code p1-p2}.
*
* @param p1x the x ordinate of the vector origin point
* @param p1y the y ordinate of the vector origin point
* @param p2x the x ordinate of the vector final point
* @param p2y the y ordinate of the vector final point
* @param qx the x ordinate of the query point
* @param qy the y ordinate of the query point
*
* @return 1 if q is counter-clockwise (left) from p1-p2
* -1 if q is clockwise (right) from p1-p2
* 0 if q is collinear with p1-p2
*/
static int orientationIndexDouble(
double p1x, double p1y, double p2x, double p2y, double qx, double qy) {
// fast filter for orientation index
// avoids use of slow extended-precision arithmetic in many cases
int index = orientationIndexFilterDouble(p1x, p1y, p2x, p2y, qx, qy);
if (index <= 1) return index;

// normalize coordinates
DD dx1 = DD.valueOf(p2x).selfAdd(-p1x);
DD dy1 = DD.valueOf(p2y).selfAdd(-p1y);
DD dx2 = DD.valueOf(qx).selfAdd(-p2x);
DD dy2 = DD.valueOf(qy).selfAdd(-p2y);

// sign of determinant - unrolled for performance
return dx1
.selfMultiplyDD(dy2)
.selfSubtractDD(dy1.selfMultiplyDD(dx2))
.signum();
}

/// Computes the sign of the determinant of the 2x2 matrix
/// with the given entries.
///
Expand Down Expand Up @@ -117,6 +152,61 @@ class CGAlgorithmsDD {
return 2;
}

/**
* A filter for computing the orientation index of three coordinates.
* <p>
* If the orientation can be computed safely using standard DP
* arithmetic, this routine returns the orientation index.
* Otherwise, a value i > 1 is returned.
* In this case the orientation index must
* be computed using some other more robust method.
* The filter is fast to compute, so can be used to
* avoid the use of slower robust methods except when they are really needed,
* thus providing better average performance.
* <p>
* Uses an approach due to Jonathan Shewchuk, which is in the public domain.
*
* @param pax A coordinate
* @param pay A coordinate
* @param pbx B coordinate
* @param pby B coordinate
* @param pcx C coordinate
* @param pcy C coordinate
* @return the orientation index if it can be computed safely
* @return i > 1 if the orientation index cannot be computed safely
*/
static int orientationIndexFilterDouble(
double pax, double pay, double pbx, double pby, double pcx, double pcy) {
double detsum;

double detleft = (pax - pcx) * (pby - pcy);
double detright = (pay - pcy) * (pbx - pcx);
double det = detleft - detright;

if (detleft > 0.0) {
if (detright <= 0.0) {
return signum(det);
} else {
detsum = detleft + detright;
}
} else if (detleft < 0.0) {
if (detright >= 0.0) {
return signum(det);
} else {
detsum = -detleft - detright;
}
} else {
return signum(det);
}

double errbound = DP_SAFE_EPSILON * detsum;
if ((det >= errbound) || (-det >= errbound)) {
return signum(det);
}

return 2;
}

static int signum(double x) {
if (x > 0) return 1;
if (x < 0) return -1;
Expand Down Expand Up @@ -221,22 +311,22 @@ class Orientation {
* dependent, when computing the orientation of a point very close to a
* line. This is possibly due to the arithmetic in the translation to the
* origin.
*
*
* For instance, the following situation produces identical results in spite
* of the inverse orientation of the line segment:
*
*
* Coordinate p0 = new Coordinate(219.3649559090992, 140.84159161824724);
* Coordinate p1 = new Coordinate(168.9018919682399, -5.713787599646864);
*
*
* Coordinate p = new Coordinate(186.80814046338352, 46.28973405831556); int
* orient = orientationIndex(p0, p1, p); int orientInv =
* orientationIndex(p1, p0, p);
*
*
* A way to force consistent results is to normalize the orientation of the
* vector using the following code. However, this may make the results of
* orientationIndex inconsistent through the triangle of points, so it's not
* clear this is an appropriate patch.
*
*
*/
return CGAlgorithmsDD.orientationIndex(p1, p2, q);

Expand Down Expand Up @@ -313,7 +403,7 @@ class Orientation {
* If disc is exactly 0, lines are collinear. There are two possible cases:
* (1) the lines lie along the x axis in opposite directions (2) the lines
* lie on top of one another
*
*
* (1) is handled by checking if next is left of prev ==> CCW (2) will never
* happen if the ring is valid, so don't check for it (Might want to assert
* this)
Expand Down Expand Up @@ -412,10 +502,10 @@ class Orientation {
}
return isCCW;
}

static bool isCCWArea(List<Coordinate> ring) {
return Area.ofRingSigned(ring) < 0;
}

}

/// Contains functions to compute intersections between lines.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/com/hydrologis/dart_jts/algorithm/convexthull.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class ConvexHull {
return null;
}
coordList.closeRing();
return coordList.toCoordinateArray();
return coordList.toCoordinateArray(true);
}

List<Coordinate> computeOctPts(List<Coordinate> inputPts) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/com/hydrologis/dart_jts/densify/densifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Densifier {
}
// this check handles empty sequences
if (pts.length > 0) coordList.addCoord(pts[pts.length - 1], false);
return coordList.toCoordinateArray();
return coordList.toCoordinateArray(true);
}

late Geometry inputGeom;
Expand Down
143 changes: 143 additions & 0 deletions lib/src/com/hydrologis/dart_jts/edgegraph/edge_graph.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
part of dart_jts;

/**
* A graph comprised of {@link HalfEdge}s.
* It supports tracking the vertices in the graph
* via edges incident on them,
* to allow efficient lookup of edges and vertices.
* <p>
* This class may be subclassed to use a
* different subclass of HalfEdge,
* by overriding {@link #createEdge(Coordinate)}.
* If additional logic is required to initialize
* edges then {@link EdgeGraph#addEdge(Coordinate, Coordinate)}
* can be overridden as well.
*
* @author Martin Davis
*
*/
class EdgeGraph {
Map<Coordinate, HalfEdge> vertexMap = {};

EdgeGraph() {}

/**
* Creates a single HalfEdge.
* Override to use a different HalfEdge subclass.
*
* @param orig the origin location
* @return a new HalfEdge with the given origin
*/
HalfEdge createEdge(Coordinate orig) {
return new HalfEdge(orig);
}

/**
* Creates a HalfEge pair, using the HalfEdge type of the graph subclass.
*
* @param p0
* @param p1
* @return
*/
HalfEdge create(Coordinate p0, Coordinate p1) {
HalfEdge e0 = createEdge(p0);
HalfEdge e1 = createEdge(p1);
e0.link(e1);
return e0;
}

/**
* Adds an edge between the coordinates orig and dest
* to this graph.
* Only valid edges can be added (in particular, zero-length segments cannot be added)
*
* @param orig the edge origin location
* @param dest the edge destination location.
* @return the created edge
* @return null if the edge was invalid and not added
*
* @see #isValidEdge(Coordinate, Coordinate)
*/
HalfEdge? addEdge(Coordinate orig, Coordinate dest) {
if (!isValidEdge(orig, dest)) return null;

/**
* Attempt to find the edge already in the graph.
* Return it if found.
* Otherwise, use a found edge with same origin (if any) to construct new edge.
*/
HalfEdge? eAdj = vertexMap[orig];
HalfEdge? eSame = null;
if (eAdj != null) {
eSame = eAdj.find(dest);
}
if (eSame != null) {
return eSame;
}

HalfEdge e = insert(orig, dest, eAdj!);
return e;
}

/**
* Tests if the given coordinates form a valid edge (with non-zero length).
*
* @param orig the start coordinate
* @param dest the end coordinate
* @return true if the edge formed is valid
*/
static bool isValidEdge(Coordinate orig, Coordinate dest) {
int cmp = dest.compareTo(orig);
return cmp != 0;
}

/**
* Inserts an edge not already present into the graph.
*
* @param orig the edge origin location
* @param dest the edge destination location
* @param eAdj an existing edge with same orig (if any)
* @return the created edge
*/
HalfEdge insert(Coordinate orig, Coordinate dest, HalfEdge? eAdj) {
// edge does not exist, so create it and insert in graph
HalfEdge e = create(orig, dest);
if (eAdj != null) {
eAdj.insert(e);
} else {
vertexMap.putIfAbsent(orig, () => e);
}

HalfEdge? eAdjDest = vertexMap[dest];
if (eAdjDest != null) {
eAdjDest.insert(e.symEdge);
} else {
vertexMap.putIfAbsent(dest, () => e.symEdge);
}
return e;
}

/**
* Gets all {@link HalfEdge}s in the graph.
* Both edges of edge pairs are included.
*
* @return a collection of the graph edges
*/
List<HalfEdge> getVertexEdges() {
return vertexMap.values.toList();
}

/**
* Finds an edge in this graph with the given origin
* and destination, if one exists.
*
* @param orig the origin location
* @param dest the destination location.
* @return an edge with the given orig and dest, or null if none exists
*/
HalfEdge? findEdge(Coordinate orig, Coordinate dest) {
HalfEdge? e = vertexMap[orig];
if (e == null) return null;
return e.find(dest);
}
}
Loading