import React, { useCallback, useState } from 'react'
import PropTypes from 'prop-types'
import { connectToContext } from 'Provider'
import { Draw, DrawPin } from '../Draw'
import { VectorLayer } from '../classes'
import olSourceVector from 'ol/source/Vector'
import olFormatPolyline from 'ol/format/Polyline'
import { toLonLat } from 'ol/proj'
import olGeomLineString from 'ol/geom/LineString'
import { startPin, endPin, waypointPin, routeStyle } from './utils'
import { Container } from './styled'
const getDirections = async (locations, apiKey) => {
const waypoints = locations
const origin = waypoints.shift().reverse()
const destination = waypoints.pop().reverse()
const formatedWaypoints = waypoints.map(c => c.reverse()).join('|')
return fetch(`https://maps.googleapis.com/maps/api/directions/json?origin=${origin}&destination=${destination}&waypoints=${formatedWaypoints}&key=${apiKey}`)
.then(response => response.json())
.then(json => {
if (json.status === 'ZERO_RESULTS') {
throw new Error('No valid route found')
} else if (json.status === 'REQUEST_DENIED') {
throw new Error('The provided API key is invalid')
} else {
const route = json.routes?.[0]?.overview_polyline?.points
const format = new olFormatPolyline()
const routeFeature = format.readFeature(route, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' })
routeFeature.setProperties(json.routes?.[0])
routeFeature.set('title', json.routes?.[0].summary)
return routeFeature
}
})
}
export function getStepFeatures (feature) {
const legs = feature.getProperties().legs
const steps = legs.map(leg => leg.steps).flat()
const format = new olFormatPolyline()
return steps.map((step, i) => {
const stepRoute = step?.polyline?.points
const feature = format.readFeature(stepRoute, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' })
const formatedProps = { ...step }
for (const prop in step) {
const val = step[prop]
if (val?.text) formatedProps[prop] = val.text
}
feature.setProperties(formatedProps)
feature.set('title', `Step ${i}`)
return feature
})
}
/**
* A button for drawing two pin points for Google Directions.
* @component
* @category GoogleDirections
* @since 1.8.0
*/
function GoogleDirections (props) {
const { map, apiKey, translations } = props
const [coords, setCoords] = useState([])
const geometryFunctionCalback = useCallback(
(coordinates, geometry) => {
if (!geometry) return new olGeomLineString(coordinates)
geometry.setCoordinates(coordinates)
setCoords(coordinates)
return geometry
}
)
const onDrawBegin = (feature) => {
setCoords([])
feature.setStyle([waypointPin, startPin, endPin])
}
const onDrawFinish = async (feature) => {
const waypoints = feature.getGeometry().getCoordinates().map(coord => toLonLat(coord))
const route = await getDirections(waypoints, apiKey)
const routeLayer = new VectorLayer({
title: 'Google Directions - Overview',
source: new olSourceVector({
features: [route]
}),
style: [startPin, endPin, routeStyle]
})
const stepLayer = new VectorLayer({
title: 'Google Directions - Step by Step',
source: new olSourceVector({
features: getStepFeatures(route)
}),
style: [routeStyle]
})
map.addLayer(routeLayer)
map.addLayer(stepLayer)
setCoords([])
}
return (
<Container>
<p>
{!coords.length ? translations['_ol_kit.directions.placeOriginPoint'] : translations['_ol_kit.directions.placeWaypoint']}
</p>
<Draw
onDrawFinish={onDrawFinish}
onDrawBegin={onDrawBegin}
onDrawCancel={() => setCoords([])}
drawOpts={{ geometryFunction: geometryFunctionCalback, style: [waypointPin, startPin, endPin] }}
>
<DrawPin type={'LineString'} tooltipTitle={translations['_ol_kit.directions.waypointLabel']} />
</Draw>
</Container>
)
}
GoogleDirections.propTypes = {
/** reference to Openlayers map object */
map: PropTypes.object.isRequired,
/* Note that you will need to create an account with Google and get an API key. Be sure to turn on all location based permissions.
You can find instructions on how to do that here https://developers.google.com/places/web-service/intro */
apiKey: PropTypes.string.isRequired,
/** translations object */
translations: PropTypes.object
}
export default connectToContext(GoogleDirections)