Skip to content

Instantly share code, notes, and snippets.

@pke
Created February 3, 2016 21:54
Show Gist options
  • Save pke/364552af2c9e5719d1a1 to your computer and use it in GitHub Desktop.
Save pke/364552af2c9e5719d1a1 to your computer and use it in GitHub Desktop.
Geo-Location tracking react component using Higher-Order-Function for location updates
import React from "react"
import LocationType from "../types/LocationType"
/* Usage: <Distance to={{longitude,latitude}}/>
Renders the distance in m/km to the given location.
If `from` is omitted then the current geo-location is used and tracked.
*/
const propTypes = {
to: LocationType.isRequired,
from: LocationType.isRequired
}
const cos = Math.cos
const p = Math.PI / 180
function getDistance(lat1, lon1, lat2, lon2) {
var a = 0.5 - cos((lat2 - lat1) * p)/2 +
cos(lat1 * p) * cos(lat2 * p) *
(1 - cos((lon2 - lon1) * p))/2
return 12742 * Math.asin(Math.sqrt(a)) // 2 * R; R = 6371 km
}
let Distance = ({from,to}) => {
let distance = getDistance(
from.latitude,
from.longitude,
to.latitude,
to.longitude)
if (distance < 1.0) {
distance = (distance * 100) + " m"
} else {
distance = distance.toFixed(2) + " km"
}
return <span>{distance}</span>
}
Distance.displayName = "Distance"
export default Distance
import { Component } from "react"
let instances = []
let watchId = 0
let lastPosition = null
export const LocationAware = ComposedComponent => class extends Component {
constructor(props) {
super(props)
this.updatePosition = this.updatePosition.bind(this)
this.onError = this.onError.bind(this)
this.state = {
currentLocation: null
}
if (lastPosition) {
this.updatePosition(lastPosition)
} else {
navigator.geolocation.getCurrentPosition(this.updatePosition, this.onError)
}
}
onError(e) {
// Maybe if e.code === PERMISSION_DENIED(1) then render a little "location"
// icon instead and handle clicks on it to re-aquire the users permission
// to read geo-location
console.error(e)
}
updatePosition(position) {
console.debug(`LocationAware:updatePosition(${position})`)
if (position) {
lastPosition = position
this.setState({currentLocation: position.coords})
}
}
componentDidMount() {
if (instances.length === 0) {
watchId = navigator.geolocation.watchPosition(position => {
instances.map(instance => instance.updatePosition(position))
}, e => {
instances.map(instance => instance.onError(e))
})
}
instances.push(this)
}
componentWillUnmount() {
instances.remove(this)
if (instances.length === 0) {
navigator.geolocation.clearWatch(watchId)
}
}
render() {
return this.state.currentLocation ? <ComposedComponent {...this.props} currentLocation={this.state.currentLocation}/> : null
}
}
import Distance from "./Distance"
import LocationAware from "./LocationAware"
let TrackingDistance = (props) => (
<Distance from={props.currentLocation} to={props.to}/>
)
TrackingDistance.displayName = "TrackingDistance"
export default TrackingDistance = LocationAware(TrackingDistance)
@pke
Copy link
Author

pke commented Feb 3, 2016

And can be used like this:

<TrackingDistance to={{longitude: 6.7666667, latitude: 51.2166667}}/>
Component will display the approx. distance between the current location and to in metric units. Changes in geo-location are instantly reflected in the components output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment