├── .gitignore
├── README.md
├── assets
├── Navigation (react-native-maps-navigation).json
└── fonts
│ └── Navigation.ttf
├── docs
├── CloseButton.md
├── DirectionInputBox.md
├── DirectionsListView.md
├── DurationDistanceLabel.md
├── DurationDistanceView.md
├── ManeuverArrow.md
├── ManeuverLabel.md
├── ManeuverView.md
├── MapViewNavigation.md
├── PositionMarker.md
├── RouteMarker.md
├── RouterPolyline.md
├── TravelModeBox.md
└── preview.gif
├── index.js
├── package.json
└── src
├── components
├── CloseButton
│ ├── index.js
│ └── styles.js
├── DirectionsListView
│ ├── index.js
│ ├── item.js
│ └── styles.js
├── DurationDistanceLabel
│ ├── index.js
│ └── styles.js
├── DurationDistanceView
│ ├── index.js
│ └── styles.js
├── ManeuverArrow
│ ├── index.js
│ └── styles.js
├── ManeuverLabel
│ ├── index.js
│ └── styles.js
├── ManeuverView
│ ├── index.js
│ └── styles.js
├── MapViewNavigation
│ └── index.js
├── PositionMarker
│ ├── index.js
│ └── styles.js
├── RouteMarker
│ ├── index.js
│ └── styles.js
├── RoutePolyline
│ └── index.js
├── TravelModeBox
│ └── index.js
└── TravelModeLabel
│ ├── index.js
│ └── styles.js
├── constants
├── DirectionTypes.js
├── MarkerTypes.js
├── NavigationIcons.js
├── NavigationModes.js
├── PolylineTypes.js
├── PropTypes.js
├── TrapTypes.js
└── TravelModes.js
├── modules
├── Directions.js
├── Geocoder.js
├── Simulator.js
├── Tools.js
└── Traps.js
└── themes
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | package-lock.json
4 | yarn.lock
5 | .idea/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-maps-navigation
2 |
3 | Enhances React Native Maps with Realtime Navigation.
4 |
5 | `Please note that this module is usable but still under heavy development. Some properties and/or component names might change without notice.`
6 |
7 | 
8 |
9 |
10 | ## Installation
11 |
12 | **React Native >= 0.49**
13 |
14 | ```bash
15 | yarn add react-native-maps-navigation
16 | ```
17 |
18 | Make sure you link the module before building:
19 |
20 | ```bash
21 | react-native link react-native-maps-navigation
22 | ```
23 |
24 | ## Example
25 |
26 | [Head over to the example application to get started right away.](https://github.com/flyandi/react-native-maps-navigation-example)
27 |
28 | The example application uses most components and api modules of this library and gets you started in a useful direction.
29 |
30 |
31 | ## Components
32 |
33 | The library exposes the following modules and components.
34 |
35 | The main component is `MapViewNavigation`:
36 |
37 | [`` Component API](docs/MapViewNavigation.md)
38 |
39 |
40 | The library also ships with various UI components:
41 |
42 | [`` Component API](docs/DirectionInputBox.md)
43 |
44 | [`` Component API](docs/DirectionsListView.md)
45 |
46 | [`` Component API](docs/DurationDistanceLabel.md)
47 |
48 | [`` Component API](docs/DurationDistanceView.md)
49 |
50 | [`` Component API](docs/ManeuverView.md)
51 |
52 | [`` Component API](docs/ManeuverLabel.md)
53 |
54 | [`` Component API](docs/TravelModeBox.md)
55 |
56 | These are internal components used by the library:
57 |
58 | [`` Component API](docs/ManeuverArrow.md)
59 |
60 | [`` Component API](docs/CloseButton.md)
61 |
62 | [`` Component API](docs/PositionMarker.md)
63 |
64 | [`` Component API](docs/RouteMarker.md)
65 |
66 | [`` Component API](docs/RouterPolyline.md)
67 |
68 | ## General Usage
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/assets/fonts/Navigation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/assets/fonts/Navigation.ttf
--------------------------------------------------------------------------------
/docs/CloseButton.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/CloseButton.md
--------------------------------------------------------------------------------
/docs/DirectionInputBox.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/DirectionInputBox.md
--------------------------------------------------------------------------------
/docs/DirectionsListView.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/DirectionsListView.md
--------------------------------------------------------------------------------
/docs/DurationDistanceLabel.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/DurationDistanceLabel.md
--------------------------------------------------------------------------------
/docs/DurationDistanceView.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/DurationDistanceView.md
--------------------------------------------------------------------------------
/docs/ManeuverArrow.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/ManeuverArrow.md
--------------------------------------------------------------------------------
/docs/ManeuverLabel.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/ManeuverLabel.md
--------------------------------------------------------------------------------
/docs/ManeuverView.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/ManeuverView.md
--------------------------------------------------------------------------------
/docs/MapViewNavigation.md:
--------------------------------------------------------------------------------
1 | # `` Component API
2 |
3 | `MapViewNavigation` is the primary component in react-native-maps-navigation that contains the navigation manager.
4 |
5 | ## Props
6 |
7 | | Prop | Type | Default | Note |
8 | |---|---|---|---|
9 | | `apiKey` | `string` | (Required) | A Google API key that has the Direction API enabled. See below for more information.
10 | | `language` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
11 | | `map` | `function` | | A function that is called to obtain the map instances.
12 | | `origin` | `any` | | A string or object that identifies the origin
13 | | `destination` | `any` | | A string or object that identifies the destination
14 | | `maxZoom` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
15 | | `minZoom` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
16 | | `animationDuration` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
17 | | `navigationMode` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
18 | | `navigationViewingAngle` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
19 | | `navigationZoomLevel` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
20 | | `directionZoomQuantifier` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
21 | | `routeStepDistance` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
22 | | `routeStepInnerTolerance` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
23 | | `routeStepCenterTolerance` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
24 | | `routeStepCourseTolerance` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
25 | | `displayDebugMarkers` | `string` | | A language identifier passed to various submodules. This is useful to show directions in the specific countries language.
26 |
27 | ## Events
28 |
29 | | Prop | Type | Default | Note |
30 | |---|---|---|---|
31 | | `provider` | `string` | | The map framework to use.
Either `"google"` for GoogleMaps, otherwise `null` or `undefined` to use the native map framework (`MapKit` in iOS and `GoogleMaps` in android).
32 |
33 |
34 |
35 | ## Methods
36 |
37 | | Method Name | Arguments | Notes
38 | |---|---|---|
39 | | `navigateRoute` | `location: LatLng`, `bearing: Number`, `angle: Number`, `duration: Number` | Navigates the route
--------------------------------------------------------------------------------
/docs/PositionMarker.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/PositionMarker.md
--------------------------------------------------------------------------------
/docs/RouteMarker.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/RouteMarker.md
--------------------------------------------------------------------------------
/docs/RouterPolyline.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/RouterPolyline.md
--------------------------------------------------------------------------------
/docs/TravelModeBox.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/TravelModeBox.md
--------------------------------------------------------------------------------
/docs/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flyandi/react-native-maps-navigation/15e0133e46971f57e29d2aadfe6d7bd6fb10c2cb/docs/preview.gif
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import TravelModeBox from './src/components/TravelModeBox';
5 | import TravelModeLabel from './src/components/TravelModeLabel';
6 | import DirectionsListView from './src/components/DirectionsListView';
7 | import MapViewNavigation from './src/components/MapViewNavigation';
8 | import ManeuverView from './src/components/ManeuverView';
9 | import ManeuverArrow from './src/components/ManeuverArrow';
10 | import ManeuverLabel from './src/components/ManeuverLabel';
11 | import DurationDistanceView from './src/components/DurationDistanceView';
12 | import DurationDistanceLabel from './src/components/DurationDistanceLabel';
13 | import TravelIcons from './src/constants/NavigationIcons';
14 | import TravelModes from './src/constants/TravelModes';
15 | import NavigationModes from './src/constants/NavigationModes';
16 |
17 | import Geocoder from './src/modules/Geocoder';
18 |
19 |
20 |
21 |
22 |
23 | /**
24 | * @exports
25 | */
26 | export {
27 | DirectionsListView,
28 | ManeuverView,
29 | ManeuverArrow,
30 | ManeuverLabel,
31 | DurationDistanceView,
32 | DurationDistanceLabel,
33 | TravelModeBox,
34 | TravelModes,
35 | TravelIcons,
36 | TravelModeLabel,
37 | Geocoder,
38 | NavigationModes,
39 | };
40 |
41 | /**
42 | * @default export
43 | */
44 | export default MapViewNavigation;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-maps-navigation",
3 | "version": "0.0.14",
4 | "description": "A helper and component library for react-native-maps that enables navigation support",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/flyandi/react-native-maps-navigation.git"
9 | },
10 | "keywords": [
11 | "react",
12 | "native",
13 | "react-native",
14 | "react-native-maps",
15 | "maps",
16 | "navigation",
17 | "gps",
18 | "ios",
19 | "android",
20 | "react-component"
21 | ],
22 | "author": "Andy Schwarz (http://flyandi.net)",
23 | "license": {
24 | "type": "MIT",
25 | "url": "https://github.com/flyandi/react-native-maps-navigation/blob/master/LICENSE"
26 | },
27 | "bugs": {
28 | "url": "https://github.com/flyandi/react-native-maps-navigation/issues"
29 | },
30 | "scripts": {
31 | "test": "jest"
32 | },
33 | "jest": {
34 | "preset": "react-native"
35 | },
36 | "homepage": "https://github.com/flyandi/react-native-maps-navigation#readme",
37 | "peerDependencies": {
38 | "react": "*",
39 | "react-native": "*",
40 | "react-native-maps": ">=0.21.0"
41 | },
42 | "dependencies": {
43 | "geolib": "^3.0.2",
44 | "prop-types": "^15.6.2",
45 | "react-native-optiongroup": "^0.0.7"
46 | },
47 | "rnpm": {
48 | "assets": [
49 | "./assets/fonts"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/CloseButton/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { Text, TouchableOpacity } from 'react-native';
7 | import Styles from './styles';
8 | import NavigationIcons from "../../constants/NavigationIcons";
9 | import {DEFAULT_DIRECTION_TYPE} from "../../constants/DirectionTypes";
10 |
11 |
12 | /**
13 | * @component
14 | */
15 | export default class CloseButton extends Component {
16 |
17 | /**
18 | * propTypes
19 | * @type {}
20 | */
21 | static propTypes = {
22 | maneuver: PropTypes.object,
23 | size: PropTypes.number,
24 | opacity: PropTypes.number,
25 | color: PropTypes.any,
26 | }
27 |
28 | /**
29 | * defaultProps
30 | * @type {}
31 | */
32 | static defaultProps = {
33 | maneuver: undefined,
34 | size: 25,
35 | opacity: 1,
36 | color: '#000000',
37 | }
38 |
39 |
40 | /**
41 | * @constructor
42 | * @param props
43 | */
44 | constructor(props)
45 | {
46 | super(props);
47 | }
48 |
49 |
50 | /**
51 | * render
52 | * @returns {XML}
53 | */
54 | render()
55 | {
56 | const styles = Styles(this.props);
57 |
58 | return (
59 |
60 |
61 | {NavigationIcons.close}
62 |
63 |
64 | );
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/src/components/CloseButton/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @maneuverView
15 | */
16 | closeButtonText: {
17 | fontFamily: 'Navigation',
18 | fontSize: props.size,
19 | color: props.color,
20 | opacity: props.opacity,
21 | textAlign: 'center',
22 | }
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/DirectionsListView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { ScrollView, View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 | import DirectionListViewItem from './item';
9 |
10 | /**
11 | * @component
12 | */
13 | export default class DirectionsListView extends Component {
14 |
15 | /**
16 | * propTypes
17 | * @type {}
18 | */
19 | static propTypes = {
20 | route: PropTypes.any.isRequired,
21 | fontFamily: PropTypes.string,
22 | fontFamilyBold: PropTypes.string,
23 | showOriginDestinationHeader: PropTypes.bool,
24 | displayTravelMode: PropTypes.bool,
25 | }
26 |
27 | /**
28 | * defaultProps
29 | * @type {}
30 | */
31 | static defaultProps = {
32 | route: undefined,
33 | fontFamily: undefined,
34 | fontFamilyBold: undefined,
35 | showOriginDestinationHeader: true,
36 | displayTravelMode: false,
37 | }
38 |
39 |
40 | /**
41 | * @constructor
42 | * @param props
43 | */
44 | constructor(props)
45 | {
46 | super(props);
47 |
48 | }
49 |
50 |
51 | /**
52 | * render
53 | * @returns {XML}
54 | */
55 | render()
56 | {
57 | let index = 0;
58 |
59 | const steps = this.props.route ? this.props.route.steps : false;
60 |
61 | if(steps.constructor !== Array) return null;
62 |
63 | const styles = Styles(this.props);
64 |
65 | return (
66 |
67 |
68 | {!this.props.showOriginDestinationHeader ? null: (
69 |
70 |
71 |
72 | FROM
73 |
74 |
75 | {this.props.route.origin.address}
76 |
77 |
78 |
79 |
80 | TO
81 |
82 |
83 | {this.props.route.destination.address}
84 |
85 |
86 |
87 | )}
88 |
89 |
90 | {this.props.route.duration.text}
91 | {this.props.route.distance.text}
92 |
93 |
94 |
95 | {steps.map((step, index) => )}
96 |
97 |
98 | );
99 | }
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/src/components/DirectionsListView/item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 | import ManeuverArrow from '../ManeuverArrow';
9 | import ManeuverLabel from '../ManeuverLabel';
10 | import DurationDistanceLabel from '../DurationDistanceLabel';
11 |
12 | /**
13 | * @component
14 | */
15 | export default class DirectionListViewItem extends Component {
16 |
17 | /**
18 | * propTypes
19 | * @type {}
20 | */
21 | static propTypes = {
22 | instructions: PropTypes.string,
23 | distance: PropTypes.object,
24 | duration: PropTypes.object,
25 | maneuver: PropTypes.object,
26 | fontFamily: PropTypes.string,
27 | fontFamilyBold: PropTypes.string,
28 | fontSize: PropTypes.number,
29 | displayTravelMode: PropTypes.bool,
30 | }
31 |
32 | /**
33 | * defaultProps
34 | * @type {}
35 | */
36 | static defaultProps = {
37 | instructions: '',
38 | fontFamily: undefined,
39 | fontFamilyBold: undefined,
40 | distance: undefined,
41 | duration: undefined,
42 | maneuver: undefined,
43 | fontSize: undefined,
44 | displayTravelMode: false,
45 | }
46 |
47 |
48 | /**
49 | * @constructor
50 | * @param props
51 | */
52 | constructor(props)
53 | {
54 | super(props);
55 | }
56 |
57 | /**
58 | * render
59 | * @returns {XML}
60 | */
61 | render()
62 | {
63 | const styles = Styles(this.props);
64 |
65 | return (
66 |
67 |
68 |
72 |
73 |
74 |
77 |
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/src/components/DirectionsListView/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @directionDetailHeader
15 | */
16 | directionDetailHeader: {
17 | padding: 25,
18 | paddingBottom: 10,
19 | backgroundColor: '#f7f7f4',
20 | flexDirection: 'column'
21 | },
22 |
23 | directionDetailHeaderSection: {
24 | marginBottom: 15,
25 | flexDirection: 'column',
26 | },
27 |
28 | directionDetailHeaderAddressText: {
29 | fontSize: 16,
30 | fontFamily: props.fontFamily,
31 | },
32 |
33 | directionDetailHeaderAddressLabel: {
34 | fontSize: 13,
35 | opacity: 0.7,
36 | fontWeight: 'bold',
37 | fontFamily: props.fontFamily,
38 | },
39 |
40 | /**
41 | * @directionDetailTravel
42 | */
43 | directionDetailTravel: {
44 | margin: 25
45 | },
46 |
47 | directionDetailTravelDuration: {
48 | fontSize: 32,
49 | fontFamily: props.fontFamily,
50 | color: '#387bc1',
51 | },
52 |
53 | directionDetailTravelDistance: {
54 | fontSize: 22,
55 | fontFamily: props.fontFamily,
56 | opacity: 0.8
57 | },
58 |
59 | /**
60 | * @fonts
61 | */
62 |
63 | bold: {
64 | fontWeight: 'bold',
65 | fontFamily: props.fontFamilyBold || props.fontFamily,
66 | fontSize: 16,
67 | flexWrap: 'wrap',
68 | },
69 |
70 | regular: {
71 | fontFamily: props.fontFamily,
72 | fontSize: 16,
73 | flexWrap: 'wrap',
74 | },
75 |
76 | extra: {
77 | fontFamily: props.fontFamily,
78 | fontSize: 13,
79 | flexWrap: 'wrap',
80 | color: '#387bc1',
81 | },
82 |
83 | durationDistance: {
84 | fontFamily: props.fontFamily,
85 | fontSize: 14,
86 | opacity: 0.8,
87 | flexWrap: 'wrap',
88 | },
89 |
90 | /**
91 | * @directionDetail
92 | */
93 |
94 | directionDetailSectionContainer: {
95 | margin: 25,
96 | marginTop: 0,
97 | flex: 1
98 | },
99 |
100 | directionDetailSection: {
101 | borderColor: '#e6e6e6',
102 | borderTopWidth: 1,
103 | paddingTop: 20,
104 | marginBottom: 20,
105 | flexDirection: 'row',
106 | flex: 1,
107 | },
108 |
109 | directionDetailIconContainer: {
110 | width: 50,
111 | flex: 0,
112 | justifyContent: 'flex-start',
113 | alignItems: 'center'
114 | },
115 |
116 | directionDetailContent: {
117 | flexDirection: 'column',
118 | flex: 1,
119 | },
120 |
121 | });
122 |
--------------------------------------------------------------------------------
/src/components/DurationDistanceLabel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 | import {MODE_MAPPING} from "../../constants/TravelModes";
9 |
10 | /**
11 | * @component
12 | */
13 | export default class DurationDistanceLabel extends Component {
14 |
15 | /**
16 | * propTypes
17 | * @type {}
18 | */
19 | static propTypes = {
20 | style: PropTypes.any,
21 | instructions: PropTypes.string,
22 | fontFamily: PropTypes.string,
23 | fontSize: PropTypes.number,
24 | distance: PropTypes.object,
25 | duration: PropTypes.object,
26 | opacity: PropTypes.number,
27 | withTravelModeIcon: PropTypes.bool,
28 | }
29 |
30 | /**
31 | * defaultProps
32 | * @type {}
33 | */
34 | static defaultProps = {
35 | style: {},
36 | fontFamily: undefined,
37 | fontSize: 16,
38 | distance: undefined,
39 | duration: undefined,
40 | opacity: 0.8,
41 | withTravelModeIcon: false,
42 | }
43 |
44 |
45 | /**
46 | * @constructor
47 | * @param props
48 | */
49 | constructor(props)
50 | {
51 | super(props);
52 | }
53 |
54 | /**
55 | * render
56 | * @returns {XML}
57 | */
58 | render()
59 | {
60 | const styles = Styles(this.props);
61 |
62 | const travelMode = MODE_MAPPING[this.props.mode];
63 |
64 | return (
65 |
66 | {!this.props.withTravelModeIcon || !travelMode ? null : (
67 | {travelMode.icon}{' '}
68 | )}
69 | {this.props.distance ? this.props.distance.text : ''}
70 | {this.props.duration ? [' (', this.props.duration.text, ')'].join("") : ''}
71 |
72 | );
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/src/components/DurationDistanceLabel/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { NavigationIconsFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @durationDistanceText
15 | */
16 | durationDistanceText: {
17 | fontFamily: props.fontFamily,
18 | fontSize: props.fontSize * 0.8,
19 | opacity: props.opacity,
20 | flexWrap: 'wrap',
21 | },
22 |
23 | durationDistanceTravelModeIcon: {
24 | ...NavigationIconsFont,
25 | fontSize: props.fontSize * 0.8,
26 | opacity: props.opacity,
27 | marginRight: 8,
28 | },
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/DurationDistanceView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { ScrollView, View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 | import CloseButton from "../CloseButton";
9 | import DurationDistanceLabel from "../DurationDistanceLabel";
10 |
11 |
12 | /**
13 | * @component
14 | */
15 | export default class ManeuverView extends Component {
16 |
17 | /**
18 | * propTypes
19 | * @type {}
20 | */
21 | static propTypes = {
22 | step: PropTypes.any.isRequired,
23 | fontFamily: PropTypes.string,
24 | fontFamilyBold: PropTypes.string,
25 | fontSize: PropTypes.number,
26 | arrowSize: PropTypes.number,
27 | arrowColor: PropTypes.string,
28 | withCloseButton: PropTypes.bool,
29 | onClose: PropTypes.func,
30 | onPress: PropTypes.func,
31 | }
32 |
33 | /**
34 | * defaultProps
35 | * @type {}
36 | */
37 | static defaultProps = {
38 | step: undefined,
39 | fontFamily: undefined,
40 | fontFamilyBold: undefined,
41 | fontSize: 20,
42 | arrowSize: 50,
43 | arrowColor: '#545455',
44 | withCloseButton: false,
45 | onClose: undefined,
46 | onPress: undefined,
47 | }
48 |
49 |
50 | /**
51 | * @constructor
52 | * @param props
53 | */
54 | constructor(props)
55 | {
56 | super(props);
57 |
58 | }
59 |
60 |
61 | /**
62 | * render
63 | * @returns {XML}
64 | */
65 | render()
66 | {
67 | const styles = Styles(this.props);
68 |
69 | const step = this.props.step;
70 |
71 | if(!step) return null;
72 |
73 | return (
74 |
75 |
76 |
77 |
78 | {step.distance ? step.distance.text : ''}
79 |
80 |
81 |
82 | {step.duration ? step.duration.text : ''}
83 |
84 |
85 |
86 | {!this.props.withCloseButton ? null : (
87 |
88 | this.props.onClose && this.props.onClose()} />
89 |
90 | )}
91 |
92 | );
93 | }
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/src/components/DurationDistanceView/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @maneuverView
15 | */
16 | durationDistanceView: {
17 | padding: 15,
18 | backgroundColor: '#f7f7f4',
19 | flexDirection: 'row',
20 | minHeight: 120,
21 | alignItems: 'center',
22 | },
23 |
24 | durationDistanceContent: {
25 | flex: 1,
26 | },
27 |
28 | durationDistanceClose: {
29 | flex: 0,
30 | width: 30,
31 | justifyContent: 'flex-end',
32 | alignItems: 'flex-end',
33 | }
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/ManeuverArrow/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { Text } from 'react-native';
7 | import Styles from './styles';
8 | import NavigationIcons from "../../constants/NavigationIcons";
9 | import {DEFAULT_DIRECTION_TYPE} from "../../constants/DirectionTypes";
10 |
11 |
12 | /**
13 | * @component
14 | */
15 | export default class ManeuverArrow extends Component {
16 |
17 | /**
18 | * propTypes
19 | * @type {}
20 | */
21 | static propTypes = {
22 | maneuver: PropTypes.object,
23 | size: PropTypes.number,
24 | opacity: PropTypes.number,
25 | color: PropTypes.any,
26 | }
27 |
28 | /**
29 | * defaultProps
30 | * @type {}
31 | */
32 | static defaultProps = {
33 | maneuver: undefined,
34 | size: 25,
35 | opacity: 1,
36 | color: '#000000',
37 | }
38 |
39 |
40 | /**
41 | * @constructor
42 | * @param props
43 | */
44 | constructor(props)
45 | {
46 | super(props);
47 | }
48 |
49 |
50 | /**
51 | * render
52 | * @returns {XML}
53 | */
54 | render()
55 | {
56 | const styles = Styles(this.props);
57 |
58 | const icon = this.props.maneuver && (this.props.maneuver.name || DEFAULT_DIRECTION_TYPE);
59 |
60 | return (
61 |
62 | {NavigationIcons[icon]}
63 |
64 | );
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/src/components/ManeuverArrow/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @maneuverView
15 | */
16 | maneuverArrow: {
17 | fontFamily: 'Navigation',
18 | fontSize: props.size,
19 | color: props.color,
20 | opacity: props.opacity,
21 | textAlign: 'center',
22 | }
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/ManeuverLabel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 |
9 |
10 | /**
11 | * @component
12 | */
13 | export default class ManeuverLabel extends Component {
14 |
15 | /**
16 | * propTypes
17 | * @type {}
18 | */
19 | static propTypes = {
20 | instructions: PropTypes.string,
21 | fontFamily: PropTypes.string,
22 | fontFamilyBold: PropTypes.string,
23 | fontSize: PropTypes.number,
24 | fontColor: PropTypes.string
25 | }
26 |
27 | /**
28 | * defaultProps
29 | * @type {}
30 | */
31 | static defaultProps = {
32 | instructions: '',
33 | fontFamily: undefined,
34 | fontFamilyBold: undefined,
35 | fontSize: 15,
36 | fontColor: undefined
37 | }
38 |
39 | /**
40 | * @constructor
41 | * @param props
42 | */
43 | constructor(props)
44 | {
45 | super(props);
46 | }
47 |
48 | /**
49 | * getParsedInstructions
50 | * @param styles
51 | * @returns {*}
52 | */
53 | getParsedInstructions(styles)
54 | {
55 | const parts = [];
56 |
57 | const regex = /(\w+)|<(.*?)>(.*?)<\/.*?>/g;
58 |
59 | const mapping = {
60 | r: styles.regular,
61 | b: styles.bold,
62 | d: styles.durationDistance,
63 | div: styles.extra,
64 | };
65 |
66 | let m;
67 | let last = false;
68 | while((m = regex.exec(this.props.instructions))) {
69 |
70 | if (m.index === regex.lastIndex) {
71 | regex.lastIndex++;
72 | last = true;
73 | }
74 |
75 | if(m[2]) {
76 | let tag = m[2].split(" ")[0];
77 |
78 | if(tag == "div") m[3] = '\n' + m[3];
79 |
80 | parts.push({m[3]}{last ? '.' : ' '});
81 |
82 | } else {
83 | parts.push({m[0]}{last ? '.': ' '});
84 | }
85 | }
86 |
87 | return (
88 |
89 | {parts}
90 |
91 | )
92 | }
93 |
94 | /**
95 | * render
96 | * @returns {XML}
97 | */
98 | render()
99 | {
100 | const styles = Styles(this.props);
101 |
102 | return (
103 |
104 | {this.getParsedInstructions(styles)}
105 |
106 | );
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/src/components/ManeuverLabel/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @maneuverLabel
15 | */
16 | maneuverLabel: {
17 | flexDirection: 'row'
18 | },
19 |
20 | /**
21 | * @fonts
22 | */
23 |
24 | bold: {
25 | fontWeight: 'bold',
26 | fontFamily: props.fontFamilyBold || props.fontFamily,
27 | fontSize: props.fontSize,
28 | flexWrap: 'wrap',
29 | color: props.color
30 | },
31 |
32 | regular: {
33 | fontFamily: props.fontFamily,
34 | fontSize: props.fontSize,
35 | flexWrap: 'wrap',
36 | color: props.color
37 | },
38 |
39 | extra: {
40 | fontFamily: props.fontFamily,
41 | fontSize: props.fontSize * 0.8,
42 | flexWrap: 'wrap',
43 | color: '#387bc1',
44 | marginTop: 4,
45 | },
46 |
47 | durationDistance: {
48 | fontFamily: props.fontFamily,
49 | fontSize: props.fontSize * 0.8,
50 | opacity: 0.8,
51 | flexWrap: 'wrap',
52 | },
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/src/components/ManeuverView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { ScrollView, View, TouchableOpacity, Text } from 'react-native';
7 | import Styles from './styles';
8 | import ManeuverArrow from '../ManeuverArrow';
9 | import ManeuverLabel from '../ManeuverLabel';
10 | import CloseButton from "../CloseButton";
11 |
12 |
13 | /**
14 | * @component
15 | */
16 | export default class ManeuverView extends Component {
17 |
18 | /**
19 | * propTypes
20 | * @type {}
21 | */
22 | static propTypes = {
23 | step: PropTypes.any.isRequired,
24 | fontFamily: PropTypes.string,
25 | fontFamilyBold: PropTypes.string,
26 | fontSize: PropTypes.number,
27 | arrowSize: PropTypes.number,
28 | arrowColor: PropTypes.string,
29 | backgroundColor: PropTypes.string,
30 | withCloseButton: PropTypes.bool,
31 | onClose: PropTypes.func,
32 | onPress: PropTypes.func,
33 | }
34 |
35 | /**
36 | * defaultProps
37 | * @type {}
38 | */
39 | static defaultProps = {
40 | step: undefined,
41 | fontFamily: undefined,
42 | fontFamilyBold: undefined,
43 | backgroundColor: '#f7f7f4',
44 | fontSize: 20,
45 | arrowSize: 50,
46 | arrowColor: '#545455',
47 | withCloseButton: false,
48 | onClose: undefined,
49 | onPress: undefined,
50 | }
51 |
52 |
53 | /**
54 | * @constructor
55 | * @param props
56 | */
57 | constructor(props)
58 | {
59 | super(props);
60 |
61 | }
62 |
63 |
64 | /**
65 | * render
66 | * @returns {XML}
67 | */
68 | render()
69 | {
70 | const styles = Styles(this.props);
71 |
72 | const step = this.props.step;
73 |
74 | if(!step) return null;
75 |
76 | const maneuver = step.maneuver;
77 |
78 | return (
79 |
80 |
81 |
86 |
87 |
88 |
94 |
95 | {!this.props.withCloseButton ? null : (
96 |
97 | this.props.onClose && this.props.onClose()} />
98 |
99 | )}
100 |
101 | );
102 | }
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/src/components/ManeuverView/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { IconFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @maneuverView
15 | */
16 | maneuverView: {
17 | padding: 15,
18 | backgroundColor: props.backgroundColor ,
19 | flexDirection: 'row',
20 | minHeight: 120,
21 | alignItems: 'center',
22 | },
23 |
24 | maneuverViewArrow: {
25 | flex: 0,
26 | width: 80,
27 | justifyContent: 'center',
28 | alignItems: 'center',
29 | },
30 |
31 | maneuverViewDirection: {
32 | flex: 1,
33 | },
34 |
35 | maneuverClose: {
36 | flex: 0,
37 | width: 30,
38 | justifyContent: 'flex-end',
39 | alignItems: 'flex-end',
40 | },
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/src/components/MapViewNavigation/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import {CoordinatePropType} from '../../constants/PropTypes';
7 | import { View, TouchableOpacity, Text, Dimensions, Geolocation } from 'react-native';
8 | import connectTheme from '../../themes';
9 | import Geocoder from '../../modules/Geocoder';
10 | import Directions from '../../modules/Directions';
11 | import TravelModes from '../../constants/TravelModes';
12 | import NavigationModes from '../../constants/NavigationModes';
13 | import * as Tools from '../../modules/Tools';
14 | import Simulator from '../../modules/Simulator';
15 | import Traps from '../../modules/Traps';
16 | import RouteMarker from '../RouteMarker';
17 | import RoutePolyline from '../RoutePolyline';
18 | import PositionMarker from '../PositionMarker';
19 | import {POSITION_ARROW} from "../../constants/MarkerTypes";
20 | import {Circle, Polygon, Polyline} from 'react-native-maps';
21 |
22 |
23 | /**
24 | * @component
25 | */
26 | export default class MapViewNavigation extends Component {
27 |
28 | /**
29 | * propTypes
30 | * @type {}
31 | */
32 | static propTypes = {
33 | origin: PropTypes.oneOfType([PropTypes.string, CoordinatePropType, PropTypes.bool]),
34 | destination: PropTypes.oneOfType([PropTypes.string, CoordinatePropType, PropTypes.bool]),
35 | apiKey: PropTypes.string.isRequired,
36 | language: PropTypes.string,
37 | map: PropTypes.func,
38 | navigationMode: PropTypes.string,
39 | travelMode: PropTypes.string,
40 | maxZoom: PropTypes.number,
41 | minZoom: PropTypes.number,
42 | animationDuration: PropTypes.number,
43 | navigationMode: PropTypes.string,
44 | navigationViewingAngle: PropTypes.number,
45 | navigationZoomLevel: PropTypes.number,
46 | directionZoomQuantifier: PropTypes.number,
47 | onRouteChange: PropTypes.func,
48 | onStepChange: PropTypes.func,
49 | onNavigationStarted: PropTypes.func,
50 | onNavigationCompleted: PropTypes.func,
51 | routeStepDistance: PropTypes.number,
52 | routeStepInnerTolerance: PropTypes.number,
53 | routeStepCenterTolerance: PropTypes.number,
54 | routeStepCourseTolerance: PropTypes.number,
55 | displayDebugMarkers: PropTypes.bool,
56 | simulate: PropTypes.bool,
57 | options: PropTypes.object
58 | }
59 |
60 | /**
61 | * defaultProps
62 | * @type {}
63 | */
64 | static defaultProps = {
65 | origin: false,
66 | destination: false,
67 | apiKey: undefined,
68 | language: undefined,
69 | map: undefined,
70 | navigationMode: NavigationModes.IDLE,
71 | travelMode: TravelModes.DRIVING,
72 | maxZoom: 21,
73 | minZoom: 5,
74 | animationDuration: 750,
75 | navigationViewingAngle: 60,
76 | navigationZoomLevel: 14,
77 | directionZoomQuantifier: 1.5,
78 | onRouteChange: undefined,
79 | onStepChange: undefined,
80 | onNavigationStarted: undefined,
81 | onNavigationCompleted: undefined,
82 | routeStepDistance: 15,
83 | routeStepInnerTolerance: 0.75,
84 | routeStepCenterTolerance: 0.1,
85 | routeStepCourseTolerance: 30, // in degress
86 | displayDebugMarkers: false,
87 | simulate: false,
88 | options: {}
89 | }
90 |
91 | /**
92 | * @constructor
93 | * @param props
94 | */
95 | constructor(props) {
96 | super(props);
97 |
98 | this.geoCoder = new Geocoder(this.props.apiKey, {
99 | language: this.props.language
100 | });
101 |
102 | this.directionsCoder = new Directions(this.props.apiKey, {
103 | language: this.props.language
104 | });
105 |
106 |
107 |
108 | this.traps = new Traps(this);
109 |
110 | this.state = {
111 | route: false,
112 | markers: [],
113 | position: {},
114 | navigationMode: NavigationModes.IDLE,
115 | travelMode: TravelModes.DRIVING,
116 | stepIndex: false,
117 | };
118 |
119 | this.theme = connectTheme(this.props.theme);
120 |
121 | const {width, height} = Dimensions.get('window');
122 |
123 | this.aspectRatio = width / height;
124 | }
125 |
126 | /**
127 | * @componentDidMount
128 | */
129 | componentDidMount()
130 | {
131 | this.watchId = navigator.geolocation.watchPosition(position => {
132 |
133 | this.setPosition(position.coords);
134 |
135 | });
136 | }
137 |
138 | /**
139 | * @componentWillUnmount
140 | */
141 | componentWillUnmount()
142 | {
143 | navigator.geolocation.clearWatch(this.watchId);
144 | }
145 |
146 | /**
147 | * @componentDidUpdate
148 | * @param prevProps
149 | * @param prevState
150 | */
151 | componentDidUpdate(prevProps, prevState)
152 | {
153 | if(this.props.origin && this.props.destination) {
154 |
155 | if(
156 | (prevProps.navigationMode != this.props.navigationMode) ||
157 | (prevProps.travelMode != this.props.travelMode) ||
158 | (prevProps.origin != this.props.origin || prevProps.destination != this.props.destination)
159 | ) {
160 | this.updateRoute();
161 | }
162 | }
163 | }
164 |
165 | /**
166 | * getCoordinates
167 | * @param address
168 | * @param raw
169 | * @returns {Promise}
170 | */
171 | getCoordinates(address, raw = false) {
172 | return this.geoCoder.getFromLocation(address).then(results => {
173 |
174 | let coordinates = raw ? results : this.geoCoder.minimizeResults(results);
175 |
176 | return coordinates.length == 1 ? coordinates[0] : coordinates;
177 | });
178 | }
179 |
180 | /**
181 | * getZoomValue
182 | * @param level
183 | */
184 | getZoomValue(level) {
185 | const value = 0.00001 * (this.props.maxZoom - (level < this.props.minZoom ? this.props.minZoom : level));
186 |
187 | return {
188 | latitudeDelta: value,
189 | longitudeDelta: value * this.aspectRatio
190 | }
191 | }
192 |
193 | /**
194 | * getBoundingBoxZoomValue
195 | * @param b
196 | * @param quantifier
197 | * @returns {*}
198 | */
199 | getBoundingBoxZoomValue(b, quantifier = 1) {
200 |
201 | if(b.length != 2) return {};
202 |
203 | const latitudeDelta = (b[0].latitude > b[1].latitude ? b[0].latitude - b[1].latitude : b[1].latitude - b[0].latitude) * quantifier;
204 |
205 | return {
206 | latitudeDelta,
207 | longitudeDelta: latitudeDelta * this.aspectRatio,
208 | };
209 | }
210 |
211 | /**
212 | * updatePosition
213 | * @param coordinate
214 | * @param duration
215 | */
216 | updatePosition(coordinate, duration = 0)
217 | {
218 | this.props.map().animateToCoordinate(coordinate, duration);
219 | }
220 |
221 | /**
222 | * updateBearing
223 | * @param bearing
224 | * @param duration
225 | */
226 | updateBearing(bearing, duration = false)
227 | {
228 | this.props.map().animateToBearing(bearing, duration || this.props.animationDuration);
229 | }
230 |
231 | /**
232 | *
233 | * @param stepIndex
234 | */
235 | updateStep(stepIndex = 0)
236 | {
237 | const step = this.state.route.steps[stepIndex < 0 ? 0 : stepIndex];
238 |
239 | const nextStep = this.state.route.steps[stepIndex + 1];
240 |
241 | this.props.onStepChange && this.props.onStepChange(step, nextStep);
242 |
243 | this.traps.watchStep(step, nextStep, {
244 | distance: this.props.routeStepDistance,
245 | innerRadiusTolerance: this.props.routeStepInnerTolerance,
246 | centerRadiusTolerance: this.props.routeStepCenterTolerance,
247 | courseTolerance: this.props.routeStepCourseTolerance,
248 | }, (trap, event, state) => {
249 |
250 | if(!nextStep && trap.isCenter()) {
251 |
252 | this.props.onNavigationCompleted && this.props.onNavigationCompleted();
253 |
254 | return this.setState({
255 | navigationMode: NavigationModes.IDLE,
256 | stepIndex: false
257 | });
258 | }
259 |
260 | if(trap.isLeaving()) {
261 | this.updateStep(this.stepIndex);
262 | }
263 | });
264 |
265 | this.stepIndex = stepIndex + 1; // ensures that this is a real number
266 | }
267 |
268 | /**
269 | * setPosition
270 | * @param position
271 | */
272 | setPosition(position)
273 | {
274 | const {latitude, longitude, heading} = position;
275 |
276 | position.coordinate = {latitude, longitude};
277 |
278 | // process traps on setPosition
279 | this.traps.execute(position);
280 |
281 | // update position on map
282 | if(this.state.navigationMode == NavigationModes.NAVIGATION) {
283 |
284 | this.updatePosition(position);
285 |
286 | this.updateBearing(heading);
287 | }
288 |
289 | this.setState({position});
290 | }
291 |
292 | /**
293 | * clearRoute
294 | * @void
295 | */
296 | clearRoute()
297 | {
298 | this.setState({route: false, step: false, stepIndex: false})
299 | }
300 |
301 | /**
302 | * updateRoute
303 | * @param origin
304 | * @param destination
305 | * @param navigationMode
306 | */
307 | updateRoute(origin = false, destination = false, navigationMode = false, options = null)
308 | {
309 | origin = origin || this.props.origin;
310 | destination = destination || this.props.destination;
311 | navigationMode = navigationMode || this.props.navigationMode;
312 | options = options || this.props.options
313 |
314 | switch(navigationMode) {
315 |
316 | case NavigationModes.ROUTE:
317 | this.displayRoute(origin, destination, options);
318 | break;
319 |
320 | case NavigationModes.NAVIGATION:
321 | this.navigateRoute(origin, destination, options);
322 | break;
323 | }
324 | }
325 |
326 | /**
327 | * Prepares the route
328 | * @param origin
329 | * @param destination
330 | * @param mode
331 | * @param options
332 | * @returns {PromiseLike | Promise}
333 | */
334 | prepareRoute(origin, destination, options = false, testForRoute = false)
335 | {
336 | if(testForRoute && this.state.route) {
337 | return Promise.resolve(this.state.route);
338 | }
339 | options = Object.assign({}, {mode: this.state.travelMode}, {mode: this.props.travelMode}, options.constructor == Object ? options : {});
340 |
341 | return this.directionsCoder.fetch(origin, destination, options).then(routes => {
342 |
343 | if(routes.length) {
344 |
345 | const route = routes[0];
346 |
347 | this.props.onRouteChange && this.props.onRouteChange(route);
348 |
349 | this.props.onStepChange && this.props.onStepChange(false);
350 |
351 | this.setState({route, step: false});
352 |
353 | return Promise.resolve(route);
354 | }
355 |
356 | return Promise.reject();
357 |
358 | });
359 | }
360 |
361 | /**
362 | * displayRoute
363 | * @param origin
364 | * @param destination
365 | * @param options
366 | * @returns {PromiseLike | Promise}
367 | */
368 | displayRoute(origin, destination, options = false)
369 | {
370 | return this.prepareRoute(origin, destination, options).then(route => {
371 |
372 | const region = {
373 | ...route.bounds.center,
374 | ...this.getBoundingBoxZoomValue(route.bounds.boundingBox, this.props.directionZoomQuantifier)
375 | }
376 |
377 | this.props.map().animateToRegion(region, this.props.animationDuration);
378 |
379 | if(!this.state.navigationMode == NavigationModes.ROUTE) {
380 | this.setState({
381 | navigationMode: NavigationModes.ROUTE,
382 | });
383 | }
384 |
385 | return Promise.resolve(route);
386 | }).catch((err) => console.log(err));
387 | }
388 |
389 | /**
390 | * navigateRoute
391 | * @param origin
392 | * @param destination
393 | * @param options
394 | * @returns {PromiseLike | Promise}
395 | */
396 | navigateRoute(origin, destination, options = false)
397 | {
398 | return this.prepareRoute(origin, destination, options, true).then(route => {
399 |
400 | const region = {
401 | ...route.origin.coordinate,
402 | ...this.getZoomValue(this.props.navigationZoomLevel),
403 | };
404 |
405 | this.props.map().animateToRegion(region, this.props.animationDuration);
406 | this.props.map().animateToViewingAngle(this.props.navigationViewingAngle, this.props.animationDuration);
407 |
408 | //this.updatePosition(route.origin.coordinate);
409 | this.updateBearing(route.initialBearing);
410 |
411 | this.setState({
412 | navigationMode: NavigationModes.NAVIGATION,
413 | });
414 |
415 | this.updateStep(0);
416 |
417 | this.props.onNavigationStarted && this.props.onNavigationStarted();
418 |
419 | if (this.props.simulate) {
420 | console.log("SIMULATING ROUTE")
421 | this.simulator = new Simulator(this);
422 | setTimeout(() => this.simulator.start(route), this.props.animationDuration * 1.5);
423 | } else {
424 | console.log("NOT SIMULATING")
425 | }
426 |
427 | return Promise.resolve(route);
428 | });
429 | }
430 |
431 | /**
432 | * getRouteMarkers
433 | * @param route
434 | * @returns {*}
435 | */
436 | getRouteMarkers(route)
437 | {
438 | if (!route || route.markers.constructor !== Array) return null;
439 |
440 | return route.markers.map((params, index) => {
441 |
442 | return (
443 |
448 | );
449 | });
450 | }
451 |
452 | /**
453 | * getPositionMarker
454 | * @param position
455 | * @param navigationMode
456 | * @returns {*}
457 | */
458 | getPositionMarker(position, navigationMode)
459 | {
460 | const type = navigationMode == NavigationModes.NAVIGATION ? POSITION_ARROW : undefined;
461 |
462 | return (
463 |
469 | )
470 | }
471 |
472 | /**
473 | * Route Polycons
474 | * @param route
475 | * @returns {*}
476 | */
477 | getRoutePolylines(route)
478 | {
479 | if (!route || route.polylines.constructor !== Array) return null;
480 |
481 | return route.polylines.map((params, index) => {
482 |
483 | return params ? (
484 |
489 | ) : null;
490 | });
491 | }
492 |
493 | /**
494 | * getDebugShapes
495 | * @param route
496 | * @returns {Array}
497 | */
498 | getDebugShapes(route)
499 | {
500 | let result = [];
501 |
502 | if(!route || !this.props.displayDebugMarkers) return result;
503 |
504 |
505 | const steps = this.state.route.steps;
506 |
507 | let c = 0;
508 |
509 | steps.forEach((step, index) => {
510 |
511 | const coordinate = step.start;
512 |
513 | [
514 | {radius: this.props.routeStepDistance, color: 'blue'},
515 | {radius: this.props.routeStepDistance * this.props.routeStepInnerTolerance, color: 'red'},
516 | {radius: this.props.routeStepDistance * this.props.routeStepCenterTolerance, color: 'green'}
517 | ].forEach(d => {
518 | result.push();
519 | c++;
520 | });
521 |
522 | [
523 | {radius: this.props.routeStepDistance, color: 'blue'}
524 | ].forEach(d => {
525 |
526 | let bearing = step.bearing; // - 180 > 0 ? step.bearing - 180 : 360 - step.bearing - 180;
527 |
528 | let coords = Tools.toArcPolygon(
529 | coordinate,
530 | bearing - this.props.routeStepCourseTolerance,
531 | bearing + this.props.routeStepCourseTolerance,
532 | this.props.routeStepDistance
533 | )
534 |
535 | result.push();
536 | c++;
537 | })
538 |
539 |
540 |
541 | });
542 |
543 |
544 | return result;
545 | }
546 |
547 |
548 |
549 | /**
550 | * @render
551 | * @returns {*[]}
552 | */
553 | render()
554 | {
555 | const result = [
556 | this.getRouteMarkers(this.state.route),
557 | this.getRoutePolylines(this.state.route),
558 | this.getPositionMarker(this.state.position, this.state.navigationMode),
559 | this.getDebugShapes(this.state.route)
560 | ];
561 |
562 | return result;
563 | }
564 | }
565 |
566 |
--------------------------------------------------------------------------------
/src/components/PositionMarker/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import { Text, View } from 'react-native';
6 | import { Marker } from 'react-native-maps';
7 | import connectTheme from '../../themes'
8 | import Styles from './styles';
9 | import PropTypes from "prop-types";
10 | import { POSITION_DOT, POSITION_ARROW } from "../../constants/MarkerTypes";
11 |
12 |
13 | /**
14 | * @class
15 | */
16 | export default class PositionMarker extends Component {
17 |
18 | /**
19 | * propTypes
20 | * @type {}
21 | */
22 | static propTypes = {
23 | coordinate: PropTypes.object,
24 | size: PropTypes.number,
25 | fontSize: PropTypes.number,
26 | type: PropTypes.any,
27 | color: PropTypes.string,
28 | angle: PropTypes.number,
29 | backgroundColor: PropTypes.string,
30 | borderColor: PropTypes.string,
31 | borderWidth: PropTypes.number,
32 | }
33 |
34 | /**
35 | * defaultProps
36 | * @type {}
37 | */
38 | static defaultProps = {
39 | coordinate: undefined,
40 | size: 40,
41 | fontSize: 30,
42 | type: POSITION_DOT,
43 | color: '#252525',
44 | angle: 60,
45 | borderWidth: 0,
46 | borderColor: undefined,
47 | backgroundColor: '#252525'
48 | }
49 |
50 |
51 | /**
52 | * constructor
53 | * @param props
54 | */
55 | constructor(props)
56 | {
57 | super(props);
58 | }
59 |
60 | /**
61 | * render
62 | * @render
63 | * @returns {*}
64 | */
65 | render()
66 | {
67 | if(!this.props.coordinate) return null;
68 |
69 | const type = this.props.type;
70 |
71 | this.theme = connectTheme(this.props.theme).Markers[type];
72 |
73 | const styles = Styles(Object.assign({}, this.props, this.theme));
74 |
75 | return (type == POSITION_ARROW) ? this.renderArrow(styles) : this.renderDot(styles);
76 | }
77 |
78 | /**
79 | * renderArrow
80 | * @param styles
81 | * @returns {*}
82 | */
83 | renderArrow(styles)
84 | {
85 | return (
86 |
90 |
91 | {this.theme.icon}
92 |
93 |
94 | )
95 | }
96 |
97 |
98 | /**
99 | * renderDot
100 | * @param styles
101 | * @returns {*}
102 | */
103 | renderDot(styles) {
104 |
105 | return (
106 |
110 |
111 | {this.theme.icon}
112 |
113 |
114 | )
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/components/PositionMarker/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { NavigationIconsFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | positionMarkerText: {
14 | ...NavigationIconsFont,
15 | fontSize: props.fontSize,
16 | color: props.color,
17 | },
18 |
19 | positionMarkerArrow: {
20 | backgroundColor: props.backgroundColor,
21 | width: props.size,
22 | height: props.size,
23 | borderRadius: props.size,
24 | justifyContent: 'center',
25 | alignItems: 'center',
26 | transform: [
27 | { rotateX: props.angle + 'deg'}
28 | ]
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/RouteMarker/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import { Text } from 'react-native';
6 | import { Marker } from 'react-native-maps';
7 | import connectTheme from '../../themes'
8 | import Styles from './styles';
9 |
10 |
11 | /**
12 | * @class
13 | */
14 | export default class RouterMarker extends Component {
15 |
16 | /**
17 | * constructor
18 | * @param props
19 | */
20 | constructor(props) {
21 | super(props);
22 |
23 | this.theme = connectTheme(props.theme).Markers[this.props.type];
24 | }
25 |
26 |
27 | /**
28 | * @render
29 | * @returns {*}
30 | */
31 | render() {
32 |
33 | const styles = Styles(this.theme);
34 |
35 | return (
36 |
39 | {this.theme.icon}
40 |
41 | )
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/RouteMarker/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { NavigationIconsFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | markerText: {
14 | ...NavigationIconsFont,
15 | fontSize: props.fontSize || 30,
16 | color: props.color || '#000000',
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/RoutePolyline/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import { Text } from 'react-native';
6 | import { Polyline } from 'react-native-maps';
7 | import connectTheme from '../../themes'
8 |
9 |
10 | /**
11 | * @class
12 | */
13 | export default class RoutePolyline extends Component {
14 |
15 | /**
16 | * constructor
17 | * @param props
18 | */
19 | constructor(props) {
20 | super(props);
21 |
22 | this.theme = connectTheme(props.theme).Polylines[this.props.type];
23 | }
24 |
25 |
26 | /**
27 | * @render
28 | * @returns {*}
29 | */
30 | render() {
31 |
32 | if(!this.props.coordinates) return null;
33 |
34 | if(!this.theme) {
35 | throw new Error("RoutePolyline does not support type " + this.props.type + ".");
36 | }
37 |
38 | const components = [
39 |
46 | ];
47 |
48 | if(this.theme.fillColor) {
49 |
50 | const borderWidth = this.theme.strokeWidth - (this.theme.borderWidth || 3);
51 |
52 | components.push(
53 |
60 | );
61 | }
62 |
63 | return components;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/TravelModeBox/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { View, TouchableOpacity, Text } from 'react-native';
7 | import OptionGroupBox from 'react-native-optiongroup';
8 | import {DEFAULT_MODES, MODE_MAPPING, DRIVING} from '../../constants/TravelModes';
9 | import {NavigationIconsFont} from '../../constants/NavigationIcons';
10 |
11 | /**
12 | * @component
13 | */
14 | export default class TravelModeBox extends Component {
15 |
16 | /**
17 | * propTypes
18 | * @type {}
19 | */
20 | static propTypes = {
21 | backgroundColor: PropTypes.string,
22 | borderColor: PropTypes.string,
23 | borderWidth: PropTypes.number,
24 | borderRadius: PropTypes.number,
25 | contentPadding: PropTypes.number,
26 | inverseTextColor: PropTypes.string,
27 | modes: PropTypes.array,
28 | selected: PropTypes.any,
29 | defaultValue: PropTypes.any,
30 | style: PropTypes.any,
31 | onChange: PropTypes.func,
32 | theme: PropTypes.string,
33 | invertKeyLabel: PropTypes.bool,
34 | fontFamily: PropTypes.string,
35 | fontSize: PropTypes.number,
36 | useIcons: PropTypes.bool,
37 | }
38 |
39 | /**
40 | * defaultProps
41 | * @type {}
42 | */
43 | static defaultProps = {
44 | backgroundColor: 'transparent',
45 | borderColor: '#FFFFFF',
46 | borderWidth: 1,
47 | borderRadius: 3,
48 | contentPadding: 10,
49 | inverseTextColor: '#FFFFFF',
50 | defaultValue: DRIVING,
51 | selected: undefined,
52 | style: {},
53 | onChange: undefined,
54 | theme: undefined,
55 | invertKeyLabel: false,
56 | fontSize: 25,
57 | fontFamily: undefined,
58 | useIcons: true,
59 | modes: DEFAULT_MODES,
60 | }
61 |
62 | /**
63 | * @constructor
64 | * @param props
65 | */
66 | constructor(props)
67 | {
68 | super(props);
69 | }
70 |
71 | /**
72 | * render
73 | * @returns {XML}
74 | */
75 | render()
76 | {
77 | const options = [];
78 |
79 | this.props.modes.map(mode => {
80 | if (MODE_MAPPING[mode]) {
81 | options.push(MODE_MAPPING[mode])
82 | }
83 | });
84 |
85 | return (
86 |
93 | );
94 | }
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/src/components/TravelModeLabel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import React, { Component } from 'react';
5 | import PropTypes from 'prop-types';
6 | import { View, TouchableOpacity, Text } from 'react-native';
7 | import {MODE_MAPPING} from '../../constants/TravelModes';
8 | import Styles from "./styles";
9 |
10 | /**
11 | * @component
12 | */
13 | export default class TravelModeLabel extends Component {
14 |
15 | /**
16 | * propTypes
17 | * @type {}
18 | */
19 | static propTypes = {
20 | size: PropTypes.number,
21 | opacity: PropTypes.number,
22 | color: PropTypes.string,
23 | fontFamily: PropTypes.string,
24 | fontSize: PropTypes.number,
25 | useIcon: PropTypes.bool,
26 | useLabel: PropTypes.bool,
27 | mode: PropTypes.string
28 | }
29 |
30 | /**
31 | * defaultProps
32 | * @type {}
33 | */
34 | static defaultProps = {
35 | color: undefined,
36 | opacity: 0.8,
37 | fontSize: 25,
38 | fontFamily: undefined,
39 | useIcon: true,
40 | useLabel: true,
41 | mode: undefined,
42 | }
43 |
44 | /**
45 | * @constructor
46 | * @param props
47 | */
48 | constructor(props)
49 | {
50 | super(props);
51 |
52 | }
53 |
54 | /**
55 | * render
56 | * @returns {XML}
57 | */
58 | render()
59 | {
60 | const styles = Styles(this.props);
61 |
62 | const travelMode = MODE_MAPPING[this.props.mode];
63 |
64 | if(!travelMode) return null;
65 |
66 | return (
67 |
68 | {!this.useIcon ? null : (
69 |
70 | {travelMode.icon}
71 |
72 | )}
73 |
74 | {!this.useLabel ? null : (
75 |
76 | {travelMode.name}
77 |
78 | )}
79 |
80 | );
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/src/components/TravelModeLabel/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import { StyleSheet} from 'react-native';
5 | import { NavigationIconsFont } from '../../constants/NavigationIcons';
6 |
7 |
8 | /**
9 | * @styles
10 | */
11 | export default props => StyleSheet.create({
12 |
13 | /**
14 | * @travelModeLabelContainer
15 | */
16 | travelModeLabelContainer: {
17 | flexDirection: 'row'
18 | },
19 |
20 | travelModeLabelIcon: {
21 | ...NavigationIconsFont,
22 | fontSize: props.size,
23 | opacity: props.opacity
24 | },
25 |
26 | travelModeLabelText: {
27 | fontFamily: props.fontFamily,
28 | fontSize: props.fontSize,
29 | opacity: props.opacity,
30 | flexWrap: 'wrap',
31 | },
32 |
33 | });
34 |
--------------------------------------------------------------------------------
/src/constants/DirectionTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {string}
3 | */
4 | export const DEFAULT_DIRECTION_TYPE = 'invalid';
5 |
6 | /**
7 | * @DirectionTypes
8 | */
9 | export default {
10 | NORTH: 'NORTH',
11 | NORTHEAST: 'NORTHEAST',
12 | EAST: 'EAST',
13 | SOUTHEAST: 'SOUTHEAST',
14 | SOUTH: 'SOUTH',
15 | SOUTHWEST: 'SOUTHWEST',
16 | WEST: 'WEST',
17 | NORTHWEST: 'NORTHWEST',
18 | };
--------------------------------------------------------------------------------
/src/constants/MarkerTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {string}
3 | */
4 | export const ORIGIN = 'ORIGIN';
5 |
6 | /**
7 | * @type {string}
8 | */
9 | export const DESTINATION = 'DESTINATION';
10 |
11 | export const POSITION_DOT = 'POSITION_DOT';
12 |
13 | export const POSITION_ARROW = 'POSITION_ARROW';
14 |
15 |
--------------------------------------------------------------------------------
/src/constants/NavigationIcons.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import DirectionTypes from './DirectionTypes';
5 |
6 | /**
7 | * @styles
8 | */
9 | export const NavigationIconsFont = {
10 | fontFamily: 'Navigation'
11 | };
12 |
13 | /**
14 | * Arrows
15 | * @type {{}}
16 | */
17 | export default {
18 |
19 | /**
20 | * @common
21 | */
22 | watch: '\ue957',
23 | timer3: '\ue958',
24 | timer10: '\ue959',
25 | timerAv: '\ue95a',
26 | timer: '\ue95b',
27 | menu: '\ue95c',
28 | zoomOutMap: '\ue95d',
29 | zoomOut: '\ue95e',
30 | zoomIn: '\ue95f',
31 | linearScale: '\ue960',
32 | room: '\ue961',
33 | place: '\ue961',
34 | zoomOutMap2: '\ue962',
35 | locationOff: '\ue963',
36 | locationCity: '\ue964',
37 | locationDisabled: '\ue965',
38 | locationSearching: '\ue966',
39 | myLocation: '\ue967',
40 | editLocation: '\ue968',
41 | addLocation: '\ue969',
42 | map: '\ue96a',
43 | directionsTransit: '\ue96b',
44 | directionsWalk: '\ue96c',
45 | directionsBike: '\ue96d',
46 | cardTravel: '\ue96e',
47 | home: '\ue96f',
48 | directionsDriving: '\ue970',
49 | navigate: '\ue975',
50 | target: '\ue971',
51 | location2: '\ue972',
52 | location3: '\ue973',
53 | compassDirection: '\ue974',
54 | compassDot: '\ue977',
55 |
56 | /**
57 | * @arrows
58 | * turn-slight-left, turn-sharp-left, uturn-left, turn-left, turn-slight-right, turn-sharp-right,
59 | * uturn-right, turn-right, straight, ramp-left, ramp-right, merge, fork-left, fork-right, ferry,
60 | * ferry-train, roundabout-left, roundabout-right
61 | */
62 | arriveLeft: '\ue900',
63 | arriveRight: '\ue901',
64 | arriveStraight: '\ue902',
65 | arrive: '\ue903',
66 | close: '\ue904',
67 | continueLeft: '\ue905',
68 | continueRight: '\ue906',
69 | continueSlightLeft: '\ue907',
70 | continueSlightRight: '\ue908',
71 | continueStraight: '\ue909',
72 | continueUturn: '\ue90a',
73 | continue: '\ue90b',
74 | keepLeft: '\ue907',
75 | keepRight: '\ue908',
76 | departLeft: '\ue90c',
77 | departRight: '\ue90d',
78 | departStraight: '\ue90e',
79 | depart: '\ue90f',
80 | endOfRoadLeft: '\ue910',
81 | endOfRoadRight: '\ue911',
82 | flag: '\ue912',
83 | forkLeft: '\ue913',
84 | forkRight: '\ue914',
85 | forkSlightLeft: '\ue915',
86 | forkSlightRight: '\ue916',
87 | forkStraight: '\ue917',
88 | fork: '\ue918',
89 | invalidLeft: '\ue919',
90 | invalidRight: '\ue91a',
91 | invalidSlightLeft: '\ue91b',
92 | invalidSlightRight: '\ue91c',
93 | invalidStraight: '\ue91d',
94 | invalidUturn: '\ue91e',
95 | invalid: '\ue91f',
96 | straight: '\ue91f',
97 | merge: '\ue976',
98 | mergeLeft: '\ue920',
99 | mergeRight: '\ue921',
100 | mergeSlightLeft: '\ue922',
101 | mergeSlightRight: '\ue923',
102 | mergeStraight: '\ue924',
103 | newNameLeft: '\ue925',
104 | newNameRight: '\ue926',
105 | newNameSharpLeft: '\ue927',
106 | newNameSharpRight: '\ue928',
107 | newNameSlightLeft: '\ue929',
108 | newNameSlightRight: '\ue92a',
109 | newNameStraight: '\ue92b',
110 | notificaitonSharpRight: '\ue92c',
111 | notificationLeft: '\ue92d',
112 | notificationRight: '\ue92e',
113 | notificationSharpLeft: '\ue92f',
114 | notificationSlightLeft: '\ue930',
115 | notificationSlightRight: '\ue931',
116 | notificationStraight: '\ue932',
117 | offRampLeft: '\ue933',
118 | offRampRight: '\ue934',
119 | offRampSlightLeft: '\ue935',
120 | offRampSlightRight: '\ue936',
121 | rampLeft: '\ue933',
122 | rampRight: '\ue934',
123 | rampSlightLeft: '\ue935',
124 | rampSlightRight: '\ue936',
125 | onRampLeft: '\ue937',
126 | onRampRight: '\ue938',
127 | onRampSharpLeft: '\ue939',
128 | onRampSharpRight: '\ue93a',
129 | onRampSlightLeft: '\ue93b',
130 | onRampSlightRight: '\ue93c',
131 | onRampStraight: '\ue93d',
132 | rotaryLeft: '\ue93e',
133 | rotaryRight: '\ue93f',
134 | rotarySharpLeft: '\ue940',
135 | rotarySharpRight: '\ue941',
136 | rotarySlightLeft: '\ue942',
137 | rotarySlightRight: '\ue943',
138 | rotaryStraight: '\ue944',
139 | rotary: '\ue945',
140 | roundaboutLeft: '\ue946',
141 | roundaboutRight: '\ue947',
142 | roundaboutSharpLeft: '\ue948',
143 | roundaboutSharpRight: '\ue949',
144 | roundaboutSlightLeft: '\ue94a',
145 | roundaboutSlightRight: '\ue94b',
146 | roundaboutStraight: '\ue94c',
147 | roundabout: '\ue94d',
148 | turnLeft: '\ue94e',
149 | turnRight: '\ue94f',
150 | turnSharpLeft: '\ue950',
151 | turnSharpRight: '\ue951',
152 | turnSlightLeft: '\ue952',
153 | turnSlightRight: '\ue953',
154 | turnStraight: '\ue954',
155 | updown: '\ue955',
156 | };
157 |
158 |
159 |
--------------------------------------------------------------------------------
/src/constants/NavigationModes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @export
3 | */
4 | export default {
5 | IDLE: 'IDLE',
6 | ROUTE: 'ROUTE',
7 | NAVIGATION: 'NAVIGATION'
8 | };
--------------------------------------------------------------------------------
/src/constants/PolylineTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {string}
3 | */
4 | export const ROUTE = 'ROUTE';
5 |
6 | /**
7 | * @type {string}
8 | */
9 | export const ROUTE_ALTERNATIVE = 'ROUTE_ALTERNATIVE';
10 |
11 | /**
12 | * @type {string}
13 | */
14 | export const PENDING = 'PENDING';
15 |
16 | /**
17 | * @type {string}
18 | */
19 | export const NEXT = 'NEXT';
20 |
21 |
--------------------------------------------------------------------------------
/src/constants/PropTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Coordinate PropType
3 | * @param props
4 | * @param propName
5 | * @param componentName
6 | * @returns {Error}
7 | * @constructor
8 | */
9 | export const CoordinatePropType = (props, propName, componentName) => {
10 |
11 | const target = props[propName];
12 |
13 | if(!target || !target.constructor == Object || !target.latitude || !target.longitude) {
14 | return new Error(propName + ' in ' + componentName + ' requires to be a coordinate object ({latitude, longitude}');
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/src/constants/TrapTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Trap Options
3 | * @type {{SINGLE_FIRE: string, AUTO_EXPIRE: string}}
4 | */
5 | export const OPTIONS = {
6 | SINGLE_FIRE: 'SINGLE_FIRE',
7 | AUTO_EXPIRE: 'AUTO_EXPIRE',
8 | };
9 |
10 | /**
11 | * TrapEvents
12 | * @type {{ENTERED: string, ENTERED_ON_COURSE: string, ENTERED_OFF_COURSE: string, INSIDE: string, INSIDE_CENTER: string, LEAVING: string, LEFT_ON_COURSE: string, LEFT_OFF_COURSE: string}}
13 | */
14 | export const EVENTS = {
15 | ENTERING: 'ENTERING',
16 | ENTERING_ON_COURSE: 'ENTERING_ON_COURSE',
17 | ENTERING_OFF_COURSE: 'ENTERING_OFF_COURSE',
18 | INSIDE: 'INSIDE',
19 | INSIDE_CENTER: 'INSIDE_CENTER',
20 | LEAVING: 'LEAVING',
21 | LEAVING_ON_COURSE: 'LEAVING_ON_COURSE',
22 | LEAVING_OFF_COURSE: 'LEAVING_OFF_COURSE',
23 | };
24 |
25 | /**
26 | *
27 | * @type {{OUTSIDE: string, ENTERED: string, INSIDE: string, LEFT: string, EXPIRED: string}}
28 | */
29 | export const STATES = {
30 | OUTSIDE: 'OUTSIDE',
31 | ENTERED: 'ENTERED',
32 | INSIDE: 'INSIDE',
33 | CENTER: 'CENTER',
34 | LEAVING: 'LEAVING',
35 | LEFT: 'LEFT',
36 | EXPIRED: 'EXPIRED',
37 | }
38 |
39 | /**
40 | * TrapTypes
41 | * @type {{POLYGON: string, CIRCLE: string, STEP: string}}
42 | */
43 | export const TYPES = {
44 | POLYGON: 'POLYGON',
45 | CIRCLE: 'CIRCLE',
46 | STEP: 'STEP',
47 | }
48 |
49 | /**
50 | * @export
51 | */
52 | export default {
53 | Events: EVENTS,
54 | States: STATES,
55 | Types: TYPES,
56 | Options: OPTIONS
57 | };
--------------------------------------------------------------------------------
/src/constants/TravelModes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @TravelModes
3 | */
4 | import NavigationIcons from './NavigationIcons';
5 |
6 | /**
7 | * @type {string}
8 | */
9 | export const DRIVING = 'DRIVING';
10 |
11 | /**
12 | * @type {string}
13 | */
14 | export const WALKING = 'WALKING';
15 |
16 | /**
17 | * @type {string}
18 | */
19 | export const TRANSIT = 'TRANSIT';
20 |
21 | /**
22 | * @type {string}
23 | */
24 | export const BICYCLING = 'BICYCLING';
25 |
26 | /**
27 | * Mapping
28 | * @type {*[]}
29 | */
30 | export const MODE_MAPPING = {
31 | [DRIVING]: {
32 | mode: DRIVING,
33 | name: 'Driving',
34 | icon: NavigationIcons.directionsDriving,
35 | },
36 | [WALKING]: {
37 | mode: WALKING,
38 | name: 'Walking',
39 | icon: NavigationIcons.directionsWalk,
40 | },
41 | [TRANSIT]: {
42 | mode: TRANSIT,
43 | name: 'Transit',
44 | icon: NavigationIcons.directionsTransit,
45 | },
46 | [BICYCLING]: {
47 | mode: BICYCLING,
48 | name: 'Bicycling',
49 | icon: NavigationIcons.directionsBike,
50 | }
51 | };
52 |
53 | /**
54 | * Default Modes
55 | * @type {*[]}
56 | */
57 | export const DEFAULT_MODES = [DRIVING, WALKING, TRANSIT, BICYCLING];
58 |
59 |
60 | /**
61 | * @default export
62 | */
63 | export default {DRIVING, WALKING, TRANSIT, BICYCLING}
64 |
65 |
--------------------------------------------------------------------------------
/src/modules/Directions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import {toQueryParams, toLatLng, toCoordinate} from './Tools';
5 | import TravelModes from '../constants/TravelModes';
6 | import * as MarkerTypes from '../constants/MarkerTypes'
7 | import * as PolylineTypes from '../constants/PolylineTypes';
8 | import DirectionTypes, { DEFAULT_DIRECTION_TYPE} from '../constants/DirectionTypes';
9 | import * as GeoLib from 'geolib';
10 | import NavigationIcons from "../constants/NavigationIcons";
11 |
12 | /**
13 | * @class
14 | */
15 | export default class Directions {
16 |
17 | /**
18 | * constructor
19 | * @param apiKey
20 | * @param options
21 | */
22 | constructor(apiKey, options = false)
23 | {
24 | this.apiKey = apiKey;
25 | this.options = options || {};
26 | }
27 |
28 | /**
29 | * Fetch route
30 | * @param origin
31 | * @param destination
32 | * @param options
33 | * @returns {Promise}
34 | */
35 | fetch(origin, destination, options = false)
36 | {
37 | options = Object.assign({
38 | key: this.apiKey,
39 | mode: TravelModes.DRIVING
40 | }, this.options, options);
41 |
42 | const queryParams = {
43 | origin: toLatLng(origin),
44 | destination: toLatLng(destination),
45 | ...options,
46 | };
47 |
48 | if(queryParams.mode) queryParams.mode = queryParams.mode.toLowerCase();
49 |
50 | const url = `https://maps.google.com/maps/api/directions/json?${toQueryParams(queryParams)}`;
51 |
52 | return fetch(url)
53 | .then(response => response.json())
54 | .then(json => {
55 |
56 | if (json.status !== 'OK') {
57 | const errorMessage = json.error_message || 'Unknown error';
58 | return Promise.reject(errorMessage);
59 | }
60 |
61 | return this.parse(json);
62 | });
63 | }
64 |
65 | /**
66 | * parse
67 | * @param json
68 | * @returns {*}
69 | */
70 | parse(json)
71 | {
72 | // parse each route
73 | if(!json.routes.length) return [];
74 |
75 | return json.routes.map(route => {
76 |
77 | if (!route.legs.length) return null;
78 |
79 | const leg = route.legs[0]; // only support primary leg - waypoint support is later
80 |
81 | // create markers
82 | const markers = [
83 | // origin
84 | {
85 | coordinate: toCoordinate(leg.start_location),
86 | type: MarkerTypes.ORIGIN,
87 | },
88 | // destination
89 | {
90 | coordinate: toCoordinate(leg.end_location),
91 | type: MarkerTypes.DESTINATION,
92 | }
93 | ];
94 |
95 |
96 | const steps = leg.steps.map((step, index) =>
97 | this.parseStep(
98 | step,
99 | leg.steps[index + 1] ? leg.steps[index + 1] : false
100 | )
101 | );
102 |
103 | steps.push({
104 | final: true,
105 | bearing: steps[steps.length-1].bearing,
106 | compass: steps[steps.length-1].compass,
107 | start: steps[steps.length-1].end,
108 | end: false,
109 | maneuver: {
110 | name: 'flag',
111 | type: 'flag',
112 | },
113 | polyline: {
114 | coordinates: [],
115 | type: PolylineTypes.ROUTE
116 | },
117 | instructions: leg.end_address,
118 | });
119 |
120 | const polylines = steps.map(step => step.polyline);
121 |
122 | const boundingBox = [toCoordinate(route.bounds.northeast), toCoordinate(route.bounds.southwest)];
123 |
124 | return {
125 | title: route.summary,
126 | markers,
127 | steps,
128 | polylines,
129 | bounds: {
130 | boundingBox,
131 | center: GeoLib.getCenter(boundingBox),
132 | northEast: toCoordinate(route.bounds.northeast),
133 | southWest: toCoordinate(route.bounds.southwest),
134 | },
135 | initialBearing: steps.length ? steps[0].bearing : 0,
136 | duration: leg.duration,
137 | distance: leg.distance,
138 | origin: {
139 | address: leg.start_address,
140 | latlng: leg.start_location,
141 | coordinate: toCoordinate(leg.start_location),
142 | },
143 | destination: {
144 | address: leg.end_address,
145 | latlng: leg.end_location,
146 | coordinate: toCoordinate(leg.end_location),
147 | },
148 |
149 | };
150 |
151 | });
152 | }
153 |
154 | /**
155 | * parseStep
156 | * @param step
157 | */
158 | parseStep(step, nextStep) {
159 |
160 | const bearing = GeoLib.getGreatCircleBearing(toCoordinate(step.start_location), toCoordinate(nextStep ? nextStep.start_location : step.end_location));
161 |
162 | return {
163 | compass: this.decodeCompass(bearing),
164 | maneuver: this.decodeManeuver(step, bearing),
165 | bearing: bearing,
166 | mode: step.travel_mode,
167 | start: toCoordinate(step.start_location),
168 | end: toCoordinate(step.end_location),
169 | polyline: {
170 | coordinates: this.decodePolylineToCoordinates(step.polyline.points),
171 | type: PolylineTypes.ROUTE,
172 | },
173 | duration: step.duration,
174 | distance: step.distance,
175 | instructions: step.html_instructions,
176 | }
177 | }
178 |
179 | /**
180 | * decodeManeuver
181 | * @param step
182 | * @returns {{name: *, type: *}}
183 | */
184 | decodeManeuver(step)
185 | {
186 | const maneuver = step.maneuver ? step.maneuver : DEFAULT_DIRECTION_TYPE;
187 |
188 | const name = (maneuver.split("-").map((d, i) => i == 0 ? d : d[0].toUpperCase() + d.slice(1))).join("");
189 |
190 | return {
191 | name,
192 | type: maneuver
193 | };
194 | }
195 |
196 | /**
197 | * decodeCompassDirection
198 | * @param bearing
199 | * @returns {{detail: *, simple: *}}
200 | */
201 | decodeCompass(bearing)
202 | {
203 | if(bearing < 0) return false;
204 |
205 | const compass = [
206 | DirectionTypes.NORTH,
207 | DirectionTypes.NORTHEAST,
208 | DirectionTypes.EAST,
209 | DirectionTypes.SOUTHEAST,
210 | DirectionTypes.SOUTH,
211 | DirectionTypes.SOUTHWEST,
212 | DirectionTypes.WEST,
213 | DirectionTypes.NORTHWEST
214 | ];
215 |
216 | const compassSimple = [
217 | DirectionTypes.NORTH,
218 | DirectionTypes.EAST,
219 | DirectionTypes.SOUTH,
220 | DirectionTypes.WEST,
221 | ];
222 |
223 | const calc = a => {
224 | let c = Math.ceil(bearing / (360 / a.length)) - 1;
225 | return a[c < 0 ? 0 : (c > (a.length-1)) ? (a.length-1) : c];
226 | }
227 |
228 | return {
229 | detail: calc(compass),
230 | simple: calc(compassSimple),
231 | };
232 | }
233 |
234 |
235 | /**
236 | * decodePolylineToCoordinates
237 | * @param t
238 | * @param e
239 | * @returns {{latitude: *, longitude: *}[]}
240 | */
241 | decodePolylineToCoordinates(t, e)
242 | {
243 | for (var n, o, u = 0, l = 0, r = 0, d = [], h = 0, i = 0, a = null, c = Math.pow(10, e || 5); u < t.length;) {
244 | a = null, h = 0, i = 0;
245 | do a = t.charCodeAt(u++) - 63, i |= (31 & a) << h, h += 5; while (a >= 32);
246 | n = 1 & i ? ~(i >> 1) : i >> 1, h = i = 0;
247 | do a = t.charCodeAt(u++) - 63, i |= (31 & a) << h, h += 5; while (a >= 32);
248 | o = 1 & i ? ~(i >> 1) : i >> 1, l += n, r += o, d.push([l / c, r / c]);
249 | }
250 |
251 | return d = d.map(function(t) {
252 | return {
253 | latitude: t[0],
254 | longitude: t[1],
255 | };
256 | });
257 | }
258 | }
--------------------------------------------------------------------------------
/src/modules/Geocoder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import {toQueryParams} from './Tools';
5 |
6 | /**
7 | * @class
8 | */
9 | export default class Geocoder {
10 |
11 | /**
12 | * AddressComponentMapping
13 | * @type {{street_number: string, route: string, postal_code: string, country: string, locality: string, administrative_area_level_1: string, administrative_area_level_2: string}}
14 | */
15 | static AddressComponentMapping = {
16 | 'street_number': 'number',
17 | 'route': 'street',
18 | 'postal_code': 'zip',
19 | 'country': 'country',
20 | 'locality': 'city',
21 | 'administrative_area_level_1': 'state',
22 | 'administrative_area_level_2': 'county',
23 | }
24 |
25 | /**
26 | * constructor
27 | * @param apiKey
28 | * @param options
29 | */
30 | constructor(apiKey, options = false)
31 | {
32 | this.apiKey = apiKey;
33 | this.options = options || {};
34 | }
35 |
36 | /**
37 | * getFromLocation
38 | * @param params
39 | * @returns {Promise}
40 | */
41 | async getFromLocation(...params)
42 | {
43 | let queryParams = params.length === 1 && typeof params[0] === 'string' ? {address: params[0]} : params;
44 |
45 | if (!params) return Promise.reject('Not enough parameters');
46 |
47 | queryParams.key = this.apiKey;
48 |
49 | if (this.options.language)
50 | queryParams.language = this.options.language;
51 |
52 | // build url
53 | const url = `https://maps.google.com/maps/api/geocode/json?${toQueryParams(queryParams)}`;
54 |
55 | let response, data;
56 |
57 | // fetch
58 | try {
59 | response = await fetch(url);
60 | } catch (error) {
61 | return Promise.reject(error);
62 | }
63 |
64 | // parse
65 | try {
66 | data = await response.json();
67 | } catch (error) {
68 | return Promise.reject(error);
69 | }
70 |
71 | // check response's data
72 | if (data.status !== 'OK') {
73 | return Promise.reject(data);
74 | }
75 |
76 | return data.results;
77 | }
78 |
79 | /**
80 | * getFromLatLng
81 | * @param lat
82 | * @param lng
83 | * @returns {*}
84 | */
85 | getFromLatLng(lat, lng)
86 | {
87 | return this.getFromLocation({latlng: `${lat},${lng}`})
88 | }
89 |
90 | /**
91 | * minimizeResults
92 | * @param results
93 | */
94 | minimizeResults(results)
95 | {
96 | if(results.constructor != Array) return [];
97 |
98 | return results.map(result => {
99 |
100 | let {lat, lng} = result.geometry.location;
101 |
102 | return {
103 | components: this.minimizeAddressComponents(result.address_components),
104 | address: result.formatted_address,
105 | coordinate: {
106 | latitude: lat,
107 | longitude: lng
108 | }
109 | };
110 | });
111 | }
112 |
113 | /**
114 | * minimizeAddressComponents
115 | * @param components
116 | */
117 | minimizeAddressComponents(components)
118 | {
119 | let results = {};
120 |
121 | const ids = Object.keys(Geocoder.AddressComponentMapping);
122 |
123 | components.forEach(component => {
124 |
125 | let index = ids.indexOf(component.types[0]);
126 |
127 | if(index != -1) {
128 |
129 | results[Geocoder.AddressComponentMapping[ids[index]]] = {
130 | short: component.short_name,
131 | long: component.long_name
132 | };
133 |
134 | }
135 | });
136 |
137 | return results;
138 | }
139 | }
--------------------------------------------------------------------------------
/src/modules/Simulator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import * as GeoLib from 'geolib';
5 |
6 | /**
7 | * @class
8 | */
9 | export default class Simulator {
10 |
11 | /**
12 | * constructor
13 | * @param apiKey
14 | * @param options
15 | */
16 | constructor(instance)
17 | {
18 | this.instance = instance;
19 | this.speed = 30;
20 | this.turnSpeed = 700;
21 | }
22 |
23 | /**
24 | * start
25 | * @param route
26 | */
27 | start(route) {
28 | this.pointIndex = 0;
29 |
30 | const steps = route.steps;
31 |
32 | let points = [];
33 | let result = [];
34 |
35 | steps.map(step => step.polyline.coordinates.map(coordinate => points.push(Object.assign({}, coordinate))));
36 |
37 |
38 | points.forEach((point, index) => {
39 |
40 | const nextPoint = points[index + 1];
41 |
42 | if(nextPoint && !nextPoint.final == true) {
43 |
44 | // calculate distance between each point
45 | const distance = Math.round(GeoLib.getDistance(point, nextPoint));
46 | const bearing = GeoLib.getGreatCircleBearing(point, nextPoint);
47 |
48 | if(bearing !== 0) {
49 |
50 | if (distance > 1) {
51 |
52 | for (var x = 1; x < distance; x++) {
53 |
54 | result.push(Object.assign({}, {bearing}, GeoLib.computeDestinationPoint(point, x, bearing)));
55 | }
56 |
57 | } else {
58 | result.push(Object.assign({}, {bearing}, point));
59 | }
60 | }
61 | }
62 | });
63 |
64 | this.pointIndex = 0;
65 | this.points = result;
66 | this.lastBearing = false;
67 |
68 | this.drive();
69 |
70 | }
71 |
72 | drive()
73 | {
74 | const point = this.points[this.pointIndex];
75 |
76 | let speed = this.speed;
77 |
78 | if(point && point.bearing) {
79 |
80 | let allowPositionUpdate = true;
81 |
82 |
83 | if(this.lastBearing != point.bearing) {
84 |
85 | // check if it's just a small bump
86 | if(point.bearing > this.lastBearing - 10 && point.bearing < this.lastBearing + 10) {
87 |
88 | this.instance.updateBearing(point.bearing, this.turnSpeed);
89 |
90 | } else {
91 | allowPositionUpdate = false;
92 | speed = this.turnSpeed;
93 | this.instance.updateBearing(point.bearing, this.turnSpeed);
94 | }
95 |
96 | this.lastBearing = point.bearing;
97 | }
98 |
99 | if(allowPositionUpdate) {
100 |
101 | this.instance.setPosition({
102 | ...point,
103 | heading: point.bearing,
104 | });
105 |
106 | this.pointIndex++;
107 | }
108 |
109 | setTimeout(() => this.drive(), speed);
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/src/modules/Tools.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import * as geolib from 'geolib';
5 |
6 | /**
7 | * toQueryParams
8 | * @param object
9 | * @returns {string}
10 | */
11 | export const toQueryParams = (object) =>
12 | {
13 | return Object.keys(object)
14 | .filter(key => !!object[key])
15 | .map(key => key + "=" + encodeURIComponent(object[key]))
16 | .join("&")
17 | }
18 |
19 | /**
20 | * toLatLng
21 | * @param value
22 | * @returns {string}
23 | */
24 | export const toLatLng = (value) =>
25 | {
26 | if(value.constructor == String) return value;
27 |
28 | return value && value.latitude && value.longitude ? `${value.latitude},${value.longitude}` : value;
29 | }
30 |
31 | /**
32 | * toCoordinate
33 | * @param latlng
34 | * @returns {{latitude: *, longitude: *}}
35 | */
36 | export const toCoordinate = (latlng) =>
37 | {
38 | const {lat, lng} = latlng;
39 |
40 | return {latitude: lat, longitude: lng};
41 | }
42 |
43 | /**
44 | * toArcPolygon
45 | * @param coordinate
46 | * @param initialBearing
47 | * @param finalBearing
48 | * @param radius
49 | * @returns {any[]}
50 | */
51 | export const toArcPolygon = (coordinate, initialBearing, finalBearing, radius) =>
52 | {
53 | const d2r = Math.PI / 180; // degrees to radians
54 | const r2d = 180 / Math.PI; // radians to degrees
55 | const points = 32;
56 | let result = [];
57 |
58 | // find the radius in lat/lon
59 | //const rlat = (radius / EARTH_RADIUS_METERS) * r2d;
60 | //const rlng = rlat / Math.cos({coordinate.latitude * d2r);
61 |
62 | if (initialBearing > finalBearing) finalBearing += 360;
63 | let deltaBearing = finalBearing - initialBearing;
64 | deltaBearing = deltaBearing/points;
65 |
66 | for (let i=0; (i < points+1); i++)
67 | {
68 | result.push(geolib.computeDestinationPoint(coordinate, radius, initialBearing + i*deltaBearing));
69 | }
70 |
71 | return result;
72 | };
73 |
74 | /**
75 | * toNameId
76 | * @param str
77 | * @param prepend
78 | * @param append
79 | * @returns {*}
80 | */
81 | export const toNameId = (str, prepend = false, append = false) =>
82 | {
83 | str = str.toLowerCase().replace(/_/g, ' ').replace(/\b[a-z]/g, letter => letter.toUpperCase()).replace(/\s/g, '');
84 |
85 | return (prepend ? prepend : '') + str + (append ? append : '');
86 | }
--------------------------------------------------------------------------------
/src/modules/Traps.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @imports
3 | */
4 | import {toQueryParams, toLatLng, toCoordinate, toNameId} from './Tools';
5 | import * as GeoLib from 'geolib';
6 | import TrapTypes from '../constants/TrapTypes';
7 |
8 | /**
9 | * @class
10 | */
11 | export default class Traps {
12 |
13 | /**
14 | * constructor
15 | * @param apiKey
16 | * @param options
17 | */
18 | constructor(instance) {
19 | this.instance = instance;
20 | this.traps = {};
21 | this.counter = 0;
22 | }
23 |
24 | /**
25 | * execute
26 | * @param position
27 | */
28 | execute(position) {
29 | const {coordinate, heading, altitude} = position;
30 |
31 | this.__matches(coordinate, heading);
32 | }
33 |
34 | /**
35 | * Add
36 | * @param trap
37 | * @param callback
38 | * @returns {*}
39 | */
40 | add(trap, callback) {
41 |
42 | this.counter++;
43 | const counter = this.counter;
44 |
45 | this.traps[counter] = Object.assign({}, trap, {
46 | index: counter,
47 | state: TrapTypes.States.OUTSIDE,
48 | callback: callback,
49 | });
50 |
51 | Object.keys(TrapTypes.States).forEach(state => this.traps[counter][toNameId(state, "is")] = () => this.traps[counter].state === state);
52 |
53 | return this.traps[counter];
54 | }
55 |
56 | /**
57 | * getArray
58 | * @returns {any[]}
59 | */
60 | getArray()
61 | {
62 | return Object.keys(this.traps).map(id => this.traps[id]);
63 | }
64 |
65 |
66 | /**
67 | * watchRadius
68 | * @param coordinate
69 | * @param radius
70 | * @param options
71 | */
72 | watchRadius(coordinate, radius, options, callback) {
73 |
74 | return this.add({
75 | type: TrapTypes.Types.CIRCLE,
76 | coordinate,
77 | radius,
78 | options
79 | }, callback);
80 | }
81 |
82 | /**
83 | * watchStep
84 | * @param step
85 | * @param nextStep
86 | * @param options
87 | * @param callback
88 | * @returns {*}
89 | */
90 | watchStep(step, nextStep, options, callback)
91 | {
92 | options = Object.assign({}, {
93 | distance: 15,
94 | innerRadiusTolerance: 0.75,
95 | centerRadiusTolerance: 0.5,
96 | courseTolerance: 30,
97 | }, options);
98 |
99 | const distanceToNextPoint = options.distance || step.distance.value; // in meters
100 |
101 | const coordinate = step.start;
102 |
103 | return this.add({
104 | type: TrapTypes.Types.STEP,
105 | innerRadius: distanceToNextPoint * options.innerRadiusTolerance,
106 | centerRadius: distanceToNextPoint * options.centerRadiusTolerance,
107 | outerRadius: distanceToNextPoint,
108 | courseTolerance: options.courseTolerance,
109 | coordinate,
110 | step,
111 | nextStep
112 | }, callback);
113 | }
114 |
115 | /**
116 | * nextStatus
117 | * @param trap
118 | * @param status
119 | * @returns {*}
120 | */
121 | nextState(trap, event, state)
122 | {
123 | // set new status
124 | this.traps[trap.index].state = state;
125 |
126 | // resolve with status
127 | if(event.constructor == String) {
128 | trap.callback && trap.callback(trap, event, state);
129 | }
130 | }
131 |
132 | /**
133 | *
134 | * @param coordinate
135 | * @private
136 | */
137 | __matches(coordinate, heading)
138 | {
139 | const traps = Object.keys(this.traps);
140 |
141 | return traps.map(index => {
142 |
143 | const trap = this.traps[index];
144 |
145 | if(trap.state != TrapTypes.States.EXPIRED) {
146 |
147 | switch (trap.type) {
148 |
149 | case TrapTypes.Types.CIRCLE:
150 |
151 | if(GeoLib.isPointWithinRadius(coordinate, trap.coordinate, trap.radius)) {
152 |
153 | }
154 |
155 | break;
156 |
157 | case TrapTypes.Types.STEP:
158 |
159 | const insideOuter = GeoLib.isPointWithinRadius(coordinate, trap.coordinate, trap.outerRadius);
160 |
161 | const insideInner = GeoLib.isPointWithinRadius(coordinate, trap.coordinate, trap.innerRadius);
162 |
163 | const stateMap = {
164 | [TrapTypes.States.OUTSIDE]: [TrapTypes.States.ENTERED, () =>
165 | {
166 | const isWithinCourse = this.isWithinCourse(trap.step.bearing, heading, trap.courseTolerance);
167 |
168 | return insideOuter ? (isWithinCourse ? TrapTypes.Events.ENTERING_ON_COURSE : TrapTypes.Events.ENTERING_OFF_COURSE) : false;
169 | }],
170 |
171 | [TrapTypes.States.ENTERED]: [TrapTypes.States.INSIDE, () =>
172 | {
173 | return insideOuter ? TrapTypes.Events.INSIDE : false;
174 | }],
175 |
176 | [TrapTypes.States.INSIDE] : [TrapTypes.States.CENTER, () =>
177 | {
178 | return insideInner ? TrapTypes.Events.INSIDE_CENTER : false;
179 | }],
180 |
181 | [TrapTypes.States.CENTER] : [TrapTypes.States.LEAVING, () =>
182 | {
183 | const isWithinCourse = this.isWithinCourse(trap.nextStep ? trap.nextStep.bearing : trap.step.bearing, heading, trap.courseTolerance);
184 |
185 | return insideOuter && !insideInner ? (isWithinCourse ? TrapTypes.Events.LEAVING_ON_COURSE : TrapTypes.Events.LEAVING_OFF_COURSE) : false;
186 | }],
187 |
188 | [TrapTypes.States.LEAVING]: [TrapTypes.States.LEFT, () =>
189 | {
190 | return !insideOuter && !insideInner ? TrapTypes.Events.LEAVING : false;
191 | }],
192 |
193 | [TrapTypes.States.LEFT]: [TrapTypes.States.EXPIRED, () =>
194 | {
195 | return true;
196 | }],
197 | }
198 |
199 | if(stateMap[trap.state]) {
200 |
201 | const func = stateMap[trap.state];
202 | const event = func[1]();
203 |
204 | if (event) {
205 |
206 | this.nextState(trap, event, func[0]);
207 |
208 | return true;
209 | }
210 | }
211 |
212 | break;
213 | }
214 | }
215 | });
216 | }
217 |
218 |
219 | /**
220 | * Quantifier
221 | * @param bearing
222 | * @param heading
223 | * @param quantifier
224 | */
225 | isWithinCourse(bearing, heading, tolerance = 0)
226 | {
227 | const low = bearing - tolerance;
228 | const high = bearing + tolerance;
229 |
230 | return ((low < 0 && heading > (360 - (-1 * low))) || (heading > low)) && ((high > 360 && heading < (high - 360)) || (heading < high));
231 | }
232 |
233 |
234 |
235 | }
--------------------------------------------------------------------------------
/src/themes/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @import
3 | */
4 | import * as MarkerTypes from '../constants/MarkerTypes';
5 | import * as PolylineTypes from '../constants/PolylineTypes';
6 | import NavigationIcons from '../constants/NavigationIcons';
7 |
8 | /**
9 | * defaultThemeSettings
10 | * @type {{[p: string]: *}}
11 | */
12 | export const defaultThemeSettings = {
13 |
14 | /**
15 | * @markers
16 | */
17 | Markers: {
18 | [MarkerTypes.ORIGIN]: {
19 | icon: NavigationIcons.place,
20 | color: '#77dd77',
21 | fontSize: 40,
22 | },
23 |
24 | [MarkerTypes.DESTINATION]: {
25 | icon: NavigationIcons.place,
26 | color: '#ff4500',
27 | fontSize: 40,
28 | },
29 |
30 | [MarkerTypes.POSITION_DOT]: {
31 | icon: NavigationIcons.compassDot,
32 | color: '#387bc1',
33 | fontSize: 30,
34 | },
35 |
36 | [MarkerTypes.POSITION_ARROW]: {
37 | icon: NavigationIcons.navigate,
38 | size: 100,
39 | fontSize: 80,
40 | color: '#ffffff',
41 | backgroundColor: '#387bc1'
42 | },
43 | },
44 |
45 | Polylines: {
46 | [PolylineTypes.ROUTE]: {
47 | fillColor: '#00b3fd',
48 | strokeColor: '#387bc1',
49 | strokeWidth: 18,
50 | borderWidth: 4,
51 | },
52 | [PolylineTypes.ROUTE_ALTERNATIVE]: {
53 | fillColor: '#cccccc',
54 | strokeColor: '#a0a0a0',
55 | strokeWidth: 18,
56 | borderWidth: 4,
57 | },
58 | }
59 |
60 | };
61 |
62 |
63 | /**
64 | * Theme Combiner
65 | * @param theme
66 | * @returns {*}
67 | */
68 | const connectTheme = (theme) =>
69 | {
70 | return Object.assign({}, defaultThemeSettings, theme);
71 | };
72 |
73 | /**
74 | * @exports
75 | */
76 | export default connectTheme;
77 |
78 |
--------------------------------------------------------------------------------