Created
September 27, 2015 23:19
-
-
Save hailpam/68a6d59445d7a6854eec to your computer and use it in GitHub Desktop.
Filter out Customers based on their geo-location. The output is a collection of Customers ordered by their own Id - ascending order.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.net.URL; | |
import java.nio.charset.Charset; | |
import java.util.List; | |
import java.util.Objects; | |
import java.util.Set; | |
import java.util.TreeSet; | |
import org.json.simple.JSONObject; | |
import org.json.simple.parser.JSONParser; | |
import org.json.simple.parser.ParseException; | |
/** | |
* Filter out Customers by their own Geo-Location. | |
* | |
* @author Paolo Maresca {@email plo.maresca@gmail.com} | |
*/ | |
public class GeoLocatedFiltering | |
{ | |
private static final float HQ_LAT = 53.3381985f; // headquarter decimal latitude | |
private static final float HQ_LNG = -6.2592576f; // headquarted decimal longitude | |
private static final Integer EARTH_RADIUS = 6371; // expressed in Kms | |
private static final Integer RANGE_DISTANCE = 100; // expressed in Kms | |
private static final String DEFAULT_LOCATION = System.getProperty("user.dir") | |
+File.separator +"customers_list.txt"; | |
private static final String DEFAULT_URL = "https://gist.githubusercontent.com/brianw/" | |
+ "19896c50afa89ad4dec3/raw/6c11047887a03483c50017c1d451667fd62a53ca/gistfile1.txt"; | |
/** | |
* Load Customers from File. | |
* | |
* @param reader A Buffered Reader having the raw list of Customers | |
* @return A list of de-serialized Customers | |
*S | |
* @throws IOException | |
* In case of any blocking exception | |
* @throws ParseException | |
* In case of JSON Parsing Exception | |
*/ | |
public static List<Customer> load(BufferedReader reader) throws IOException, ParseException | |
{ | |
if(reader == null) | |
throw new IllegalArgumentException("Input Reader cannot be NULL"); | |
List<Customer> customers = new LinkedList<>(); | |
String line; | |
JSONParser parser = new JSONParser(); | |
while((line = reader.readLine()) != null) { | |
JSONObject jsonCustomer = (JSONObject) parser.parse(line); | |
Customer customer = new Customer(Float.parseFloat((String) jsonCustomer.get("latitude")), | |
Float.parseFloat((String) jsonCustomer.get("longitude")), | |
(String) jsonCustomer.get("name"), | |
(Long) jsonCustomer.get("user_id")); | |
customers.add(customer); | |
} | |
return customers; | |
} | |
/** | |
* Select the Customers loaded from File according to the Range-based criteria. | |
* | |
* E.g. Given the HQ at (53.3381985, -6.2592576f), select the geo-located Customer in a range of | |
* 100 Kms. | |
* | |
* @param customers A list of Customer from which to select | |
* @return An ordered set | |
*/ | |
public static Set<Customer> select(List<Customer> customers) | |
{ | |
if(customers == null) | |
throw new IllegalArgumentException("Customers cannot be NULL"); | |
Double distance = new Double(Integer.toString(RANGE_DISTANCE)); | |
Set<Customer> ordered = new TreeSet<>(); | |
for(Customer customer: customers) { | |
if(customer.howFarFrom(customer.getLatRadians(), customer.getLngRadians()) <= distance) | |
ordered.add(customer); | |
} | |
return ordered; | |
} | |
/** | |
* Main. | |
* | |
* @param args Command Line Arguments. | |
*/ | |
public static void main(String[] args) | |
{ | |
File jsonList = null; | |
BufferedReader reader = null; | |
if(args.length == 0) | |
jsonList = new File(DEFAULT_LOCATION); | |
else | |
jsonList = new File(args[0]); | |
List<Customer> customersList = null; | |
try { | |
if(!jsonList.exists()) { | |
URL jsonUrl = new URL(DEFAULT_URL); | |
reader = new BufferedReader(new InputStreamReader(jsonUrl.openStream(), | |
Charset.forName("UTF-8"))); | |
} else | |
reader = new BufferedReader(new FileReader(jsonList)); | |
customersList = load(reader); | |
} catch(IOException ioe) { | |
System.err.println("Error: Something wrong happened working with your file: [" +ioe.getMessage()+ "]"); | |
System.exit(1); | |
} catch(ParseException pe) { | |
System.err.println("Error: Something wrong happened parsing the JSON: [" +pe.getMessage()+ "]"); | |
System.exit(1); | |
} finally { | |
try { | |
reader.close(); | |
} catch(IOException ioe) { | |
System.err.println("Error: Buffered Reader cannot be closed properly"); | |
System.exit(1); | |
} | |
} | |
Set<Customer> filteredList = select(customersList); | |
for(Customer customer: filteredList) | |
System.out.println("Id: [" +customer.getId()+ "] - Name: [" +customer.getName()+ "]"); | |
} | |
/** | |
* Customer POJO with a helper method to get the distance from point of interest. | |
*/ | |
public static final class Customer implements Comparable<Customer> | |
{ | |
private final Float lat; | |
private final Float lng; | |
private final String name; | |
private final Long id; | |
public Customer(Float lat, Float lng, String name, Long id) | |
{ | |
this.lat = lat; | |
this.lng = lng; | |
this.name = name; | |
this.id = id; | |
} | |
@Override | |
public int compareTo(Customer t) | |
{ | |
if(id == t.getId()) | |
return 0; | |
else if(id > t.getId()) | |
return 1; | |
else | |
return -1; | |
} | |
public Double howFarFrom(Double lat, Double lng) | |
{ | |
Double latAbsDifference, lngAbsDifference, centralAngle; | |
latAbsDifference = Math.abs(Math.toRadians(HQ_LAT) - lat); | |
lngAbsDifference = Math.abs(Math.toRadians(HQ_LNG) - lng); | |
centralAngle = Math.acos((Math.sin(Math.toRadians(HQ_LAT)) * Math.sin(lat)) + | |
(Math.cos(Math.toRadians(HQ_LAT)) * Math.cos(lat) * | |
Math.cos(lngAbsDifference))); | |
return EARTH_RADIUS * centralAngle; // arc length, sperical distance | |
} | |
public Float getLat() { return lat; } | |
public Float getLng() { return lng; } | |
public Double getLatRadians() { return Math.toRadians(lat); } | |
public Double getLngRadians() { return Math.toRadians(lng); } | |
public String getName() { return name; } | |
public Long getId() { return id; } | |
@Override | |
public int hashCode() | |
{ | |
int hash = 7; | |
hash = 53 * hash + Float.floatToIntBits(this.lat); | |
hash = 53 * hash + Float.floatToIntBits(this.lng); | |
hash = 53 * hash + Objects.hashCode(this.name); | |
hash = 53 * hash + Objects.hashCode(this.id); | |
return hash; | |
} | |
@Override | |
public boolean equals(Object obj) | |
{ | |
if (obj == null) { | |
return false; | |
} | |
if (getClass() != obj.getClass()) { | |
return false; | |
} | |
final Customer other = (Customer) obj; | |
if (Float.floatToIntBits(this.lat) != Float.floatToIntBits(other.lat)) { | |
return false; | |
} | |
if (Float.floatToIntBits(this.lng) != Float.floatToIntBits(other.lng)) { | |
return false; | |
} | |
if (!Objects.equals(this.name, other.name)) { | |
return false; | |
} | |
if (!Objects.equals(this.id, other.id)) { | |
return false; | |
} | |
return true; | |
} | |
@Override | |
public String toString() | |
{ | |
return "Customer { " + "lat=" + lat + ", lng=" + lng + ", name=" + name + ", id=" + id + " }"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment