/*******************************************************************************
* Copyright (c) 2015 Voyager Search and MITRE
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0 which
* accompanies this distribution and is available at
* http://www.apache.org/licenses/LICENSE-2.0.txt
******************************************************************************/
package org.locationtech.spatial4j.distance;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
/**
* Calculates based on Euclidean / Cartesian 2d plane.
*/
public class CartesianDistCalc extends AbstractDistanceCalculator {
public static final CartesianDistCalc INSTANCE = new CartesianDistCalc();
public static final CartesianDistCalc INSTANCE_SQUARED = new CartesianDistCalc(true);
private final boolean squared;
public CartesianDistCalc() {
this.squared = false;
}
/**
* @param squared Set to true to have {@link #distance(org.locationtech.spatial4j.shape.Point, org.locationtech.spatial4j.shape.Point)}
* return the square of the correct answer. This is a
* performance optimization used when sorting in which the
* actual distance doesn't matter so long as the sort order is
* consistent.
*/
public CartesianDistCalc(boolean squared) {
this.squared = squared;
}
@Override
public double distance(Point from, double toX, double toY) {
double xSquaredPlusYSquared = distanceSquared(from.getX(), from.getY(), toX, toY);
if (squared)
return xSquaredPlusYSquared;
return Math.sqrt(xSquaredPlusYSquared);
}
private static double distanceSquared(double fromX, double fromY, double toX, double toY) {
double deltaX = fromX - toX;
double deltaY = fromY - toY;
return deltaX*deltaX + deltaY*deltaY;
}
/**
* Distance from point to a line segment formed between points 'v' and 'w'.
* It respects the "squared" option.
*/
// TODO add to generic DistanceCalculator and develop geo versions.
public double distanceToLineSegment(Point point, double vX, double vY, double wX, double wY) {
// Translated from: http://bl.ocks.org/mbostock/4218871
double d = distanceSquared(vX, vY, wX, wY);
double toX;
double toY;
if (d <= 0) {
toX = vX;
toY = vY;
} else {
// t = ((point[0] - v[0]) * (w[0] - v[0]) + (point[1] - v[1]) * (w[1] - v[1])) / d
double t = ((point.getX() - vX) * (wX - vX) + (point.getY() - vY) * (wY - vY)) / d;
if (t < 0) {
toX = vX;
toY = vY;
} else if (t > 1) {
toX = wX;
toY = wY;
} else {
toX = vX + t * (wX - vX);
toY = vY + t * (wY - vY);
}
}
return distance(point, toX, toY);
}
@Override
public boolean within(Point from, double toX, double toY, double distance) {
double deltaX = from.getX() - toX;
double deltaY = from.getY() - toY;
return deltaX*deltaX + deltaY*deltaY <= distance*distance;
}
@Override
public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) {
if (distDEG == 0) {
if (reuse == null)
return from;
reuse.reset(from.getX(), from.getY());
return reuse;
}
double bearingRAD = DistanceUtils.toRadians(bearingDEG);
double x = from.getX() + Math.sin(bearingRAD) * distDEG;
double y = from.getY() + Math.cos(bearingRAD) * distDEG;
if (reuse == null) {
return ctx.makePoint(x, y);
} else {
reuse.reset(x, y);
return reuse;
}
}
@Override
public Rectangle calcBoxByDistFromPt(Point from, double distDEG, SpatialContext ctx, Rectangle reuse) {
double minX = from.getX() - distDEG;
double maxX = from.getX() + distDEG;
double minY = from.getY() - distDEG;
double maxY = from.getY() + distDEG;
if (reuse == null) {
return ctx.makeRectangle(minX, maxX, minY, maxY);
} else {
reuse.reset(minX, maxX, minY, maxY);
return reuse;
}
}
@Override
public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx) {
return from.getY();
}
@Override
public double area(Rectangle rect) {
return rect.getArea(null);
}
@Override
public double area(Circle circle) {
return circle.getArea(null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartesianDistCalc that = (CartesianDistCalc) o;
if (squared != that.squared) return false;
return true;
}
@Override
public int hashCode() {
return (squared ? 1 : 0);
}
}