package de.fub.agg2graph.structs.frechet; import de.fub.agg2graph.agg.AggConnection; import de.fub.agg2graph.agg.AggContainer; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.structs.GPSCalc; import de.fub.agg2graph.structs.GPSEdge; import de.fub.agg2graph.structs.GPSPoint; import de.fub.agg2graph.structs.ILocation; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.TreeSet; import java.util.logging.Logger; /** * * @author Martinus * */ public class FrechetDistance { @SuppressWarnings("unused") private static final Logger logger = Logger .getLogger("agg2graph.agg.frechet.dist"); public double maxDistance = 1; public List<AggConnection> P; // AGG public List<GPSEdge> Q; // TRA public Cell[] cells = null; // List of Container AggContainer aggContainer; public FrechetDistance(double maxDistance) { P = null; Q = null; this.maxDistance = maxDistance; } public int getSizeP() { return (P != null) ? P.size() : 0; } public int getSizeQ() { return (Q != null) ? Q.size() : 0; } public FrechetDistance(List<GPSEdge> a, List<GPSEdge> t, double epsilon) { this.P = new ArrayList<AggConnection>(); for (GPSEdge agg : a) { P.add(new AggConnection(agg.getFrom(), agg.getTo(), aggContainer)); } this.Q = t; this.maxDistance = epsilon; calculateReachableSpace(); } void updateFreeSpace() { for (int i = 0; i < P.size(); ++i) { for (int j = 0; j < Q.size(); ++j) { getCell(i, j).updateCell(); } } calculateReachableSpace(); } public double getEpsilon() { return maxDistance; } public void setEpsilon(double epsilon) { this.maxDistance = epsilon; updateFreeSpace(); } public class Cell { public int i; public int j; private double a, b, c, d; public GPSEdge q; public AggConnection p; public int width; boolean isRelevant; public Point from = new Point(-1, -1); public Point to = new Point(-1, -1); // Extension List<Point> convexHull = new ArrayList<Point>(); // Convex Hull of white // region Integer convexTracker = Integer.MIN_VALUE; // A help variable to // determine the hull // intervals of the reachable space Interval leftF; Interval bottomF; Interval leftR; Interval bottomR; public Interval getLeftF() { return leftF; } public void setLeftF(Interval leftF) { this.leftF = leftF; } public Interval getLeftR() { return leftR; } public void setLeftR(Interval leftR) { this.leftR = leftR; } public Interval getBottomF() { return bottomF; } public void setBottomF(Interval bottomF) { this.bottomF = bottomF; } public Interval getBottomR() { return bottomR; } public void setBottomR(Interval bottomR) { this.bottomR = bottomR; } public Cell(int i, int j) { this.i = i; this.j = j; p = P.get(i); q = Q.get(j); isRelevant = p.getFrom().isRelevant(); updateCell(); } void updateCell() { leftF = GPSCalc.getSegmentCircleIntersection2(p.getFrom().getLon(), p.getFrom().getLat(), p.getTo().getLon(), p.getTo() .getLat(), q.getFrom().getLon(), q.getFrom() .getLat(), maxDistance); a = leftF.start; b = leftF.end; bottomF = GPSCalc.getSegmentCircleIntersection2(q.getFrom() .getLon(), q.getFrom().getLat(), q.getTo().getLon(), q .getTo().getLat(), p.getFrom().getLon(), p.getFrom() .getLat(), maxDistance); c = bottomF.start; d = bottomF.end; } /** * Draw the free space region of the cell with given epsilon. * * @param img * @param width * @return */ public BufferedImage getFreeSpace(BufferedImage img, int width) { this.width = width; BufferedImage buffer = img; if (buffer == null) { buffer = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB); } double stepsize = 1.0 / width; /** * Mark white-black region and convexhull of the white region TODO: * Optimization */ // AGG for (int s = 0; s < width; ++s) { double sStep = s * stepsize; ILocation pAtt = p.at(sStep); convexTracker = Integer.MIN_VALUE; List<Point> current = new ArrayList<Point>(2); // TRA for (int t = 0; t < width; ++t) { double tStep = t * stepsize; ILocation qAtt = q.at(tStep); double distance = GPSCalc.getDistanceTwoPointsDouble(pAtt, qAtt); if (distance < maxDistance && isRelevant) { buffer.setRGB(t, width - 1 - s, Color.WHITE.getRGB()); if (convexTracker == Integer.MIN_VALUE) { current.add(new Point(s, t)); convexTracker = s; } else if (convexTracker == s) { if (current.size() == 2) { current.remove(1); } current.add(new Point(s, t)); } } else { buffer.setRGB(t, width - 1 - s, Color.BLACK.getRGB()); } } convexHull.addAll(current); } // TRA for (int t = 0; t < width; ++t) { double tStep = t * stepsize; ILocation qAtt = q.at(tStep); convexTracker = Integer.MIN_VALUE; List<Point> current = new ArrayList<Point>(2); // AGG for (int s = 0; s < width; ++s) { double sStep = s * stepsize; ILocation pAtt = p.at(sStep); double distance = GPSCalc.getDistanceTwoPointsDouble(qAtt, pAtt); if (distance < maxDistance && isRelevant) { if (convexTracker == Integer.MIN_VALUE) { current.add(new Point(s, t)); convexTracker = t; } else if (convexTracker == t) { if (current.size() == 2) { current.remove(1); } current.add(new Point(s, t)); } } } convexHull.addAll(current); } // Sorting All-List Collections.sort(convexHull, new Comparator<Point>() { @Override public int compare(Point p1, Point p2) { if (p1.y != p2.y) { return p1.y - p2.y; } else { if (p1.x < p2.x) { return -1; } else if (p1.x > p2.x) { return 1; } return 0; } } }); // Remove duplicate elements for (int i = 0; i < convexHull.size() - 1; i++) { if (convexHull.get(i).equals(convexHull.get(i + 1))) { convexHull.remove(i--); } } // Draw the path if possible if (from.x != -1 && from.y != -1 && to.x != -1 && to.y != -1) { Graphics2D g2 = (Graphics2D) buffer.getGraphics(); g2.setColor(Color.BLUE); g2.setStroke(new BasicStroke(5)); g2.drawLine(from.y, width - 1 - from.x, to.y, width - 1 - to.x); } return buffer; } /** * Draw the lines marking the cell boundary intervals. */ public BufferedImage getParameterMarks(BufferedImage img, int width) { BufferedImage buffer = img; if (buffer == null) { buffer = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB); } Graphics2D g2 = (Graphics2D) buffer.getGraphics(); g2.clearRect(0, 0, width, width); if (a < Double.MAX_VALUE && b > Double.MIN_VALUE) { g2.draw(new Line2D.Double(0., width - a * width, 0., width - b * width)); } if (c < Double.MAX_VALUE && d > Double.MIN_VALUE) { g2.draw(new Line2D.Double(c * width, width - 1, d * width, width - 1)); } return buffer; } /** * Draws the reachable space markers. */ public BufferedImage getReachableMarks(BufferedImage img, int width) { BufferedImage buffer = img; if (buffer == null) { buffer = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB); } Graphics2D g2 = (Graphics2D) buffer.getGraphics(); g2.clearRect(0, 0, width, width); g2.setColor(Color.red); g2.setStroke(new BasicStroke(2.f)); if (!leftF.isEmpty()) { g2.draw(new Line2D.Double(4., width - leftF.start * width, 4., width - leftF.end * width)); } if (!bottomF.isEmpty()) { g2.draw(new Line2D.Double(bottomF.start * width, width - 5., bottomF.end * width, width - 5.)); } g2.setComposite(AlphaComposite.SrcAtop.derive(0.4f)); if (!leftF.isEmpty()) { g2.fill(new Rectangle2D.Double(4., width - leftF.end * width, width - 8, (leftF.end - leftF.start) * width)); } if (!bottomF.isEmpty()) { g2.fill(new Rectangle2D.Double(bottomF.start * width, 5., (bottomF.end - bottomF.start) * width, width - 10)); } return buffer; } /** * This grid may be used to create a height plot of the distance field. */ public double[] getDistanceGrid(double[] grid, int size) { double[] buffer = grid; if (buffer == null) { buffer = new double[size]; } double stepsize = 1.0 / size; for (int s = 0; s < size; ++s) { double sStep = s * stepsize; ILocation traceAt = q.at(sStep); for (int t = 0; t < size; ++t) { double tStep = t * stepsize; ILocation aggAt = p.at(tStep); buffer[(s * size + t)] = GPSCalc .getDistanceTwoPointsDouble(traceAt, aggAt); } } return buffer; } @Override public String toString() { return "(" + i + ", " + j + ") left=" + leftF + " bottom=" + bottomF; } /** * Check if there is white region * * @return */ public boolean isWhiteEmpty() { return convexHull.isEmpty() ? true : false; } /** * Get path with given direction * * @param direction = true ? agg : trace; */ public void getPath(boolean direction) { int mXindex = -1; int mYindex = -1; Point to = new Point(-1, -1); // get the most right x and lowest from this x -> direction trace if (direction) { for (Point p : convexHull) { if (mYindex < p.y && p.y >= from.y) { mYindex = p.y; } } for (Point p : convexHull) { if ((p.y == mYindex && p.x >= from.x) && (mXindex == -1 || mXindex > p.x)) { mXindex = p.x; to = p; } } } // get the most top y and most left from this y -> direction agg else { for (Point p : convexHull) { if (mXindex < p.x && p.x >= from.x) { mXindex = p.x; } } for (Point p : convexHull) { if ((p.x == mXindex && p.y >= from.y) && (mYindex == -1 || mYindex > p.y)) { mYindex = p.y; to = p; } } } this.to = to; if (to.x == -1 || to.y == -1) { // System.out.println("i = " + this.i + " : j = " + this.j); // System.out.println("STIMMT nicht"); } } /** * Get the from point. from shall be very left and bottom * * @param limit * @return from point */ public Point setLowestFrom(int limit) { int y = Integer.MAX_VALUE, bestI = -1; int best = Integer.MAX_VALUE, current; for (int k = 0; k < convexHull.size(); k++) { current = convexHull.get(k).x + convexHull.get(k).y; if (best > current && y > convexHull.get(k).y && convexHull.get(k).y >= limit) { best = current; y = convexHull.get(k).y; bestI = k; } } if (bestI == -1) { return new Point(-1, -1); } return convexHull.get(bestI); } /** * Get the longest path of a cell * * @return the y-Coord */ public int getLongestPath() { int best = -1, current; Point bestTo = null, currentTo; for (int i = 0; i < convexHull.size(); i++) { currentTo = convexHull.get(i); if (from.x < currentTo.x && from.y < currentTo.y) { current = (currentTo.x - from.x) + (currentTo.y - from.y); if (best < current) { bestTo = currentTo; best = current; } } } this.to = bestTo; return this.to.y; } } /** * Checks the condition for the decision problem. The cells must be update * with the actual epsilon beforehand. * * @return */ public boolean isInDistance() { if (P.size() < 1 || Q.size() < 1) { return false; } Cell lastCell = getCell(P.size() - 1, Q.size() - 1); // Calculate a b for and imaginary cell p,q+1; Interval gate = new Interval(); AggConnection lastSegOfP = P.get(P.size() - 1); ILocation lastPointOfQ = Q.get(Q.size() - 1).getTo(); gate = GPSCalc.getSegmentCircleIntersection2(lastSegOfP.getFrom() .getLon(), lastSegOfP.getFrom().getLat(), lastSegOfP.getTo() .getLon(), lastSegOfP.getTo().getLat(), lastPointOfQ.getLon(), lastPointOfQ.getLat(), maxDistance); if (lastCell.leftF.isEmpty() && lastCell.bottomF.isEmpty()) { return false; // the gate would be empty. } else if (GPSCalc.compareDouble(gate.end, 1.) == 0) { return true; } else { return false; } } /** * Simple newton approximation for the epsilon. The algorithm starts with * the maximum distance of the path end points. With that start given * maxSteps of newton steps will be made to refine epsilon. * * @param maxSteps * @return */ public double approximate(int maxSteps) { double startValue = Math.max(GPSCalc.getDistanceTwoPointsDouble(P .get(0).getFrom(), Q.get(0).getFrom()), GPSCalc .getDistanceTwoPointsDouble(P.get(P.size() - 1).getTo(), Q.get(Q.size() - 1).getTo())); setEpsilon(startValue); if (isInDistance()) { return startValue; } double lastWorkingEpsilon = Double.MAX_VALUE; double stepsize = startValue; int steps = 0; double epsilonToTest = 0.; while (++steps <= maxSteps) { if (epsilonToTest == (maxDistance + stepsize)) { break; } epsilonToTest = maxDistance + stepsize; setEpsilon(epsilonToTest); if (isInDistance()) { lastWorkingEpsilon = maxDistance; stepsize = -(stepsize / 2.); } else { stepsize = 2. * stepsize; } } setEpsilon(lastWorkingEpsilon); return maxDistance; } /** * Pair.first is point in P Pair.second is point in Q */ public HashMap<Pair<ILocation, ILocation>, Double> distanceMatrix = new HashMap<>(); /** * The location indices, also corresponding index of the edges in P or Q * matched to the points on the other curve */ public HashMap<Integer, TreeSet<AggNode>> fromP = new HashMap<>(); public HashMap<Integer, TreeSet<GPSPoint>> fromQ = new HashMap<>(); boolean needsRecalculation = true; private void addP(int cellIndexi, ILocation trace, ILocation other) { distanceMatrix.put(new Pair<>(trace, other), GPSCalc.getDistanceTwoPointsDouble(trace, other)); if (cellIndexi != -1) { if (!fromP.containsKey(cellIndexi) || fromP.get(cellIndexi) == null) { fromP.put(cellIndexi, new TreeSet<AggNode>()); } fromP.get(cellIndexi).add(new AggNode(other, aggContainer)); } } private void addQ(int cellIndexj, ILocation agg, ILocation other) { distanceMatrix.put(new Pair<>(other, agg), GPSCalc.getDistanceTwoPointsDouble(other, agg)); if (cellIndexj != -1) { if (!fromQ.containsKey(cellIndexj) || fromQ.get(cellIndexj) == null) { fromQ.put(cellIndexj, new TreeSet<GPSPoint>()); } fromQ.get(cellIndexj).add(new GPSPoint(other)); } } /** * Compute the critical edges. These are used by the FrechetBasedMerge merge * algorithm. */ public void computeMetaData() { distanceMatrix.clear(); fromP.clear(); fromQ.clear(); // case a) addP(0, P.get(0).getFrom(), Q.get(0).getFrom()); addQ(0, Q.get(0).getFrom(), P.get(0).getFrom()); addP(P.size(), P.get(P.size() - 1).getTo(), Q.get(Q.size() - 1).getTo()); addQ(Q.size(), Q.get(Q.size() - 1).getTo(), P.get(P.size() - 1).getTo()); // addP(0, P.firstElement().getFrom(), Q.firstElement().getFrom()); // addQ(0, Q.firstElement().getFrom(), P.firstElement().getFrom()); // // addP(P.size(), P.lastElement().getTo(), Q.lastElement().getTo()); // addQ(Q.size(), Q.lastElement().getTo(), P.lastElement().getTo()); // Case b) for (int i = 0; i < P.size(); ++i) { for (int j = 0; j < Q.size(); ++j) { AggConnection p = P.get(i); GPSEdge q = Q.get(j); ILocation intersectionPerpQThroughPWithQ = GPSCalc .intersectionWithPerpendicularThrough(q.getFrom(), q.getTo(), p.getFrom()); ILocation intersectionPerpPThroughQWithP = GPSCalc .intersectionWithPerpendicularThrough(p.getFrom(), p.getTo(), q.getFrom()); if (intersectionPerpPThroughQWithP != null && GPSCalc.PntOnLine(p.getFrom(), p.getTo(), intersectionPerpPThroughQWithP)) { addP(-1, intersectionPerpPThroughQWithP, q.getFrom()); addQ(j, q.getFrom(), intersectionPerpPThroughQWithP); } if (intersectionPerpQThroughPWithQ != null && GPSCalc.PntOnLine(q.getFrom(), q.getTo(), intersectionPerpQThroughPWithQ)) { addP(i, p.getFrom(), intersectionPerpQThroughPWithQ); addQ(-1, intersectionPerpQThroughPWithQ, p.getFrom()); } } } // Case c) // The path of p has to be converted into a vertex list. ArrayList<GPSPoint> a = new ArrayList<>(); for (AggConnection p : P) { a.add(p.getFrom()); } a.add(P.get(P.size() - 1).getTo()); for (int i = 0; i < a.size() - 1; ++i) { for (int k = i + 1; k < a.size(); ++k) { GPSEdge ik = new GPSEdge((GPSPoint) a.get(i), (GPSPoint) a.get(k)); ILocation mid = ik.at(0.5); for (int j = 0; j < Q.size(); ++j) { GPSEdge q = Q.get(j); // Intersection test of the bisector of P(i), P(k) with Q(j) ILocation intersection = GPSCalc .IntersectionOfPerpendicularWithLine(ik.getFrom(), ik.getTo(), mid, q.getFrom(), q.getTo()); if (intersection != null && GPSCalc.PntOnLine(q.getFrom(), q.getTo(), intersection)) { addP(i, a.get(i), intersection); addQ(-1, intersection, a.get(i)); addP(k, a.get(k), intersection); addQ(-1, intersection, a.get(k)); } } } } a.clear(); for (GPSEdge q : Q) { a.add(q.getFrom()); } a.add(Q.get(Q.size() - 1).getTo()); for (int i = 0; i < a.size() - 1; ++i) { for (int k = i + 1; k < a.size(); ++k) { GPSEdge ik = new GPSEdge((GPSPoint) a.get(i), (GPSPoint) a.get(k)); ILocation mid = ik.at(0.5); for (int j = 0; j < P.size(); ++j) { AggConnection p = P.get(j); // Intersection test of the bisector of Q(i), Q(k) with P(j) ILocation intersection = GPSCalc .IntersectionOfPerpendicularWithLine(ik.getFrom(), ik.getTo(), mid, p.getFrom(), p.getTo()); if (intersection != null && GPSCalc.PntOnLine(p.getFrom(), p.getTo(), intersection)) { addP(-1, intersection, a.get(i)); addQ(i, a.get(i), intersection); addP(-1, intersection, a.get(k)); addQ(k, a.get(k), intersection); } } } } } public List<Double> criticalValues = new ArrayList<Double>(); /** * Compute epsilon by first calculate all critical points of type a, b and c * described in the paper Alt and Godau 95. Then do a binary search to find * the smallest of the critical values that passes the inDistance test. * * @return */ public double computeEpsilon() { criticalValues.clear(); // case a) criticalValues.add(GPSCalc.getDistanceTwoPointsDouble(P.get(0) .getFrom(), Q.get(0).getFrom())); criticalValues.add(GPSCalc.getDistanceTwoPointsDouble( P.get(P.size() - 1).getTo(), Q.get(Q.size() - 1).getTo())); // criticalValues.add(locationToLocationDistance.getDistance(P.firstElement().getFrom(), // Q.firstElement().getFrom())); // criticalValues.add(locationToLocationDistance.getDistance(P.lastElement().getTo(), // Q.lastElement().getTo()) ); // Case b) for (int i = 0; i < P.size(); ++i) { for (int j = 0; j < Q.size(); ++j) { // L^F AggConnection p = P.get(i); GPSEdge q = Q.get(j); criticalValues.add(GPSCalc.getDistancePointToEdgeDouble( p.getFrom(), q.getFrom(), q.getTo())); criticalValues.add(GPSCalc.getDistancePointToEdgeDouble( q.getFrom(), p.getFrom(), p.getTo())); // criticalValues.add(locationToEdgeDistance.getDistance(p.getFrom(), // q)); // criticalValues.add(locationToEdgeDistance.getDistance(q.getFrom(), // p)); } } // The missing last elements. { AggConnection p = P.get(P.size() - 1); // P.get(P.size() - 1); for (int j = 0; j < Q.size(); ++j) { criticalValues.add(GPSCalc.getDistancePointToEdgeDouble( p.getTo(), Q.get(j).getFrom(), Q.get(j).getTo())); // criticalValues.add(locationToEdgeDistance.getDistance(p.getTo(), // Q.get(j))); } GPSEdge q = Q.get(Q.size() - 1); for (int i = 0; i < P.size(); ++i) { criticalValues.add(GPSCalc.getDistancePointToEdgeDouble( q.getTo(), P.get(i).getFrom(), P.get(i).getTo())); // criticalValues.add(locationToEdgeDistance.getDistance(q.getTo(), // P.get(i))); } } // Case c) // The path of p has to be converted into a vertex list. ArrayList<ILocation> a = new ArrayList<ILocation>(); // for (int i = 0; i < Q.size(); i++) { // a.add(Q.get(i)); // } for (AggConnection p : P) { a.add(p.getFrom()); } a.add(P.get(P.size() - 1).getTo()); for (int i = 0; i < a.size(); ++i) { for (int k = 0; k < a.size(); ++k) { if (i == k) { continue; } GPSEdge ik = new GPSEdge((GPSPoint) a.get(i), (GPSPoint) a.get(k)); ILocation mid = ik.at(0.5); for (int j = 0; j < Q.size(); ++j) { GPSEdge q = Q.get(j); // Intersection test of the bisector of P(i), P(k) with Q(j) ILocation intersection = GPSCalc .IntersectionOfPerpendicularWithLine(ik.getFrom(), ik.getTo(), mid, q.getFrom(), q.getTo()); if (intersection != null // && Algorithms.PntOnLine(q.getFrom(), q.getTo(), // intersection) ) { criticalValues.add(GPSCalc.getDistanceTwoPointsDouble( a.get(i), intersection)); criticalValues.add(GPSCalc.getDistanceTwoPointsDouble( a.get(k), intersection)); // criticalValues.add(a.get(i).getDistanceTo(intersection)); // criticalValues.add(a.get(k).getDistanceTo(intersection)); } } } } a.clear(); // for (int i = 0; i < P.size(); i++) { // a.add(P.get(i)); // } for (GPSEdge q : Q) { a.add(q.getFrom()); } a.add(Q.get(Q.size() - 1).getTo()); for (int i = 0; i < a.size(); ++i) { for (int k = 0; k < a.size(); ++k) { if (i == k) { continue; } GPSEdge ik = new GPSEdge((GPSPoint) a.get(i), (GPSPoint) a.get(k)); ILocation mid = ik.at(0.5); for (int j = 0; j < P.size(); ++j) { AggConnection p = P.get(j); // Intersection test of the bisector of Q(i), Q(k) with P(j) ILocation intersection = GPSCalc .IntersectionOfPerpendicularWithLine(ik.getFrom(), ik.getTo(), mid, p.getFrom(), p.getTo()); if (intersection != null // && Algorithms.PntOnLine(p.getFrom(), p.getTo(), // intersection) ) { criticalValues.add(GPSCalc.getDistanceTwoPointsDouble( a.get(i), intersection)); criticalValues.add(GPSCalc.getDistanceTwoPointsDouble( a.get(k), intersection)); // criticalValues.add(a.get(i).getDistanceTo(intersection)); // criticalValues.add(a.get(k).getDistanceTo(intersection)); } } } } // Finally binsearch for the real value of epsilon. Collections.sort(criticalValues); final double epsilon2 = binSearch(criticalValues); return epsilon2; } private double binSearch(List<Double> criticalValues) { int low = 0; int high = criticalValues.size() - 1; int median = 0; int goodMedian = -1; double goodDistance = Double.POSITIVE_INFINITY; while (low <= high) { median = (low + high) / 2; double test = criticalValues.get(median); setEpsilon(test); if (isInDistance()) { high = median - 1; if (test < goodDistance) { goodDistance = test; goodMedian = median; } } else { low = median + 1; } } if (goodMedian < 0) { double check = criticalValues.get(criticalValues.size() - 1); // Seems rounding errors lead to not detect good values for distance // right at the ends of the lines. check += 0.0000001; setEpsilon(check); if (isInDistance()) { goodDistance = check; } } setEpsilon(goodDistance); return goodDistance; } void calculateReachableSpace() { calculateReachableSpace(0, 0, false); } // The reachable space is calculated by dynamic programming. void calculateReachableSpace(int iOfPivot, int jOfPivot, boolean skipInit) { if (P.size() < 1 || Q.size() < 1) { return; } if (!skipInit) { Cell nullnull = getCell(iOfPivot, jOfPivot); if (nullnull == null) { return; } if (nullnull.leftF.start > 0 && nullnull.bottomF.start > 0) { nullnull.leftF = new Interval(); nullnull.bottomF = new Interval(); } } // Calculate Rv_i0 for (int i = iOfPivot + 1; i < P.size(); ++i) { Cell cell = getCell(i, jOfPivot); cell.leftF = new Interval(); if (cell.bottomF.isEmpty()) { continue; } Cell bottomCell = getCell(i - 1, jOfPivot); if (bottomCell.bottomF.isEmpty() || bottomCell.bottomF.start > cell.bottomF.end) { cell.bottomF = new Interval(); // Empty. } } // Calculate Rh_0j for (int j = jOfPivot + 1; j < Q.size(); ++j) { Cell cell = getCell(iOfPivot, j); cell.bottomF = new Interval(); if (cell.leftF.isEmpty()) { continue; // All following cells will be made empty. } Cell leftCell = getCell(iOfPivot, j - 1); if (leftCell.leftF.isEmpty() || leftCell.leftF.start > cell.leftF.end) { cell.leftF = new Interval(); // Empty Interval. } } for (int i = iOfPivot; i < P.size(); ++i) { for (int j = jOfPivot; j < Q.size(); ++j) { Cell cell = getCell(i, j); Cell rightCell = (j < Q.size() - 1) ? getCell(i, j + 1) : null; Cell topCell = (i < P.size() - 1) ? getCell(i + 1, j) : null; // Cell rightCell = (j < Q.size() - 1) ? getCell(i, j + 1) : // null; // Cell topCell = (i < P.size() - 1) ? getCell(i + 1, j) : null; if (rightCell != null) { if ((!cell.bottomF.isEmpty()) || cell.leftF.start < rightCell.leftF.start) { rightCell.leftF = rightCell.leftF; } else if (cell.leftF.isEmpty() || cell.leftF.start > rightCell.leftF.end) { rightCell.leftF = new Interval(); // Empty. } else { rightCell.leftF = new Interval(cell.leftF.start, rightCell.leftF.end); } } if (topCell != null) { if ((!cell.leftF.isEmpty()) || cell.bottomF.start < topCell.bottomF.start) { topCell.bottomF = topCell.bottomF; } else if (cell.bottomF.isEmpty() || cell.bottomF.start > topCell.bottomF.end) { topCell.bottomF = new Interval(); } else { topCell.bottomF = new Interval(cell.bottomF.start, topCell.bottomF.end); } } } } } void resizeCells() { int newSize = Q.size() * P.size(); if (cells == null) { cells = new Cell[newSize]; } else { // if(cells.length > newSize) { // return; // } else { // cells = new Cell[newSize + 10*10]; // needsRecalculation = true; // } cells = new Cell[newSize]; } } // public Cell getCell(int i, int j) { assert (0 <= i && i < P.size()); assert (0 <= j && j < Q.size()); if (!(0 <= i && i < P.size() && 0 <= j && j < Q.size())) { return null; } if (cells == null) { cells = new Cell[Q.size() * P.size()]; } if (cells[j * P.size() + i] == null) { cells[j * P.size() + i] = new Cell(i, j); } return cells[j * P.size() + i]; } public double getDistance(List<AggConnection> a, List<GPSEdge> b) { if (a == null || b == null || a.size() < 1 || b.size() < 1) { return Double.POSITIVE_INFINITY; } this.P = new ArrayList<AggConnection>(a); this.Q = new ArrayList<GPSEdge>(b); resizeCells(); double result = computeEpsilon(); this.P = null; this.Q = null; return result; } // public boolean isInDistance(List<GPSEdge> a, List<GPSEdge> b, double epsilon) { if (a == null || b == null || a.size() < 1 || b.size() < 1) { return false; } this.P = new ArrayList<AggConnection>(); for (GPSEdge agg : a) { P.add(new AggConnection(agg.getFrom(), agg.getTo(), aggContainer)); this.Q = new ArrayList<GPSEdge>(b); } resizeCells(); setEpsilon(epsilon); boolean result = isInDistance(); this.P = null; this.Q = null; return result; } }