Untitled
unknown
plain_text
2 years ago
18 kB
7
Indexable
import React, { useState, useEffect } from 'react';
import GPXReader from './GPXReader';
import MapView from './MapView';
import './css/Home.css';
function GeolocationTracker() {
const [isCollecting, setIsCollecting] = useState(false);
const [isCollectingFake, setIsCollectingFake] = useState(false);
const [startTime, setStartTime] = useState(null);
const [lastTime, setLastTime] = useState(null);
const [distance, setDistance] = useState(0);
const [startPosition, setStartPosition] = useState(null);
const [lastPosition, setLastPosition] = useState(null);
const [speed, setSpeed] = useState(0);
const [accelerationY, setAccelerationY] = useState(0);
const [accuracy, setAccuracy] = useState(null);
const [isDriving, setIsDriving] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [speedProfile, setSpeedProfile] = useState([]);
const [fakePosition, setFakePosition] = useState([]);
const [geojson, setGeojson] = useState(null);
useEffect(() => {
if (!isCollecting) return;
const watchId = navigator.geolocation.watchPosition(
(position) => {
const currentTime = new Date().getTime();
if (!startTime) {
setStartTime(currentTime);
setLastTime(currentTime);
setStartPosition({ latitude: position.coords.latitude, longitude: position.coords.longitude });
setLastPosition({ latitude: position.coords.latitude, longitude: position.coords.longitude });
} else {
const deltaTime = (currentTime - lastTime) / 1000; // elapsed time in seconds
const currentDistance = calculateDistance(lastPosition.latitude, lastPosition.longitude, position.coords.latitude, position.coords.longitude);
setDistance((prevDistance) => prevDistance + currentDistance);
const currentSpeed = currentDistance / deltaTime; // current speed in m/s
setSpeed(currentSpeed);
setLastTime(currentTime);
setLastPosition({ latitude: position.coords.latitude, longitude: position.coords.longitude });
setSpeedProfile((prevProfile) => [
...prevProfile,
{ time: (currentTime - startTime) / 1000, speed: currentSpeed, distance: distance + currentDistance }
]);
}
setAccuracy(position.coords.accuracy);
setIsDriving(position.coords.speed > 0.1); // Assuming speed > 0.1 m/s indicates driving
},
(error) => {
console.error('Error getting geolocation:', error);
},
{
maximumAge: 0,
enableHighAccuracy: true
}
);
const handleMotionEvent = (event) => {
setAccelerationY(event.accelerationIncludingGravity.y);
};
window.addEventListener('devicemotion', handleMotionEvent);
const intervalId = setInterval(() => {
setCurrentTime((new Date().getTime() - startTime) / 1000);
}, 1000);
return () => {
clearInterval(intervalId);
navigator.geolocation.clearWatch(watchId);
window.removeEventListener('devicemotion', handleMotionEvent);
};
}, [isCollecting, startTime, lastTime, distance, lastPosition]);
// Function to calculate distance between two coordinates
const calculateDistance = (lat1, lon1, lat2, lon2) => {
// Haversine formula
const R = 6371e3; // metres
const φ1 = (lat1 * Math.PI) / 180; // φ, λ in radians
const φ2 = (lat2 * Math.PI) / 180;
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
const Δλ = ((lon2 - lon1) * Math.PI) / 180;
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // in meters
};
const clearData = () => {
setIsCollecting(false);
setStartTime(null);
setLastTime(null);
setDistance(0);
setStartPosition(null);
setLastPosition(null);
setSpeed(0);
setAccelerationY(0);
setAccuracy(null);
setIsDriving(false);
setCurrentTime(0);
setSpeedProfile([]);
};
const restartCollecting = () => {
setIsCollecting(true);
setStartTime(null);
setLastTime(null);
setDistance(0);
setStartPosition(null);
setLastPosition(null);
setSpeed(0);
setAccelerationY(0);
setAccuracy(null);
setIsDriving(false);
setCurrentTime(0);
setSpeedProfile([]);
};
useEffect(() => {
if (!isCollectingFake) return;
const fakeRideInterval = setInterval(() => {
const currentTime = new Date().getTime();
if (!startTime) {
setStartTime(currentTime);
setLastTime(currentTime);
const initialPosition = { latitude: 0, longitude: 0 }; // Set initial fake position
setStartPosition(initialPosition);
setLastPosition(initialPosition);
setFakePosition([initialPosition]);
} else {
const deltaTime = (currentTime - lastTime) / 1000; // elapsed time in seconds
const fakeLastPosition = lastPosition || { latitude: 0, longitude: 0 };
const fakeCurrentPosition = {
latitude: fakeLastPosition.latitude + 0.0001, // Simulate movement
longitude: fakeLastPosition.longitude + 0.0001
};
const currentDistance = calculateDistance(fakeLastPosition.latitude, fakeLastPosition.longitude, fakeCurrentPosition.latitude, fakeCurrentPosition.longitude);
setDistance((prevDistance) => prevDistance + currentDistance);
const currentSpeed = currentDistance / deltaTime; // current speed in m/s
setSpeed(currentSpeed);
setLastTime(currentTime);
setLastPosition(fakeCurrentPosition);
setSpeedProfile((prevProfile) => [
...prevProfile,
{ time: (currentTime - startTime) / 1000, speed: currentSpeed, distance: distance + currentDistance }
]);
setFakePosition((prevPositions) => [
...prevPositions,
fakeCurrentPosition
]);
}
setAccuracy(5); // Set a fixed accuracy for fake data
setIsDriving(true); // Assuming always driving in fake mode
}, 1000);
return () => {
clearInterval(fakeRideInterval);
};
}, [isCollectingFake, startTime, lastTime, distance, lastPosition]);
// Function to get speed profile by seconds
const getSpeedProfileBySeconds = () => {
const profileBySeconds = {};
speedProfile.forEach(entry => {
const second = Math.floor(entry.time);
if (!profileBySeconds[second]) {
profileBySeconds[second] = { speed: 0, distance: 0 };
}
profileBySeconds[second] = { speed: entry.speed, distance: entry.distance };
});
return profileBySeconds;
};
// Component to display speed table
const Table = ({ profileBySeconds, maxSeconds }) => (
<div style={styles.tableContainer}>
<div className="customScrollbar" style={styles.tableScroll}>
<table style={styles.speedTable}>
<thead>
<tr>
<th style={{borderBottom: '0px solid ', padding: '10px',textAlign: 'center',verticalAlign: 'top'}}>Speed profile seconds</th>
<th style={styles.th}>km/h</th>
<th style={{borderBottom: '0px solid ', padding: '10px',textAlign: 'center',verticalAlign: 'top'}}>m</th>
</tr>
</thead>
<tbody>
{Array.from({ length: maxSeconds }).map((_, index) => {
const second = index + 1;
const entry = profileBySeconds[second] || { speed: 0, distance: 0 };
const speedKmh = entry.speed * 3.6; // Convert from m/s to km/h
const speedMph = speedKmh * 0.621371; // Convert from km/h to mph
return (
<tr key={second}>
<td style={{ padding: '10px', borderBottom: '0px solid', textAlign: 'center', verticalAlign: 'top' }}>{second}</td>
<td style={styles.td}>{speedKmh.toFixed(2)}</td>
<td style={styles.td}>{speedMph.toFixed(2)}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
const profileBySeconds = getSpeedProfileBySeconds();
const maxSeconds = Math.max(...Object.keys(profileBySeconds).map(key => parseInt(key, 10)));
const handleGPXLoad = (data) => {
setGeojson(data);
};
return (
<div style={{ display: 'flex', justifyContent: 'center'}}>
{/*{!isCollecting ? <div style={{ marginRight: '20px' }}> /!* Add margin-right to create space *!/*/}
{/* <GPXReader onGPXLoad={handleGPXLoad} />*/}
{/* {geojson && <MapView geojson={geojson} currentSpeed={speed} />}*/}
{/*</div> : null}*/}
<div style={{ }}> {/* Add margin-left to create space */}
<div className="container" style={styles.container}>
<h2 style={{ color: 'white' }}>Speed Coach</h2>
<table style={styles.table}>
<tbody>
<tr style={styles.row}>
<td style={styles.label}>State:</td>
<td style={styles.data}>{isDriving ? 'Driving' : 'Standing'}</td>
</tr>
<tr style={styles.row}>
<td style={styles.label}>GPS Speed:</td>
<td style={styles.data}>{(speed * 3.6).toFixed(2)} km/h</td>
</tr>
<tr style={styles.row}>
<td style={styles.label}>Y Acceleration:</td>
<td style={styles.data}>{accelerationY.toFixed(2)}</td>
</tr>
<tr style={styles.row}>
<td style={styles.label}>GPS Accuracy:</td>
<td style={styles.data}>{accuracy ? `${accuracy} meters` : 'N/A'}</td>
</tr>
<tr style={styles.row}>
<td style={styles.label}>Distance from start:</td>
<td style={styles.data}>{distance.toFixed(0)} meters</td>
</tr>
</tbody>
</table>
<div style={styles.tableContainer}>
<div style={styles.tableScroll}>
<table style={styles.speedTable}>
<th style={{borderBottom: '0px solid ', padding: '10px',textAlign: 'center',verticalAlign: 'top'}}>Speed profile seconds</th>
<th style={styles.th}>km/h</th>
<th style={{borderBottom: '0px solid ', padding: '10px',textAlign: 'center',verticalAlign: 'top'}}>m</th>
<tbody>
{Array.from({ length: maxSeconds }).map((_, index) => {
const second = index + 1;
const entry = profileBySeconds[second] || { speed: 0, distance: 0 };
const speedKmh = entry.speed * 3.6; // Convert from m/s to km/h
const speedMph = speedKmh * 0.621371; // Convert from km/h to mph
return (
<tr key={second}>
<td style={{ padding: '10px', borderBottom: '0px solid', textAlign: 'center', verticalAlign: 'top' }}>{second}</td>
<td style={styles.td}>{speedKmh.toFixed(2)}</td>
<td style={styles.td}>{speedMph.toFixed(2)}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', position: 'relative', bottom: 0, left: 0, right: 0 }}>
<button style={styles.button} onClick={isCollecting ? clearData : restartCollecting}>
{isCollecting ? 'Clear' : 'Restart'}
</button>
{/*<button style={{ backgroundColor: 'transparent',*/}
{/* color: 'transparent',*/}
{/* border: 'none',*/}
{/* cursor: 'pointer',*/}
{/* fontWeight: 'bold',*/}
{/* justifyContent: 'center',marginLeft:10}}>*/}
{/* {isCollectingFake ? 'Fake ride stop' : 'Fake ride start'}*/}
{/*</button>*/}
<button style={{ backgroundColor: '#70309f',
color: '#fff',
border: 'none',
cursor: 'pointer',
fontWeight: 'bold',
justifyContent: 'center',marginLeft:10}}
onClick={() => setIsCollectingFake(!isCollectingFake)}>
{isCollectingFake ? 'Fake ride stop' : 'Fake ride start'}
</button>
</div>
</div>
</div>
);
};
const styles = {
overlay: {
// justifyContent: 'center',
// alignItems: 'center',
// flexDirection: 'column',
// color: 'white',
// height: '100%',
// width:'300',
// marginTop: 40,
// display:'flex-center'
},
container: {
backgroundColor: 'rgba(38,38,38,255)',
padding: '2px',
borderRadius: '8px',
width: '100%',
maxWidth: '400px',
textAlign: 'center',
marginBottom: '20px',
margin: '20px auto'
},
button_container: {
padding: '20px',
borderRadius: '8px',
width: '90%',
maxWidth: '400px',
textAlign: 'center',
marginBottom: '20px',
},
table: {
width: '100%',
borderCollapse: 'collapse',
marginBottom: '20px',
color: 'white',
},
row: {
borderBottom: '1px solid rgba(0, 0, 0, 0.1)',
},
label: {
textAlign: 'left',
padding: '10px',
fontWeight: 'bold',
},
data: {
textAlign: 'right',
padding: '10px',
},
buttonContainer: {
display: 'flex',
justifyContent: 'flex-end',
},
button: {
backgroundColor: '#70309f',
color: '#fff',
border: 'none',
cursor: 'pointer',
fontWeight: 'bold',
position: 'absolute',
bottom: 0,
right: 0,
marginTop:'10px'
},
listViewContainer: {
width: '90%',
maxWidth: '400px',
backgroundColor: 'rgba(38,38,38,255)',
padding: '20px',
borderRadius: '8px',
textAlign: 'center',
color: 'white',
height: '150px',
overflowY: 'auto', // Add this line
position: 'relative', // Add this line
},
listViewHeader: {
display: 'flex',
top: '0', // Add this line
backgroundColor: 'rgba(38,38,38,255)', // Add this line
padding: '10px 0', // Add this line
justifyContent: 'space-between',
overflow:'false'
},
listView: {
display: 'flex',
},
tableContainer: {
backgroundColor: 'rgba(38,38,38,255)',
paddingTop: '5px',
borderRadius: '8px',
width: '90%',
maxWidth: '400px',
textAlign: 'right',
marginBottom: '20px',
color: 'white',
},
tableScroll: {
maxHeight: '200px',
overflowY: 'auto',
// paddingRight: '10px', // Add padding to the right to create space between the scrollbar and table
},
speedTable: {
width: '100%',
borderCollapse: 'collapse',
},
th: {
borderBottom: '0px solid ',
padding: '10px',
textAlign: 'start', // Align text to the left
verticalAlign: 'top', // Align text to the top
},
td: {
padding: '10px',
borderBottom: '0px solid ',
textAlign: 'left', // Align text to the left
verticalAlign: 'top', // Align text to the top
},
};
export default GeolocationTracker;
Editor is loading...
Leave a Comment