diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java new file mode 100644 index 0000000000000000000000000000000000000000..c0354a6ebd59fa1a3bda6bcb55d05e2daf94983f --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java @@ -0,0 +1,87 @@ +package tdt4140.gr1800.app.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Optional; +import java.util.stream.Collectors; + +public class GeoLocations implements Iterable<LatLong> { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private Collection<LatLong> locations = new ArrayList<LatLong>(); + private boolean path = false; + + public GeoLocations(LatLong...latLongs) { + for (int i = 0; i < latLongs.length; i++) { + addLocation(latLongs[i]); + } + } + + public GeoLocations(String name, LatLong...latLongs) { + this(latLongs); + setName(name); + } + + public boolean isPath() { + return path; + } + + public void setPath(boolean path) { + this.path = path; + } + + public Comparator<? super LatLong> latLongComparator(LatLong latLong) { + return (latLong1, latLong2) -> (int) Math.signum(latLong.distance(latLong1) - latLong.distance(latLong2)); + } + + public Collection<LatLong> findLocationsNearby(LatLong latLong, double distance) { + return locations.stream() + .filter(latLong2 -> latLong.distance(latLong2) <= distance) + .sorted(latLongComparator(latLong)) + .collect(Collectors.toList()); + } + + public LatLong findNearestLocation(LatLong latLong) { + Optional<LatLong> min = locations.stream() + .min(latLongComparator(latLong)); + return min.isPresent() ? min.get() : null; + } + + // + + public void addLocation(LatLong latLong) { + if (! locations.contains(latLong)) { + locations.add(latLong); + } + } + + public void removeLocations(LatLong latLong, double distance) { + Iterator<LatLong> it = locations.iterator(); + while (it.hasNext()) { + LatLong latLong2 = it.next(); + if (latLong.distance(latLong2) <= distance) { + it.remove(); + } + } + } + + public int size() { + return locations.size(); + } + + @Override + public Iterator<LatLong> iterator() { + return locations.iterator(); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/LatLong.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/LatLong.java new file mode 100644 index 0000000000000000000000000000000000000000..7b99cdfc4e11edbc670219190dc3df65a591d8c3 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/LatLong.java @@ -0,0 +1,115 @@ +package tdt4140.gr1800.app.core; + +public class LatLong { + + public final double latitude, longitude; + + public LatLong(double latitude, double longitude) { + super(); + this.latitude = latitude; + this.longitude = longitude; + } + + public double distance(LatLong loc) { + return distance(this.latitude, this.longitude, loc.latitude, loc.longitude); + } + + public final static String SEPARATOR = ","; + + @Override + public String toString() { + return latitude + SEPARATOR + longitude; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + LatLong other = (LatLong) obj; + return (Double.doubleToLongBits(latitude) == Double.doubleToLongBits(other.latitude) && + Double.doubleToLongBits(longitude) == Double.doubleToLongBits(other.longitude)); + } + + public static LatLong valueOf(String s) { + return valueOf(s, SEPARATOR); + } + + public static LatLong valueOf(String s, String sep) { + int pos = s.indexOf(sep); + if (pos < 0) { + throw new IllegalArgumentException("No '" + sep + "' in " + s); + } + double lat = Double.valueOf(s.substring(0, pos).trim()); + double lon = Double.valueOf(s.substring(pos + sep.length()).trim()); + + return new LatLong(lat, lon); + } + + /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + /*:: :*/ + /*:: This routine calculates the distance between two points (given the :*/ + /*:: latitude/longitude of those points). It is being used to calculate :*/ + /*:: the distance between two locations using GeoDataSource (TM) products :*/ + /*:: :*/ + /*:: Definitions: :*/ + /*:: South latitudes are negative, east longitudes are positive :*/ + /*:: :*/ + /*:: Passed to function: :*/ + /*:: lat1, lon1 = Latitude and Longitude of point 1 (in decimal degrees) :*/ + /*:: lat2, lon2 = Latitude and Longitude of point 2 (in decimal degrees) :*/ + /*:: Worldwide cities and other features databases with latitude longitude :*/ + /*:: are available at http://www.geodatasource.com :*/ + /*:: :*/ + /*:: For enquiries, please contact sales@geodatasource.com :*/ + /*:: :*/ + /*:: Official Web site: http://www.geodatasource.com :*/ + /*:: :*/ + /*:: GeoDataSource.com (C) All Rights Reserved 2015 :*/ + /*:: :*/ + /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + public static double distance(double lat1, double lon1, double lat2, double lon2) { + double theta = lon1 - lon2; + double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)); + dist = Math.acos(dist); + // convert to degrees + dist = rad2deg(dist); + dist = dist * 60 * 1.1515; + // convert to meters + dist = dist * 1609.344; + return dist; + } + + public static double distance(LatLong latLong1, LatLong latLong2) { + return distance(latLong1.latitude, latLong1.longitude, latLong2.latitude, latLong2.longitude); + } + + /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + /*:: This function converts decimal degrees to radians :*/ + /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + private static double deg2rad(double deg) { + return (deg * Math.PI / 180.0); + } + + /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + /*:: This function converts radians to decimal degrees :*/ + /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ + private static double rad2deg(double rad) { + return (rad * 180 / Math.PI); + } +} diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..aab486beb437ef7bcafc4a4ea80e43e232f84c43 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java @@ -0,0 +1,74 @@ +package tdt4140.gr1800.app.core; + +import java.util.Collection; +import java.util.Iterator; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class GeoLocationsTest { + + private GeoLocations geoLocations; + + @Before + public void setUp() { + geoLocations = new GeoLocations(); + } + + @Test + public void testGeoLocations() { + Assert.assertEquals(0, geoLocations.size()); + Assert.assertEquals(1, new GeoLocations(new LatLong(0, 0)).size()); + Assert.assertEquals(1, new GeoLocations(new LatLong(0, 0), new LatLong(0, 0)).size()); + Assert.assertEquals(2, new GeoLocations(new LatLong(0, 0), new LatLong(1, 1)).size()); + } + + private void assertGeoLocations(LatLong...latLongs) { + assertGeoLocations(geoLocations, latLongs); + } + + public static void assertGeoLocations(GeoLocations geoLocations, LatLong...latLongs) { + Iterator<LatLong> it = geoLocations.iterator(); + Assert.assertEquals(latLongs.length, geoLocations.size()); + int pos = 0; + while (it.hasNext()) { + Assert.assertEquals(latLongs[pos], it.next()); + pos++; + } + } + + @Test + public void testAddLocation() { + Assert.assertEquals(0, geoLocations.size()); + LatLong latLong1 = new LatLong(0, 0); + geoLocations.addLocation(latLong1); + assertGeoLocations(latLong1); + + geoLocations.addLocation(new LatLong(0, 0)); + assertGeoLocations(latLong1); + + LatLong latLong2 = new LatLong(1, 1); + geoLocations.addLocation(latLong2); + assertGeoLocations(latLong1, latLong2); + } + + @Test + public void testFindLocationsNearby() { + LatLong latLong = new LatLong(0, 0); + Assert.assertTrue(geoLocations.findLocationsNearby(latLong, 0).isEmpty()); + geoLocations.addLocation(latLong); + Collection<LatLong> locationsNearby = geoLocations.findLocationsNearby(latLong, 0); + Assert.assertEquals(1, locationsNearby.size()); + Assert.assertEquals(latLong, geoLocations.iterator().next()); + } + + @Test + public void testFindNearestLocation() { + LatLong latLong = new LatLong(0, 0); + Assert.assertNull(geoLocations.findNearestLocation(latLong)); + geoLocations.addLocation(latLong); + LatLong nearestlocations = geoLocations.findNearestLocation(latLong); + Assert.assertEquals(latLong, nearestlocations); + } +}