/******************************************************************************* * Gaggle is Copyright 2010 by Geeksville Industries LLC, a California limited liability corporation. * * Gaggle is distributed under a dual license. We've chosen this approach because within Gaggle we've used a number * of components that Geeksville Industries LLC might reuse for commercial products. Gaggle can be distributed under * either of the two licenses listed below. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Commercial Distribution License * If you would like to distribute Gaggle (or portions thereof) under a license other than * the "GNU General Public License, version 2", contact Geeksville Industries. Geeksville Industries reserves * the right to release Gaggle source code under a commercial license of its choice. * * GNU Public License, version 2 * All other distribution of Gaggle must conform to the terms of the GNU Public License, version 2. The full * text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt. ******************************************************************************/ package com.geeksville.location; import android.database.Cursor; import android.location.Location; import android.util.FloatMath; public final class LocationUtils { /** * Format as degrees, minutes, secs * * @param degIn * @param isLatitude * @return a string like 120deg */ public static String[] degreesToDMS(double degIn, boolean isLatitude) { boolean isPos = degIn >= 0; char dirLetter = isLatitude ? (isPos ? 'N' : 'S') : (isPos ? 'E' : 'W'); degIn = Math.abs(degIn); int degOut = (int) degIn; double minutes = 60 * (degIn - degOut); int minwhole = (int) minutes; double seconds = ((minutes - minwhole) * 60); return new String[] { Integer.toString(degOut), Integer.toString(minwhole), Double.toString(seconds), Character.toString(dirLetter) }; } public static String[] degreesToDM(double degIn, boolean isLatitude) { boolean isPos = degIn >= 0; char dirLetter = isLatitude ? (isPos ? 'N' : 'S') : (isPos ? 'E' : 'W'); degIn = Math.abs(degIn); int degOut = (int) degIn; double minutes = 60 * (degIn - degOut); int seconds = 0; return new String[] { Integer.toString(degOut), Double.toString(minutes), Integer.toString(seconds), Character.toString(dirLetter) }; } public static String[] degreesToD(double degIn, boolean isLatitude) { boolean isPos = degIn >= 0; char dirLetter = isLatitude ? (isPos ? 'N' : 'S') : (isPos ? 'E' : 'W'); degIn = Math.abs(degIn); double degOut = degIn; int minutes = 0; int seconds = 0; return new String[] { Double.toString(degOut), Integer.toString(minutes), Integer.toString(seconds), Character.toString(dirLetter) }; } /** * A not super efficent mapping from a starting lat/long + a distance at a * certain direction * * @param lat * @param longitude * @param distMeters * @param theta * in radians, 0 == north * @return an array with lat and long */ public static double[] addDistance(double lat, double longitude, double distMeters, double theta) { double dx = distMeters * Math.sin(theta); // theta measured clockwise // from due north double dy = distMeters * Math.cos(theta); // dx, dy same units as R double dLong = dx / (111320 * Math.cos(lat)); // dx, dy in meters double dLat = dy / 110540; // result in degrees long/lat return new double[] { lat + dLat, longitude + dLong }; } public static double LatLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b) { float[] results = new float[1]; Location.distanceBetween(lat_a, lng_a, lat_b, lng_b, results); return results[0]; } /** * Convert degrees/mins/secs to a single double * * @param degrees * @param minutes * @param seconds * @param isPostive * @return */ public static double DMSToDegrees(int degrees, int minutes, float seconds, boolean isPostive) { double r = (isPostive ? 1 : -1) * (degrees + (minutes / 60.0) + (seconds / 3600.0)); return r; } public static double DMSToDegrees(double degrees, double minutes, double seconds, boolean isPostive) { double r = (isPostive ? 1 : -1) * (degrees + (minutes / 60.0) + (seconds / 3600.0)); return r; } /** * Utility glue for reading from a DB and writing to a position writer (used * for file export) * * @param db * @param dest * @param fltId * * I didn't want to infect the DB stuff with knowledge of * position writers, nor position writers with knowledge of DBs. * So here we are... */ public static void dbToWriter(LocationLogDbAdapter db, PositionWriter dest, long fltId) { // First get all the flight info Cursor fltinfo = db.fetchFlight(fltId); int nameCol = fltinfo.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_FLT_PILOTNAME); int notesCol = fltinfo.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_DESCRIPTION); // FIXME - use this info when writing String pilotName = fltinfo.getString(nameCol); String flightDesc = fltinfo.getString(notesCol); fltinfo.close(); Cursor pts = db.fetchLocations(fltId); int latCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LATITUDE); int longCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LONGITUDE); int altCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_ALTITUDE); int timeCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_TIME); int bearingCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_GNDTRACK); int speedCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_GNDSPEED); int accxCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_ACCX); int accyCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_ACCY); int acczCol = pts.getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_ACCZ); int vspdCol = pts .getColumnIndexOrThrow(LocationLogDbAdapter.KEY_LOC_VSPD); int numPts = pts.getCount(); dest.emitProlog(); float[] accelArray = new float[3]; for (int i = 0; i < numPts; i++) { double latitude = pts.getDouble(latCol); double longitude = pts.getDouble(longCol); int altitude = pts.getInt(altCol); long time = pts.getLong(timeCol); int bearing = pts.getInt(bearingCol); int groundSpeed = pts.getInt(speedCol); float[] accel = accelArray; if (pts.isNull(accxCol)) accel = null; // No accel data available for this point else { accel[0] = pts.getFloat(accxCol); accel[1] = pts.getFloat(accyCol); accel[2] = pts.getFloat(acczCol); } float vspd = pts.isNull(vspdCol) ? Float.NaN : pts .getFloat(vspdCol); dest.emitPosition(time, latitude, longitude, altitude, bearing, groundSpeed, accel, vspd); pts.moveToNext(); } pts.close(); dest.emitEpilog(); } /** * Computes the bearing in degrees between two points on Earth. * * @param lat1 * Latitude of the first point * @param lon1 * Longitude of the first point * @param lat2 * Latitude of the second point * @param lon2 * Longitude of the second point * @return Bearing between the two points in degrees. A value of 0 means due * north. */ public static double bearing(double lat1, double lon1, double lat2, double lon2) { double lat1Rad = Math.toRadians(lat1); double lat2Rad = Math.toRadians(lat2); double deltaLonRad = Math.toRadians(lon2 - lon1); double y = Math.sin(deltaLonRad) * Math.cos(lat2Rad); double x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(deltaLonRad); return radToBearing(Math.atan2(y, x)); } /** * Converts an angle in radians to degrees */ public static double radToBearing(double rad) { return (Math.toDegrees(rad) + 360) % 360; } }