diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java new file mode 100644 index 0000000000..c934584aa9 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.distance; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.util.Assert; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * The Fréchet distance is a measure of similarity between curves. Thus, it can + * be used like the Hausdorff distance. + *

+ * An analogy for the Fréchet distance taken from + * + * Computing Discrete Fréchet Distance + *

+ * A man is walking a dog on a leash: the man can move
+ * on one curve, the dog on the other; both may vary their
+ * speed, but backtracking is not allowed.
+ * 
+ *

+ * Its metric is better than Hausdorff's because it takes the flow of the curves + * into account. It is possible that two curves have a small Hausdorff but a large + * Fréchet distance. + *

+ * This implementation is base on the following optimized Fréchet distance algorithm: + *

Thomas Devogele, Maxence Esnault, Laurent Etienne. Distance discrète de Fréchet optimisée. Spatial
+ * Analysis and Geomatics (SAGEO), Nov 2016, Nice, France. hal-02110055
+ *

+ * Several matrix storage implementations are provided + * + * @see Fréchet distance + * @see + * Computing Discrete Fréchet Distance + * @see Distance discrète de Fréchet optimisée + * @see + * Fast Discrete Fréchet Distance + */ +public class DiscreteFrechetDistance { + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1) { + + DiscreteFrechetDistance dist = new DiscreteFrechetDistance(g0, g1); + return dist.distance(); + } + + private final Geometry g0; + private final Geometry g1; + private PointPairDistance ptDist; + + /** + * Creates an instance of this class using the provided geometries. + * + * @param g0 a geometry + * @param g1 a geometry + */ + private DiscreteFrechetDistance(Geometry g0, Geometry g1) { + this.g0 = g0; + this.g1 = g1; + } + + /** + * Compute the {@code Discrete Fréchet Distance} between two geometries + * + * @return the Discrete Fréchet Distance + */ + private double distance() { + Coordinate[] coords0 = g0.getCoordinates(); + Coordinate[] coords1 = g1.getCoordinates(); + + MatrixStorage distances = createMatrixStorage(coords0.length, coords1.length); + int[] diagonal = bresenhamLine(coords0.length, coords1.length); + + HashMap distanceToPair = new HashMap<>(); + computeCoordinateDistances(coords0, coords1, diagonal, distances, distanceToPair); + ptDist = computeFrechet(coords0, coords1, diagonal, distances, distanceToPair); + + return ptDist.getDistance(); + } + + /** + * Creates a matrix storage + * @param rows the number of rows + * @param cols the number of columns + * @return a matrix storage + */ + private MatrixStorage createMatrixStorage(int rows, int cols) { + + int max = Math.max(rows, cols); + // NOTE: these constraints need to be verified + if (max < 1024) + return new RectMatrix(rows, cols, Double.POSITIVE_INFINITY); + + return new CsrMatrix(rows, cols, Double.POSITIVE_INFINITY); + } + + /** + * Gets the pair of Coordinates that are {@link #distance()} apart. + * + * @return the pair of Coordinates that are {@link #distance()} apart + */ + public Coordinate[] getCoordinates() { + if (ptDist == null) + distance(); + + return ptDist.getCoordinates(); + } + + /** + * Compute the Fréchet Distance for the given distance matrix. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param distances a sparse distance matrix + * @param distanceToPair a lookup for distance and a coordinate pair + * @param diagonal an array of alternating row/col index values for the diagonal of the distance matrix + * + */ + private static PointPairDistance computeFrechet(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, + MatrixStorage distances, HashMap distanceToPair) { + for (int d = 0; d < diagonal.length; d += 2) { + int i0 = diagonal[d]; + int j0 = diagonal[d + 1]; + + for (int i = i0; i < coords0.length; i++) { + if (distances.isValueSet(i, j0)) { + double dist = getMinDistanceAtCorner(distances, i, j0); + if (dist > distances.get(i, j0)) + distances.set(i, j0, dist); + } + else { + break; + } + } + for (int j = j0 + 1; j < coords1.length; j++) { + if (distances.isValueSet(i0, j)) { + double dist = getMinDistanceAtCorner(distances, i0, j); + if (dist > distances.get(i0, j)) + distances.set(i0, j, dist); + } + else { + break; + } + } + } + + PointPairDistance result = new PointPairDistance(); + double distance = distances.get(coords0.length-1, coords1.length - 1); + int[] index = distanceToPair.get(distance); + if (index != null) + result.initialize(coords0[index[0]], coords1[index[1]], distance); + else + Assert.shouldNeverReachHere("Pair of points not recorded for computed distance"); + + return result; + } + + /** + * Returns the minimum distance at the corner ({@code i, j}). + * + * @param matrix A sparse matrix + * @param i the column index + * @param j the row index + * @return the minimum distance + */ + private static double getMinDistanceAtCorner(MatrixStorage matrix, int i, int j) { + if (i > 0 && j > 0) { + double d0 = matrix.get(i - 1, j - 1); + double d1 = matrix.get(i - 1, j); + double d2 = matrix.get(i, j - 1); + return Math.min(Math.min(d0, d1), d2); + } + if (i == 0 && j == 0) + return matrix.get(0, 0); + + if (i == 0) + return matrix.get(0, j - 1); + + // j == 0 + return matrix.get(i - 1, 0); + } + + /** + * Computes relevant distances between pairs of {@link Coordinate}s for the + * computation of the {@code Discrete Fréchet Distance}. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param diagonal an array of encoded row/col index values for the diagonal of the distance matrix + * @param distances the sparse distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance. + */ + private void computeCoordinateDistances(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, + MatrixStorage distances, HashMap distanceToPair) { + int numDiag = diagonal.length; + double maxDistOnDiag = 0d; + int imin = 0, jmin = 0; + int numCoords0 = coords0.length; + int numCoords1 = coords1.length; + + // First compute all the distances along the diagonal. + // Record the maximum distance. + + for (int k = 0; k < numDiag; k += 2) { + int i0 = diagonal[k]; + int j0 = diagonal[k + 1]; + double diagDist = coords0[i0].distance(coords1[j0]); + if (diagDist > maxDistOnDiag) maxDistOnDiag = diagDist; + distances.set(i0, j0, diagDist); + distanceToPair.putIfAbsent(diagDist, new int[] {i0, j0}); + } + + // Check for distances shorter than maxDistOnDiag along the diagonal + for (int k = 0; k < numDiag - 2; k += 2) { + // Decode index + int i0 = diagonal[k]; + int j0 = diagonal[k + 1]; + + // Get reference coordinates for col and row + Coordinate coord0 = coords0[i0]; + Coordinate coord1 = coords1[j0]; + + // Check for shorter distances in this row + int i = i0 + 1; + for (; i < numCoords0; i++) { + if (!distances.isValueSet(i, j0)) { + double dist = coords0[i].distance(coord1); + if (dist < maxDistOnDiag || i < imin) { + distances.set(i, j0, dist); + distanceToPair.putIfAbsent(dist, new int[] {i, j0}); + } + else + break; + } + else + break; + } + imin = i; + + // Check for shorter distances in this column + int j = j0 + 1; + for (; j < numCoords1; j++) { + if (!distances.isValueSet(i0, j)) { + double dist = coord0.distance(coords1[j]); + if (dist < maxDistOnDiag || j < jmin) + { + distances.set(i0, j, dist); + distanceToPair.putIfAbsent(dist, new int[] {i0, j}); + } + else + break; + } + else + break; + } + jmin = j; + } + + //System.out.println(distances.toString()); + } + + /** + * Implementation of the + * Bresenham's line algorithm for the diagonal of a {@code numCols x numRows} grid. + * + * @param numCols the number of columns + * @param numRows the number of rows + * @return an array of column and row indices bitwise-or combined. + */ + private static int[] bresenhamLine(int numCols, int numRows) { + int dim = Math.max(numCols, numRows); + int[] pairs = new int[2 * dim]; + + int sx = 0 > numCols ? -1 : 1; + int sy = 0 > numRows ? -1 : 1; + int x = 0; + int y = 0; + + int err; + if (numCols > numRows) { + err = numCols / 2; + for (int i = 0, j = 0; i < numCols; i++) { + pairs[j++] = x; + pairs[j++] = y; + err -= numRows; + if (err < 0) { + y += sy; + err += numCols; + } + x += sx; + } + } else { + err = numRows / 2; + for (int i = 0, j = 0; i < numRows; i++) { + pairs[j++] = x; + pairs[j++] = y; + err -= numCols; + if (err < 0) { + x += sx; + err += numRows; + } + y += sy; + } + } + return pairs; + } + + /** + * Abstract base class for storing 2d matrix data + */ + abstract static class MatrixStorage { + + protected final int numRows; + protected final int numCols; + protected final double defaultValue; + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + public MatrixStorage(int numRows, int numCols, double defaultValue) + { + this.numRows = numRows; + this.numCols = numCols; + this.defaultValue = defaultValue; + } + + /** + * Gets the matrix value at i, j + * @param i the row index + * @param j the column index + * @return The matrix value at i, j + */ + public abstract double get(int i, int j); + + /** + * Sets the matrix value at i, j + * @param i the row index + * @param j the column index + * @param value The matrix value to set at i, j + */ + public abstract void set(int i, int j, double value); + + /** + * Gets a flag indicating if the matrix has a set value, e.g. one that is different + * than {@link MatrixStorage#defaultValue}. + * @param i the row index + * @param j the column index + * @return a flag indicating if the matrix has a set value + */ + public abstract boolean isValueSet(int i, int j); + + /* For debugging purposes only + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < this.numRows; i++) + { + sb.append('['); + for(int j = 0; j < this.numCols; j++) + { + if (j > 0) + sb.append(", "); + sb.append(String.format(java.util.Locale.ROOT, "%8.4f", get(i, j))); + } + sb.append(']'); + if (i < this.numRows - 1) sb.append(",\n"); + } + sb.append(']'); + return sb.toString(); + } + */ + } + + /** + * Straight forward implementation of a rectangular matrix + */ + final static class RectMatrix extends MatrixStorage { + + private final double[] matrix; + + /** + * Creates an instance of this matrix using the given number of rows and columns. + * A default value can be specified + * + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + public RectMatrix(int numRows, int numCols, double defaultValue) + { + super(numRows, numCols, defaultValue); + this.matrix = new double[numRows * numCols]; + Arrays.fill(this.matrix, defaultValue); + } + + public double get(int i, int j) { return this.matrix[i * numCols + j]; } + + public void set(int i, int j, double value) { + this.matrix[i * numCols + j] = value; + } + + public boolean isValueSet(int i, int j) { + return Double.doubleToLongBits(get(i, j)) != Double.doubleToLongBits(this.defaultValue); + } + } + + /** + * A matrix implementation that adheres to the + * + * Compressed sparse row format.
+ * Note: Unfortunately not as fast as expected. + */ + final static class CsrMatrix extends MatrixStorage { + + private double[] v; + private final int[] ri; + private int[] ci; + + public CsrMatrix(int numRows, int numCols, double defaultValue) { + this(numRows, numCols, defaultValue, expectedValuesHeuristic(numRows, numCols)); + } + public CsrMatrix(int numRows, int numCols, double defaultValue, int expectedValues) { + super(numRows, numCols, defaultValue); + this.v = new double[expectedValues]; + this.ci = new int[expectedValues]; + this.ri = new int[numRows + 1]; + } + + /** + * Computes an initial value for the number of expected values + * @param numRows the number of rows + * @param numCols the number of columns + * @return the expected number of values in the sparse matrix + */ + private static int expectedValuesHeuristic(int numRows, int numCols) { + int max = Math.max(numRows, numCols); + return max * max / 10; + } + + private int indexOf(int i, int j) { + int cLow = this.ri[i]; + int cHigh = this.ri[i+1]; + if (cHigh <= cLow) return ~cLow; + + return Arrays.binarySearch(this.ci, cLow, cHigh, j); + } + + @Override + public double get(int i, int j) { + + // get the index in the vector + int vi = indexOf(i, j); + + // if the vector index is negative, return default value + if (vi < 0) + return defaultValue; + + return this.v[vi]; + } + + @Override + public void set(int i, int j, double value) { + + // get the index in the vector + int vi = indexOf(i, j); + + // do we already have a value? + if (vi < 0) + { + // no, we don't, we need to ensure space! + ensureCapacity(this.ri[this.numRows] + 1); + + // update row indices + for (int ii = i + 1; ii <= this.numRows; ii++) + ri[ii] += 1; + + // move and update column indices, move values + vi = ~vi; + for (int ii = this.ri[this.numRows]; ii > vi; ii--) + { + this.ci[ii] = this.ci[ii - 1]; + this.v[ii] = this.v[ii - 1]; + } + + // insert column index + ci[vi] = j; + } + + // set the new value + v[vi] = value; + } + + @Override + public boolean isValueSet(int i, int j) { + return indexOf(i, j) >= 0; + } + + + /** + * Ensures that the column index vector (ci) and value vector (v) are sufficiently large. + * @param required the number of items to store in the matrix + */ + private void ensureCapacity(int required) { + if (required < this.v.length) + return; + + int increment = Math.max(this.numRows, this.numCols); + this.v = Arrays.copyOf(this.v, this.v.length + increment); + this.ci = Arrays.copyOf(this.ci, this.v.length + increment); + } + } + + /** + * A sparse matrix based on java's {@link HashMap}. + */ + final static class HashMapMatrix extends MatrixStorage { + + private final HashMap matrix; + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue a default value + */ + public HashMapMatrix(int numRows, int numCols, double defaultValue) { + super(numRows, numCols, defaultValue); + this.matrix = new HashMap<>(); + } + + public double get(int i, int j) { + long key = (long)i << 32 | j; + return matrix.getOrDefault(key, this.defaultValue); + } + + public void set(int i, int j, double value) { + long key = (long)i << 32 | j; + matrix.put(key, value); + } + + public boolean isValueSet(int i, int j) { + long key = (long)i << 32 | j; + return matrix.containsKey(key); + } + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java index d3adef89fe..3f01fb81c5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java @@ -22,31 +22,38 @@ */ public class PointPairDistance { - private Coordinate[] pt = { new Coordinate(), new Coordinate() }; + private final Coordinate[] pt = { new Coordinate(), new Coordinate() }; private double distance = Double.NaN; private boolean isNull = true; + /** + * Creates an instance of this class + */ public PointPairDistance() { } + /** + * Initializes this instance. + */ public void initialize() { isNull = true; } - public void initialize(Coordinate p0, Coordinate p1) - { - pt[0].setCoordinate(p0); - pt[1].setCoordinate(p1); - distance = p0.distance(p1); - isNull = false; + /** + * Initializes the points, computing the distance between them. + * @param p0 the 1st point + * @param p1 the 2nd point + */ + public void initialize(Coordinate p0, Coordinate p1) { + initialize(p0, p1, p0.distance(p1)); } /** * Initializes the points, avoiding recomputing the distance. - * @param p0 - * @param p1 + * @param p0 the 1st point + * @param p1 the 2nd point * @param distance the distance between p0 and p1 */ - private void initialize(Coordinate p0, Coordinate p1, double distance) + void initialize(Coordinate p0, Coordinate p1, double distance) { pt[0].setCoordinate(p0); pt[1].setCoordinate(p1); @@ -54,10 +61,23 @@ private void initialize(Coordinate p0, Coordinate p1, double distance) isNull = false; } + /** + * Gets the distance between the paired points + * @return the distance between the paired points + */ public double getDistance() { return distance; } + /** + * Gets the paired points + * @return the paired points + */ public Coordinate[] getCoordinates() { return pt; } + /** + * Gets one of the paired points + * @param i the index of the paired point (0 or 1) + * @return A point + */ public Coordinate getCoordinate(int i) { return pt[i]; } public void setMaximum(PointPairDistance ptDist) @@ -91,7 +111,7 @@ public void setMinimum(Coordinate p0, Coordinate p1) if (dist < distance) initialize(p0, p1, dist); } - + public String toString() { return WKTWriter.toLineString(pt[0], pt[1]); diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java new file mode 100644 index 0000000000..35236e0ea7 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.MultiPoint; + +/** + * Measures the degree of similarity between two + * {@link Geometry}s using the Fréchet distance metric. + * The measure is normalized to lie in the range [0, 1]. + * Higher measures indicate a great degree of similarity. + *

+ * The measure is computed by computing the Fréchet distance + * between the input geometries, and then normalizing + * this by dividing it by the diagonal distance across + * the envelope of the combined geometries. + *

+ * Note: the input should be normalized, especially when + * measuring {@link MultiPoint} geometries because for the + * Fréchet distance the order of {@link Coordinate}s is + * important. + * + * @author Felix Obermaier + * + */ +public class FrechetSimilarityMeasure implements SimilarityMeasure { + + /** + * Creates an instance of this class. + */ + public FrechetSimilarityMeasure() + { } + + @Override + public double measure(Geometry g1, Geometry g2) { + + // Check if input is of same type + if (!g1.getGeometryType().equals(g2.getGeometryType())) + throw new IllegalArgumentException("g1 and g2 are of different type"); + + // Compute the distance + double frechetDistance = DiscreteFrechetDistance.distance(g1, g2); + if (frechetDistance == 0d) return 1; + + // Compute envelope diagonal size + Envelope env = new Envelope(g1.getEnvelopeInternal()); + env.expandToInclude(g2.getEnvelopeInternal()); + double envDiagSize = HausdorffSimilarityMeasure.diagonalSize(env); + + // normalize so that more similarity produces a measure closer to 1 + return 1 - frechetDistance / envDiagSize; + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java index 74ecedc905..c841fc7389 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java @@ -24,13 +24,13 @@ *

* The measure is computed by computing the Hausdorff distance * between the input geometries, and then normalizing - * this by dividing it by the diagonal distance across + * this by dividing it by the diagonal distance across * the envelope of the combined geometries. - * + * * @author mbdavis * */ -public class HausdorffSimilarityMeasure +public class HausdorffSimilarityMeasure implements SimilarityMeasure { /* @@ -40,36 +40,39 @@ public static double measure(Geometry a, Geometry b) return gv.measure(); } */ - + public HausdorffSimilarityMeasure() { } - + /* * Densify a small amount to increase accuracy of Hausdorff distance */ private static final double DENSIFY_FRACTION = 0.25; - + public double measure(Geometry g1, Geometry g2) - { + { double distance = DiscreteHausdorffDistance.distance(g1, g2, DENSIFY_FRACTION); - + if (distance == 0d) return 1d; + Envelope env = new Envelope(g1.getEnvelopeInternal()); env.expandToInclude(g2.getEnvelopeInternal()); double envSize = diagonalSize(env); - // normalize so that more similarity produces a measure closer to 1 + + // normalize so that more similarity produces a measure closer to 1 double measure = 1 - distance / envSize; - + //System.out.println("Hausdorff distance = " + distance + ", measure = " + measure); return measure; } - + public static double diagonalSize(Envelope env) { if (env.isNull()) return 0.0; - - double width = env.getWidth(); + + double width = env.getWidth(); double hgt = env.getHeight(); + return Math.sqrt(width * width + hgt * hgt); } } diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java new file mode 100644 index 0000000000..8d6ebb5fc4 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java @@ -0,0 +1,105 @@ +package org.locationtech.jts.algorithm.distance; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; + +/** + * Linear Discrete Fréchet Distance computation + */ +public class DiscreteFrechetDistanceLinear { + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1) { + DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, false); + return dist.distance(); + } + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1, boolean getCoordinates) { + DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, getCoordinates); + return dist.distance(); + } + private final Geometry g0; + private final Geometry g1; + private final boolean getCoordinates; + + private DiscreteFrechetDistanceLinear(Geometry g0, Geometry g1, boolean getCoordinates) { + this.g0 = g0; + this.g1 = g1; + this.getCoordinates = getCoordinates; + } + + public double distance() { + + Coordinate[] coords0 = this.g0.getCoordinates(); + Coordinate[] coords1 = this.g1.getCoordinates(); + double[][] distances = new double[coords0.length][]; + for (int i = 0; i < coords0.length; i++) + distances[i] = new double[coords1.length]; + + for (int i = 0; i < coords0.length; i++) { + for (int j = 0; j < coords1.length; j++) + { + double distance = coords0[i].distance(coords1[j]); + if (i > 0 && j > 0) + { + distances[i][j] = Math.max(Math.min(Math.min(distances[i-1][j], distances[i-1][j-1]), distances[i][j-1]), distance); + } + else if (i > 0) + { + distances[i][j] = Math.max(distances[i-1][0], distance); + } + else if (j > 0) + { + distances[i][j] = Math.max(distances[0][j-1], distance); + } + else + { + distances[i][j] = distance; + } + } + } + + //System.out.println(toString(coords0.length, coords1.length, distances)); + //System.out.println(); + return distances[coords0.length-1][coords1.length-1]; + } + + /* + // For debugging purposes only + private static String toString(int numRows, int numCols, + double[][] sparse) { + + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < numRows; i++) + { + sb.append('['); + for(int j = 0; j < numCols; j++) + { + if (j > 0) + sb.append(", "); + sb.append(String.format("%8.4f", sparse[i][j])); + } + sb.append(']'); + if (i < numRows - 1) sb.append(",\n"); + } + sb.append(']'); + return sb.toString(); + } + */ + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java new file mode 100644 index 0000000000..e196a2c7d4 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jts.algorithm.distance; + +import org.junit.Test; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.util.Stopwatch; +import test.jts.GeometryTestCase; + +public class DiscreteFrechetDistanceTest extends GeometryTestCase { + + public DiscreteFrechetDistanceTest(String name) { + super(name); + } + + @Test + public void testLineSegments() + { + runTest( + "LINESTRING(0 0, 1 0.0, 2 0.0, 3 0.0, 4 0)", + "LINESTRING(0 1, 1 1.1, 2 1.2, 3 1.1, 4 1)", 1.2); + } + + @Test + public void testOrientation() + { + runTest( + "LINESTRING(0 0, 10 10, 20 15)", + "LINESTRING(0 1, 8 9, 12 11, 21 15)",2.23606797749979); + } + @Test + public void testFromDHD() { + runTest( + "LINESTRING (130 0, 0 0, 0 150)", + "LINESTRING (10 10, 10 150, 130 10)", 191.049731745428); + } + + public void testDevogeleEtAlPaper() { + runTest("LINESTRING(0.2 2.0, 1.5 2.8, 2.3 1.6, 2.9 1.8, 4.1 3.1, 5.6 2.9, 7.2 1.3, 8.2 1.1)", + "LINESTRING(0.3 1.6, 3.2 3.0, 3.8 1.8, 5.2 3.1, 6.5 2.8, 7.0 0.8, 8.9 0.6)", 1.697056); + } + + public void testLongEifelWalk() { + runTest( + "MultiLineStringZ ((3334206.2900000000372529 5575410.37999999988824129 442.10000000000002274, 3334209.33999999985098839 5575407.62000000011175871 442.80000000000001137, 3334210.75 5575409.12999999988824129 442.69999999999998863, 3334190.06000000005587935 5575386.17999999970197678 443.60000000000002274, 3334184.52000000001862645 5575375.44000000040978193 444.69999999999998863, 3334176.30000000027939677 5575352.2099999999627471 447, 3334149.60999999986961484 5575360.71999999973922968 446.90000000000003411, 3334137.68000000016763806 5575366.88999999966472387 446.5, 3334125.9599999999627471 5575379.83000000007450581 445.30000000000001137, 3334115.97000000020489097 5575386.7099999999627471 444.80000000000001137, 3334106.01000000024214387 5575391.91999999992549419 444.40000000000003411, 3334096.97000000020489097 5575394.66000000014901161 444.30000000000001137, 3334087.49000000022351742 5575394.83999999985098839 444.40000000000003411, 3334078.58999999985098839 5575393 444.69999999999998863, 3334071.85999999986961484 5575390.10000000055879354 444.80000000000001137, 3334059.89999999990686774 5575404.5 442.13999999999998636, 3334046.47999999998137355 5575415.37999999988824129 438.81000000000000227, 3334129.45000000018626451 5575530.35000000055879354 437.62000000000000455, 3334179.47999999998137355 5575497.06000000052154064 439.01999999999998181, 3334196.87000000011175871 5575537.81000000052154064 440.03000000000002956, 3334229.08000000007450581 5575541.70000000018626451 441.62999999999999545, 3334209 5575622.25999999977648258 442.37999999999999545, 3334134.66000000014901161 5575601.10000000055879354 438.60000000000002274, 3334134.07000000029802322 5575602.57000000029802322 438.60000000000002274, 3334131.07000000029802322 5575609.45000000018626451 438.69999999999998863, 3334118.20000000018626451 5575638.02000000048428774 438.90000000000003411, 3334114.66999999992549419 5575645.81000000052154064 438.90000000000003411, 3334102.81000000005587935 5575667.88999999966472387 439.40000000000003411, 3334100.2900000000372529 5575676.42999999970197678 439.69999999999998863, 3334098.2900000000372529 5575683.16999999992549419 440, 3334095.64999999990686774 5575696.71999999973922968 440.69999999999998863, 3334095.16999999992549419 5575710.99000000022351742 441.40000000000003411, 3334095.97999999998137355 5575727.77000000048428774 442.10000000000002274, 3334098.89000000013038516 5575747.71999999973922968 442.90000000000003411, 3334099.78000000026077032 5575751.37000000011175871 443.10000000000002274, 3334101.35000000009313226 5575758 443.30000000000001137, 3334097.9599999999627471 5575759.21999999973922968 443.5, 3334093.58999999985098839 5575760.91000000014901161 443.69999999999998863, 3334081.22999999998137355 5575773.66000000014901161 445.30000000000001137, 3334081.82000000029802322 5575776.53000000026077032 445.40000000000003411, 3334081.87999999988824129 5575783.2099999999627471 445.69999999999998863, 3334072.4599999999627471 5575817.00999999977648258 447.60000000000002274, 3334070.62000000011175871 5575831.32000000029802322 447.90000000000003411, 3334074.35000000009313226 5575863.92999999970197678 447.60000000000002274, 3334078.08000000007450581 5575896.54999999981373549 447.10000000000002274, 3334076.18999999994412065 5575897.61000000033527613 447.19999999999998863, 3334070.14999999990686774 5575905.03000000026077032 447.40000000000003411, 3334062.60999999986961484 5575953.58999999985098839 446.5, 3334055.07000000029802322 5576002.03000000026077032 447.90000000000003411, 3334047.60000000009313226 5576050.4599999999627471 450, 3334039.10000000009313226 5576059.08000000007450581 449.90000000000003411, 3334003.41000000014901161 5576080.90000000037252903 450.19999999999998863, 3333973.05000000027939677 5576099.44000000040978193 453.10000000000002274, 3333967.72999999998137355 5576102.73000000044703484 453.60000000000002274, 3334005.68999999994412065 5576130.92999999970197678 453.5, 3334024.99000000022351742 5576141.4599999999627471 453.30000000000001137, 3334015.52000000001862645 5576159.90000000037252903 455.30000000000001137, 3334010.81000000005587935 5576166.83999999985098839 455.5, 3333996.70000000018626451 5576178.52000000048428774 455.69999999999998863, 3333994.28000000026077032 5576183.16000000014901161 455.80000000000001137, 3333992.4599999999627471 5576186.56000000052154064 456, 3333982.58000000007450581 5576198.88999999966472387 457.10000000000002274, 3333964.35999999986961484 5576218.38999999966472387 460.5, 3333933.55000000027939677 5576245.41000000014901161 466.69999999999998863, 3333902.72999999998137355 5576272.41999999992549419 470.69999999999998863, 3333876.7900000000372529 5576293.27000000048428774 472.19999999999998863, 3333850.85000000009313226 5576314.24000000022351742 474.10000000000002274, 3333845.35000000009313226 5576318.63999999966472387 474.69999999999998863, 3333819.91999999992549419 5576337.69000000040978193 476.30000000000001137, 3333844.28000000026077032 5576355.08000000007450581 477.30000000000001137, 3333868.64999999990686774 5576372.57000000029802322 477.80000000000001137, 3333897.91000000014901161 5576391.46999999973922968 478.60000000000002274, 3333927.08999999985098839 5576410.37000000011175871 479, 3333933.47999999998137355 5576416.17999999970197678 479.10000000000002274, 3333946.58999999985098839 5576438.48000000044703484 479.5, 3333961.68000000016763806 5576472.07000000029802322 479.60000000000002274, 3333976.85999999986961484 5576505.66999999992549419 478.40000000000003411, 3333977.74000000022351742 5576506.53000000026077032 478.30000000000001137, 3333982.37000000011175871 5576510.83999999985098839 478, 3333937.39000000013038516 5576531.83999999985098839 479.80000000000001137, 3333892.47999999998137355 5576552.73000000044703484 481.80000000000001137, 3333853.27000000001862645 5576571.10000000055879354 483.80000000000001137, 3333847.57000000029802322 5576573.73000000044703484 484.10000000000002274, 3333877.45000000018626451 5576603.17999999970197678 482.5, 3333907.32000000029802322 5576632.75 481.10000000000002274, 3333937.18999999994412065 5576662.2099999999627471 480.19999999999998863, 3333967.07000000029802322 5576691.78000000026077032 479.30000000000001137, 3333996.93999999994412065 5576721.23000000044703484 478.69999999999998863, 3334017.08999999985098839 5576736.07000000029802322 478.40000000000003411, 3334053.24000000022351742 5576759.10000000055879354 477, 3334089.33000000007450581 5576782.02000000048428774 473.90000000000003411, 3334133.33999999985098839 5576805.58000000007450581 471, 3334161.47000000020489097 5576824.74000000022351742 469.30000000000001137, 3334189.66000000014901161 5576843.88999999966472387 467.5, 3334196.74000000022351742 5576849.12999999988824129 467.5, 3334191.56000000005587935 5576854.52000000048428774 467.5, 3334200.33000000007450581 5576856.69000000040978193 467.5, 3334224.60999999986961484 5576862.73000000044703484 467.30000000000001137, 3334257.60000000009313226 5576870.92999999970197678 466, 3334286.28000000026077032 5576876.04999999981373549 464.69999999999998863, 3334315.03000000026077032 5576881.16000000014901161 464.80000000000001137, 3334321.03000000026077032 5576881.53000000026077032 464.90000000000003411, 3334328.2099999999627471 5576880.86000000033527613 464.80000000000001137, 3334366.72999999998137355 5576867.62999999988824129 464.19999999999998863, 3334405.85000000009313226 5576855.71999999973922968 463.10000000000002274, 3334445.05000000027939677 5576843.81000000052154064 461.69999999999998863, 3334450.10999999986961484 5576841.42999999970197678 461.60000000000002274, 3334488.43999999994412065 5576863.27000000048428774 462, 3334526.70000000018626451 5576885.12000000011175871 462.69999999999998863, 3334528.83999999985098839 5576887.40000000037252903 462.69999999999998863, 3334530.37999999988824129 5576888.67999999970197678 462.80000000000001137, 3334558.78000000026077032 5576912.2900000000372529 463.40000000000003411, 3334588.7099999999627471 5576937.17999999970197678 464.90000000000003411, 3334618.64999999990686774 5576962.07000000029802322 465.5, 3334648.66000000014901161 5576986.96999999973922968 466.60000000000002274, 3334654.95000000018626451 5576989.78000000026077032 466.80000000000001137, 3334679.01000000024214387 5577002.27000000048428774 466.90000000000003411, 3334703.07000000029802322 5577014.77000000048428774 467.30000000000001137, 3334745.20000000018626451 5577037.38999999966472387 467.5, 3334784.43999999994412065 5577058.87999999988824129 468.80000000000001137, 3334823.60999999986961484 5577080.37000000011175871 469.90000000000003411, 3334862.85000000009313226 5577101.74000000022351742 471.30000000000001137, 3334900.35000000009313226 5577122.28000000026077032 472, 3334902.08999999985098839 5577123.23000000044703484 472.10000000000002274, 3334877.7900000000372529 5577153.27000000048428774 472.30000000000001137, 3334853.47999999998137355 5577183.19000000040978193 471, 3334835.93000000016763806 5577205.56000000052154064 471.69999999999998863, 3334796.37000000011175871 5577235.50999999977648258 472.30000000000001137, 3334756.72999999998137355 5577265.36000000033527613 472.30000000000001137, 3334718.41000000014901161 5577293.83000000007450581 473.10000000000002274, 3334680.08000000007450581 5577322.29999999981373549 473.60000000000002274, 3334665.62999999988824129 5577334.44000000040978193 474, 3334660.56000000005587935 5577338.71999999973922968 474.10000000000002274, 3334688.05000000027939677 5577360.4599999999627471 474.69999999999998863, 3334715.60000000009313226 5577382.20000000018626451 476.19999999999998863, 3334743.16000000014901161 5577403.94000000040978193 478.40000000000003411, 3334776.10999999986961484 5577427.2900000000372529 480.69999999999998863, 3334789.16999999992549419 5577441.36000000033527613 481.10000000000002274, 3334798.64000000013038516 5577454.87000000011175871 481.69999999999998863, 3334823.64000000013038516 5577490.60000000055879354 482.5, 3334850.62999999988824129 5577530.95000000018626451 483.90000000000003411, 3334863.64000000013038516 5577550.36000000033527613 483.90000000000003411, 3334879.57000000029802322 5577574.02000000048428774 482.90000000000003411, 3334895.49000000022351742 5577597.67999999970197678 482, 3334915.58000000007450581 5577627.23000000044703484 481.19999999999998863, 3334932.68999999994412065 5577652.41000000014901161 481.69999999999998863, 3334739.87000000011175871 5577803.70000000018626451 472.40000000000003411, 3334741.5400000000372529 5577811.54999999981373549 472.40000000000003411, 3334745.35000000009313226 5577817.11000000033527613 472.60000000000002274, 3334751.37000000011175871 5577822.7099999999627471 472.90000000000003411, 3334764.9599999999627471 5577833.20000000018626451 473.60000000000002274, 3334775.78000000026077032 5577841.87999999988824129 474.30000000000001137, 3334802.14999999990686774 5577866.77000000048428774 476.10000000000002274, 3334824.31000000005587935 5577884.78000000026077032 477.5, 3334846.39999999990686774 5577902.79999999981373549 478.69999999999998863, 3334858 5577913.7900000000372529 479.30000000000001137, 3334866.75 5577922.08999999985098839 479.69999999999998863, 3334886.20000000018626451 5577935.62000000011175871 480.60000000000002274, 3334917.02000000001862645 5577963.94000000040978193 481.60000000000002274, 3334947.83999999985098839 5577992.25999999977648258 482.69999999999998863, 3334980.27000000001862645 5578019.29999999981373549 483.90000000000003411, 3334997.02000000001862645 5578035.48000000044703484 484.5, 3335011.66000000014901161 5578050.04999999981373549 485.10000000000002274, 3335018.89999999990686774 5578055.83999999985098839 485.5, 3335026.52000000001862645 5578060.28000000026077032 485.80000000000001137, 3335034.10000000009313226 5578063.37999999988824129 486.19999999999998863, 3335042.83000000007450581 5578066.56000000052154064 486.69999999999998863, 3335051.32000000029802322 5578069.29999999981373549 487.19999999999998863, 3335062.22000000020489097 5578071.29999999981373549 487.69999999999998863, 3335071.83000000007450581 5578071 488.30000000000001137, 3335072.49000000022351742 5578075.87999999988824129 488.30000000000001137, 3335069.37000000011175871 5578081.10000000055879354 488, 3335068.56000000005587935 5578082.4599999999627471 488, 3335061.85000000009313226 5578089.12000000011175871 487.5, 3335057.62999999988824129 5578095.49000000022351742 487.19999999999998863, 3335052.24000000022351742 5578105.67999999970197678 486.80000000000001137, 3335049.64000000013038516 5578118.11000000033527613 486.60000000000002274, 3335050.78000000026077032 5578125.20000000018626451 486.60000000000002274, 3335067.66000000014901161 5578145.27000000048428774 487.40000000000003411, 3335069.05000000027939677 5578146.33999999985098839 487.40000000000003411, 3335070.51000000024214387 5578147.52000000048428774 487.5, 3335071.70000000018626451 5578155.83999999985098839 487.5, 3335070.89999999990686774 5578162.2099999999627471 487.40000000000003411, 3335058.05000000027939677 5578200.67999999970197678 486.19999999999998863, 3335055.60999999986961484 5578213.78000000026077032 485.90000000000003411, 3335055.58000000007450581 5578231.25999999977648258 485.5, 3335060.80000000027939677 5578250.02000000048428774 485.19999999999998863, 3335070.4599999999627471 5578262.52000000048428774 485.30000000000001137, 3335088.83000000007450581 5578273.53000000026077032 485.80000000000001137, 3335122.58000000007450581 5578283.83000000007450581 487.19999999999998863, 3335129.10999999986961484 5578287.53000000026077032 487.40000000000003411, 3335135.32000000029802322 5578292.33999999985098839 487.60000000000002274, 3335142.18000000016763806 5578304.37999999988824129 487.60000000000002274, 3335153.49000000022351742 5578328.74000000022351742 487.5, 3335157.57000000029802322 5578333.73000000044703484 487.60000000000002274, 3335166.31000000005587935 5578339.81000000052154064 487.90000000000003411, 3335176.18999999994412065 5578343.62000000011175871 488.40000000000003411, 3335188.18000000016763806 5578348.58999999985098839 489, 3335198.06000000005587935 5578352.17999999970197678 489.5, 3335212.2900000000372529 5578356.19000000040978193 490.30000000000001137, 3335232.75 5578358.56000000052154064 491.60000000000002274, 3335246.12999999988824129 5578360.03000000026077032 492.40000000000003411, 3335261.60000000009313226 5578364.78000000026077032 493.40000000000003411, 3335269.58000000007450581 5578369.2099999999627471 493.80000000000001137, 3335272.35999999986961484 5578373.91000000014901161 493.90000000000003411, 3335273.53000000026077032 5578379.44000000040978193 493.90000000000003411, 3335268.93000000016763806 5578394.28000000026077032 493.30000000000001137, 3335258.22000000020489097 5578407.29999999981373549 492.30000000000001137, 3335250.87999999988824129 5578423.56000000052154064 491.60000000000002274, 3335247.0400000000372529 5578441.94000000040978193 491.19999999999998863, 3335244.08000000007450581 5578470.75 490.90000000000003411, 3335241.12999999988824129 5578499.57000000029802322 490.90000000000003411, 3335241.02000000001862645 5578528.07000000029802322 491.10000000000002274, 3335240.97999999998137355 5578556.4599999999627471 491.40000000000003411, 3335242.97999999998137355 5578568.20000000018626451 491.60000000000002274, 3335250.39999999990686774 5578588.90000000037252903 492.10000000000002274, 3335267.2900000000372529 5578623.44000000040978193 493, 3335282.62999999988824129 5578646.90000000037252903 493.5, 3335293.02000000001862645 5578660.15000000037252903 493.80000000000001137, 3335299.7099999999627471 5578671.19000000040978193 493.90000000000003411, 3335313.74000000022351742 5578689.23000000044703484 494, 3335332.68000000016763806 5578706.90000000037252903 494.30000000000001137, 3335351.55000000027939677 5578724.67999999970197678 494.40000000000003411, 3335359.97999999998137355 5578732.10000000055879354 494.60000000000002274, 3335371.70000000018626451 5578739.98000000044703484 494.80000000000001137, 3335382.33999999985098839 5578742.99000000022351742 495.19999999999998863, 3335393.41000000014901161 5578743.75999999977648258 495.80000000000001137, 3335401.12000000011175871 5578741.74000000022351742 496.19999999999998863, 3335425.22999999998137355 5578728.74000000022351742 497.60000000000002274, 3335444.9599999999627471 5578716.87999999988824129 498.80000000000001137, 3335448.28000000026077032 5578715.66999999992549419 499, 3335452.37000000011175871 5578712.08999999985098839 499.19999999999998863, 3335457.45000000018626451 5578710.37000000011175871 499.5, 3335464.91999999992549419 5578710.13999999966472387 499.90000000000003411, 3335472.25 5578711.81000000052154064 500.10000000000002274, 3335474.93000000016763806 5578713.38999999966472387 500.19999999999998863, 3335487.43999999994412065 5578721.4599999999627471 500.69999999999998863, 3335482.37999999988824129 5578728.29999999981373549 500.10000000000002274, 3335480.02000000001862645 5578730.37999999988824129 499.90000000000003411, 3335482.37999999988824129 5578728.29999999981373549 500.10000000000002274, 3335487.43999999994412065 5578721.4599999999627471 500.69999999999998863, 3335498.16000000014901161 5578711 501.90000000000003411, 3335535.64999999990686774 5578681.11000000033527613 504.69999999999998863, 3335554.72999999998137355 5578668.71999999973922968 505.40000000000003411, 3335562.2900000000372529 5578666.58999999985098839 505.60000000000002274, 3335572.64999999990686774 5578665.27000000048428774 505.90000000000003411, 3335617.82000000029802322 5578662.31000000052154064 506.80000000000001137, 3335630.16000000014901161 5578662.48000000044703484 507.10000000000002274, 3335657.70000000018626451 5578665.96999999973922968 508, 3335682.35999999986961484 5578672.66999999992549419 509.10000000000002274, 3335707.02000000001862645 5578679.46999999973922968 510.60000000000002274, 3335716.41000000014901161 5578683.62999999988824129 511.10000000000002274, 3335737.83999999985098839 5578696.54999999981373549 512.39999999999997726, 3335772.5 5578727.08999999985098839 514.29999999999995453, 3335807.56000000005587935 5578759.17999999970197678 516.20000000000004547, 3335842.62000000011175871 5578791.27000000048428774 518.10000000000002274, 3335877.76000000024214387 5578823.35000000055879354 520.10000000000002274, 3335912.10999999986961484 5578855.4599999999627471 522, 3335946.4599999999627471 5578887.67999999970197678 523.79999999999995453, 3335980.74000000022351742 5578919.91000000014901161 525.10000000000002274, 3336015.08999999985098839 5578952.02000000048428774 526.10000000000002274, 3336041.95000000018626451 5578974.7900000000372529 526.70000000000004547, 3336068.74000000022351742 5578997.56000000052154064 527.20000000000004547, 3336088.93999999994412065 5579019.20000000018626451 527.29999999999995453, 3336130.89999999990686774 5579027.81000000052154064 528.89999999999997726, 3336172.7900000000372529 5579036.41999999992549419 530.60000000000002274, 3336214.66999999992549419 5579044.91999999992549419 532.79999999999995453, 3336256.62999999988824129 5579053.53000000026077032 535.29999999999995453, 3336256.07000000029802322 5579051.44000000040978193 535.29999999999995453, 3336256.62999999988824129 5579053.53000000026077032 535.29999999999995453, 3336302.26000000024214387 5579063.37000000011175871 537.60000000000002274, 3336347.89000000013038516 5579073.08999999985098839 539.10000000000002274, 3336393.52000000001862645 5579082.91999999992549419 540, 3336439.08000000007450581 5579092.75999999977648258 540.5, 3336448.77000000001862645 5579092.90000000037252903 540.5, 3336473.87999999988824129 5579070.53000000026077032 540.20000000000004547, 3336498.99000000022351742 5579048.27000000048428774 539.60000000000002274, 3336504.80000000027939677 5579044.75 539.5, 3336535.35000000009313226 5579025.54999999981373549 538.20000000000004547, 3336549.68000000016763806 5579018.77000000048428774 537.10000000000002274, 3336583.76000000024214387 5578998.23000000044703484 534.29999999999995453, 3336617.89999999990686774 5578977.70000000018626451 531, 3336627.97999999998137355 5578971.60000000055879354 529.89999999999997726, 3336660.87000000011175871 5578951.99000000022351742 526.20000000000004547, 3336687.24000000022351742 5578931.36000000033527613 523, 3336695.5400000000372529 5578923.20000000018626451 521.79999999999995453, 3336703.18999999994412065 5578916.83999999985098839 520.60000000000002274, 3336719.33999999985098839 5578902.2099999999627471 518.10000000000002274, 3336726.20000000018626451 5578893.5400000000372529 517, 3336746.83000000007450581 5578866.74000000022351742 513.5, 3336751.32000000029802322 5578859.70000000018626451 512.79999999999995453, 3336754.32000000029802322 5578853.25999999977648258 512.10000000000002274, 3336756.2900000000372529 5578847.74000000022351742 511.60000000000002274, 3336757.47000000020489097 5578835.02000000048428774 510.69999999999998863, 3336758.87999999988824129 5578806.91999999992549419 509, 3336760.22000000020489097 5578778.7099999999627471 507.69999999999998863, 3336775.47000000020489097 5578769.45000000018626451 505.69999999999998863, 3336787 5578768.75999999977648258 504.40000000000003411, 3336828.33999999985098839 5578771.27000000048428774 500.5, 3336833.91000000014901161 5578771.66000000014901161 500, 3336871.18999999994412065 5578771.83999999985098839 496.10000000000002274, 3336882.47000000020489097 5578769.94000000040978193 495.10000000000002274, 3336905.57000000029802322 5578763.21999999973922968 493.80000000000001137, 3336909.60000000009313226 5578762.08999999985098839 493.60000000000002274, 3336915.16000000014901161 5578760.03000000026077032 493.40000000000003411, 3336920.07000000029802322 5578757.31000000052154064 493.30000000000001137, 3336938.26000000024214387 5578755.53000000026077032 493, 3336963.85000000009313226 5578753.52000000048428774 492.60000000000002274, 3336995.78000000026077032 5578748.63999999966472387 491.90000000000003411, 3337027.70000000018626451 5578743.65000000037252903 490.40000000000003411, 3337037.77000000001862645 5578739.78000000026077032 490, 3337054.26000000024214387 5578729.25 489.60000000000002274, 3337062.87999999988824129 5578721.96999999973922968 489.40000000000003411, 3337070.01000000024214387 5578712.9599999999627471 489.40000000000003411, 3337079.2099999999627471 5578706.21999999973922968 489, 3337103.58000000007450581 5578685.2099999999627471 486.80000000000001137, 3337128.03000000026077032 5578664.20000000018626451 485.19999999999998863, 3337136.2099999999627471 5578654.48000000044703484 484.69999999999998863, 3337149.72999999998137355 5578639.92999999970197678 484.19999999999998863, 3337169.16000000014901161 5578618.07000000029802322 484.80000000000001137, 3337188.58000000007450581 5578596.10000000055879354 484.69999999999998863, 3337199.12999999988824129 5578582.19000000040978193 484.5, 3337206.60000000009313226 5578567.92999999970197678 484.30000000000001137, 3337210.70000000018626451 5578559.90000000037252903 484, 3337217.64999999990686774 5578540.10000000055879354 483.40000000000003411, 3337220.58000000007450581 5578530.87999999988824129 483.10000000000002274, 3337225.10999999986961484 5578509.13999999966472387 482.80000000000001137, 3337228.07000000029802322 5578503.49000000022351742 482.80000000000001137, 3337233 5578498.99000000022351742 482.60000000000002274, 3337238.93000000016763806 5578494.91000000014901161 482.5, 3337246.08000000007450581 5578493.25 482.40000000000003411, 3337253.07000000029802322 5578493.48000000044703484 482.30000000000001137, 3337259.5 5578494.06000000052154064 482.19999999999998863, 3337301.62000000011175871 5578505.57000000029802322 482.40000000000003411, 3337327.77000000001862645 5578512.11000000033527613 482.80000000000001137, 3337371.64000000013038516 5578522.57000000029802322 483.60000000000002274, 3337412.07000000029802322 5578530.12000000011175871 482.69999999999998863, 3337426.83999999985098839 5578533.12000000011175871 482, 3337445.99000000022351742 5578534.75999999977648258 481.30000000000001137, 3337467.58000000007450581 5578534.5400000000372529 480.60000000000002274, 3337479.07000000029802322 5578532.74000000022351742 480.5, 3337502.45000000018626451 5578525.7900000000372529 480.19999999999998863, 3337536.2099999999627471 5578511.06000000052154064 480.19999999999998863, 3337569.2099999999627471 5578492.24000000022351742 480.5, 3337582.7099999999627471 5578481.69000000040978193 480, 3337586.41000000014901161 5578479.24000000022351742 479.90000000000003411, 3337611.68999999994412065 5578464.54999999981373549 477.80000000000001137, 3337636.97000000020489097 5578449.75 474.80000000000001137, 3337635.28000000026077032 5578445.90000000037252903 475.30000000000001137, 3337630.18999999994412065 5578433.15000000037252903 476.69999999999998863, 3337625.18000000016763806 5578414.0400000000372529 477.90000000000003411, 3337624.4599999999627471 5578409.04999999981373549 478, 3337623.97999999998137355 5578407.40000000037252903 478.10000000000002274, 3337174.80000000027939677 5577945.83999999985098839 515.5, 3337159.64999999990686774 5577937.62000000011175871 516.70000000000004547, 3337157.97999999998137355 5577936.78000000026077032 516.79999999999995453, 3337136.99000000022351742 5577924.07000000029802322 518.70000000000004547, 3337106.87999999988824129 5577904.50999999977648258 521.10000000000002274, 3337096.45000000018626451 5577899.15000000037252903 521.70000000000004547, 3337063.07000000029802322 5577875.24000000022351742 524.10000000000002274, 3337038.06000000005587935 5577854.75 525.5, 3337018.52000000001862645 5577840.77000000048428774 526.39999999999997726, 3336996.72999999998137355 5577822.9599999999627471 528, 3336975.85000000009313226 5577806.90000000037252903 529.29999999999995453, 3336937.47999999998137355 5577778.58000000007450581 530.70000000000004547, 3336928.76000000024214387 5577773.06000000052154064 530.89999999999997726, 3336918.9599999999627471 5577762.66999999992549419 531.29999999999995453, 3336916.62000000011175871 5577760.85000000055879354 531.39999999999997726, 3336897.55000000027939677 5577741.07000000029802322 532.29999999999995453, 3336883.32000000029802322 5577728.15000000037252903 532.70000000000004547, 3336874.9599999999627471 5577722.83999999985098839 532.89999999999997726, 3336868.77000000001862645 5577723.25 532.70000000000004547, 3336858.4599999999627471 5577721.67999999970197678 532.39999999999997726, 3336848.10000000009313226 5577718.54999999981373549 532, 3336834.16000000014901161 5577712.74000000022351742 531.60000000000002274, 3336805.22999999998137355 5577696.82000000029802322 530.79999999999995453, 3336776.35999999986961484 5577680.7900000000372529 530.39999999999997726, 3336751.16000000014901161 5577665.87000000011175871 529.89999999999997726, 3336740.97999999998137355 5577661.62000000011175871 529.60000000000002274, 3336718.66000000014901161 5577649.83999999985098839 529.5, 3336696.33999999985098839 5577638.06000000052154064 529.29999999999995453, 3336658.81000000005587935 5577611.37999999988824129 528.29999999999995453, 3336637.93000000016763806 5577597.78000000026077032 527.70000000000004547, 3336627.7099999999627471 5577589.96999999973922968 527.20000000000004547, 3336620.9599999999627471 5577586.06000000052154064 526.89999999999997726, 3336614.82000000029802322 5577581.12000000011175871 526.79999999999995453, 3336603.93000000016763806 5577574.88999999966472387 526.5, 3336593.30000000027939677 5577567.75999999977648258 526.20000000000004547, 3336583.91000000014901161 5577558.91999999992549419 526, 3336552.62999999988824129 5577536.2900000000372529 524.89999999999997726, 3336521.33999999985098839 5577513.65000000037252903 523.39999999999997726, 3336495.5 5577491.74000000022351742 521.89999999999997726, 3336470.58999999985098839 5577467.57000000029802322 520.20000000000004547, 3336462.5400000000372529 5577460.91999999992549419 519.70000000000004547, 3336446.43999999994412065 5577444.94000000040978193 518.60000000000002274, 3336435.85000000009313226 5577434.46999999973922968 517.89999999999997726, 3336428.47000000020489097 5577426.23000000044703484 517.39999999999997726, 3336424.35000000009313226 5577412.88999999966472387 517.29999999999995453, 3336419.75 5577388.65000000037252903 517.20000000000004547, 3336413.91000000014901161 5577349.75999999977648258 514.89999999999997726, 3336405.95000000018626451 5577338.98000000044703484 513.70000000000004547, 3336391.60999999986961484 5577324.73000000044703484 512.39999999999997726, 3336379.16000000014901161 5577313.87000000011175871 511.69999999999998863, 3336388.25 5577310.46999999973922968 511, 3336401.57000000029802322 5577303.37999999988824129 509.90000000000003411, 3336406.75 5577297.77000000048428774 509.30000000000001137, 3336426.43000000016763806 5577261.08999999985098839 506.40000000000003411, 3336430.89000000013038516 5577250.7099999999627471 505.90000000000003411, 3336448.58999999985098839 5577214.5400000000372529 502.60000000000002274, 3336459.14999999990686774 5577191.50999999977648258 500.30000000000001137, 3336469.62999999988824129 5577168.46999999973922968 498.19999999999998863, 3336483.31000000005587935 5577138.21999999973922968 495.90000000000003411, 3336496.91999999992549419 5577107.9599999999627471 493.5, 3336485.72000000020489097 5577100.74000000022351742 493.69999999999998863, 3336460.83999999985098839 5577084.58000000007450581 494, 3336424.68000000016763806 5577061.2099999999627471 494.40000000000003411, 3336409.10999999986961484 5577050.88999999966472387 494.69999999999998863, 3336372.41999999992549419 5577026.53000000026077032 494.60000000000002274, 3336340.64000000013038516 5577001.4599999999627471 494, 3336308.78000000026077032 5576976.50999999977648258 493.19999999999998863, 3336302.85999999986961484 5576971.90000000037252903 493.10000000000002274, 3336263.68999999994412065 5576943.28000000026077032 492.40000000000003411, 3336250.35999999986961484 5576917.75 492.10000000000002274, 3336237.01000000024214387 5576892.12000000011175871 491.69999999999998863, 3336223.74000000022351742 5576864.13999999966472387 491.30000000000001137, 3336211.68999999994412065 5576836.35000000055879354 491.30000000000001137, 3336199.7099999999627471 5576808.44000000040978193 491.5, 3336188.30000000027939677 5576785.41000000014901161 492, 3336176.89000000013038516 5576762.28000000026077032 493.69999999999998863, 3336204.49000000022351742 5576743.95000000018626451 496.19999999999998863, 3336221.08000000007450581 5576729.29999999981373549 497.30000000000001137, 3336228.05000000027939677 5576719.73000000044703484 498, 3336241.25 5576694.61000000033527613 500, 3336248.16000000014901161 5576682.92999999970197678 501, 3336259.7900000000372529 5576676.21999999973922968 501.60000000000002274, 3336245.49000000022351742 5576649.38999999966472387 503.10000000000002274, 3336237.14999999990686774 5576631.06000000052154064 503.90000000000003411, 3336221.27000000001862645 5576606.28000000026077032 503.80000000000001137, 3336208.24000000022351742 5576581.52000000048428774 502.80000000000001137, 3336203.08999999985098839 5576571.99000000022351742 502.19999999999998863, 3336188.9599999999627471 5576543.7099999999627471 501.19999999999998863, 3336186.05000000027939677 5576534.88999999966472387 501, 3336183.64000000013038516 5576521.61000000033527613 501.10000000000002274, 3336186.01000000024214387 5576489.91999999992549419 501.10000000000002274, 3336193.57000000029802322 5576464.53000000026077032 499.90000000000003411, 3336195.89999999990686774 5576457 499.40000000000003411, 3336196.97000000020489097 5576443.04999999981373549 498.80000000000001137, 3336196.89000000013038516 5576431.13999999966472387 498.19999999999998863, 3336195.74000000022351742 5576423.94000000040978193 498, 3336196.45000000018626451 5576398.65000000037252903 496.19999999999998863, 3336199.43000000016763806 5576391.2099999999627471 495.19999999999998863, 3336218.22000000020489097 5576357.7900000000372529 490.19999999999998863, 3336234.33999999985098839 5576320.77000000048428774 485.69999999999998863, 3336241.76000000024214387 5576295.61000000033527613 483.19999999999998863, 3336242.74000000022351742 5576292.91000000014901161 482.90000000000003411, 3336255.18000000016763806 5576252.11000000033527613 479.10000000000002274, 3336256.97000000020489097 5576241.03000000026077032 477.90000000000003411, 3336259.22000000020489097 5576205.45000000018626451 473.69999999999998863, 3336258.70000000018626451 5576193.11000000033527613 472.40000000000003411, 3336257.53000000026077032 5576189.70000000018626451 472.19999999999998863, 3336254.62999999988824129 5576183.54999999981373549 471.80000000000001137, 3336245.75 5576175.58999999985098839 471.69999999999998863, 3336235.85999999986961484 5576169.33000000007450581 471.60000000000002274, 3336199.12999999988824129 5576141.2900000000372529 470.60000000000002274, 3336162.39000000013038516 5576113.25999999977648258 466, 3336159.12000000011175871 5576108.91000000014901161 465.30000000000001137, 3336160.60000000009313226 5576105.86000000033527613 464.80000000000001137, 3336175.18000000016763806 5576088.60000000055879354 463, 3336182.14000000013038516 5576078.58999999985098839 462.19999999999998863, 3336179.08000000007450581 5576069.44000000040978193 460.80000000000001137, 3336174.64999999990686774 5576066.91000000014901161 460, 3336165.03000000026077032 5576064.63999999966472387 458.90000000000003411, 3336150.52000000001862645 5576065.87000000011175871 457.90000000000003411, 3336129.62000000011175871 5576065.74000000022351742 456.40000000000003411, 3336120.02000000001862645 5576064.37000000011175871 455.60000000000002274, 3336113.97000000020489097 5576064.66000000014901161 455.40000000000003411, 3336107.87999999988824129 5576066.08000000007450581 455.40000000000003411, 3336096.77000000001862645 5576068.87000000011175871 455.80000000000001137, 3336090.51000000024214387 5576071.50999999977648258 456.10000000000002274, 3336069.57000000029802322 5576074.83000000007450581 456.90000000000003411, 3336023.22000000020489097 5576074.58999999985098839 458.69999999999998863, 3336007.26000000024214387 5576075.31000000052154064 459.19999999999998863, 3335991.80000000027939677 5576077.7900000000372529 459.80000000000001137, 3335944.06000000005587935 5576090.74000000022351742 459.69999999999998863, 3335947.2900000000372529 5576084.40000000037252903 459.5, 3335950.58999999985098839 5576078.17999999970197678 459.30000000000001137, 3335954.0400000000372529 5576072.06000000052154064 459.19999999999998863, 3335958.28000000026077032 5576063.91000000014901161 459.10000000000002274, 3335978.33999999985098839 5576030.00999999977648258 458.90000000000003411, 3335986.72999999998137355 5576020.16999999992549419 458.90000000000003411, 3336024.12999999988824129 5575988.85000000055879354 457.5, 3336061.60000000009313226 5575957.52000000048428774 455.90000000000003411, 3336093.37000000011175871 5575931.16000000014901161 454.90000000000003411, 3336125.14000000013038516 5575904.67999999970197678 453.90000000000003411, 3336156.99000000022351742 5575878.31000000052154064 453.10000000000002274, 3336188.76000000024214387 5575851.95000000018626451 451.90000000000003411, 3336220.5400000000372529 5575825.58999999985098839 450.19999999999998863, 3336246.77000000001862645 5575804.40000000037252903 449, 3336273 5575783.21999999973922968 447.5, 3336288.2099999999627471 5575772.50999999977648258 447.10000000000002274, 3336319.78000000026077032 5575746.27000000048428774 446.19999999999998863, 3336351.33999999985098839 5575720.02000000048428774 445, 3336382.91000000014901161 5575693.66000000014901161 444.30000000000001137, 3336388.26000000024214387 5575689.16000000014901161 444.30000000000001137, 3336392.78000000026077032 5575685.4599999999627471 444.19999999999998863, 3336421.60000000009313226 5575662.63999999966472387 444.5, 3336457.01000000024214387 5575633.7099999999627471 445.69999999999998863, 3336492.41000000014901161 5575604.7900000000372529 446.80000000000001137, 3336527.89000000013038516 5575575.75 447.69999999999998863, 3336563.30000000027939677 5575546.83000000007450581 447.19999999999998863, 3336584.70000000018626451 5575528.69000000040978193 446.30000000000001137, 3336606.18000000016763806 5575510.66999999992549419 445.60000000000002274, 3336638.53000000026077032 5575484.06000000052154064 444.90000000000003411, 3336643.05000000027939677 5575480.36000000033527613 444.90000000000003411, 3336649.37999999988824129 5575475.16000000014901161 444.90000000000003411, 3336686.28000000026077032 5575443.9599999999627471 447.60000000000002274, 3336696.18000000016763806 5575436.53000000026077032 448.5, 3336721.07000000029802322 5575417.9599999999627471 450.60000000000002274, 3336746.37999999988824129 5575399.13999999966472387 452.19999999999998863, 3336760.9599999999627471 5575391.00999999977648258 453.10000000000002274, 3336777.72000000020489097 5575386.27000000048428774 454.10000000000002274, 3336809.41000000014901161 5575384.73000000044703484 455.5, 3336824.4599999999627471 5575380.15000000037252903 456.10000000000002274, 3336846.91999999992549419 5575363.87999999988824129 456.69999999999998863, 3336861.77000000001862645 5575343.49000000022351742 456.90000000000003411, 3336874.9599999999627471 5575313.36000000033527613 456.80000000000001137, 3336884.85999999986961484 5575280.44000000040978193 456.40000000000003411, 3336897.53000000026077032 5575258.67999999970197678 456, 3336901.95000000018626451 5575253.87000000011175871 455.80000000000001137, 3336924.41000000014901161 5575232.91000000014901161 454.90000000000003411, 3336954.28000000026077032 5575209.40000000037252903 454.60000000000002274, 3336984.08000000007450581 5575185.77000000048428774 453.5, 3337013.95000000018626451 5575162.25 452.80000000000001137, 3337043.75 5575138.62999999988824129 451.80000000000001137, 3337048.12000000011175871 5575136.7099999999627471 451.69999999999998863, 3337055.70000000018626451 5575126.02000000048428774 451.40000000000003411, 3337054.9599999999627471 5575120.25 451.5, 3337054.77000000001862645 5575118.81000000052154064 451.5, 3337029.89000000013038516 5575093.63999999966472387 452.40000000000003411, 3337004.93999999994412065 5575068.4599999999627471 453.10000000000002274, 3336980.06000000005587935 5575043.2900000000372529 453.5, 3336975.95000000018626451 5575039.62999999988824129 453.60000000000002274, 3336978.56000000005587935 5575024.86000000033527613 453.80000000000001137, 3336985.31000000005587935 5575012.17999999970197678 453.80000000000001137, 3336993.08000000007450581 5574995.91000000014901161 453.80000000000001137, 3337004.89000000013038516 5574958.03000000026077032 454.40000000000003411, 3337016.78000000026077032 5574920.0400000000372529 455.40000000000003411, 3337028.60000000009313226 5574882.16000000014901161 456.40000000000003411, 3337041.03000000026077032 5574841.15000000037252903 457.30000000000001137, 3337053.4599999999627471 5574800.12999999988824129 457.69999999999998863, 3337065.89999999990686774 5574759.12000000011175871 458.5, 3337078.26000000024214387 5574718.11000000033527613 458.80000000000001137, 3337082.18999999994412065 5574695.04999999981373549 459.10000000000002274, 3337085.85000000009313226 5574668 459.5, 3337089.87000000011175871 5574624.67999999970197678 460.80000000000001137, 3337087.72000000020489097 5574596.46999999973922968 461.60000000000002274, 3337080.72000000020489097 5574563.62999999988824129 463.10000000000002274, 3337070.7099999999627471 5574530.20000000018626451 464.5, 3337060.76000000024214387 5574496.78000000026077032 466.10000000000002274, 3337062.0400000000372529 5574487.16000000014901161 466.80000000000001137, 3337056.80000000027939677 5574493.11000000033527613 466.30000000000001137, 3337051.66999999992549419 5574497.83999999985098839 465.80000000000001137, 3337043.66000000014901161 5574501.98000000044703484 465.30000000000001137, 3337037.35000000009313226 5574505.40000000037252903 464.80000000000001137, 3337029.43999999994412065 5574510.5400000000372529 464.80000000000001137, 3337027.62999999988824129 5574507.15000000037252903 464.90000000000003411, 3337017.93999999994412065 5574491.29999999981373549 465.60000000000002274, 3336992.25 5574456.25 466.90000000000003411, 3336958.70000000018626451 5574422.87999999988824129 467.30000000000001137, 3336936.89999999990686774 5574407.41000000014901161 467.40000000000003411, 3336915.16999999992549419 5574391.92999999970197678 467.10000000000002274, 3336910.97000000020489097 5574389.95000000018626451 467.10000000000002274, 3336868.83999999985098839 5574369.98000000044703484 466.60000000000002274, 3336832.55000000027939677 5574358.85000000055879354 465.69999999999998863, 3336818.07000000029802322 5574356.62999999988824129 465.5, 3336804.47999999998137355 5574355.48000000044703484 465, 3336788.07000000029802322 5574355.2099999999627471 464.5, 3336782.18000000016763806 5574353.94000000040978193 464.40000000000003411, 3336776.87000000011175871 5574350.66000000014901161 464.5, 3336774.76000000024214387 5574346.83000000007450581 464.80000000000001137, 3336773.35000000009313226 5574340.62999999988824129 465.19999999999998863, 3336786.91000000014901161 5574343 465.19999999999998863, 3336773.35000000009313226 5574340.62999999988824129 465.19999999999998863, 3336733.10000000009313226 5574344.42999999970197678 465.10000000000002274, 3336724.05000000027939677 5574347.27000000048428774 465.10000000000002274, 3336719.35000000009313226 5574349.87000000011175871 464.90000000000003411, 3336714.27000000001862645 5574353.91999999992549419 464.60000000000002274, 3336674.58000000007450581 5574357.48000000044703484 464.60000000000002274, 3336634.82000000029802322 5574361.15000000037252903 464.69999999999998863, 3336588.37000000011175871 5574365.48000000044703484 464.5, 3336541.85000000009313226 5574369.69000000040978193 464.69999999999998863, 3336517.89000000013038516 5574372.2099999999627471 464.90000000000003411, 3336490.55000000027939677 5574378.50999999977648258 464.80000000000001137, 3336463.2099999999627471 5574384.81000000052154064 464.80000000000001137, 3336439.30000000027939677 5574393.66999999992549419 464.60000000000002274, 3336415.39999999990686774 5574402.65000000037252903 464.90000000000003411, 3336393.39999999990686774 5574415.67999999970197678 464.40000000000003411, 3336369.78000000026077032 5574429.10000000055879354 464.19999999999998863, 3336343.27000000001862645 5574446.06000000052154064 464, 3336316.76000000024214387 5574463.02000000048428774 464.5, 3336311.07000000029802322 5574468.32000000029802322 464.90000000000003411, 3336302.41000000014901161 5574471.92999999970197678 465.30000000000001137, 3336293.99000000022351742 5574474.08000000007450581 465.69999999999998863, 3336289.16999999992549419 5574475.23000000044703484 465.80000000000001137, 3336284.32000000029802322 5574477.61000000033527613 466, 3336280.57000000029802322 5574480.73000000044703484 466.19999999999998863, 3336276.72999999998137355 5574485.74000000022351742 466.30000000000001137, 3336246.95000000018626451 5574512.27000000048428774 467.40000000000003411, 3336217.22999999998137355 5574538.67999999970197678 469.40000000000003411, 3336187.45000000018626451 5574565.08999999985098839 470.40000000000003411, 3336165.18000000016763806 5574583.03000000026077032 471.10000000000002274, 3336142.97999999998137355 5574600.98000000044703484 471.80000000000001137, 3336103.99000000022351742 5574627.33999999985098839 471.80000000000001137, 3336071.12000000011175871 5574653.07000000029802322 471.90000000000003411, 3336038.32000000029802322 5574678.79999999981373549 472, 3336005.52000000001862645 5574704.63999999966472387 472.19999999999998863, 3335970.87000000011175871 5574730.53000000026077032 471.80000000000001137, 3335936.22000000020489097 5574756.32000000029802322 471.19999999999998863, 3335901.64999999990686774 5574782.2099999999627471 470.30000000000001137, 3335867 5574808.11000000033527613 469.10000000000002274, 3335832.35000000009313226 5574834.00999999977648258 468.40000000000003411, 3335800.43000000016763806 5574860.37999999988824129 468, 3335768.51000000024214387 5574886.75 467.40000000000003411, 3335736.58999999985098839 5574913.12000000011175871 468, 3335704.66000000014901161 5574939.49000000022351742 467.90000000000003411, 3335701.47000000020489097 5574942.37000000011175871 468, 3335664.37000000011175871 5574971.91000000014901161 468, 3335627.28000000026077032 5575001.56000000052154064 467.69999999999998863, 3335601.16000000014901161 5575017.28000000026077032 468.5, 3335591.97000000020489097 5575022.36000000033527613 468.69999999999998863, 3335571.7099999999627471 5575033.56000000052154064 469.40000000000003411, 3335548.62000000011175871 5575043.2900000000372529 470.10000000000002274, 3335522.22999999998137355 5575052.67999999970197678 471, 3335477.7099999999627471 5575065.87000000011175871 472.40000000000003411, 3335435.31000000005587935 5575076.31000000052154064 472.40000000000003411, 3335417.43000000016763806 5575079.41999999992549419 472.69999999999998863, 3335383.37999999988824129 5575085.37999999988824129 472.5, 3335355.27000000001862645 5575092.0400000000372529 472.19999999999998863, 3335345.97000000020489097 5575095.56000000052154064 472.69999999999998863, 3335311.0400000000372529 5575112.23000000044703484 470.60000000000002274, 3335302.85999999986961484 5575115.15000000037252903 470.5, 3335294.14000000013038516 5575121.44000000040978193 469.90000000000003411, 3335289.72000000020489097 5575137.83000000007450581 468.69999999999998863, 3335244.85000000009313226 5575116.85000000055879354 472.5, 3335236.68999999994412065 5575115.87000000011175871 472.90000000000003411, 3335188.01000000024214387 5575123.95000000018626451 473.40000000000003411, 3335139.26000000024214387 5575131.92999999970197678 473.10000000000002274, 3335090.58999999985098839 5575140.00999999977648258 472.90000000000003411, 3335041.91000000014901161 5575148.08999999985098839 474.10000000000002274, 3334999.33000000007450581 5575157.2099999999627471 475.40000000000003411, 3334956.75 5575166.21999999973922968 477.30000000000001137, 3334914.16999999992549419 5575175.33000000007450581 479.30000000000001137, 3334871.58999999985098839 5575184.45000000018626451 480.19999999999998863, 3334823.58000000007450581 5575195.85000000055879354 482.19999999999998863, 3334775.66000000014901161 5575207.37000000011175871 482.90000000000003411, 3334763.95000000018626451 5575209.40000000037252903 482.90000000000003411, 3334730.95000000018626451 5575214.54999999981373549 481.30000000000001137, 3334697.93999999994412065 5575219.70000000018626451 480.40000000000003411, 3334664.93999999994412065 5575224.9599999999627471 479, 3334640.33000000007450581 5575226.83999999985098839 477.30000000000001137, 3334596.78000000026077032 5575232.31000000052154064 474.69999999999998863, 3334574.66999999992549419 5575236.90000000037252903 474, 3334553.53000000026077032 5575242.7900000000372529 473.60000000000002274, 3334535.97999999998137355 5575249.57000000029802322 472.69999999999998863, 3334518.08999999985098839 5575259.25999999977648258 471.5, 3334513.35000000009313226 5575262.53000000026077032 471, 3334499.68999999994412065 5575275.31000000052154064 470, 3334479.58999999985098839 5575298.31000000052154064 468.40000000000003411, 3334477.66000000014901161 5575300.7099999999627471 468.19999999999998863, 3334471.16000000014901161 5575309.49000000022351742 467.5, 3334466.4599999999627471 5575314.20000000018626451 467.10000000000002274, 3334456.53000000026077032 5575318.17999999970197678 466.69999999999998863, 3334442.80000000027939677 5575305.58000000007450581 466.60000000000002274, 3334438.51000000024214387 5575300.82000000029802322 466.30000000000001137, 3334432.60000000009313226 5575294.2099999999627471 465.90000000000003411, 3334424.10000000009313226 5575282.35000000055879354 465.60000000000002274, 3334408.10000000009313226 5575247.33000000007450581 466.19999999999998863, 3334380.16999999992549419 5575266.4599999999627471 464.19999999999998863, 3334352.16999999992549419 5575285.70000000018626451 460.5, 3334334.43000000016763806 5575293.27000000048428774 458.40000000000003411, 3334327.99000000022351742 5575290.12999999988824129 458.5, 3334314.89999999990686774 5575288.98000000044703484 458.30000000000001137, 3334289.87999999988824129 5575282.53000000026077032 457.80000000000001137, 3334264.85999999986961484 5575276.19000000040978193 450.90000000000003411, 3334258.58000000007450581 5575303.54999999981373549 449, 3334252.22999999998137355 5575330.91000000014901161 447.60000000000002274, 3334248.93999999994412065 5575334.91000000014901161 447, 3334246.99000000022351742 5575338.75 446.60000000000002274, 3334241.76000000024214387 5575356.28000000026077032 445.69999999999998863, 3334229.43999999994412065 5575386.62000000011175871 444, 3334223.16000000014901161 5575397.95000000018626451 443.30000000000001137))", + "MultiLineStringZ ((3334205 5575410 442.10000000000002274, 3334210 5575410 442.80000000000001137, 3334190 5575385 443.60000000000002274, 3334185 5575375 444.69999999999998863, 3334175 5575350 447, 3334150 5575360 446.90000000000003411, 3334140 5575365 446.5, 3334125 5575380 445.30000000000001137, 3334115 5575385 444.80000000000001137, 3334105 5575390 444.40000000000003411, 3334095 5575395 444.30000000000001137, 3334085 5575395 444.40000000000003411, 3334080 5575395 444.69999999999998863, 3334070 5575390 444.80000000000001137, 3334060 5575405 442.13999999999998636, 3334045 5575415 438.81000000000000227, 3334130 5575530 437.62000000000000455, 3334180 5575495 439.01999999999998181, 3334195 5575540 440.03000000000002956, 3334230 5575540 441.62999999999999545, 3334210 5575620 442.37999999999999545, 3334135 5575600 438.60000000000002274, 3334135 5575605 438.60000000000002274, 3334130 5575610 438.69999999999998863, 3334120 5575640 438.90000000000003411, 3334115 5575645 438.90000000000003411, 3334105 5575670 439.40000000000003411, 3334100 5575675 439.69999999999998863, 3334100 5575685 440, 3334095 5575695 440.69999999999998863, 3334095 5575710 441.40000000000003411, 3334095 5575730 442.10000000000002274, 3334100 5575750 442.90000000000003411, 3334100 5575760 443.30000000000001137, 3334095 5575760 443.69999999999998863, 3334080 5575775 445.30000000000001137, 3334080 5575785 445.69999999999998863, 3334070 5575815 447.60000000000002274, 3334070 5575830 447.90000000000003411, 3334075 5575865 447.60000000000002274, 3334080 5575895 447.10000000000002274, 3334075 5575900 447.19999999999998863, 3334070 5575905 447.40000000000003411, 3334065 5575955 446.5, 3334055 5576000 447.90000000000003411, 3334050 5576050 450, 3334040 5576060 449.90000000000003411, 3334005 5576080 450.19999999999998863, 3333975 5576100 453.10000000000002274, 3333970 5576105 453.60000000000002274, 3334005 5576130 453.5, 3334025 5576140 453.30000000000001137, 3334015 5576160 455.30000000000001137, 3334010 5576165 455.5, 3333995 5576180 455.69999999999998863, 3333995 5576185 455.80000000000001137, 3333990 5576185 456, 3333985 5576200 457.10000000000002274, 3333965 5576220 460.5, 3333935 5576245 466.69999999999998863, 3333905 5576270 470.69999999999998863, 3333875 5576295 472.19999999999998863, 3333850 5576315 474.10000000000002274, 3333845 5576320 474.69999999999998863, 3333820 5576340 476.30000000000001137, 3333845 5576355 477.30000000000001137, 3333870 5576375 477.80000000000001137, 3333900 5576390 478.60000000000002274, 3333925 5576410 479, 3333935 5576415 479.10000000000002274, 3333945 5576440 479.5, 3333960 5576470 479.60000000000002274, 3333975 5576505 478.40000000000003411, 3333980 5576505 478.30000000000001137, 3333980 5576510 478, 3333935 5576530 479.80000000000001137, 3333890 5576555 481.80000000000001137, 3333855 5576570 483.80000000000001137, 3333850 5576575 484.10000000000002274, 3333875 5576605 482.5, 3333905 5576635 481.10000000000002274, 3333935 5576660 480.19999999999998863, 3333965 5576690 479.30000000000001137, 3333995 5576720 478.69999999999998863, 3334015 5576735 478.40000000000003411, 3334055 5576760 477, 3334090 5576780 473.90000000000003411, 3334135 5576805 471, 3334160 5576825 469.30000000000001137, 3334190 5576845 467.5, 3334195 5576850 467.5, 3334190 5576855 467.5, 3334200 5576855 467.5, 3334225 5576865 467.30000000000001137, 3334260 5576870 466, 3334285 5576875 464.69999999999998863, 3334315 5576880 464.80000000000001137, 3334320 5576880 464.90000000000003411, 3334330 5576880 464.80000000000001137, 3334365 5576870 464.19999999999998863, 3334405 5576855 463.10000000000002274, 3334445 5576845 461.69999999999998863, 3334450 5576840 461.60000000000002274, 3334490 5576865 462, 3334525 5576885 462.69999999999998863, 3334530 5576885 462.69999999999998863, 3334530 5576890 462.80000000000001137, 3334560 5576910 463.40000000000003411, 3334590 5576935 464.90000000000003411, 3334620 5576960 465.5, 3334650 5576985 466.60000000000002274, 3334655 5576990 466.80000000000001137, 3334680 5577000 466.90000000000003411, 3334705 5577015 467.30000000000001137, 3334745 5577035 467.5, 3334785 5577060 468.80000000000001137, 3334825 5577080 469.90000000000003411, 3334865 5577100 471.30000000000001137, 3334900 5577120 472, 3334900 5577125 472.10000000000002274, 3334880 5577155 472.30000000000001137, 3334855 5577185 471, 3334835 5577205 471.69999999999998863, 3334795 5577235 472.30000000000001137, 3334755 5577265 472.30000000000001137, 3334720 5577295 473.10000000000002274, 3334680 5577320 473.60000000000002274, 3334665 5577335 474, 3334660 5577340 474.10000000000002274, 3334690 5577360 474.69999999999998863, 3334715 5577380 476.19999999999998863, 3334745 5577405 478.40000000000003411, 3334775 5577425 480.69999999999998863, 3334790 5577440 481.10000000000002274, 3334800 5577455 481.69999999999998863, 3334825 5577490 482.5, 3334850 5577530 483.90000000000003411, 3334865 5577550 483.90000000000003411, 3334880 5577575 482.90000000000003411, 3334895 5577600 482, 3334915 5577625 481.19999999999998863, 3334935 5577650 481.69999999999998863, 3334740 5577805 472.40000000000003411, 3334740 5577810 472.40000000000003411, 3334745 5577815 472.60000000000002274, 3334750 5577825 472.90000000000003411, 3334765 5577835 473.60000000000002274, 3334775 5577840 474.30000000000001137, 3334800 5577865 476.10000000000002274, 3334825 5577885 477.5, 3334845 5577905 478.69999999999998863, 3334860 5577915 479.30000000000001137, 3334865 5577920 479.69999999999998863, 3334885 5577935 480.60000000000002274, 3334915 5577965 481.60000000000002274, 3334950 5577990 482.69999999999998863, 3334980 5578020 483.90000000000003411, 3334995 5578035 484.5, 3335010 5578050 485.10000000000002274, 3335020 5578055 485.5, 3335025 5578060 485.80000000000001137, 3335035 5578065 486.19999999999998863, 3335045 5578065 486.69999999999998863, 3335050 5578070 487.19999999999998863, 3335060 5578070 487.69999999999998863, 3335070 5578070 488.30000000000001137, 3335070 5578075 488.30000000000001137, 3335070 5578080 488, 3335060 5578090 487.5, 3335060 5578095 487.19999999999998863, 3335050 5578105 486.80000000000001137, 3335050 5578120 486.60000000000002274, 3335050 5578125 486.60000000000002274, 3335070 5578145 487.40000000000003411, 3335070 5578150 487.5, 3335070 5578155 487.5, 3335070 5578160 487.40000000000003411, 3335060 5578200 486.19999999999998863, 3335055 5578215 485.90000000000003411, 3335055 5578230 485.5, 3335060 5578250 485.19999999999998863, 3335070 5578265 485.30000000000001137, 3335090 5578275 485.80000000000001137, 3335125 5578285 487.19999999999998863, 3335130 5578290 487.40000000000003411, 3335135 5578290 487.60000000000002274, 3335140 5578305 487.60000000000002274, 3335155 5578330 487.5, 3335160 5578335 487.60000000000002274, 3335165 5578340 487.90000000000003411, 3335175 5578345 488.40000000000003411, 3335190 5578350 489, 3335200 5578350 489.5, 3335210 5578355 490.30000000000001137, 3335235 5578360 491.60000000000002274, 3335245 5578360 492.40000000000003411, 3335260 5578365 493.40000000000003411, 3335270 5578370 493.80000000000001137, 3335270 5578375 493.90000000000003411, 3335275 5578380 493.90000000000003411, 3335270 5578395 493.30000000000001137, 3335260 5578405 492.30000000000001137, 3335250 5578425 491.60000000000002274, 3335245 5578440 491.19999999999998863, 3335245 5578470 490.90000000000003411, 3335240 5578500 490.90000000000003411, 3335240 5578530 491.10000000000002274, 3335240 5578555 491.40000000000003411, 3335245 5578570 491.60000000000002274, 3335250 5578590 492.10000000000002274, 3335265 5578625 493, 3335285 5578645 493.5, 3335295 5578660 493.80000000000001137, 3335300 5578670 493.90000000000003411, 3335315 5578690 494, 3335335 5578705 494.30000000000001137, 3335350 5578725 494.40000000000003411, 3335360 5578730 494.60000000000002274, 3335370 5578740 494.80000000000001137, 3335380 5578745 495.19999999999998863, 3335395 5578745 495.80000000000001137, 3335400 5578740 496.19999999999998863, 3335425 5578730 497.60000000000002274, 3335445 5578715 498.80000000000001137, 3335450 5578715 499, 3335450 5578710 499.19999999999998863, 3335455 5578710 499.5, 3335465 5578710 499.90000000000003411, 3335470 5578710 500.10000000000002274, 3335475 5578715 500.19999999999998863, 3335485 5578720 500.69999999999998863, 3335480 5578730 500.10000000000002274, 3335485 5578720 500.69999999999998863, 3335500 5578710 501.90000000000003411, 3335535 5578680 504.69999999999998863, 3335555 5578670 505.40000000000003411, 3335560 5578665 505.60000000000002274, 3335575 5578665 505.90000000000003411, 3335620 5578660 506.80000000000001137, 3335630 5578660 507.10000000000002274, 3335660 5578665 508, 3335680 5578675 509.10000000000002274, 3335705 5578680 510.60000000000002274, 3335715 5578685 511.10000000000002274, 3335740 5578695 512.39999999999997726, 3335775 5578725 514.29999999999995453, 3335810 5578760 516.20000000000004547, 3335845 5578790 518.10000000000002274, 3335880 5578825 520.10000000000002274, 3335910 5578855 522, 3335945 5578890 523.79999999999995453, 3335980 5578920 525.10000000000002274, 3336015 5578950 526.10000000000002274, 3336040 5578975 526.70000000000004547, 3336070 5579000 527.20000000000004547, 3336090 5579020 527.29999999999995453, 3336130 5579030 528.89999999999997726, 3336175 5579035 530.60000000000002274, 3336215 5579045 532.79999999999995453, 3336255 5579055 535.29999999999995453, 3336255 5579050 535.29999999999995453, 3336255 5579055 535.29999999999995453, 3336300 5579065 537.60000000000002274, 3336350 5579075 539.10000000000002274, 3336395 5579085 540, 3336440 5579095 540.5, 3336450 5579095 540.5, 3336475 5579070 540.20000000000004547, 3336500 5579050 539.60000000000002274, 3336505 5579045 539.5, 3336535 5579025 538.20000000000004547, 3336550 5579020 537.10000000000002274, 3336585 5579000 534.29999999999995453, 3336620 5578980 531, 3336630 5578970 529.89999999999997726, 3336660 5578950 526.20000000000004547, 3336685 5578930 523, 3336695 5578925 521.79999999999995453, 3336705 5578915 520.60000000000002274, 3336720 5578900 518.10000000000002274, 3336725 5578895 517, 3336745 5578865 513.5, 3336750 5578860 512.79999999999995453, 3336755 5578855 512.10000000000002274, 3336755 5578850 511.60000000000002274, 3336755 5578835 510.69999999999998863, 3336760 5578805 509, 3336760 5578780 507.69999999999998863, 3336775 5578770 505.69999999999998863, 3336785 5578770 504.40000000000003411, 3336830 5578770 500.5, 3336835 5578770 500, 3336870 5578770 496.10000000000002274, 3336880 5578770 495.10000000000002274, 3336905 5578765 493.80000000000001137, 3336910 5578760 493.60000000000002274, 3336915 5578760 493.40000000000003411, 3336920 5578755 493.30000000000001137, 3336940 5578755 493, 3336965 5578755 492.60000000000002274, 3336995 5578750 491.90000000000003411, 3337030 5578745 490.40000000000003411, 3337040 5578740 490, 3337055 5578730 489.60000000000002274, 3337065 5578720 489.40000000000003411, 3337070 5578715 489.40000000000003411, 3337080 5578705 489, 3337105 5578685 486.80000000000001137, 3337130 5578665 485.19999999999998863, 3337135 5578655 484.69999999999998863, 3337150 5578640 484.19999999999998863, 3337170 5578620 484.80000000000001137, 3337190 5578595 484.69999999999998863, 3337200 5578580 484.5, 3337205 5578570 484.30000000000001137, 3337210 5578560 484, 3337220 5578540 483.40000000000003411, 3337220 5578530 483.10000000000002274, 3337225 5578510 482.80000000000001137, 3337230 5578505 482.80000000000001137, 3337235 5578500 482.60000000000002274, 3337240 5578495 482.5, 3337245 5578495 482.40000000000003411, 3337255 5578495 482.30000000000001137, 3337260 5578495 482.19999999999998863, 3337300 5578505 482.40000000000003411, 3337330 5578510 482.80000000000001137, 3337370 5578525 483.60000000000002274, 3337410 5578530 482.69999999999998863, 3337425 5578535 482, 3337445 5578535 481.30000000000001137, 3337470 5578535 480.60000000000002274, 3337480 5578535 480.5, 3337500 5578525 480.19999999999998863, 3337535 5578510 480.19999999999998863, 3337570 5578490 480.5, 3337585 5578480 480, 3337610 5578465 477.80000000000001137, 3337635 5578450 474.80000000000001137, 3337635 5578445 475.30000000000001137, 3337630 5578435 476.69999999999998863, 3337625 5578415 477.90000000000003411, 3337625 5578410 478, 3337625 5578405 478.10000000000002274, 3337175 5577945 515.5, 3337160 5577940 516.70000000000004547, 3337160 5577935 516.79999999999995453, 3337135 5577925 518.70000000000004547, 3337105 5577905 521.10000000000002274, 3337095 5577900 521.70000000000004547, 3337065 5577875 524.10000000000002274, 3337040 5577855 525.5, 3337020 5577840 526.39999999999997726, 3336995 5577825 528, 3336975 5577805 529.29999999999995453, 3336935 5577780 530.70000000000004547, 3336930 5577775 530.89999999999997726, 3336920 5577765 531.29999999999995453, 3336915 5577760 531.39999999999997726, 3336900 5577740 532.29999999999995453, 3336885 5577730 532.70000000000004547, 3336875 5577725 532.89999999999997726, 3336870 5577725 532.70000000000004547, 3336860 5577720 532.39999999999997726, 3336850 5577720 532, 3336835 5577715 531.60000000000002274, 3336805 5577695 530.79999999999995453, 3336775 5577680 530.39999999999997726, 3336750 5577665 529.89999999999997726, 3336740 5577660 529.60000000000002274, 3336720 5577650 529.5, 3336695 5577640 529.29999999999995453, 3336660 5577610 528.29999999999995453, 3336640 5577600 527.70000000000004547, 3336630 5577590 527.20000000000004547, 3336620 5577585 526.89999999999997726, 3336615 5577580 526.79999999999995453, 3336605 5577575 526.5, 3336595 5577570 526.20000000000004547, 3336585 5577560 526, 3336555 5577535 524.89999999999997726, 3336520 5577515 523.39999999999997726, 3336495 5577490 521.89999999999997726, 3336470 5577470 520.20000000000004547, 3336465 5577460 519.70000000000004547, 3336445 5577445 518.60000000000002274, 3336435 5577435 517.89999999999997726, 3336430 5577425 517.39999999999997726, 3336425 5577415 517.29999999999995453, 3336420 5577390 517.20000000000004547, 3336415 5577350 514.89999999999997726, 3336405 5577340 513.70000000000004547, 3336390 5577325 512.39999999999997726, 3336380 5577315 511.69999999999998863, 3336390 5577310 511, 3336400 5577305 509.90000000000003411, 3336405 5577300 509.30000000000001137, 3336425 5577260 506.40000000000003411, 3336430 5577250 505.90000000000003411, 3336450 5577215 502.60000000000002274, 3336460 5577190 500.30000000000001137, 3336470 5577170 498.19999999999998863, 3336485 5577140 495.90000000000003411, 3336495 5577110 493.5, 3336485 5577100 493.69999999999998863, 3336460 5577085 494, 3336425 5577060 494.40000000000003411, 3336410 5577050 494.69999999999998863, 3336370 5577025 494.60000000000002274, 3336340 5577000 494, 3336310 5576975 493.19999999999998863, 3336305 5576970 493.10000000000002274, 3336265 5576945 492.40000000000003411, 3336250 5576920 492.10000000000002274, 3336235 5576890 491.69999999999998863, 3336225 5576865 491.30000000000001137, 3336210 5576835 491.30000000000001137, 3336200 5576810 491.5, 3336190 5576785 492, 3336175 5576760 493.69999999999998863, 3336205 5576745 496.19999999999998863, 3336220 5576730 497.30000000000001137, 3336230 5576720 498, 3336240 5576695 500, 3336250 5576685 501, 3336260 5576675 501.60000000000002274, 3336245 5576650 503.10000000000002274, 3336235 5576630 503.90000000000003411, 3336220 5576605 503.80000000000001137, 3336210 5576580 502.80000000000001137, 3336205 5576570 502.19999999999998863, 3336190 5576545 501.19999999999998863, 3336185 5576535 501, 3336185 5576520 501.10000000000002274, 3336185 5576490 501.10000000000002274, 3336195 5576465 499.90000000000003411, 3336195 5576455 499.40000000000003411, 3336195 5576445 498.80000000000001137, 3336195 5576430 498.19999999999998863, 3336195 5576425 498, 3336195 5576400 496.19999999999998863, 3336200 5576390 495.19999999999998863, 3336220 5576360 490.19999999999998863, 3336235 5576320 485.69999999999998863, 3336240 5576295 483.19999999999998863, 3336245 5576295 482.90000000000003411, 3336255 5576250 479.10000000000002274, 3336255 5576240 477.90000000000003411, 3336260 5576205 473.69999999999998863, 3336260 5576195 472.40000000000003411, 3336260 5576190 472.19999999999998863, 3336255 5576185 471.80000000000001137, 3336245 5576175 471.69999999999998863, 3336235 5576170 471.60000000000002274, 3336200 5576140 470.60000000000002274, 3336160 5576115 466, 3336160 5576110 465.30000000000001137, 3336160 5576105 464.80000000000001137, 3336175 5576090 463, 3336180 5576080 462.19999999999998863, 3336180 5576070 460.80000000000001137, 3336175 5576065 460, 3336165 5576065 458.90000000000003411, 3336150 5576065 457.90000000000003411, 3336130 5576065 456.40000000000003411, 3336120 5576065 455.60000000000002274, 3336115 5576065 455.40000000000003411, 3336110 5576065 455.40000000000003411, 3336095 5576070 455.80000000000001137, 3336090 5576070 456.10000000000002274, 3336070 5576075 456.90000000000003411, 3336025 5576075 458.69999999999998863, 3336005 5576075 459.19999999999998863, 3335990 5576080 459.80000000000001137, 3335945 5576090 459.69999999999998863, 3335945 5576085 459.5, 3335950 5576080 459.30000000000001137, 3335955 5576070 459.19999999999998863, 3335960 5576065 459.10000000000002274, 3335980 5576030 458.90000000000003411, 3335985 5576020 458.90000000000003411, 3336025 5575990 457.5, 3336060 5575960 455.90000000000003411, 3336095 5575930 454.90000000000003411, 3336125 5575905 453.90000000000003411, 3336155 5575880 453.10000000000002274, 3336190 5575850 451.90000000000003411, 3336220 5575825 450.19999999999998863, 3336245 5575805 449, 3336275 5575785 447.5, 3336290 5575775 447.10000000000002274, 3336320 5575745 446.19999999999998863, 3336350 5575720 445, 3336385 5575695 444.30000000000001137, 3336390 5575690 444.30000000000001137, 3336395 5575685 444.19999999999998863, 3336420 5575665 444.5, 3336455 5575635 445.69999999999998863, 3336490 5575605 446.80000000000001137, 3336530 5575575 447.69999999999998863, 3336565 5575545 447.19999999999998863, 3336585 5575530 446.30000000000001137, 3336605 5575510 445.60000000000002274, 3336640 5575485 444.90000000000003411, 3336645 5575480 444.90000000000003411, 3336650 5575475 444.90000000000003411, 3336685 5575445 447.60000000000002274, 3336695 5575435 448.5, 3336720 5575420 450.60000000000002274, 3336745 5575400 452.19999999999998863, 3336760 5575390 453.10000000000002274, 3336780 5575385 454.10000000000002274, 3336810 5575385 455.5, 3336825 5575380 456.10000000000002274, 3336845 5575365 456.69999999999998863, 3336860 5575345 456.90000000000003411, 3336875 5575315 456.80000000000001137, 3336885 5575280 456.40000000000003411, 3336900 5575260 456, 3336900 5575255 455.80000000000001137, 3336925 5575235 454.90000000000003411, 3336955 5575210 454.60000000000002274, 3336985 5575185 453.5, 3337015 5575160 452.80000000000001137, 3337045 5575140 451.80000000000001137, 3337050 5575135 451.69999999999998863, 3337055 5575125 451.40000000000003411, 3337055 5575120 451.5, 3337030 5575095 452.40000000000003411, 3337005 5575070 453.10000000000002274, 3336980 5575045 453.5, 3336975 5575040 453.60000000000002274, 3336980 5575025 453.80000000000001137, 3336985 5575010 453.80000000000001137, 3336995 5574995 453.80000000000001137, 3337005 5574960 454.40000000000003411, 3337015 5574920 455.40000000000003411, 3337030 5574880 456.40000000000003411, 3337040 5574840 457.30000000000001137, 3337055 5574800 457.69999999999998863, 3337065 5574760 458.5, 3337080 5574720 458.80000000000001137, 3337080 5574695 459.10000000000002274, 3337085 5574670 459.5, 3337090 5574625 460.80000000000001137, 3337090 5574595 461.60000000000002274, 3337080 5574565 463.10000000000002274, 3337070 5574530 464.5, 3337060 5574495 466.10000000000002274, 3337060 5574485 466.80000000000001137, 3337055 5574495 466.30000000000001137, 3337050 5574500 465.80000000000001137, 3337045 5574500 465.30000000000001137, 3337035 5574505 464.80000000000001137, 3337030 5574510 464.80000000000001137, 3337030 5574505 464.90000000000003411, 3337020 5574490 465.60000000000002274, 3336990 5574455 466.90000000000003411, 3336960 5574425 467.30000000000001137, 3336935 5574405 467.40000000000003411, 3336915 5574390 467.10000000000002274, 3336910 5574390 467.10000000000002274, 3336870 5574370 466.60000000000002274, 3336835 5574360 465.69999999999998863, 3336820 5574355 465.5, 3336805 5574355 465, 3336790 5574355 464.5, 3336780 5574355 464.40000000000003411, 3336775 5574350 464.5, 3336775 5574345 464.80000000000001137, 3336775 5574340 465.19999999999998863, 3336785 5574345 465.19999999999998863, 3336775 5574340 465.19999999999998863, 3336735 5574345 465.10000000000002274, 3336725 5574345 465.10000000000002274, 3336720 5574350 464.90000000000003411, 3336715 5574355 464.60000000000002274, 3336675 5574355 464.60000000000002274, 3336635 5574360 464.69999999999998863, 3336590 5574365 464.5, 3336540 5574370 464.69999999999998863, 3336520 5574370 464.90000000000003411, 3336490 5574380 464.80000000000001137, 3336465 5574385 464.80000000000001137, 3336440 5574395 464.60000000000002274, 3336415 5574405 464.90000000000003411, 3336395 5574415 464.40000000000003411, 3336370 5574430 464.19999999999998863, 3336345 5574445 464, 3336315 5574465 464.5, 3336310 5574470 464.90000000000003411, 3336300 5574470 465.30000000000001137, 3336295 5574475 465.69999999999998863, 3336290 5574475 465.80000000000001137, 3336285 5574480 466, 3336280 5574480 466.19999999999998863, 3336275 5574485 466.30000000000001137, 3336245 5574510 467.40000000000003411, 3336215 5574540 469.40000000000003411, 3336185 5574565 470.40000000000003411, 3336165 5574585 471.10000000000002274, 3336145 5574600 471.80000000000001137, 3336105 5574625 471.80000000000001137, 3336070 5574655 471.90000000000003411, 3336040 5574680 472, 3336005 5574705 472.19999999999998863, 3335970 5574730 471.80000000000001137, 3335935 5574755 471.19999999999998863, 3335900 5574780 470.30000000000001137, 3335865 5574810 469.10000000000002274, 3335830 5574835 468.40000000000003411, 3335800 5574860 468, 3335770 5574885 467.40000000000003411, 3335735 5574915 468, 3335705 5574940 467.90000000000003411, 3335700 5574940 468, 3335665 5574970 468, 3335625 5575000 467.69999999999998863, 3335600 5575015 468.5, 3335590 5575020 468.69999999999998863, 3335570 5575035 469.40000000000003411, 3335550 5575045 470.10000000000002274, 3335520 5575055 471, 3335480 5575065 472.40000000000003411, 3335435 5575075 472.40000000000003411, 3335415 5575080 472.69999999999998863, 3335385 5575085 472.5, 3335355 5575090 472.19999999999998863, 3335345 5575095 472.69999999999998863, 3335310 5575110 470.60000000000002274, 3335305 5575115 470.5, 3335295 5575120 469.90000000000003411, 3335290 5575140 468.69999999999998863, 3335245 5575115 472.5, 3335235 5575115 472.90000000000003411, 3335190 5575125 473.40000000000003411, 3335140 5575130 473.10000000000002274, 3335090 5575140 472.90000000000003411, 3335040 5575150 474.10000000000002274, 3335000 5575155 475.40000000000003411, 3334955 5575165 477.30000000000001137, 3334915 5575175 479.30000000000001137, 3334870 5575185 480.19999999999998863, 3334825 5575195 482.19999999999998863, 3334775 5575205 482.90000000000003411, 3334765 5575210 482.90000000000003411, 3334730 5575215 481.30000000000001137, 3334700 5575220 480.40000000000003411, 3334665 5575225 479, 3334640 5575225 477.30000000000001137, 3334595 5575230 474.69999999999998863, 3334575 5575235 474, 3334555 5575245 473.60000000000002274, 3334535 5575250 472.69999999999998863, 3334520 5575260 471.5, 3334515 5575265 471, 3334500 5575275 470, 3334480 5575300 468.40000000000003411, 3334470 5575310 467.5, 3334465 5575315 467.10000000000002274, 3334455 5575320 466.69999999999998863, 3334445 5575305 466.60000000000002274, 3334440 5575300 466.30000000000001137, 3334435 5575295 465.90000000000003411, 3334425 5575280 465.60000000000002274, 3334410 5575245 466.19999999999998863, 3334380 5575265 464.19999999999998863, 3334350 5575285 460.5, 3334335 5575295 458.40000000000003411, 3334330 5575290 458.5, 3334315 5575290 458.30000000000001137, 3334290 5575285 457.80000000000001137, 3334265 5575275 450.90000000000003411, 3334260 5575305 449, 3334250 5575330 447.60000000000002274, 3334250 5575335 447, 3334245 5575340 446.60000000000002274, 3334240 5575355 445.69999999999998863, 3334230 5575385 444, 3334225 5575400 443.30000000000001137))", + 3.36203807 + ); + } + + public void testShort() { + runTest("LINESTRING (1 1, 2 2)", + "LINESTRING (1 4, 2 3)", 3d); + } + private static final double TOLERANCE = 0.00001; + + private void runTest(String wkt1, String wkt2, double expectedDistance) { + Geometry g1 = read(wkt1); + Geometry g2 = read(wkt2); + + DiscreteFrechetDistanceLinear.distance(g1, g2); + Stopwatch sw = new Stopwatch(); + sw.start(); + double distance0 = DiscreteFrechetDistanceLinear.distance(g1, g2); + sw.stop(); + //System.out.println(String.format("DiscreteFrechetDistanceLinear %dms.%n", sw.getTime())); + assertEquals(expectedDistance, distance0, TOLERANCE); + + DiscreteFrechetDistance.distance(g1, g2); + sw.reset(); + sw.start(); + double distance1 = DiscreteFrechetDistance.distance(g1, g2); + sw.stop(); + //System.out.println(String.format("DiscreteFrechetDistance %dms.%n", sw.getTime())); + assertEquals(expectedDistance, distance1, TOLERANCE); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java new file mode 100644 index 0000000000..3ae5b52b53 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jts.algorithm.distance; + +import junit.framework.TestCase; + +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.MatrixStorage; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.HashMapMatrix; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.CsrMatrix; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.RectMatrix; + +import org.junit.Test; + +public class MatrixStorageImplTests extends TestCase { + + @Test + public void testCsrMatrix() + { + MatrixStorage mat = new CsrMatrix(4, 6, 0d, 8); + runOrderedTest(mat); + mat = new CsrMatrix(4, 6, 0d, 8); + runUnorderedTest(mat); + } + @Test + public void testHashMapMatrix() + { + MatrixStorage mat = new HashMapMatrix(4, 6, 0d); + runOrderedTest(mat); + mat = new HashMapMatrix(4, 6, 0d); + runUnorderedTest(mat); + } + @Test + public void testRectMatrix() + { + MatrixStorage mat = new RectMatrix(4, 6, 0d); + runOrderedTest(mat); + mat = new RectMatrix(4, 6, 0d); + runUnorderedTest(mat); + } + + private static void runOrderedTest(MatrixStorage mat) { + mat.set(0, 0, 10); + mat.set(0, 1, 20); + mat.set(1, 1, 30); + mat.set(1, 3, 40); + mat.set(2, 2, 50); + mat.set(2, 3, 60); + mat.set(2, 4, 70); + mat.set(3, 5, 80); + + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 0, 10d, mat.get(0, 0)), 10d, mat.get(0, 0)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 1, 20d, mat.get(0, 1)), 20d, mat.get(0, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 1, 30d, mat.get(1, 1)), 30d, mat.get(1, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 3, 40d, mat.get(1, 3)), 40d, mat.get(1, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 2, 50d, mat.get(2, 2)), 50d, mat.get(2, 2)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 3, 60d, mat.get(2, 3)), 60d, mat.get(2, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 4, 70d, mat.get(2, 4)), 70d, mat.get(2, 4)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 3, 5, 80d, mat.get(3, 5)), 80d, mat.get(3, 5)); + + } + + private static void runUnorderedTest(MatrixStorage mat) { + mat.set(0, 0, 10); + mat.set(3, 5, 80); + mat.set(0, 1, 20); + mat.set(2, 4, 70); + mat.set(1, 1, 30); + mat.set(2, 3, 60); + mat.set(2, 2, 50); + mat.set(1, 3, 40); + + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 0, 10d, mat.get(0, 0)), 10d, mat.get(0, 0)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 1, 20d, mat.get(0, 1)), 20d, mat.get(0, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 1, 30d, mat.get(1, 1)), 30d, mat.get(1, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 3, 40d, mat.get(1, 3)), 40d, mat.get(1, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 2, 50d, mat.get(2, 2)), 50d, mat.get(2, 2)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 3, 60d, mat.get(2, 3)), 60d, mat.get(2, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 4, 70d, mat.get(2, 4)), 70d, mat.get(2, 4)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 3, 5, 80d, mat.get(3, 5)), 80d, mat.get(3, 5)); + + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java new file mode 100644 index 0000000000..42501f7467 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.geom.Geometry; +import test.jts.GeometryTestCase; + +public class FrechetSimilarityMeasureTest extends GeometryTestCase { + + public FrechetSimilarityMeasureTest(String name) { + super(name); + } + + public void testDifferentGeometryTypesThrowIAE() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1)"); + + try { + SimilarityMeasure sm = new FrechetSimilarityMeasure(); + sm.measure(g1, g2); + fail("Different geometry types should fail!"); + } + catch (Exception e) { + assertTrue(true); + } + } + + public void testEqualGeometriesReturn1() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("POINT(1 1)"); + assertEquals("Point", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + + g1 = read("LINESTRING(1 1, 2 1)"); + g2 = read("LINESTRING(1 1, 2 1)"); + assertEquals("LineString", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + + g1 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + g2 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + assertEquals("POLYGON", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + } + + public void testGreaterFrechetDistanceReturnsPoorerSimilarity() + { + Geometry g1 = read("LINESTRING(1 1, 2 1.0, 3 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1.1, 3 1)"); + Geometry g3 = read("LINESTRING(1 1, 2 1.2, 3 1)"); + + SimilarityMeasure sm = new FrechetSimilarityMeasure(); + double m12 = sm.measure(g1, g2); + double m13 = sm.measure(g1, g3); + + assertTrue("Greater distance, poorer similarity", m13 < m12); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java new file mode 100644 index 0000000000..9c805fc266 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.geom.Geometry; +import test.jts.GeometryTestCase; + +public class HausdorffSimilarityMeasureTest extends GeometryTestCase { + public HausdorffSimilarityMeasureTest(String name) { + super(name); + } + + public void testEqualGeometriesReturn1() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("POINT(1 1)"); + assertEquals("Point", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + + g1 = read("LINESTRING(1 1, 2 1)"); + g2 = read("LINESTRING(1 1, 2 1)"); + assertEquals("LineString", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + + g1 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + g2 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + assertEquals("POLYGON", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + } + + public void testGreaterHausdorffDistanceReturnsPoorerSimilarity() + { + Geometry g1 = read("LINESTRING(1 1, 2 1.0, 3 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1.1, 3 1)"); + Geometry g3 = read("LINESTRING(1 1, 2 1.2, 3 1)"); + + SimilarityMeasure sm = new HausdorffSimilarityMeasure(); + double m12 = sm.measure(g1, g2); + double m13 = sm.measure(g1, g3); + + assertTrue("Greater distance, poorer similarity", m13 < m12); + } + +}