package br.com.citframework.util.geo; import br.com.citframework.util.Assert; /** * Classe que representa um ponto na superf�cie de uma esfera * * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/10/2014 * */ public class GeoLocation { private static final String INVALID_LATITUDE = "Latitude is not a valid value: '%s'"; private static final String INVALID_LONGITUDE = "Longitude is not a valid value: '%s'"; private static final String LOCATION_MUST_NOT_BE_NULL = "Location must not be null"; private GeoLocation() {} private double radiansLatitude; private double radiansLongitude; private double degreesLatitude; private double degreesLongitude; public double getLatitudeInDegrees() { return degreesLatitude; } public double getLongitudeInDegrees() { return degreesLongitude; } public double getLatitudeInRadians() { return radiansLatitude; } public double getLongitudeInRadians() { return radiansLongitude; } /** * Calcula um c�rculo de dist�ncia entre o ponto geogr�fico informado como argumento e o ponto geogr�fico atual * * @param radius * o raio na esfera, para calculo da dist�ncia. Como exemplo, o raio aproximado da terra � 6371.01 quil�metros * @return dist�ncia, na mesma unidade de medida do raio informado como argumento * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/10/2014 */ public double distanceTo(final GeoLocation location, final double radius) { Assert.notNull(location, LOCATION_MUST_NOT_BE_NULL); return Math.acos(Math.sin(radiansLatitude) * Math.sin(location.getLatitudeInRadians()) + Math.cos(this.getLatitudeInRadians()) * Math.cos(location.getLatitudeInRadians()) * Math.cos(this.getLongitudeInRadians() - location.getLongitudeInRadians())) * radius; } /** * Calcula dist�ncia em quil�metros entre dois pontos presentes na terra * * @param pointOne * primeiro ponto para c�lculo da dist�ncia * @param pointTwo * segundo ponto para c�lculo da dist�ncia * @return dist�ncia, em quil�metros, entre os pontos * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/10/2014 */ public static double distanceToOnEarth(final GeoLocation pointOne, final GeoLocation pointTwo) { Assert.notNull(pointOne, LOCATION_MUST_NOT_BE_NULL); return pointOne.distanceTo(pointTwo, GeoUtils.EARTH_RADIUS); } /** * <p> * Calcula as coordenadas limite de todos os pontos da superf�cie de uma esfera, tendo como refer�ncia {@link GeoLocation} e que a dist�ncia � menor ou igual � dist�ncia * {@code distance} * </p> * * @param distance * a dist�ncia do ponto representado pela inst�ncia. Ser� medido na mesma unidade do argumento {@code radius} * @param radius * raio da esfera onde ser� calculado a dist�ncia * @return um array contendo dois objetos {@link GeoLocation}, sendo o primeiro contendo as menores coordenadas e o segundo contendo as maiores coordenadas */ public GeoLocation[] boundingCoordinates(final double distance, final double radius) { if (radius < 0d || distance < 0d) { throw new IllegalArgumentException(); } final double radDist = distance / radius; double minLat = radiansLatitude - radDist; double maxLat = radiansLatitude + radDist; double minLon, maxLon; if (minLat > GeoUtils.MIN_LAT && maxLat < GeoUtils.MAX_LAT) { final double deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radiansLatitude)); minLon = radiansLongitude - deltaLon; if (minLon < GeoUtils.MIN_LON) { minLon += 2d * Math.PI; } maxLon = radiansLongitude + deltaLon; if (maxLon > GeoUtils.MAX_LON) { maxLon -= 2d * Math.PI; } } else { minLat = Math.max(minLat, GeoUtils.MIN_LAT); maxLat = Math.min(maxLat, GeoUtils.MAX_LAT); minLon = GeoUtils.MIN_LON; maxLon = GeoUtils.MAX_LON; } return new GeoLocation[] {fromRadians(minLat, minLon), fromRadians(maxLat, maxLon)}; } /** * Gera um objeto {@link GeoLocation} a partir de ponto geogr�fico representado em graus * * @param latitude * latitude em graus * @param longitude * longitude em graus * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/10/2014 */ public static GeoLocation fromDegrees(final double latitude, final double longitude) { final GeoLocation result = new GeoLocation(); result.radiansLatitude = Math.toRadians(latitude); result.radiansLongitude = Math.toRadians(longitude); result.degreesLatitude = latitude; result.degreesLongitude = longitude; result.checkBounds(); return result; } /** * Gera um objeto {@link GeoLocation} a partir de ponto geogr�fico representado em radianos * * @param latitude * latitude em radianos * @param longitude * longitude em radianos * @author bruno.ribeiro - <a href="mailto:bruno.ribeiro@centrait.com.br">bruno.ribeiro@centrait.com.br</a> * @since 23/10/2014 */ public static GeoLocation fromRadians(final double latitude, final double longitude) { final GeoLocation result = new GeoLocation(); result.radiansLatitude = latitude; result.radiansLongitude = longitude; result.degreesLatitude = Math.toDegrees(latitude); result.degreesLongitude = Math.toDegrees(longitude); result.checkBounds(); return result; } private void checkBounds() { Assert.isTrue(radiansLatitude >= GeoUtils.MIN_LAT, String.format(INVALID_LATITUDE, radiansLatitude)); Assert.isTrue(radiansLatitude <= GeoUtils.MAX_LAT, String.format(INVALID_LATITUDE, radiansLatitude)); Assert.isTrue(radiansLongitude >= GeoUtils.MIN_LON, String.format(INVALID_LONGITUDE, radiansLongitude)); Assert.isTrue(radiansLongitude <= GeoUtils.MAX_LON, String.format(INVALID_LONGITUDE, radiansLongitude)); } }