diff --git a/Ejectable/App.tsx b/Ejectable/App.tsx index da127ffe..24e9eb29 100644 --- a/Ejectable/App.tsx +++ b/Ejectable/App.tsx @@ -1,7 +1,7 @@ import 'react-native-gesture-handler'; import { StatusBar } from 'expo-status-bar'; import React, { useState, useEffect } from 'react'; -import { Alert,Text } from 'react-native'; +import { Text } from 'react-native'; import * as Location from 'expo-location'; import { SafeAreaProvider } from 'react-native-safe-area-context'; @@ -42,6 +42,7 @@ export default function App() { expoGeoState = JSON.stringify(location); } + if (!isLoadingComplete) { return null; } else { @@ -61,4 +62,5 @@ export default function App() { ); } + } diff --git a/Ejectable/Coordinate.tsx b/Ejectable/Coordinate.tsx deleted file mode 100644 index 563be6a7..00000000 --- a/Ejectable/Coordinate.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/** Class for handling coordinates original by Linus Helgesson */ - export class Coordinate { - - public mLatitude:number = 0.0; - public mLongitude:number = 0.0; - mResults:any = [0, 0]; - PI_OVER_180:number = 0.017453292519943295769236907684886; - EARTH_RADIUS:number = 6371009; - - constructor (longitude:number, latitude:number) { - this.mLongitude = longitude; - this.mLatitude = latitude; - } - getLongitude() { return this.mLongitude; } - setLongitude(longitude:number) { this.mLongitude = longitude; } - getLatitude() { return this.mLatitude; } - setLatitude(latitude:number) { this.mLatitude = latitude; } - /** - * Calculates a bounding box of a certain size arund a coordinate.This function takes a size in meters as - * a parameter and returns an array of two Coordinate objects. The first Coordinate is the upper left corner - * while the last coordinate is the bottom right corner.er than the second. - */ - getBoundingBox(side: number) { - var ret:any = [Coordinate, Coordinate]; - - var degLatM:number , degLatM:number, degLongM:number, deltaLat:number, deltaLong:number; - - degLatM = 110574.235; - degLongM = 110572.833 * Math.cos(this.mLatitude * this.PI_OVER_180); - deltaLat = side / degLatM; - deltaLong = side / degLongM; - - ret[0] = new Coordinate(this.getLongitude() - deltaLong, this.getLatitude() - deltaLat); - ret[1] = new Coordinate(this.getLongitude() + deltaLong, this.getLatitude() + deltaLat); - - return ret; - }; - /** - * Calculates the distance between two Coordinate objects using the Spherical law of cosines found at: - */ - distanceTo(dest:Coordinate) { - this.computeDistanceAndBearing(this.mLatitude, this.mLongitude, dest.getLatitude(), dest.getLongitude(), this.mResults); - return this.mResults[0]; - }; - computeDistanceAndBearing(lat1:number, lon1:number, lat2:number, lon2:number, results:any) { - var MAXITERS = 20; - lat1 *= Math.PI / 180.0; - lat2 *= Math.PI / 180.0; - lon1 *= Math.PI / 180.0; - lon2 *= Math.PI / 180.0; - var a = 6378137.0; - var b = 6356752.3142; - var f = (a - b) / a; - var aSqMinusBSqOverBSq = (a * a - b * b) / (b * b); - var L = lon2 - lon1; - var A = 0.0; - var U1 = Math.atan((1.0 - f) * Math.tan(lat1)); - var U2 = Math.atan((1.0 - f) * Math.tan(lat2)); - var cosU1 = Math.cos(U1); - var cosU2 = Math.cos(U2); - var sinU1 = Math.sin(U1); - var sinU2 = Math.sin(U2); - var cosU1cosU2 = cosU1 * cosU2; - var sinU1sinU2 = sinU1 * sinU2; - var sigma = 0.0; - var deltaSigma = 0.0; - var cosSqAlpha = 0.0; - var cos2SM = 0.0; - var cosSigma = 0.0; - var sinSigma = 0.0; - var cosLambda = 0.0; - var sinLambda = 0.0; - var lambda = L; - for (var iter = 0; iter < MAXITERS; iter++) { - { - var lambdaOrig = lambda; - cosLambda = Math.cos(lambda); - sinLambda = Math.sin(lambda); - var t1 = cosU2 * sinLambda; - var t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda; - var sinSqSigma = t1 * t1 + t2 * t2; - sinSigma = Math.sqrt(sinSqSigma); - cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; - sigma = Math.atan2(sinSigma, cosSigma); - var sinAlpha = (sinSigma === 0) ? 0.0 : cosU1cosU2 * sinLambda / sinSigma; - cosSqAlpha = 1.0 - sinAlpha * sinAlpha; - cos2SM = (cosSqAlpha === 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; - var uSquared = cosSqAlpha * aSqMinusBSqOverBSq; - A = 1 + (uSquared / 16384.0) * (4096.0 + uSquared * (-768 + uSquared * (320.0 - 175.0 * uSquared))); - var B = (uSquared / 1024.0) * (256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared))); - var C = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); - var cos2SMSq = cos2SM * cos2SM; - deltaSigma = B * sinSigma * (cos2SM + (B / 4.0) * (cosSigma * (-1.0 + 2.0 * cos2SMSq) - (B / 6.0) * cos2SM * (-3.0 + 4.0 * sinSigma * sinSigma) * (-3.0 + 4.0 * cos2SMSq))); - lambda = L + (1.0 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SM + C * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); - var delta = (lambda - lambdaOrig) / lambda; - if (Math.abs(delta) < 1.0E-12) { - break; - } - } - ; - } - var distance = (b * A * (sigma - deltaSigma)); - results[0] = distance; - if (results.length > 1) { - var initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2 - sinU1 * cosU2 * cosLambda); - initialBearing *= 180.0 / Math.PI; - results[1] = initialBearing; - if (results.length > 2) { - var finalBearing = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda); - finalBearing *= 180.0 / Math.PI; - results[2] = finalBearing; - } - } - }; - -} - - - diff --git a/Ejectable/GT2.tsx b/Ejectable/GT2.tsx index ccdef6ab..77899c25 100644 --- a/Ejectable/GT2.tsx +++ b/Ejectable/GT2.tsx @@ -1,19 +1,20 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, Component } from 'react'; import * as Location from 'expo-location'; -import { Coordinate } from "./Coordinate"; import { Text, View } from './components/Themed'; import { StyleSheet } from 'react-native'; +import * as geolib from 'geolib'; +import { GeolibInputCoordinates } from 'geolib/es/types'; - var debug:boolean = false; -export var endPending:boolean = false; + var debug:number = 9; + var testCount = 0; + var bgEnabled:boolean = false; var expoGeoState:any = null; export var locEnabled:boolean = false; const heartbeat:number = 500; const displayBeat:number = 3; - -export function getEndPending() { return(endPending); } -export function setEndPending(value:boolean) { endPending = value; } + const ticksPerDs = 1; + const geoLibAccuracy = 0.34; const styles = StyleSheet.create({ tripText: { @@ -24,37 +25,60 @@ const styles = StyleSheet.create({ fontSize: 12, fontWeight: 'bold', }}); + +interface expoGeoObj { + coords: [ + altitude:number, + altitudeAccuracy:number, + latitude:number, + accuracy:number, + longitude:number, + heading:number, + speed:number, + timestamp:number + ] +} + class Coordinate { -function Monitor() { + public mLatitude:number = 0.0; + public mLongitude:number = 0.0; + public glCoords:any = {}; + + constructor (longitude:number, latitude:number) { + this.mLongitude = longitude; + this.mLatitude = latitude; + this,this.get_glcoords(); + } - const [location, setLocation] = useState(Object); - const [errorMsg, setErrorMsg] = useState(""); - - useEffect(() => { - (async () => { - let { status } = await Location.requestBackgroundPermissionsAsync(); - if (status !== 'granted') { - setErrorMsg('Permission to monitor location was denied'); - return; - } else Trips.setLocEnabled(true); - - let location = await Location.getCurrentPositionAsync({}); - setLocation(location); - Trips.deltaLoc(location); - })(); - }, []); - - expoGeoState = 'Waiting..'; - if (errorMsg) { - expoGeoState = errorMsg; - } else if (location) { - expoGeoState = JSON.stringify(location); - } - - - } - + public get_glcoords() { + this.glCoords['lat'] = this.mLatitude; + this.glCoords['lon'] = this.mLongitude; + } + + public distanceTo(otherPoint:Coordinate) : number { + return geolib.getDistance(this.glCoords,otherPoint.glCoords,geoLibAccuracy); + } + + } + + async function startTracking(client:any){ + if (debug > 5) console.log('Starting tracking') + if(!client.location){ + client.location = await Location.watchPositionAsync({ + accuracy: Location.Accuracy.Highest, + distanceInterval: 3, + timeInterval: (heartbeat / 2), + }, (loc) => { + Trips.deltaLoc(loc) + }); + } +} + +async function stopTracking(client:any){ + if (debug > 5) console.log('Remove tracking') + await client.location.remove(); +} class Trip { @@ -62,45 +86,35 @@ class Trip { ticks:number = 0; interval:any; segments:number = 1; - distance:number = 0; ds:number = 0; CO2:number = 0.0; - - loc:Coordinate = new Coordinate(0.0,0.0); - startPoint:Coordinate = new Coordinate(0.0,0.0) + location:any = null; + lastDSFixTick = 0; + lastFix:Coordinate = new Coordinate(0.0,0.0); + loc:Coordinate = new Coordinate(0.0,0.0); tick() {let hours:number = 0, minutes:number = 0, seconds:number = 0; - this.ticks++; + this.ticks += ( 1000 / heartbeat ) ; hours = this.ticks < 3600 ? 0 : (this.ticks / 3600); minutes = this.ticks < 60 ? 0 : (this.ticks - (hours * 3600)) / 60; seconds = this.ticks % 60; - this.elapsed = hours.toFixed(0) + ":" + minutes.toFixed(0) + ":" + seconds.toFixed(0); - } - public start() { this.interval = setInterval(() => this.tick(), heartbeat); } - public resume() { this.startPoint = new Coordinate(0.0,0.0) - this.loc = new Coordinate(0.0, 0.0); + this.elapsed = hours.toFixed(0) + ":" + minutes.toFixed(0) + ":" + seconds.toFixed(0); + } + + public start() { this.interval = setInterval(() => this.tick(), heartbeat); + if (!bgEnabled) startTracking(this); + } + public resume() { this.lastFix.mLatitude = 0.0; this.segments++; this.interval = setInterval(() => this.tick(), heartbeat); } - public stop() { this.ds = this.loc.distanceTo(this.startPoint); - this.distance += this.ds; + public stop() { Trips.distance += this.ds; + this.ds = 0.0; clearInterval(this.interval); - + if (!bgEnabled) stopTracking(this); } } -interface expoGeoObj { - coords: [ - altitude:number, - altitudeAccuracy:number, - latitude:number, - accuracy:number, - longitude:number, - heading:number, - speed:number, - timestamp:number - ] -} export class GT2 { @@ -120,27 +134,54 @@ export class GT2 { public units:string = "km"; public CO2Effect:string = "carbon burden"; - nTrips:number = 0; n:number = 0.0; m:number = 0.0; - public deltaLoc(lastFix:any) { lastFix = JSON.stringify(lastFix); let expoFix:expoGeoObj = JSON.parse(lastFix); - this.trip.loc.setLongitude(expoFix.coords['longitude']); - this.trip.loc.setLatitude(expoFix.coords['latitude']); + if (testCount < 1) { + var n:number = -1; + + var nyc:Coordinate = new Coordinate ( 40.7128 , 74.0060 ); + var ord:Coordinate = new Coordinate ( 41.8781 , 87.6298); + + n = nyc.distanceTo(ord); + + console.log('nyc ord ' + n); + testCount++; - if (this.trip.startPoint.mLatitude == 0.0) { - this.trip.startPoint.mLatitude = this.trip.loc.mLatitude; - this.trip.startPoint.mLongitude = this.trip.loc.mLongitude; } - this.trip.ds = this.trip.loc.distanceTo(this.trip.startPoint); + this.trip.loc.mLongitude = expoFix.coords['longitude']; + this.trip.loc.mLatitude = expoFix.coords['latitude']; + this.trip.loc.get_glcoords(); + + if (Trips.startPoint.mLatitude == 0.0) { + Trips.startPoint.mLatitude = this.trip.loc.mLatitude; + Trips.startPoint.mLongitude = this.trip.loc.mLongitude; + this.trip.loc.get_glcoords(); + } + + if (this.trip.lastFix.mLatitude != 0.0) { + if ((this.trip.ticks - this.trip.lastDSFixTick) >= ticksPerDs) { + this.trip.lastDSFixTick = this.trip.ticks; + this.trip.lastFix.mLatitude = this.trip.loc.mLatitude; + this.trip.lastFix.mLatitude = this.trip.loc.mLatitude; + this.trip.loc.get_glcoords(); + this.trip.ds += this.trip.lastFix.distanceTo(this.trip.loc); + if (debug > 8) console.log(this.trip.ds); + } + } + else { + this.trip.lastFix.mLatitude = this.trip.loc.mLatitude; + this.trip.lastFix.mLatitude = this.trip.loc.mLatitude; + this.trip.loc.get_glcoords(); + } } @@ -149,7 +190,6 @@ export class GT2 { this.trip.stop(); this.inProgress = false; this.nTrips++; - setEndPending(true); } @@ -192,24 +232,30 @@ export class GT2 { public getTripSummary() : string { + var preferredUnits:number = ((this.units == 'km') ? (this.distance / 1000) + : (this.distance / 1609.34)); + return ( 'Elapsed - ' + this.trip.elapsed + '\n' + 'Origin: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + 'Destination: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + - ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + - 'CO2: ' + this.trip.CO2 + ' grams ' + this.CO2Effect ); + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + + 'CO2: ' + this.trip.CO2 + ' grams ' + this.CO2Effect + '\n\n' + + 'Distance covered while consuming fuel: ' + preferredUnits.toFixed(4) + ' ' + this.units ); } public getTripPanel() : string { + var bigDS:number = this.distance + this.trip.ds; + if (this.inProgress) { return ( 'Elapsed - ' + this.trip.elapsed + '\n' + 'Geo: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + - 'Vector: ' + 'distance: ' + this.trip.ds + ' velocity: ' + this.v + '\n' + + 'Vector: ' + 'distance: ' + bigDS.toFixed(4) + ' velocity: ' + this.v + 'm/s \n' + 'Altitude: ' + this.elevation + '\n'); } else return("No trip in progress. " + this.nTrips + " trip(s) completed."); @@ -243,7 +289,7 @@ export class TripDisplay extends React.Component { render() { - if (!debug) + if (debug > 10) return( diff --git a/Ejectable/package-lock.json b/Ejectable/package-lock.json index a6139bd5..3d33b1b3 100644 --- a/Ejectable/package-lock.json +++ b/Ejectable/package-lock.json @@ -6592,6 +6592,11 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, + "geolib": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.1.tgz", + "integrity": "sha512-sfahBXFcgELdpumDZV5b3KWiINkZxC5myAkLk067UUcTmTXaiE9SWmxMEHztn/Eus4JX6kesHxaIuZlniYgUtg==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", diff --git a/Ejectable/package.json b/Ejectable/package.json index 0ee752e4..1d2a396f 100644 --- a/Ejectable/package.json +++ b/Ejectable/package.json @@ -25,7 +25,9 @@ "expo-location": "~12.1.2", "expo-splash-screen": "~0.11.2", "expo-status-bar": "~1.0.4", + "expo-task-manager": "~9.2.2", "expo-web-browser": "~9.2.0", + "geolib": "^3.3.1", "react": "16.13.1", "react-dom": "16.13.1", "react-native": "https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz", @@ -33,8 +35,7 @@ "react-native-reanimated": "~2.2.0", "react-native-safe-area-context": "3.2.0", "react-native-screens": "~3.4.0", - "react-native-web": "~0.13.12", - "expo-task-manager": "~9.2.2" + "react-native-web": "~0.13.12" }, "devDependencies": { "@babel/core": "^7.9.0", diff --git a/Ejectable/screens/ModalScreen.tsx b/Ejectable/screens/ModalScreen.tsx index 921617cb..400fa888 100644 --- a/Ejectable/screens/ModalScreen.tsx +++ b/Ejectable/screens/ModalScreen.tsx @@ -32,7 +32,7 @@ const styles = StyleSheet.create({ fontWeight: 'bold', }, separator: { - marginVertical: 30, + marginVertical: 10, height: 1, width: '80%', }, diff --git a/Ejectable/screens/SettingsScreen.tsx b/Ejectable/screens/SettingsScreen.tsx index 08a58d21..7252379d 100644 --- a/Ejectable/screens/SettingsScreen.tsx +++ b/Ejectable/screens/SettingsScreen.tsx @@ -10,7 +10,9 @@ import { RootTabScreenProps } from '../types'; export default function SettingsScreen( { navigation }: RootTabScreenProps<'Settings'>) { const [number, onChangeNumber] = React.useState(""); const [isKM, setMiles] = useState(false); - const toggleUnits = () => setMiles(previousState => !previousState); + const toggleUnits = () => { setMiles(previousState => !previousState) + Trips.units = isKM ? "km" : "mi"; + }; return ( diff --git a/Ejectable/screens/TripScreen.tsx b/Ejectable/screens/TripScreen.tsx index 78a68c60..b826e738 100644 --- a/Ejectable/screens/TripScreen.tsx +++ b/Ejectable/screens/TripScreen.tsx @@ -3,11 +3,11 @@ import { useState } from 'react'; import { Alert, BackHandler, Button, StyleSheet } from 'react-native'; import { Text, View } from '../components/Themed'; import { ScreenInfo2 } from '../components/ScreenInfo'; -import EndScreenInfo from '../components/EndScreenInfo'; -import { locEnabled, getEndPending, setEndPending, TripDisplay, Trips } from '../GT2'; +import { locEnabled, TripDisplay, Trips } from '../GT2'; import { RootTabScreenProps } from '../types'; - var debug:number = 10; +var advised:boolean = false; +var debug:number = 10; const styles = StyleSheet.create({ container: { @@ -34,10 +34,10 @@ const styles = StyleSheet.create({ function startTrip() { - if (!locEnabled && debug != 10) { - Alert.alert("You must enable both foreground\n and background location tracking."); + /* if (!locEnabled) { + Alert.alert("Location permission required."); return; - } + } */ Trips.start(); @@ -46,11 +46,9 @@ function startTrip() { function pauseTrip() { if (!Trips.paused) { - Alert.alert('Trip Paused'); Trips.pause(); } else { - Alert.alert('Trip Resumed'); Trips.pause(); } @@ -72,13 +70,13 @@ export default function TripScreen( { navigation }: RootTabScreenProps<'Trip'>)