diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8f5098..8aefcbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# `react-native-maps-directions` Changelog
+## 1.9.1 – 2023-03-15
+
+- Add `avoidTolls` option;
+- Add `avoidHighways` option;
+- Add `avoidFerries` option;
+
## 1.9.0 – 2022-07-27
- Fix: `timePrecision` prop type to `MapViewDirectionsTimePrecision`
@@ -46,7 +52,6 @@
- Return “Duration with traffic” as `duration`
- Return `fare` in `onReady`
-
## 1.6.0 - 2018-03-09
- Add `directionsServiceBaseUrl` prop to allow customisation of service to use.
diff --git a/README.md b/README.md
index 63e636d..2603c6c 100644
--- a/README.md
+++ b/README.md
@@ -56,22 +56,26 @@ Once the directions in between `destination` and `origin` has been fetched, a `M
### Props
-| Prop | Type | Default | Note
-|---|---|---|---|
-| `origin` | `LatLng` or `String` | | The origin location to start routing from.
-| `destination` | `LatLng` or `String` | | The destination location to start routing to.
-| `apikey` | `String` | | Your Google Maps API Key _(request one [here](https://developers.google.com/maps/documentation/directions/get-api-key); if you're using an existing Google Maps API Key make sure you've enabled the Google Maps Directions API for that key using the [Google API Console](https://console.developers.google.com/apis/) by hitting the “Enable APIs and Services“ button)_.
-| `waypoints` | [`LatLng` or `String`] | `[]` | Array of waypoints to use between origin and destination.
-| `language` | `String` | `"en"` | The language to use when calculating directions. See [here](https://developers.google.com/maps/documentation/javascript/localization) for more info.
-| `mode` | `String` | `"DRIVING"` | Which transportation mode to use when calculating directions. Allowed values are `"DRIVING"`, `"BICYCLING"`, `"WALKING"`, and `"TRANSIT"`. _(See [here](https://developers.google.com/maps/documentation/javascript/examples/directions-travel-modes) for more info)_.
-| `resetOnChange` | `boolean` | `true` | Tweak if the rendered `MapView.Polyline` should reset or not when calculating the route between `origin` and `destionation`. Set to `false` if you see the directions line glitching.
-| `optimizeWaypoints` | `boolean` | `false` | Set it to true if you would like Google Maps to re-order all the waypoints to optimize the route for the fastest route. Please be aware that if this option is enabled, you will be billed at a higher rate by Google as stated [here](https://developers.google.com/maps/documentation/javascript/directions#Waypoints).
-| `splitWaypoints` | `boolean` | `false` | Directions API has a [limit](https://developers.google.com/maps/documentation/directions/usage-and-billing#directions-advanced) of 10 or 25 (depends on the billing plan) waypoints per route. When exceeding this limit you will be billed at a higher reate by Google. Set this to `true` if you would like to automatically split waypoints into multiple routes, thus bypassing this waypoints limit.
-| `directionsServiceBaseUrl` | `string` | _(Google's)_ | Base URL of the Directions Service (API) you are using. By default the Google Directions API is used (`"https://maps.googleapis.com/maps/api/directions/json"`). Usually you won't need to change this.
-| `region` | `String` | | If you are using strings for **origin** or **destination**, sometimes you will get an incorrect route because Google Maps API needs the region where this places belong to. See [here](https://developers.google.com/maps/documentation/javascript/localization#Region) for more info.
-| `precision` | `String` | `"low"` | The precision level of detail of the drawn polyline. Allowed values are "high", and "low". Setting to "low" will yield a polyline that is an approximate (smoothed) path of the resulting directions. Setting to "high" may cause a hit in performance in case a complex route is returned.
-| `timePrecision` | `String` | `"none"` | The timePrecision to get Realtime traffic info. Allowed values are "none", and "now". Defaults to "none".
-| `channel` | `String` | `null` | If you include the channel parameter in your requests, you can generate a Successful Requests report that shows a breakdown of your application's API requests across different applications that use the same client ID (such as externally facing access vs. internally facing access).
+| Prop | Type | Default | Note |
+| -------------------------- | ---------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `origin` | `LatLng` or `String` | | The origin location to start routing from. |
+| `destination` | `LatLng` or `String` | | The destination location to start routing to. |
+| `apikey` | `String` | | Your Google Maps API Key _(request one [here](https://developers.google.com/maps/documentation/directions/get-api-key); if you're using an existing Google Maps API Key make sure you've enabled the Google Maps Directions API for that key using the [Google API Console](https://console.developers.google.com/apis/) by hitting the “Enable APIs and Services“ button)_. |
+| `waypoints` | [`LatLng` or `String`] | `[]` | Array of waypoints to use between origin and destination. |
+| `language` | `String` | `"en"` | The language to use when calculating directions. See [here](https://developers.google.com/maps/documentation/javascript/localization) for more info. |
+| `mode` | `String` | `"DRIVING"` | Which transportation mode to use when calculating directions. Allowed values are `"DRIVING"`, `"BICYCLING"`, `"WALKING"`, and `"TRANSIT"`. _(See [here](https://developers.google.com/maps/documentation/javascript/examples/directions-travel-modes) for more info)_. |
+| `resetOnChange` | `boolean` | `true` | Tweak if the rendered `MapView.Polyline` should reset or not when calculating the route between `origin` and `destionation`. Set to `false` if you see the directions line glitching. |
+| `optimizeWaypoints` | `boolean` | `false` | Set it to true if you would like Google Maps to re-order all the waypoints to optimize the route for the fastest route. Please be aware that if this option is enabled, you will be billed at a higher rate by Google as stated [here](https://developers.google.com/maps/documentation/javascript/directions#Waypoints). |
+| `splitWaypoints` | `boolean` | `false` | Directions API has a [limit](https://developers.google.com/maps/documentation/directions/usage-and-billing#directions-advanced) of 10 or 25 (depends on the billing plan) waypoints per route. When exceeding this limit you will be billed at a higher reate by Google. Set this to `true` if you would like to automatically split waypoints into multiple routes, thus bypassing this waypoints limit. |
+| `directionsServiceBaseUrl` | `string` | _(Google's)_ | Base URL of the Directions Service (API) you are using. By default the Google Directions API is used (`"https://maps.googleapis.com/maps/api/directions/json"`). Usually you won't need to change this. |
+| `region` | `String` | | If you are using strings for **origin** or **destination**, sometimes you will get an incorrect route because Google Maps API needs the region where this places belong to. See [here](https://developers.google.com/maps/documentation/javascript/localization#Region) for more info. |
+| `precision` | `String` | `"low"` | The precision level of detail of the drawn polyline. Allowed values are "high", and "low". Setting to "low" will yield a polyline that is an approximate (smoothed) path of the resulting directions. Setting to "high" may cause a hit in performance in case a complex route is returned. |
+| `timePrecision` | `String` | `"none"` | The timePrecision to get Realtime traffic info. Allowed values are "none", and "now". Defaults to "none". |
+| `channel` | `String` | `null` | If you include the channel parameter in your requests, you can generate a Successful Requests report that shows a breakdown of your application's API requests across different applications that use the same client ID (such as externally facing access vs. internally facing access). |
+| `avoidTolls` | `boolean` | `false` | Set to `true` if you want direction routing to avoid Tollways when possible. Can be combined with other 'avoid' parameters. There might be instances that setting `avoidTolls` may be anough to avoid directions passing both tollways and highways, so just test which parameters apply to your application. |
+| `avoidHighways` | `boolean` | `false` | Set to `true` if you want direction routing to avoid Highways when possible. |
+| `avoidFerries` | `boolean` | `false` | Set to `true` if you want direction routing to avoid Ferries when possible. |
+
#### More props
Since the result rendered on screen is a `MapView.Polyline` component, all [`MapView.Polyline` props](https://github.com/airbnb/react-native-maps/blob/master/docs/polyline.md#props) – except for `coordinates` – are also accepted.
@@ -93,7 +97,7 @@ Since the result rendered on screen is a `MapView.Polyline` component, all [`Map
The values for the `origin` and `destination` props can take several forms. They can either be:
- Coordinates in the form of an object with `latitude` and `longitude` keys
-- Coordinates in the form of a string with `latitude` and `longitude` values separated by a comma
+- Coordinates in the form of a string with `latitude` and `longitude` values separated by a comma
- Strings representing an address
- Strings representing a location
- Strings containing a Place Id from the Google Maps Place API prefixed with `place_id:`
@@ -114,35 +118,34 @@ Tip: Don't forget to tweak the `language` prop when using localized location nam
### Events/Callbacks
-| Event Name | Returns | Notes
-|---|---|---|
-| `onStart` | `{ origin, destination, waypoints: [] }` | Callback that is called when the routing has started.
-| `onReady` | `{ distance: Number, duration: Number, coordinates: [], fare: Object, waypointOrder: [[]] }` | Callback that is called when the routing has succesfully finished. Note: distance returned in kilometers and duration in minutes.
-| `onError` | `errorMessage` | Callback that is called in case the routing has failed.
+| Event Name | Returns | Notes |
+| ---------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
+| `onStart` | `{ origin, destination, waypoints: [] }` | Callback that is called when the routing has started. |
+| `onReady` | `{ distance: Number, duration: Number, coordinates: [], fare: Object, waypointOrder: [[]] }` | Callback that is called when the routing has succesfully finished. Note: distance returned in kilometers and duration in minutes. |
+| `onError` | `errorMessage` | Callback that is called in case the routing has failed. |
## Extended Example
This example will draw a route between AirBnB's Office and Apple's HQ
```js
-import React, { Component } from 'react';
-import { Dimensions, StyleSheet } from 'react-native';
-import MapView from 'react-native-maps';
-import MapViewDirections from 'react-native-maps-directions';
+import React, { Component } from 'react'
+import { Dimensions, StyleSheet } from 'react-native'
+import MapView from 'react-native-maps'
+import MapViewDirections from 'react-native-maps-directions'
-const { width, height } = Dimensions.get('window');
-const ASPECT_RATIO = width / height;
-const LATITUDE = 37.771707;
-const LONGITUDE = -122.4053769;
-const LATITUDE_DELTA = 0.0922;
-const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
+const { width, height } = Dimensions.get('window')
+const ASPECT_RATIO = width / height
+const LATITUDE = 37.771707
+const LONGITUDE = -122.4053769
+const LATITUDE_DELTA = 0.0922
+const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO
-const GOOGLE_MAPS_APIKEY = '…';
+const GOOGLE_MAPS_APIKEY = '…'
class Example extends Component {
-
constructor(props) {
- super(props);
+ super(props)
// AirBnB's Office, and Apple Park
this.state = {
@@ -156,18 +159,15 @@ class Example extends Component {
longitude: -122.4053769,
},
],
- };
+ }
- this.mapView = null;
+ this.mapView = null
}
onMapPress = (e) => {
this.setState({
- coordinates: [
- ...this.state.coordinates,
- e.nativeEvent.coordinate,
- ],
- });
+ coordinates: [...this.state.coordinates, e.nativeEvent.coordinate],
+ })
}
render() {
@@ -180,48 +180,59 @@ class Example extends Component {
longitudeDelta: LONGITUDE_DELTA,
}}
style={StyleSheet.absoluteFill}
- ref={c => this.mapView = c}
+ ref={(c) => (this.mapView = c)}
onPress={this.onMapPress}
>
- {this.state.coordinates.map((coordinate, index) =>
+ {this.state.coordinates.map((coordinate, index) => (
- )}
- {(this.state.coordinates.length >= 2) && (
+ ))}
+ {this.state.coordinates.length >= 2 && (
2) ? this.state.coordinates.slice(1, -1): undefined}
- destination={this.state.coordinates[this.state.coordinates.length-1]}
+ waypoints={
+ this.state.coordinates.length > 2
+ ? this.state.coordinates.slice(1, -1)
+ : undefined
+ }
+ destination={
+ this.state.coordinates[this.state.coordinates.length - 1]
+ }
apikey={GOOGLE_MAPS_APIKEY}
strokeWidth={3}
- strokeColor="hotpink"
+ strokeColor='hotpink'
optimizeWaypoints={true}
onStart={(params) => {
- console.log(`Started routing between "${params.origin}" and "${params.destination}"`);
+ console.log(
+ `Started routing between "${params.origin}" and "${params.destination}"`
+ )
}}
- onReady={result => {
+ onReady={(result) => {
console.log(`Distance: ${result.distance} km`)
console.log(`Duration: ${result.duration} min.`)
this.mapView.fitToCoordinates(result.coordinates, {
edgePadding: {
- right: (width / 20),
- bottom: (height / 20),
- left: (width / 20),
- top: (height / 20),
- }
- });
+ right: width / 20,
+ bottom: height / 20,
+ left: width / 20,
+ top: height / 20,
+ },
+ })
}}
onError={(errorMessage) => {
// console.log('GOT AN ERROR');
}}
+ avoidTolls={true}
+ avoidFerries={true}
+ avoidHighways={true}
/>
)}
- );
+ )
}
}
-export default Example;
+export default Example
```
## Example App
diff --git a/index.d.ts b/index.d.ts
index b19991a..62f7d0c 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -1,4 +1,4 @@
-declare module "react-native-maps-directions" {
+declare module 'react-native-maps-directions' {
// Type definitions for react-native-maps-directions 1.6
// Project: https://github.com/bramus/react-native-maps-directions
// Definitions by: Ali Oguzhan Yildiz
@@ -6,153 +6,85 @@ declare module "react-native-maps-directions" {
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.3
- import * as React from "react";
-
- export type MapDirectionsLegs =[
- {
- distance: {
- text: string,
- value: number
- },
- duration: {
- text: string,
- value: number
- },
- end_address: string,
- end_location: {
- lat: number,
- lng: number
- },
- start_address: string,
- start_location: {
- lat: number,
- lng: number
- },
- steps: [{
- distance: {
- text: string,
- value: number
- },
- duration: {
- text: string,
- value: number
- },
- end_location: {
- lat: number,
- lng: number
- },
- start_location: {
- lat: number,
- lng: number
- },
- html_instructions: string,
- polyline: {
- points: string
- },
- travel_mode: string,
- maneuver: string | undefined
- }],
- traffic_speed_entry: [],
- via_waypoint: [],
- }]
-
- export type MapDirectionsResponse = {
- coordinates: [
- {
- latitude: number,
- longitude: number
- }],
- distance: number,
- duration: number,
- fares: [],
- legs: MapDirectionsLegs,
- waypointOrder: [[number]]
- }
-
-
-
+ import * as React from 'react'
export type MapViewDirectionsOrigin =
| string
| {
- latitude: number;
- longitude: number;
- };
+ latitude: number
+ longitude: number
+ }
export type MapViewDirectionsWaypoints =
| string
| {
- latitude: number;
- longitude: number;
- };
+ latitude: number
+ longitude: number
+ }
export type MapViewDirectionsDestination =
| string
| {
- latitude: number;
- longitude: number;
- };
+ latitude: number
+ longitude: number
+ }
export type MapViewDirectionsMode =
- | "DRIVING"
- | "BICYCLING"
- | "TRANSIT"
- | "WALKING";
+ | 'DRIVING'
+ | 'BICYCLING'
+ | 'TRANSIT'
+ | 'WALKING'
- export type MapViewDirectionsPrecision =
- | "high"
- | "low";
+ export type MapViewDirectionsPrecision = 'high' | 'low'
- export type MapViewDirectionsTimePrecision =
- | "now"
- | "none";
+ export type MapViewDirectionsTimePrecision = 'now' | 'none'
export interface MapViewDirectionsProps {
/**
* The origin location to start routing from.
*/
- origin?: MapViewDirectionsOrigin;
+ origin?: MapViewDirectionsOrigin
/**
* Array of waypoints to use between origin and destination.
*/
- waypoints?: MapViewDirectionsWaypoints[];
+ waypoints?: MapViewDirectionsWaypoints[]
/**
* The destination location to start routing to.
*/
- destination?: MapViewDirectionsDestination;
+ destination?: MapViewDirectionsDestination
/**
* Your Google Maps API Key
*/
- apikey: string;
+ apikey: string
/**
* Callback that is called when the routing has started.
*/
- onStart?: (...args: any[]) => any;
+ onStart?: (...args: any[]) => any
/**
* Callback that is called when the routing has succesfully finished.
*/
- onReady?: (...args: MapDirectionsResponse[]) => any;
+ onReady?: (...args: any[]) => any
/**
* Callback that is called in case the routing has failed.
*/
- onError?: (...args: any[]) => any;
+ onError?: (...args: any[]) => any
/**
* Which transportation mode to use when calculating directions.
* Allowed values are "DRIVING", "BICYCLING", "WALKING", and "TRANSIT".
*/
- mode?: MapViewDirectionsMode;
+ mode?: MapViewDirectionsMode
/**
* The precision to draw the polyline with.
* Allowed values are "high", and "low".
* Defaults to "low"
*/
- precision?: MapViewDirectionsPrecision;
+ precision?: MapViewDirectionsPrecision
/**
* The timePrecision to get Realtime traffic info.
* Allowed values are "none", and "now".
* Defaults to "none"
*/
- timePrecision?: MapViewDirectionsTimePrecision;
+ timePrecision?: MapViewDirectionsPrecision
/**
* If you include the channel parameter in your requests,
* you can generate a Successful Requests report that shows a breakdown
@@ -160,64 +92,64 @@ declare module "react-native-maps-directions" {
* use the same client ID (such as externally facing access vs. internally
* facing access).
*/
- channel?: string;
+ channel?: string
/**
* The language to use when calculating directions.
*/
- language?: string;
+ language?: string
/**
* Tweak if the rendered MapView. Polyline should reset or not
* when calculating the route between origin and destionation.
* Set to false if you see the directions line glitching.
*/
- resetOnChange?: boolean;
+ resetOnChange?: boolean
/**
* Set it to true if you would like Google Maps to re-order all the
* waypoints to optimize the route for the fastest route.
* Please be aware that if this option is enabled,
* you will be billed for a higher rate by Google
*/
- optimizeWaypoints?: boolean;
+ optimizeWaypoints?: boolean
/**
* Directions API has a limit of 10 or 25 (depends on the billing plan)
* waypoints per route. So waypoints array size is limited to those numbers.
* Set this to true if you would like to automatically split waypoints to
* multiple routes and by that avoid waypoints limit.
*/
- splitWaypoints?: boolean;
+ splitWaypoints?: boolean
/**
* Base URL of the Directions Service (API) you are using.
* By default the Google Directions API is used
* ("https://maps.googleapis.com/maps/api/directions/json").
* Usually you won't need to change this.
*/
- directionsServiceBaseUrl?: string;
+ directionsServiceBaseUrl?: string
/**
* If you are using strings for origin or destination,
* sometimes you will get an incorrect route because
* Google Maps API needs the region where this places belong to.
*/
- region?: string;
+ region?: string
/**
* @number
* The stroke width to use for the path - the line displayed
* by polyline between two navigation points.
* Default: 1
*/
- strokeWidth?: number;
+ strokeWidth?: number
/**
* @string
* The stroke color to use for the path.
* Default: "#000"
*/
- strokeColor?: string;
+ strokeColor?: string
/**
* @Array
* The stroke colors to use for the path (iOS only).
* Must be the same length as coordinates.
* Default: null
*/
- strokeColors?: Array;
+ strokeColors?: Array
/**
* @string
* The line cap style to apply to the open ends of the path.
@@ -225,14 +157,14 @@ declare module "react-native-maps-directions" {
* Note: lineCap is not yet supported for GoogleMaps provider on iOS.
* Default: "round"
*/
- lineCap?: string;
+ lineCap?: string
/**
* @string
* The line join style to apply to corners of the path.
* Possible values are miter, round or bevel.
* Default: "round"
*/
- lineJoin?: string;
+ lineJoin?: string
/**
* @number
* The limiting value that helps avoid spikes at junctions
@@ -243,7 +175,7 @@ declare module "react-native-maps-directions" {
* to a bevel join. The default miter limit is 10, which results in the
* conversion of miters whose angle at the joint is less than 11 degrees.
*/
- miterLimit?: number;
+ miterLimit?: number
/**
* @boolean
* Boolean to indicate whether to draw each segment of the line as a geodesic
@@ -251,7 +183,7 @@ declare module "react-native-maps-directions" {
* shortest path between two points on the Earth's surface.
* The geodesic curve is constructed assuming the Earth is a sphere.
*/
- geodesic?: boolean;
+ geodesic?: boolean
/**
* @number
* (iOS only) The offset (in points) at which to start drawing the
@@ -260,7 +192,7 @@ declare module "react-native-maps-directions" {
* the patter 5-2-3-2 would cause drawing to begin in the middle of the first gap.
* Default: 0
*/
- lineDashPhase?: number;
+ lineDashPhase?: number
/**
* @Array
* An array of numbers specifying the dash pattern to use for the path.
@@ -270,18 +202,30 @@ declare module "react-native-maps-directions" {
* followed by the second line segment length, and so on.
* Default: null
*/
- lineDashPattern?: Array;
+ lineDashPattern?: Array
/**
* @boolean
* Boolean to allow a polyline to be tappable and use the onPress function.
*/
- tappable?: boolean;
+ tappable?: boolean
+ /**
+ * Set it to true if you would like Google Maps to avoid tolls where possible.
+ */
+ avoidTolls?: boolean
+ /**
+ * Set it to true if you would like Google Maps to avoid highways where possible.
+ */
+ avoidHighways?: boolean
+ /**
+ * Set it to true if you would like Google Maps to avoid ferries where possible.
+ */
+ avoidFerries?: boolean
}
export default class MapViewDirections extends React.Component<
MapViewDirectionsProps,
any
> {
- render(): JSX.Element;
+ render(): JSX.Element
}
}
diff --git a/src/MapViewDirections.js b/src/MapViewDirections.js
index 8e05906..09bf5be 100644
--- a/src/MapViewDirections.js
+++ b/src/MapViewDirections.js
@@ -1,357 +1,429 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { Polyline } from 'react-native-maps';
-import isEqual from 'lodash.isequal';
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import MapView from 'react-native-maps'
+import isEqual from 'lodash.isequal'
-const WAYPOINT_LIMIT = 10;
+const WAYPOINT_LIMIT = 10
class MapViewDirections extends Component {
-
- constructor(props) {
- super(props);
-
- this.state = {
- coordinates: null,
- distance: null,
- duration: null,
- };
- }
-
- componentDidMount() {
- this.fetchAndRenderRoute(this.props);
- }
-
- componentDidUpdate(prevProps) {
- if (!isEqual(prevProps.origin, this.props.origin) || !isEqual(prevProps.destination, this.props.destination) || !isEqual(prevProps.waypoints, this.props.waypoints) || !isEqual(prevProps.mode, this.props.mode) || !isEqual(prevProps.precision, this.props.precision) || !isEqual(prevProps.splitWaypoints, this.props.splitWaypoints)) {
- if (this.props.resetOnChange === false) {
- this.fetchAndRenderRoute(this.props);
- } else {
- this.resetState(() => {
- this.fetchAndRenderRoute(this.props);
- });
- }
- }
- }
-
- resetState = (cb = null) => {
- this.setState({
- coordinates: null,
- distance: null,
- duration: null,
- }, cb);
- }
-
- decode(t) {
- let points = [];
- for (let step of t) {
- let encoded = step.polyline.points;
- let index = 0, len = encoded.length;
- let lat = 0, lng = 0;
- while (index < len) {
- let b, shift = 0, result = 0;
- do {
- b = encoded.charAt(index++).charCodeAt(0) - 63;
- result |= (b & 0x1f) << shift;
- shift += 5;
- } while (b >= 0x20);
-
- let dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
- lat += dlat;
- shift = 0;
- result = 0;
- do {
- b = encoded.charAt(index++).charCodeAt(0) - 63;
- result |= (b & 0x1f) << shift;
- shift += 5;
- } while (b >= 0x20);
- let dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
- lng += dlng;
-
- points.push({ latitude: (lat / 1E5), longitude: (lng / 1E5) });
- }
- }
- return points;
- }
-
- fetchAndRenderRoute = (props) => {
-
- let {
- origin: initialOrigin,
- destination: initialDestination,
- waypoints: initialWaypoints = [],
- apikey,
- onStart,
- onReady,
- onError,
- mode = 'DRIVING',
- language = 'en',
- optimizeWaypoints,
- splitWaypoints,
- directionsServiceBaseUrl = 'https://maps.googleapis.com/maps/api/directions/json',
- region,
- precision = 'low',
- timePrecision = 'none',
- channel,
- } = props;
-
- if (!apikey) {
- console.warn(`MapViewDirections Error: Missing API Key`); // eslint-disable-line no-console
- return;
- }
-
- if (!initialOrigin || !initialDestination) {
- return;
- }
-
- const timePrecisionString = timePrecision==='none' ? '' : timePrecision;
-
- // Routes array which we'll be filling.
- // We'll perform a Directions API Request for reach route
- const routes = [];
-
- // We need to split the waypoints in chunks, in order to not exceede the max waypoint limit
- // ~> Chunk up the waypoints, yielding multiple routes
- if (splitWaypoints && initialWaypoints && initialWaypoints.length > WAYPOINT_LIMIT) {
- // Split up waypoints in chunks with chunksize WAYPOINT_LIMIT
- const chunckedWaypoints = initialWaypoints.reduce((accumulator, waypoint, index) => {
- const numChunk = Math.floor(index / WAYPOINT_LIMIT);
- accumulator[numChunk] = [].concat((accumulator[numChunk] || []), waypoint);
- return accumulator;
- }, []);
-
- // Create routes for each chunk, using:
- // - Endpoints of previous chunks as startpoints for the route (except for the first chunk, which uses initialOrigin)
- // - Startpoints of next chunks as endpoints for the route (except for the last chunk, which uses initialDestination)
- for (let i = 0; i < chunckedWaypoints.length; i++) {
- routes.push({
- waypoints: chunckedWaypoints[i],
- origin: (i === 0) ? initialOrigin : chunckedWaypoints[i-1][chunckedWaypoints[i-1].length - 1],
- destination: (i === chunckedWaypoints.length - 1) ? initialDestination : chunckedWaypoints[i+1][0],
- });
- }
- }
-
- // No splitting of the waypoints is requested/needed.
- // ~> Use one single route
- else {
- routes.push({
- waypoints: initialWaypoints,
- origin: initialOrigin,
- destination: initialDestination,
- });
- }
-
- // Perform a Directions API Request for each route
- Promise.all(routes.map((route, index) => {
- let {
- origin,
- destination,
- waypoints,
- } = route;
-
- if (origin.latitude && origin.longitude) {
- origin = `${origin.latitude},${origin.longitude}`;
- }
-
- if (destination.latitude && destination.longitude) {
- destination = `${destination.latitude},${destination.longitude}`;
- }
-
- waypoints = waypoints
- .map(waypoint => (waypoint.latitude && waypoint.longitude) ? `${waypoint.latitude},${waypoint.longitude}` : waypoint)
- .join('|');
-
- if (optimizeWaypoints) {
- waypoints = `optimize:true|${waypoints}`;
- }
-
- if (index === 0) {
- onStart && onStart({
- origin,
- destination,
- waypoints: initialWaypoints,
- });
- }
-
- return (
- this.fetchRoute(directionsServiceBaseUrl, origin, waypoints, destination, apikey, mode, language, region, precision, timePrecisionString, channel)
- .then(result => {
- return result;
- })
- .catch(errorMessage => {
- return Promise.reject(errorMessage);
- })
- );
- })).then(results => {
- // Combine all Directions API Request results into one
- const result = results.reduce((acc, { distance, duration, coordinates, fare, legs, waypointOrder }) => {
- acc.coordinates = [
- ...acc.coordinates,
- ...coordinates,
- ];
- acc.distance += distance;
- acc.duration += duration;
- acc.fares = [
- ...acc.fares,
- fare,
- ];
- acc.legs = legs;
- acc.waypointOrder = [
- ...acc.waypointOrder,
- waypointOrder,
- ];
-
- return acc;
- }, {
- coordinates: [],
- distance: 0,
- duration: 0,
- fares: [],
- legs: [],
- waypointOrder: [],
- });
-
- // Plot it out and call the onReady callback
- this.setState({
- coordinates: result.coordinates,
- }, function() {
- if (onReady) {
- onReady(result);
- }
- });
- })
- .catch(errorMessage => {
- this.resetState();
- console.warn(`MapViewDirections Error: ${errorMessage}`); // eslint-disable-line no-console
- onError && onError(errorMessage);
- });
- }
-
- fetchRoute(directionsServiceBaseUrl, origin, waypoints, destination, apikey, mode, language, region, precision, timePrecision, channel) {
-
- // Define the URL to call. Only add default parameters to the URL if it's a string.
- let url = directionsServiceBaseUrl;
- if (typeof (directionsServiceBaseUrl) === 'string') {
- url += `?origin=${origin}&waypoints=${waypoints}&destination=${destination}&key=${apikey}&mode=${mode.toLowerCase()}&language=${language}®ion=${region}`;
- if(timePrecision){
- url+=`&departure_time=${timePrecision}`;
- }
- if(channel){
- url+=`&channel=${channel}`;
- }
- }
-
- return fetch(url)
- .then(response => response.json())
- .then(json => {
-
- if (json.status !== 'OK') {
- const errorMessage = json.error_message || json.status || 'Unknown error';
- return Promise.reject(errorMessage);
- }
-
- if (json.routes.length) {
-
- const route = json.routes[0];
-
- return Promise.resolve({
- distance: route.legs.reduce((carry, curr) => {
- return carry + curr.distance.value;
- }, 0) / 1000,
- duration: route.legs.reduce((carry, curr) => {
- return carry + (curr.duration_in_traffic ? curr.duration_in_traffic.value : curr.duration.value);
- }, 0) / 60,
- coordinates: (
- (precision === 'low') ?
- this.decode([{polyline: route.overview_polyline}]) :
- route.legs.reduce((carry, curr) => {
- return [
- ...carry,
- ...this.decode(curr.steps),
- ];
- }, [])
- ),
- fare: route.fare,
- waypointOrder: route.waypoint_order,
- legs: route.legs,
- });
-
- } else {
- return Promise.reject();
- }
- })
- .catch(err => {
- return Promise.reject(`Error on GMAPS route request: ${err}`);
- });
- }
-
- render() {
- const { coordinates } = this.state;
-
- if (!coordinates) {
- return null;
- }
-
- const {
- origin, // eslint-disable-line no-unused-vars
- waypoints, // eslint-disable-line no-unused-vars
- splitWaypoints, // eslint-disable-line no-unused-vars
- destination, // eslint-disable-line no-unused-vars
- apikey, // eslint-disable-line no-unused-vars
- onReady, // eslint-disable-line no-unused-vars
- onError, // eslint-disable-line no-unused-vars
- mode, // eslint-disable-line no-unused-vars
- language, // eslint-disable-line no-unused-vars
- region, // eslint-disable-line no-unused-vars
- precision, // eslint-disable-line no-unused-vars
- ...props
- } = this.props;
-
- return (
-
- );
- }
-
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ coordinates: null,
+ distance: null,
+ duration: null,
+ }
+ }
+
+ componentDidMount() {
+ this.fetchAndRenderRoute(this.props)
+ }
+
+ componentDidUpdate(prevProps) {
+ if (
+ !isEqual(prevProps.origin, this.props.origin) ||
+ !isEqual(prevProps.destination, this.props.destination) ||
+ !isEqual(prevProps.waypoints, this.props.waypoints) ||
+ !isEqual(prevProps.mode, this.props.mode) ||
+ !isEqual(prevProps.precision, this.props.precision) ||
+ !isEqual(prevProps.splitWaypoints, this.props.splitWaypoints) ||
+ !isEqual(prevProps.avoidTolls, this.props.avoidTolls) ||
+ !isEqual(prevProps.avoidHighways, this.props.avoidHighways) ||
+ !isEqual(prevProps.avoidFerries, this.props.avoidFerries)
+ ) {
+ if (this.props.resetOnChange === false) {
+ this.fetchAndRenderRoute(this.props)
+ } else {
+ this.resetState(() => {
+ this.fetchAndRenderRoute(this.props)
+ })
+ }
+ }
+ }
+
+ resetState = (cb = null) => {
+ this.setState(
+ {
+ coordinates: null,
+ distance: null,
+ duration: null,
+ },
+ cb
+ )
+ }
+
+ decode(t) {
+ let points = []
+ for (let step of t) {
+ let encoded = step.polyline.points
+ let index = 0,
+ len = encoded.length
+ let lat = 0,
+ lng = 0
+ while (index < len) {
+ let b,
+ shift = 0,
+ result = 0
+ do {
+ b = encoded.charAt(index++).charCodeAt(0) - 63
+ result |= (b & 0x1f) << shift
+ shift += 5
+ } while (b >= 0x20)
+
+ let dlat = (result & 1) != 0 ? ~(result >> 1) : result >> 1
+ lat += dlat
+ shift = 0
+ result = 0
+ do {
+ b = encoded.charAt(index++).charCodeAt(0) - 63
+ result |= (b & 0x1f) << shift
+ shift += 5
+ } while (b >= 0x20)
+ let dlng = (result & 1) != 0 ? ~(result >> 1) : result >> 1
+ lng += dlng
+
+ points.push({ latitude: lat / 1e5, longitude: lng / 1e5 })
+ }
+ }
+ return points
+ }
+
+ fetchAndRenderRoute = (props) => {
+ let {
+ origin: initialOrigin,
+ destination: initialDestination,
+ waypoints: initialWaypoints = [],
+ apikey,
+ onStart,
+ onReady,
+ onError,
+ mode = 'DRIVING',
+ language = 'en',
+ optimizeWaypoints,
+ splitWaypoints,
+ directionsServiceBaseUrl = 'https://maps.googleapis.com/maps/api/directions/json',
+ region,
+ precision = 'low',
+ timePrecision = 'none',
+ channel,
+ avoidTolls,
+ avoidHighways,
+ avoidFerries,
+ } = props
+
+ if (!apikey) {
+ console.warn(`MapViewDirections Error: Missing API Key`) // eslint-disable-line no-console
+ return
+ }
+
+ if (!initialOrigin || !initialDestination) {
+ return
+ }
+
+ const timePrecisionString = timePrecision === 'none' ? '' : timePrecision
+
+ // Routes array which we'll be filling.
+ // We'll perform a Directions API Request for reach route
+ const routes = []
+
+ // We need to split the waypoints in chunks, in order to not exceede the max waypoint limit
+ // ~> Chunk up the waypoints, yielding multiple routes
+ if (
+ splitWaypoints &&
+ initialWaypoints &&
+ initialWaypoints.length > WAYPOINT_LIMIT
+ ) {
+ // Split up waypoints in chunks with chunksize WAYPOINT_LIMIT
+ const chunckedWaypoints = initialWaypoints.reduce(
+ (accumulator, waypoint, index) => {
+ const numChunk = Math.floor(index / WAYPOINT_LIMIT)
+ accumulator[numChunk] = [].concat(
+ accumulator[numChunk] || [],
+ waypoint
+ )
+ return accumulator
+ },
+ []
+ )
+
+ // Create routes for each chunk, using:
+ // - Endpoints of previous chunks as startpoints for the route (except for the first chunk, which uses initialOrigin)
+ // - Startpoints of next chunks as endpoints for the route (except for the last chunk, which uses initialDestination)
+ for (let i = 0; i < chunckedWaypoints.length; i++) {
+ routes.push({
+ waypoints: chunckedWaypoints[i],
+ origin:
+ i === 0
+ ? initialOrigin
+ : chunckedWaypoints[i - 1][chunckedWaypoints[i - 1].length - 1],
+ destination:
+ i === chunckedWaypoints.length - 1
+ ? initialDestination
+ : chunckedWaypoints[i + 1][0],
+ })
+ }
+ }
+
+ // No splitting of the waypoints is requested/needed.
+ // ~> Use one single route
+ else {
+ routes.push({
+ waypoints: initialWaypoints,
+ origin: initialOrigin,
+ destination: initialDestination,
+ })
+ }
+
+ // Perform a Directions API Request for each route
+ Promise.all(
+ routes.map((route, index) => {
+ let { origin, destination, waypoints } = route
+
+ if (origin.latitude && origin.longitude) {
+ origin = `${origin.latitude},${origin.longitude}`
+ }
+
+ if (destination.latitude && destination.longitude) {
+ destination = `${destination.latitude},${destination.longitude}`
+ }
+
+ waypoints = waypoints
+ .map((waypoint) =>
+ waypoint.latitude && waypoint.longitude
+ ? `${waypoint.latitude},${waypoint.longitude}`
+ : waypoint
+ )
+ .join('|')
+
+ if (optimizeWaypoints) {
+ waypoints = `optimize:true|${waypoints}`
+ }
+
+ if (index === 0) {
+ onStart &&
+ onStart({
+ origin,
+ destination,
+ waypoints: initialWaypoints,
+ })
+ }
+
+ return this.fetchRoute(
+ directionsServiceBaseUrl,
+ origin,
+ waypoints,
+ destination,
+ apikey,
+ mode,
+ language,
+ region,
+ precision,
+ timePrecisionString,
+ channel,
+ avoidTolls,
+ avoidFerries,
+ avoidHighways
+ )
+ .then((result) => {
+ return result
+ })
+ .catch((errorMessage) => {
+ return Promise.reject(errorMessage)
+ })
+ })
+ )
+ .then((results) => {
+ // Combine all Directions API Request results into one
+ const result = results.reduce(
+ (acc, { distance, duration, coordinates, fare, waypointOrder }) => {
+ acc.coordinates = [...acc.coordinates, ...coordinates]
+ acc.distance += distance
+ acc.duration += duration
+ acc.fares = [...acc.fares, fare]
+ acc.waypointOrder = [...acc.waypointOrder, waypointOrder]
+
+ return acc
+ },
+ {
+ coordinates: [],
+ distance: 0,
+ duration: 0,
+ fares: [],
+ waypointOrder: [],
+ }
+ )
+
+ // Plot it out and call the onReady callback
+ this.setState(
+ {
+ coordinates: result.coordinates,
+ },
+ function () {
+ if (onReady) {
+ onReady(result)
+ }
+ }
+ )
+ })
+ .catch((errorMessage) => {
+ this.resetState()
+ console.warn(`MapViewDirections Error: ${errorMessage}`) // eslint-disable-line no-console
+ onError && onError(errorMessage)
+ })
+ }
+
+ fetchRoute(
+ directionsServiceBaseUrl,
+ origin,
+ waypoints,
+ destination,
+ apikey,
+ mode,
+ language,
+ region,
+ precision,
+ timePrecision,
+ channel,
+ avoidTolls,
+ avoidFerries,
+ avoidHighways
+ ) {
+ // Define the URL to call. Only add default parameters to the URL if it's a string.
+ let url = directionsServiceBaseUrl
+ if (typeof directionsServiceBaseUrl === 'string') {
+ url += `?origin=${origin}&waypoints=${waypoints}&destination=${destination}&key=${apikey}&mode=${mode.toLowerCase()}&language=${language}®ion=${region}`
+ if (timePrecision) {
+ url += `&departure_time=${timePrecision}`
+ }
+ if (channel) {
+ url += `&channel=${channel}`
+ }
+ if (avoidTolls) {
+ url += `&avoid=tolls`
+ }
+ if (avoidHighways) {
+ url += `&avoid=highways`
+ }
+ if (avoidFerries) {
+ url += `&avoid=ferries`
+ }
+ }
+
+ return fetch(url)
+ .then((response) => response.json())
+ .then((json) => {
+ if (json.status !== 'OK') {
+ const errorMessage =
+ json.error_message || json.status || 'Unknown error'
+ return Promise.reject(errorMessage)
+ }
+
+ if (json.routes.length) {
+ const route = json.routes[0]
+
+ return Promise.resolve({
+ distance:
+ route.legs.reduce((carry, curr) => {
+ return carry + curr.distance.value
+ }, 0) / 1000,
+ duration:
+ route.legs.reduce((carry, curr) => {
+ return (
+ carry +
+ (curr.duration_in_traffic
+ ? curr.duration_in_traffic.value
+ : curr.duration.value)
+ )
+ }, 0) / 60,
+ coordinates:
+ precision === 'low'
+ ? this.decode([{ polyline: route.overview_polyline }])
+ : route.legs.reduce((carry, curr) => {
+ return [...carry, ...this.decode(curr.steps)]
+ }, []),
+ fare: route.fare,
+ waypointOrder: route.waypoint_order,
+ })
+ } else {
+ return Promise.reject()
+ }
+ })
+ .catch((err) => {
+ return Promise.reject(`Error on GMAPS route request: ${err}`)
+ })
+ }
+
+ render() {
+ const { coordinates } = this.state
+
+ if (!coordinates) {
+ return null
+ }
+
+ const {
+ origin, // eslint-disable-line no-unused-vars
+ waypoints, // eslint-disable-line no-unused-vars
+ splitWaypoints, // eslint-disable-line no-unused-vars
+ destination, // eslint-disable-line no-unused-vars
+ apikey, // eslint-disable-line no-unused-vars
+ onReady, // eslint-disable-line no-unused-vars
+ onError, // eslint-disable-line no-unused-vars
+ mode, // eslint-disable-line no-unused-vars
+ language, // eslint-disable-line no-unused-vars
+ region, // eslint-disable-line no-unused-vars
+ precision, // eslint-disable-line no-unused-vars
+ avoidFerries, // eslint-disable-line no-unused-vars
+ avoidHighways, // eslint-disable-line no-unused-vars
+ avoidTolls, // eslint-disable-line no-unused-vars
+ ...props
+ } = this.props
+
+ return
+ }
}
MapViewDirections.propTypes = {
- origin: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.shape({
- latitude: PropTypes.number.isRequired,
- longitude: PropTypes.number.isRequired,
- }),
- ]),
- waypoints: PropTypes.arrayOf(
- PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.shape({
- latitude: PropTypes.number.isRequired,
- longitude: PropTypes.number.isRequired,
- }),
- ]),
- ),
- destination: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.shape({
- latitude: PropTypes.number.isRequired,
- longitude: PropTypes.number.isRequired,
- }),
- ]),
- apikey: PropTypes.string.isRequired,
- onStart: PropTypes.func,
- onReady: PropTypes.func,
- onError: PropTypes.func,
- mode: PropTypes.oneOf(['DRIVING', 'BICYCLING', 'TRANSIT', 'WALKING']),
- language: PropTypes.string,
- resetOnChange: PropTypes.bool,
- optimizeWaypoints: PropTypes.bool,
- splitWaypoints: PropTypes.bool,
- directionsServiceBaseUrl: PropTypes.string,
- region: PropTypes.string,
- precision: PropTypes.oneOf(['high', 'low']),
- timePrecision: PropTypes.oneOf(['now', 'none']),
- channel: PropTypes.string,
-};
-
-export default MapViewDirections;
+ origin: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.shape({
+ latitude: PropTypes.number.isRequired,
+ longitude: PropTypes.number.isRequired,
+ }),
+ ]),
+ waypoints: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.shape({
+ latitude: PropTypes.number.isRequired,
+ longitude: PropTypes.number.isRequired,
+ }),
+ ])
+ ),
+ destination: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.shape({
+ latitude: PropTypes.number.isRequired,
+ longitude: PropTypes.number.isRequired,
+ }),
+ ]),
+ apikey: PropTypes.string.isRequired,
+ onStart: PropTypes.func,
+ onReady: PropTypes.func,
+ onError: PropTypes.func,
+ mode: PropTypes.oneOf(['DRIVING', 'BICYCLING', 'TRANSIT', 'WALKING']),
+ language: PropTypes.string,
+ resetOnChange: PropTypes.bool,
+ optimizeWaypoints: PropTypes.bool,
+ splitWaypoints: PropTypes.bool,
+ directionsServiceBaseUrl: PropTypes.string,
+ region: PropTypes.string,
+ precision: PropTypes.oneOf(['high', 'low']),
+ timePrecision: PropTypes.oneOf(['now', 'none']),
+ channel: PropTypes.string,
+ avoidFerries: PropTypes.bool,
+ avoidTolls: PropTypes.bool,
+ avoidHighways: PropTypes.bool,
+}
+
+export default MapViewDirections