diff --git a/app/src/main/java/com/geeksville/mesh/ui/LocationUtils.kt b/app/src/main/java/com/geeksville/mesh/ui/LocationUtils.kt new file mode 100644 index 00000000..b674ccc7 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/LocationUtils.kt @@ -0,0 +1,188 @@ +package com.geeksville.mesh.ui + +import kotlin.math.cos +import kotlin.math.sin + +/******************************************************************************* + * Revive some of my old Gaggle source code... + * + * 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. + ******************************************************************************/ + + +object LocationUtils { + /** + * Format as degrees, minutes, secs + * + * @param degIn + * @param isLatitude + * @return a string like 120deg + */ + fun degreesToDMS( + _degIn: Double, + isLatitude: Boolean + ): Array { + var degIn = _degIn + val isPos = degIn >= 0 + val dirLetter = + if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W' + degIn = Math.abs(degIn) + val degOut = degIn.toInt() + val minutes = 60 * (degIn - degOut) + val minwhole = minutes.toInt() + val seconds = (minutes - minwhole) * 60 + return arrayOf( + Integer.toString(degOut), Integer.toString(minwhole), + java.lang.Double.toString(seconds), + Character.toString(dirLetter) + ) + } + + fun degreesToDM(_degIn: Double, isLatitude: Boolean): Array { + var degIn = _degIn + val isPos = degIn >= 0 + val dirLetter = + if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W' + degIn = Math.abs(degIn) + val degOut = degIn.toInt() + val minutes = 60 * (degIn - degOut) + val seconds = 0 + return arrayOf( + Integer.toString(degOut), java.lang.Double.toString(minutes), + Integer.toString(seconds), + Character.toString(dirLetter) + ) + } + + fun degreesToD(_degIn: Double, isLatitude: Boolean): Array { + var degIn = _degIn + val isPos = degIn >= 0 + val dirLetter = + if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W' + degIn = Math.abs(degIn) + val degOut = degIn + val minutes = 0 + val seconds = 0 + return arrayOf( + java.lang.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 + */ + fun addDistance( + lat: Double, + longitude: Double, + distMeters: Double, + theta: Double + ): DoubleArray { + val dx = distMeters * Math.sin(theta) // theta measured clockwise + // from due north + val dy = distMeters * Math.cos(theta) // dx, dy same units as R + val dLong = dx / (111320 * Math.cos(lat)) // dx, dy in meters + val dLat = dy / 110540 // result in degrees long/lat + return doubleArrayOf(lat + dLat, longitude + dLong) + } + + fun LatLongToMeter( + lat_a: Double, + lng_a: Double, + lat_b: Double, + lng_b: Double + ): Double { + val pk = (180 / 3.14169) + val a1 = lat_a / pk + val a2 = lng_a / pk + val b1 = lat_b / pk + val b2 = lng_b / pk + val t1 = + Math.cos(a1) * Math.cos(a2) * Math.cos(b1) * Math.cos( + b2 + ) + val t2 = + Math.cos(a1) * Math.sin(a2) * Math.cos(b1) * Math.sin( + b2 + ) + val t3 = Math.sin(a1) * Math.sin(b1) + var tt = Math.acos(t1 + t2 + t3) + if (java.lang.Double.isNaN(tt)) tt = 0.0 // Must have been the same point? + return 6366000 * tt + } + + /** + * Convert degrees/mins/secs to a single double + * + * @param degrees + * @param minutes + * @param seconds + * @param isPostive + * @return + */ + fun DMSToDegrees( + degrees: Int, + minutes: Int, + seconds: Float, + isPostive: Boolean + ): Double { + return (if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0) + } + + fun DMSToDegrees( + degrees: Double, + minutes: Double, + seconds: Double, + isPostive: Boolean + ): Double { + return (if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0) + } + + /** + * 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. + */ + fun bearing( + lat1: Double, + lon1: Double, + lat2: Double, + lon2: Double + ): Double { + val lat1Rad = Math.toRadians(lat1) + val lat2Rad = Math.toRadians(lat2) + val deltaLonRad = Math.toRadians(lon2 - lon1) + val y = sin(deltaLonRad) * cos(lat2Rad) + val x = + cos(lat1Rad) * sin(lat2Rad) - (sin(lat1Rad) * cos(lat2Rad) + * Math.cos(deltaLonRad)) + return radToBearing(Math.atan2(y, x)) + } + + /** + * Converts an angle in radians to degrees + */ + fun radToBearing(rad: Double): Double { + return (Math.toDegrees(rad) + 360) % 360 + } +} \ No newline at end of file