package com.revolsys.geometry.algorithm; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.LineString; /** * Computes various distance functions for determining how far apart are two * matched segments. In general these functions use the Hausdorff distance to * compute how far apart geometries are, since the normal Euclidean distance is * not a useful measure of "far apartness". */ public final class MatchDistance { private static double farLength(final Geometry a, final Geometry b, final double tolerance) { final Geometry farA = a.difference(b.buffer(tolerance)); final double farALen = farA.getLength(); return farALen; } /** * Computes the maximum distance apart between two linestrings. (Note this is * NOT the distance between the two furthest points on the linestrings, which * is not a useful measure of "farness"). * * @param a * @param b * @return */ public static double maxDistance(final LineString a, final LineString b) { return VertexHausdorffDistance.distance(a, b); } /** * Computes the fraction of length of LineStrings which is within a given * tolerance value, after trimming. The previously computed maxDistance * between the lines can be supplied to allow optimizing the calculation (if * maxDistance < tolerance, the nearness fraction = 1.0). * * @param a a LineString * @param b a LineString * @param fullDistance the full distance between the lines (previously * computed) * @param tolerance the distance beyond which to total the length * @return the fraction of length beyond the tolerance */ public static double nearnessFraction(final LineString a, final LineString b, final double tolerance) { final double lenA = a.getLength(); final double lenB = b.getLength(); final double lenAB = lenA + lenB; // this can happen if the segments are badly aligned and get trimmed to // points if (lenAB <= 0.0) { final boolean inTolerance = a.distance(b) <= tolerance; if (inTolerance) { return 0.0; } else { return 1.0; } } final double farLenA = farLength(a, b, tolerance); final double farLenB = farLength(b, a, tolerance); final double nearLenA = lenA - farLenA; final double nearLenB = lenB - farLenB; // avoid division by 0.0 double nearPctA; if (lenA > 0) { nearPctA = nearLenA / lenA; } else { nearPctA = 0.0; } double nearPctB; if (lenB > 0) { nearPctB = nearLenB / lenB; } else { nearPctB = 0.0; } // choose the worst case scenario as the final value final double nearnessFrac = Math.min(nearPctA, nearPctB); return nearnessFrac; } /** * Computes the fraction of length of LineStrings which is within a given * tolerance value, after trimming. * * @param a a LineString * @param b a LineString * @param tolerance the distance beyond which to total the length * @param trimLines <code>true</code> if the computation should take the * trimmed lines into account * @return the fraction of matched line length beyond the tolerance */ public static double nearnessFraction(final LineString a, final LineString b, final double tolerance, final boolean trimLines) { double nearnessFrac = nearnessFraction(a, b, tolerance); if (trimLines) { final LineString trimmedA = a.getMaximalNearestSubline(b); final LineString trimmedB = b.getMaximalNearestSubline(a); final double trimmedNF = nearnessFraction(trimmedA, trimmedB, tolerance); // choose the largest fraction // (it can happen that the original nearness is greater, if the lines are // not well-aligned) if (trimmedNF > nearnessFrac) { nearnessFrac = trimmedNF; } } return nearnessFrac; } /** * Computes the fraction of length of matched LineStrings which is nearer than * a given tolerance value (optionally after trimming). The previously * computed maxDistance between the lines can be supplied to allow optimizing * the calculation (if maxDistance < tolerance, the nearness fraction = 1.0). * * @param a a LineString * @param b a LineString * @param maxDistance the maximum distance between the lines (if previously * computed) * @param tolerance the distance beyond which to total the length * @param trimLines <code>true</code> if the computation should take the * trimmed lines into account * @return the fraction of matched line length beyond the tolerance */ public static double nearnessFraction(final LineString a, final LineString b, final double maxDistance, final double tolerance, final boolean trimLines) { // if the orginal lines are closer than the tolerance there is no need for // further computation if (maxDistance < tolerance) { return 1.0; } return nearnessFraction(a, b, tolerance, trimLines); } /** * Computes how far apart are two linestrings after trimming any unmatched * length at the ends. * * @param a * @param b * @return * @see MaximalNearestSubline */ public static double trimmedDistance(final LineString a, final LineString b) { final LineString trimA = a.getMaximalNearestSubline(b); final LineString trimB = b.getMaximalNearestSubline(a); return VertexHausdorffDistance.distance(trimA, trimB); } private MatchDistance() { } }