This commit is contained in:
Ren Juan 2021-08-25 02:25:04 +00:00
parent 80950b3426
commit 8d83acad60
8 changed files with 148 additions and 224 deletions

View File

@ -1,7 +1,7 @@
import 'react-native-gesture-handler'; import 'react-native-gesture-handler';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Alert,Text } from 'react-native'; import { Text } from 'react-native';
import * as Location from 'expo-location'; import * as Location from 'expo-location';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
@ -42,6 +42,7 @@ export default function App() {
expoGeoState = JSON.stringify(location); expoGeoState = JSON.stringify(location);
} }
if (!isLoadingComplete) { if (!isLoadingComplete) {
return null; return null;
} else { } else {
@ -61,4 +62,5 @@ export default function App() {
</SafeAreaProvider> </SafeAreaProvider>
); );
} }
} }

View File

@ -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;
}
}
};
}

View File

@ -1,19 +1,20 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, Component } from 'react';
import * as Location from 'expo-location'; import * as Location from 'expo-location';
import { Coordinate } from "./Coordinate";
import { Text, View } from './components/Themed'; import { Text, View } from './components/Themed';
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import * as geolib from 'geolib';
import { GeolibInputCoordinates } from 'geolib/es/types';
var debug:boolean = false; var debug:number = 9;
export var endPending:boolean = false; var testCount = 0;
var bgEnabled:boolean = false;
var expoGeoState:any = null; var expoGeoState:any = null;
export var locEnabled:boolean = false; export var locEnabled:boolean = false;
const heartbeat:number = 500; const heartbeat:number = 500;
const displayBeat:number = 3; const displayBeat:number = 3;
const ticksPerDs = 1;
export function getEndPending() { return(endPending); } const geoLibAccuracy = 0.34;
export function setEndPending(value:boolean) { endPending = value; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
tripText: { tripText: {
@ -24,37 +25,60 @@ const styles = StyleSheet.create({
fontSize: 12, fontSize: 12,
fontWeight: 'bold', 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); public get_glcoords() {
const [errorMsg, setErrorMsg] = useState(""); this.glCoords['lat'] = this.mLatitude;
this.glCoords['lon'] = this.mLongitude;
useEffect(() => { }
(async () => {
let { status } = await Location.requestBackgroundPermissionsAsync(); public distanceTo(otherPoint:Coordinate) : number {
if (status !== 'granted') { return geolib.getDistance(this.glCoords,otherPoint.glCoords,geoLibAccuracy);
setErrorMsg('Permission to monitor location was denied'); }
return;
} else Trips.setLocEnabled(true); }
let location = await Location.getCurrentPositionAsync({}); async function startTracking(client:any){
setLocation(location); if (debug > 5) console.log('Starting tracking')
Trips.deltaLoc(location); if(!client.location){
})(); client.location = await Location.watchPositionAsync({
}, []); accuracy: Location.Accuracy.Highest,
distanceInterval: 3,
expoGeoState = 'Waiting..'; timeInterval: (heartbeat / 2),
if (errorMsg) { }, (loc) => {
expoGeoState = errorMsg; Trips.deltaLoc(loc)
} else if (location) { });
expoGeoState = JSON.stringify(location); }
} }
async function stopTracking(client:any){
} if (debug > 5) console.log('Remove tracking')
await client.location.remove();
}
class Trip { class Trip {
@ -62,45 +86,35 @@ class Trip {
ticks:number = 0; ticks:number = 0;
interval:any; interval:any;
segments:number = 1; segments:number = 1;
distance:number = 0;
ds:number = 0; ds:number = 0;
CO2:number = 0.0; CO2:number = 0.0;
location:any = null;
loc:Coordinate = new Coordinate(0.0,0.0); lastDSFixTick = 0;
startPoint:Coordinate = new Coordinate(0.0,0.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; 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); hours = this.ticks < 3600 ? 0 : (this.ticks / 3600);
minutes = this.ticks < 60 ? 0 : (this.ticks - (hours * 3600)) / 60; minutes = this.ticks < 60 ? 0 : (this.ticks - (hours * 3600)) / 60;
seconds = this.ticks % 60; seconds = this.ticks % 60;
this.elapsed = hours.toFixed(0) + ":" + minutes.toFixed(0) + ":" + seconds.toFixed(0); 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) public start() { this.interval = setInterval(() => this.tick(), heartbeat);
this.loc = new Coordinate(0.0, 0.0); if (!bgEnabled) startTracking(this);
}
public resume() { this.lastFix.mLatitude = 0.0;
this.segments++; this.segments++;
this.interval = setInterval(() => this.tick(), heartbeat); } this.interval = setInterval(() => this.tick(), heartbeat); }
public stop() { this.ds = this.loc.distanceTo(this.startPoint); public stop() { Trips.distance += this.ds;
this.distance += this.ds; this.ds = 0.0;
clearInterval(this.interval); 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 { export class GT2 {
@ -120,27 +134,54 @@ export class GT2 {
public units:string = "km"; public units:string = "km";
public CO2Effect:string = "carbon burden"; public CO2Effect:string = "carbon burden";
nTrips:number = 0; nTrips:number = 0;
n:number = 0.0; n:number = 0.0;
m:number = 0.0; m:number = 0.0;
public deltaLoc(lastFix:any) { public deltaLoc(lastFix:any) {
lastFix = JSON.stringify(lastFix); lastFix = JSON.stringify(lastFix);
let expoFix:expoGeoObj = JSON.parse(lastFix); let expoFix:expoGeoObj = JSON.parse(lastFix);
this.trip.loc.setLongitude(expoFix.coords['longitude']); if (testCount < 1) {
this.trip.loc.setLatitude(expoFix.coords['latitude']); 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.trip.stop();
this.inProgress = false; this.inProgress = false;
this.nTrips++; this.nTrips++;
setEndPending(true);
} }
@ -192,24 +232,30 @@ export class GT2 {
public getTripSummary() : string { public getTripSummary() : string {
var preferredUnits:number = ((this.units == 'km') ? (this.distance / 1000)
: (this.distance / 1609.34));
return ( return (
'Elapsed - ' + this.trip.elapsed + '\n' + 'Elapsed - ' + this.trip.elapsed + '\n' +
'Origin: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + 'Origin: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' +
'Destination: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + 'Destination: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' +
'CO2: ' + this.trip.CO2 + ' grams ' + this.CO2Effect ); 'CO2: ' + this.trip.CO2 + ' grams ' + this.CO2Effect + '\n\n' +
'Distance covered while consuming fuel: ' + preferredUnits.toFixed(4) + ' ' + this.units );
} }
public getTripPanel() : string { public getTripPanel() : string {
var bigDS:number = this.distance + this.trip.ds;
if (this.inProgress) { if (this.inProgress) {
return ( return (
'Elapsed - ' + this.trip.elapsed + '\n' + 'Elapsed - ' + this.trip.elapsed + '\n' +
'Geo: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) + 'Geo: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' + ' 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'); 'Altitude: ' + this.elevation + '\n');
} }
else return("No trip in progress. " + this.nTrips + " trip(s) completed."); else return("No trip in progress. " + this.nTrips + " trip(s) completed.");
@ -243,7 +289,7 @@ export class TripDisplay extends React.Component {
render() { render() {
if (!debug) if (debug > 10)
return( return(
<View> <View>
<Text style={styles.tripText}> <Text style={styles.tripText}>

View File

@ -6592,6 +6592,11 @@
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" "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": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",

View File

@ -25,7 +25,9 @@
"expo-location": "~12.1.2", "expo-location": "~12.1.2",
"expo-splash-screen": "~0.11.2", "expo-splash-screen": "~0.11.2",
"expo-status-bar": "~1.0.4", "expo-status-bar": "~1.0.4",
"expo-task-manager": "~9.2.2",
"expo-web-browser": "~9.2.0", "expo-web-browser": "~9.2.0",
"geolib": "^3.3.1",
"react": "16.13.1", "react": "16.13.1",
"react-dom": "16.13.1", "react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz", "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-reanimated": "~2.2.0",
"react-native-safe-area-context": "3.2.0", "react-native-safe-area-context": "3.2.0",
"react-native-screens": "~3.4.0", "react-native-screens": "~3.4.0",
"react-native-web": "~0.13.12", "react-native-web": "~0.13.12"
"expo-task-manager": "~9.2.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",

View File

@ -32,7 +32,7 @@ const styles = StyleSheet.create({
fontWeight: 'bold', fontWeight: 'bold',
}, },
separator: { separator: {
marginVertical: 30, marginVertical: 10,
height: 1, height: 1,
width: '80%', width: '80%',
}, },

View File

@ -10,7 +10,9 @@ import { RootTabScreenProps } from '../types';
export default function SettingsScreen( { navigation }: RootTabScreenProps<'Settings'>) { export default function SettingsScreen( { navigation }: RootTabScreenProps<'Settings'>) {
const [number, onChangeNumber] = React.useState(""); const [number, onChangeNumber] = React.useState("");
const [isKM, setMiles] = useState(false); const [isKM, setMiles] = useState(false);
const toggleUnits = () => setMiles(previousState => !previousState); const toggleUnits = () => { setMiles(previousState => !previousState)
Trips.units = isKM ? "km" : "mi";
};
return ( return (
<View style={styles.container}> <View style={styles.container}>

View File

@ -3,11 +3,11 @@ import { useState } from 'react';
import { Alert, BackHandler, Button, StyleSheet } from 'react-native'; import { Alert, BackHandler, Button, StyleSheet } from 'react-native';
import { Text, View } from '../components/Themed'; import { Text, View } from '../components/Themed';
import { ScreenInfo2 } from '../components/ScreenInfo'; import { ScreenInfo2 } from '../components/ScreenInfo';
import EndScreenInfo from '../components/EndScreenInfo'; import { locEnabled, TripDisplay, Trips } from '../GT2';
import { locEnabled, getEndPending, setEndPending, TripDisplay, Trips } from '../GT2';
import { RootTabScreenProps } from '../types'; import { RootTabScreenProps } from '../types';
var debug:number = 10; var advised:boolean = false;
var debug:number = 10;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -34,10 +34,10 @@ const styles = StyleSheet.create({
function startTrip() { function startTrip() {
if (!locEnabled && debug != 10) { /* if (!locEnabled) {
Alert.alert("You must enable both foreground\n and background location tracking."); Alert.alert("Location permission required.");
return; return;
} } */
Trips.start(); Trips.start();
@ -46,11 +46,9 @@ function startTrip() {
function pauseTrip() { function pauseTrip() {
if (!Trips.paused) { if (!Trips.paused) {
Alert.alert('Trip Paused');
Trips.pause(); Trips.pause();
} }
else { else {
Alert.alert('Trip Resumed');
Trips.pause(); Trips.pause();
} }
@ -72,13 +70,13 @@ export default function TripScreen( { navigation }: RootTabScreenProps<'Trip'>)
<Button <Button
title={sButtonText} title={sButtonText}
onPress={() => { onPress={() => {
if (!Trips.inProgress) {setSButtonText("End"); if (!advised) {
startTrip(); Alert.alert("2.1.0 and later will use background tracking.");
setPButtonText('Pause');} advised = true;
else {setSButtonText('Start'); }
endTrip(); if (!Trips.inProgress) {startTrip();
navigation.push('Modal'); if (Trips.inProgress) { setSButtonText("End"); setPButtonText('Pause');}}
}} else {setSButtonText('Start'); endTrip(); navigation.push('Modal'); }}
} }
/> />
<Button <Button
@ -92,17 +90,6 @@ export default function TripScreen( { navigation }: RootTabScreenProps<'Trip'>)
<TripDisplay></TripDisplay> <TripDisplay></TripDisplay>
</View> </View>
); );
return (
<View style={styles.container}>
<Text style={styles.title}>Trip Control</Text>
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
<EndScreenInfo path="/screens/TripScreen.tsx" />
<Button title={'Done'}
onPress={() => { setEndPending(false); navigation.jumpTo('Trip'); }}
/>
</View>
);
} }