If you’ve been working with React Native you must have used or heard about react-native-maps. React native maps is a react native package that provide Google Maps API for React Native. Using static google maps is very common but today we are going to use one of the widely used feature of google maps, location tracking using React native.
Find the project repo here
Getting Started
Start by creating a React Native project and name it whatever you want. I’m using the react-native-cli but create-react-native-app should also work just fine.
react-native init locationTracking
Installing react-native-maps
First, download the library from npm:
npm install react-native-maps --save
Installing react-native-maps requires adding code to native iOS and Android files. Follow the installation instructions provided by the react-native-maps. After installation make sure that the project builds successfully before moving forward.
Installing react-native-maps is not a simple task. If you are doing it for the first time I would suggest going through the installation instructions carefully. Believe me this will be the toughest task of this tutorial. Use google if you face any errors.
Using react-native-maps
Great, now that you’ve successfully installed the react-native-maps. Let’s move to the juicy part, where the real magic happens.
Let first set some initial states that will be used for this project.
constructor(props) {
super(props);
this.state = {
latitude: LATITUDE,
longitude: LONGITUDE,
routeCoordinates: [],
distanceTravelled: 0,
prevLatLng: {},
coordinate: new AnimatedRegion({
latitude: LATITUDE,
longitude: LONGITUDE
})
};
}
We’ll use these states later in the app, the only thing of interest here is the new AnimatedRegion
which will help us to animate our markers when the location updates.
Watch for location changes
Now we’ll need to get the location coordinates every time the user moves. Google maps geolocation API has watchPosition
method which will hep us get the location coordinates whenever they gets changed.
componentDidMount() {
this.watchID = navigator.geolocation.watchPosition(
position => {
const { coordinate, routeCoordinates, distanceTravelled } = this.state;
const { latitude, longitude } = position.coords;
const newCoordinate = {
latitude,
longitude
};
if (Platform.OS === "android") {
if (this.marker) {
this.marker._component.animateMarkerToCoordinate(
newCoordinate,
500
);
}
} else {
coordinate.timing(newCoordinate).start();
}
this.setState({
latitude,
longitude,
routeCoordinates: routeCoordinates.concat([newCoordinate]),
distanceTravelled:
distanceTravelled + this.calcDistance(newCoordinate),
prevLatLng: newCoordinate
});
},
error => console.log(error),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);
}
The watchPosition gives us the information about user’s location whenever it get’s changed. We then use ES6 Destructuring to get latitude & longitude from the position.coords. Also, we’ll get coordinate, routeCoordinates & distanceTravelled from initial state.
We’ll than create newCoordinate variable that will store these new updated location coordinates that we received from position.coords. Now that we have got the updates coordinates we’ll animate the marker to these new coordinates. Both Android and iOS have different way of handling this so we’ll use Platform Specific Code to handle this.
if (Platform.OS === "android") {
if (this.marker) {
this.marker._component.animateMarkerToCoordinate(
newCoordinate,
500
);
}
} else {
coordinate.timing(newCoordinate).start();
}
Now its time to update our initial states with the new one.
this.setState({
latitude,
longitude,
routeCoordinates: routeCoordinates.concat([newCoordinate]),
distanceTravelled: distanceTravelled + this.calcDistance(newCoordinate),
prevLatLng: newCoordinate
});
Calculating Distance Travelled
We have used distanceTravelled state variable to store the distance travelled by the user. To calculate this distance we’ll create a new function calcDistance that will take newLatLng as parameter and prevLatLng as state variable and will return distance.
calcDistance = newLatLng => {
const { prevLatLng } = this.state;
return haversine(prevLatLng, newLatLng) || 0;
};
Looking at this function you maybe wondering when did calculating the distance got so complex. Apparently due to the curvature of the earth calculating distance with the help of latitude and longitude is not so straightforward. Earth has got some curves.
To calculate the distance using latitude and longitude we need to use Haversine formula. If you are like me who struggles with mathematics 😅, there is a hope in the form of a haversine npm package that can help us calculate distance using latitude and longitude. Install haversine npm package using the following command.
npm install haversine
Rendering MapView
Since component need region prop which accepts an object with location coordinates. We are going to create a function that returns all necessary information needed.
getMapRegion = () => ({
latitude: this.state.latitude,
longitude: this.state.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
});
Now we’ve all the information need to render the map.
<MapView
style={styles.map}
showUserLocation
followUserLocation
loadingEnabled
region={this.getMapRegion()}
>
<Polyline coordinates={this.state.routeCoordinates} strokeWidth={5} />
<Marker.Animated
ref={marker => {
this.marker = marker;
}}
coordinate={this.state.coordinate}
/>
</MapView>
We’ve also used the Google Maps Polyline to draw the path as the user moves. Polyline has acoordinate props which accepts an array of coordinates which we can get from our routeCoordinates. We’ve also set the strokeWidth to so we can see the path clearly.
Next to show the Animated marker we’ll use Marker.Animated component to show marker at user current position. It has coordinate props which will get the coordinate object from the state.
Show Distance Travelled
Finally to show the distance travelled by the user we’ll set up a View with the proper styling applied.
<View style={styles.buttonContainer}>
<TouchableOpacity style={[styles.bubble, styles.button]}>
<Text style={styles.bottomBarContent}>
{parseFloat(this.state.distanceTravelled).toFixed(2)} km
</Text>
</TouchableOpacity>
</View>
Testing
Now that we’ve all the necessary pieces in place we can finally run our app.
To test the app on iOS simulator we’ll use the location mode available in iOS simulators. After running the app go to Debug
> Location
> Freeway Drive
setting in simulator to turn on this feature. Doing so should result in something similar as below.
For Android, it’s not that straightforward as Android emulators doesn’t have any in built feature to test the dynamic location changes. To test it, either you can manually change your position by walking some distance or you can use 3rd party mock location apps. I was able to test this using GPS Joystick app on Android.
Conclusion
We have successfully created a React Native app that tracks an user location and draws the path. It can also calculate the distance travelled by the user.
I’ve skipped the styles and some other boilerplate code needed for this app but you can find that in the github repo here.
I hope this helps you to understand the Google maps API. Maybe you can take inspiration from this to build something amazing. Please feel free to leave any feedback, I’m always looking for better solutions!
Originally published on Medium
Top comments (2)
Shared this on ranktutorials :)
Good explanation