package com.livingsocial.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.io.Text;
//import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.hive.serde2.io.DoubleWritable;
// import org.apache.log4j.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Description(
name = "gpsDistanceFrom",
value = "_FUNC_(double,double,double,double, text {optional} ) - Returns distance as a double in miles (by default). Accepts 'km' as an optional parameter",
extended = "Example:\n" +
" > SELECT gpsDistanceFrom(a.latitude,a.longitude,b.latitude,b.longitude,'km') FROM visits a join users u on a.user_id=u.id;\n" +
" 1.215615431564 "
)
/*
calculation of distance between two points using the haversine formula
may look into overloading evaluate to accept 'km' as a 5th arg, returning distance in km. For now, returns distance in miles.
** 2012-12-18 - updated to return null if any of the inputs return null; changed return type to DoubleWritable.
*/
public class gpsDistanceFrom extends UDF {
private DoubleWritable result = new DoubleWritable();
static final Log LOG = LogFactory.getLog(gpsDistanceFrom.class.getName());
public DoubleWritable evaluate(DoubleWritable lat1, DoubleWritable lng1, DoubleWritable lat2, DoubleWritable lng2, Text options) {
//set up a few variables
double earthRadius = 3958.75;
double kmConversion = 1.609344;
if(lat1==null || lng1 == null || lat2 == null || lng2 == null)
return null;
double dLat=Math.toRadians(lat2.get()-lat1.get());
double dLng=Math.toRadians(lng2.get()-lng1.get());
double p1 = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1.get())) * Math.cos(Math.toRadians(lat2.get())) *
Math.sin(dLng/2) * Math.sin(dLng/2);
double p2 = 2 * Math.atan2(Math.sqrt(p1), Math.sqrt(1-p1));
double dist=earthRadius * p2;
// add option to return kilometers
if (options != null && options.toString().toLowerCase().equals("km")) {
result.set(dist * kmConversion);
}
else {
result.set(dist);
}
return result;
}
public DoubleWritable evaluate(DoubleWritable lat1, DoubleWritable lng1, DoubleWritable lat2, DoubleWritable lng2) {
return evaluate(lat1, lng1, lat2, lng2, null);
}
}