I have two set of locations say A(20K locations) and B(2k locations). I want to have a map for all locations in set A to their closest location in set B based on their latitude and longitude.
Is there any solution in java or R. Java solution preferred.
As from the answer from @rosscova
Take the points, but as a matrix
pointsA <- as.matrix(data.frame( lat = c( 10, 12, 20 ), lon = c( 12, 17, 10 ) ))
pointsB <- as.matrix(data.frame( lat = c( 11, 15 ), lon = c( 15, 15 ) ))
Then, when dealing with coordinates, you may prefer to use the Great Circle (WGS84 ellipsoid) distance instead of the euclidean. I usually use the spDists function from the sp package
library( sp )
out_Dists <- spDists(x = pointsA, y = pointsB, longlat = TRUE, segments = FALSE, diagonal = FALSE)
and at the end using the apply function with the which.min over the rows to get the nearest pointB to pointA
pointsA[ apply(out_Dists, 1, which.min), ]
I agree with @STaefi in the comments, it's a good idea to show at least a little bit of work on your part before asking others to write code for you. Having said that, your problem has piqued my interest enough to grab a few minutes of my time, so here's a method in R
. Note though that this is only considering distance as a sqrt(a^2+b^2)
function of the coordinates, which won't be accurate anywhere except at the equator. You'd need to modify this to suit if you need better accuracy there.
Take a small hypothetical dataset:
pointsA <- data.frame( lat = c( 10, 12, 20 ), lon = c( 12, 17, 10 ) )
pointsB <- data.frame( lat = c( 11, 15 ), lon = c( 15, 15 ) )
Write a function for computing the closest point:
closest_point <- function( lat1, lon1, lat2, lon2 ) {
x_dist <- abs( lon1 - lon2 )
y_dist <- abs( lat1 - lat2 )
dist <- sqrt( x_dist ^ 2 + y_dist ^ 2 )
closest <- data.frame( lat = lat2[ which.min( dist ) ],
lon = lon2[ which.min( dist ) ] )
return( closest )
}
We can execute that function on a single point from pointsA
, returning the closest point from pointsB
:
closest_point( pointsA[1,"lat"], pointsA[1,"lon"], pointsB[,"lat"], pointsB[,"lon"] )
# lat lon
# 1 11 15
Or, we can apply it to ALL points from pointsA
, returning the closest point from pointsB
for each of the points in pointsA
closest.points <- lapply( seq_len( nrow( pointsA ) ),
function(x) {
closest_point( lat1 = pointsA[x,"lat"],
lon1 = pointsA[x,"lon"],
lat2 = pointsB[,"lat"],
lon2 = pointsB[,"lon"] )
} )
closest.points <- do.call( rbind, closest.points )
closest.points
# lat lon
# 1 11 15
# 2 11 15
# 3 15 15
Here's one in Java. The math is simplified by converting the latitude and longitude to a normalised vector representation, and noting that the closer the two vectors are the larger their dot product will be (approaching one when they are equal).
This assumes that earth is spherical. If you want "perfect" results you'll need to convert your coordinates using a closer approximation (WGS84, for example).
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.Math.*;
public class LatLong {
public static void main(String[] args) throws Exception {
// A random lat/long
double[] target = randLatLong();
// Transform to ECEF vector
double[] targetv = toEcef(target);
// 2000 random candidates
List<double[]> b = Stream.generate(LatLong::randLatLong).limit(2000).collect(Collectors.toList());
// Transform candidates to ECEF representation
List<double[]> bv = b.stream().map(LatLong::toEcef).collect(Collectors.toList());
// Find the closest candidate to the target
int i = closest(targetv, bv);
System.out.println("Closest point to " + target[0] + ", " + target[1] + " is " + b.get(i)[0] + ", " + b.get(i)[1]);
}
// index of closest vector to target from list of candidates
public static int closest(double[] target, List<double[]> candidates) {
double p = Double.MIN_VALUE;
int closest = -1;
for (int i = 0; i < candidates.size(); i++) {
double next = dotProduct(target, candidates.get(i));
if (next > p) {
p = next;
closest = i;
}
}
return closest;
}
// dot product of two 3vectors
public static double dotProduct(double[] v1, double[] v2) {
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
// lat/long in degrees to normalised ECEF vector
public static double[] toEcef(double[] latLong) {
return toEcef(toRadians(latLong[0]), toRadians(latLong[1]));
}
// lat/long in radians to normalised ECEF vector
public static double[] toEcef(double φ, double λ) {
return new double[] {cos(φ) * cos(λ), cos(φ) * sin(λ), sin(φ)};
}
// A random lat/long
public static double[] randLatLong() {
return new double[] {Math.random() * 180 - 90, Math.random() * 360 - 180};
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.