I'm doing the mobile application in Expo React Native that can track the vehicle speed.
Currently I am using Expo Location API to track the vehicle location for every 5 seconds.
import * as Location from 'expo-location';
const startLocationTracking = async () => {
await Location.startLocationUpdatesAsync(LOCATION_TRACKING, {
accuracy: Location.Accuracy.Highest,
timeInterval: 5000,
distanceInterval: 0,
});
const hasStarted = await Location.hasStartedLocationUpdatesAsync(
LOCATION_TRACKING
);
setLocationStarted(hasStarted);
console.log('tracking started?', hasStarted);
};
TaskManager.defineTask(LOCATION_TRACKING, async ({ data, error }) => {
if (error) {
console.log('LOCATION_TRACKING task ERROR:', error);
return;
}
if (data) {
Location.g
const { locations } = data;
let lat = locations[0].coords.latitude;
let long = locations[0].coords.longitude;
l1 = lat;
l2 = long;
origin = lat+','+long;
}
});
In Expo Location Document Document there is a option to get speed.
Can anyone let me know how to track speed using this option. Or any other alternatives to track speed using coordinate movement change.
Related
I am trying to record video while using BlazePose with React Native. Wherever I add in cameraRef.current.camera.recordAsync() into causes the video to freeze and app to crash.
From what I've found, it might need to go in the handleCameraStream but I am unsure. Here is that method:
const handleCameraStream = async (
images: IterableIterator<tf.Tensor3D>,
updatePreview: () => void,
gl: ExpoWebGLRenderingContext) => {
const loop = async () => {
// const video = cameraRef.current.camera.recordAsync()
// Get the tensor and run pose detection.
const imageTensor = images.next().value as tf.Tensor3D;
const startTs = Date.now();
const poses = await model!.estimatePoses(
imageTensor,
undefined,
Date.now()
);
const latency = Date.now() - startTs;
setFps(Math.floor(1000 / latency));
setPoses(poses);
tf.dispose([imageTensor]);
if (rafId.current === 0) {
return;
}
// Render camera preview manually when autorender=false.
if (!AUTO_RENDER) {
updatePreview();
gl.endFrameEXP();
}
rafId.current = requestAnimationFrame(loop);
};
loop();
};
And here is my useEffect:
useEffect(() => {
async function prepare() {
rafId.current = null;
// Set initial orientation.
const curOrientation = await ScreenOrientation.getOrientationAsync();
setOrientation(curOrientation);
// Listens to orientation change.
ScreenOrientation.addOrientationChangeListener((event) => {
setOrientation(event.orientationInfo.orientation);
});
// Camera permission.
await Camera.requestCameraPermissionsAsync();
// Wait for tfjs to initialize the backend.
await tf.ready();
// Load model
const model = await posedetection.createDetector(
posedetection.SupportedModels.BlazePose,
{
runtime: "tfjs",
enableSmoothing: true,
modelType: "full",
}
);
setModel(model);
// Ready!
setTfReady(true);
}
prepare();}, []);
And finally the TensorCamera
<TensorCamera
ref={cameraRef}
style={styles.camera}
autorender={AUTO_RENDER}
type={cameraType}
// tensor related props
resizeWidth={getOutputTensorWidth()}
resizeHeight={getOutputTensorHeight()}
resizeDepth={3}
rotation={getTextureRotationAngleInDegrees()}
onReady={handleCameraStream}
/>
This is where i trigger Location.startLocationUpdatesAsync
useEffect(() => {
if (isPermissionReady && destination && !intervalHandle.current) {
// Register location fetch to task manager
Location.startLocationUpdatesAsync(TASK_NAME, {
accuracy: Location.Accuracy.Balanced,
// activityType: Location.ActivityType.AutomotiveNavigation,
// deferredUpdatesTimeout: INTERVAL_MS,
})
// Repeatedly read local storage to update currentPos state
intervalHandle.current = setInterval(updateCurrentPos, INTERVAL_MS)
}
}, [isPermissionReady, destination])
And this is my TaskManager (declared separately in index.tsx not inside a lifecycle or hook):
TaskManager.defineTask(TASK_NAME, ({ data, error }) => {
if (error) {
console.error('Task Manager Failed')
return
}
if (data) {
const { locations } = (data as any) ?? { locations: [] }
const { latitude, longitude } = locations[0]?.coords ?? {}
try {
AsyncStorage.setItem(STORAGE_KEY, JSON.stringify({ latitude, longitude }))
} catch {
console.error('SetItem Failed')
}
}
})
My taskmanager reads location data from device and save to LocalStorage(AsyncStorage) and react native app fetches this data with setInterval. However, data set by TaskManager is updated too rarely and it is not called regularly in constant time(imo) and never called if I stay at the same place with all deferredOOO options disabled.
Does startLocationUpdatesAsync trigger only after some distance change even with default values? or am I doing something wrong? (I want it to be called in regular basis or at least have a clear understanding of when it is called)
Plus, is it normal for taskmanager to not show any console.log?
I'm doing a test app to learn react native, and I'm trying to use secure store (a bit like async storage) to store my individual goals and save them. So far it's working, however when I refresh the app only the last goal I entered gets loaded.
Where am I going wrong here? In my console log the full array is shown with both the old and the new ones I add, then I refresh and I only have one left.
const [goals, setGoals] = useState([])
const addGoal = async (goal) => {
try{
const goalJson = JSON.stringify({text: goal, id:`${Math.random()}`, todos:[], date: Date.now(), percentage:0})
await SecureStore.setItemAsync("Goal", goalJson)
load()
}
catch (err) {alert(err)}
}
const load = async() => {
try {
const goalValue = await SecureStore.getItemAsync("Goal")
const parsed = JSON.parse(goalValue)
if(goals !== null) {
setGoals([...goals, parsed])
console.log(goals)
}
}catch (err) {alert(err)}
}
useEffect(()=> {
load()
},[])
SecureStore is like a key-value database, so currently you're always writing to the same key Goal and your addGoal function is erasing the previous value with goalJson content.
Instead, load once the goals from storage, then update the goals state when a new goal is added, and write them all to on storage each time goals value is updated.
This how effects works, by "reacting" to a change of value. This is just a little bit more complicated because of SecureStorage async functions.
Here is my (untested) improved code. I renamed the storage key from Goal to Goals.
const [goals, setGoals] = useState([])
const [loaded, setLoaded] = useState(false)
useEffect(()=> {
async function load() {
try {
const goalsValue = await SecureStore.getItemAsync("Goals")
const goalsParsed = JSON.parse(goalsValue)
if (goalsParsed !== null) {
setGoals(goalsParsed)
}
setLoaded(true)
} catch (err) { alert(err) }
}
load()
}, []) // load only when component mount
const addGoal = (text) => {
const goal = { text, id:`${Math.random()}`, todos:[],
date: Date.now(), percentage:0 }
setGoals([...goals, goal])
})
useEffect(() => {
async function saveGoals() {
try {
// save all goals to storage
const goalsJson = JSON.stringify(goals)
await SecureStore.setItemAsync("Goals", goalsJson)
}
catch (err) {alert(err)}
}
if (loaded) { // don't save before saved goals were loaded
saveGoals();
}
}, [goals, loaded]) // run the effect each time goals is changed
I am testing background location tasks in react native with Expo's taskmanager to test when I enter a geofence. I've got the basics working but I am now trying to access the values within a promise that is returned from one of the tasks so i can display if you've entered or exited the geofence. Can anyone help me figure out how to properly access the values I want?
Below is the task portion relevant to the geofence. I return the value region which comes as a promise. I haven't been able to figure out how to send directly from task manager yet:
// 3 Define geofencing task
TaskManager.defineTask(TASK_CHECK_GEOFENCE, ({ data: { eventType, region }, error }) => {
if (error) {
// check `error.message` for more details.
return;
}
if (eventType === Location.GeofencingEventType.Enter) {
console.log("You've entered region:", region);
//this.props.setEnterRegion({ inRegion: region })
const final = region
return final
} else if (eventType === Location.GeofencingEventType.Exit) {
console.log("You've left region:", region);
const final = region
return final
}
});
Then within App.js I call the app in componentDidMount:
componentDidMount() {
this.getLocationsPermissions();
this.startBackgroundUpdate();
this.startGeofence();
}
//define and start geofence
startGeofence = async () => {
console.log('starting geofencing test ...')
let x = Location.startGeofencingAsync('TASK_CHECK_GEOFENCE',
[
{
identifier: 'court',
latitude: this.state.myLocation.latitude,
longitude: this.state.myLocation.longitude,
radius: 20,
notifyOnEnter: true,
notifyOnExit: true,
}
]
)
this.sendTask(x)
};
When I try to access the value I can see the promise and I am unsure where I am going wrong. I would perfer to send to the store directly from taskmanager but I am having trouble with that:
//send to store from task
sendTask = (x) => {
console.log('entered region...', x)
this.props.setEnterRegion(x)
entered region... Promise {
"_40": 0,
"_55": null,
"_65": 0,
"_72": null,
}
Is this at all possible? I'm currently using react-native-track-player to stream audio files and I would love to be able to store the last position when my users exit the app and resume when they re-open (e.g. similar to how Spotify works)
Right now I'm tracking this info via a simple interval:
this.keepTime = setInterval(async () => {
const state = await TrackPlayer.getState()
if (state == TrackPlayer.STATE_PLAYING) {
const ID = await TrackPlayer.getCurrentTrack()
const position = await TrackPlayer.getPosition()
await AsyncStorage.setItem(ID, String(position))
}
}, 10000)
Problem is, I need to clear the interval when my app moves to the background or else it will crash. I would also much rather only need to call this code once as opposed to periodically if that is possible.
I know I could use headless JS on android but the app is cross platform, so my iOS user experience would be lesser.
Any suggestions?
I think you can use componentWillUnmount() function for this.
You could add a listener to get the App State and then log the position when it goes to background.
class App extends React.Component {
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this.handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
this.saveTrackPosition();
}
handleAppStateChange = (nextAppState) => {
if (nextAppState.match(/inactive|background/) && this.state.appState === 'active') {
this.saveTrackPosition();
}
this.setState({appState: nextAppState});
}
saveTrackPosition = () => {
if (state == TrackPlayer.STATE_PLAYING) {
const ID = await TrackPlayer.getCurrentTrack()
const position = await TrackPlayer.getPosition()
await AsyncStorage.setItem(ID, String(position))
}
}
}