React-native-maps: Draw route between two points

Created on 4 Jan 2017  ·  46Comments  ·  Source: react-native-maps/react-native-maps

Apparently, we are not able to draw a route between geo points at this moment. We have polylines, yes. But they are not real world paths. Is this feature on the roadmap? If it is not, What options do we have?

Can we achieve this by using both react-native-maps and native modules ?

Most helpful comment

yes, you can make a request to google, and get the points

const mode = 'driving'; // 'walking';
const origin = 'coords or address';
const destination = 'coords or address';
const APIKEY = 'XXXXXXXXXXXX';
const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${origin}&destination=${destination}&key=${APIKEY}&mode=${mode}`;

fetch(url)
    .then(response => response.json())
    .then(responseJson => {
        if (responseJson.routes.length) {
            this.setState({
                coords: this.decode(responseJson.routes[0].overview_polyline.points) // definition below
            });
        }
    }).catch(e => {console.warn(e)});

Decode function:

decode: function(t,e){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;){a=null,h=0,i=0;do a=t.charCodeAt(u++)-63,i|=(31&a)<<h,h+=5;while(a>=32);n=1&i?~(i>>1):i>>1,h=i=0;do a=t.charCodeAt(u++)-63,i|=(31&a)<<h,h+=5;while(a>=32);o=1&i?~(i>>1):i>>1,l+=n,r+=o,d.push([l/c,r/c])}return d=d.map(function(t){return{latitude:t[0],longitude:t[1]}})}
// transforms something like this geocFltrhVvDsEtA}ApSsVrDaEvAcBSYOS_@... to an array of coordinates
````

Then in your JSX:

```jsx
<MapView.Polyline
    coordinates={[
        {latitude: initial.latitude, longitude: initial.longitude}, // optional
        ...this.state.coords,
        {latitude: final.latitude, longitude: final.longitude}, // optional
    ]}
    strokeWidth={4}
/>

All 46 comments

one way to do this would be to make a request to google directions API with the two points. This returns a path that you can then plot on the map using the polyline component

yes, you can make a request to google, and get the points

const mode = 'driving'; // 'walking';
const origin = 'coords or address';
const destination = 'coords or address';
const APIKEY = 'XXXXXXXXXXXX';
const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${origin}&destination=${destination}&key=${APIKEY}&mode=${mode}`;

fetch(url)
    .then(response => response.json())
    .then(responseJson => {
        if (responseJson.routes.length) {
            this.setState({
                coords: this.decode(responseJson.routes[0].overview_polyline.points) // definition below
            });
        }
    }).catch(e => {console.warn(e)});

Decode function:

decode: function(t,e){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;){a=null,h=0,i=0;do a=t.charCodeAt(u++)-63,i|=(31&a)<<h,h+=5;while(a>=32);n=1&i?~(i>>1):i>>1,h=i=0;do a=t.charCodeAt(u++)-63,i|=(31&a)<<h,h+=5;while(a>=32);o=1&i?~(i>>1):i>>1,l+=n,r+=o,d.push([l/c,r/c])}return d=d.map(function(t){return{latitude:t[0],longitude:t[1]}})}
// transforms something like this geocFltrhVvDsEtA}ApSsVrDaEvAcBSYOS_@... to an array of coordinates
````

Then in your JSX:

```jsx
<MapView.Polyline
    coordinates={[
        {latitude: initial.latitude, longitude: initial.longitude}, // optional
        ...this.state.coords,
        {latitude: final.latitude, longitude: final.longitude}, // optional
    ]}
    strokeWidth={4}
/>

If anyone wants to extends on top of what @DevManny said.
Go through: https://developers.google.com/maps/documentation/directions/

Speaking of directions api, (this may not be the best place to discuss this)

Why Google charges developers for Directions Api while Apple provides this for free?

Hi @DevManny, thanks for sharing your insight. Now stretching your response a bit, assume working on something Uberesque, how would you go about updating both the devices position and the drawn route when in motion?

My first thought would be to listen for changes in location/position and redraw the route polyline each time but I'm not sure how performant that would be. Please any ideas or code hints? Thanks

@tolu360 I'm also stuck with the same idea. If you found out an efficient method, can you please share it with us?

@privateOmega - unfortunately, I haven't revisited the idea since posting my thoughts and questions. After observing the Uber app more closely though, I have concluded they do just what I suggested - listen for changes in driver's position and re-draw the polyline.

While on several Uber trips, I see the route redrawn every so often - I suggest, since the latlng for the destination won't change, the only change one would have to listen for is, in this case, the driver's device location, and then the polyline can be redrawn with the driver's current latlng as the starting point.

Still just my thoughts, since I'm implementing this in a project I'm about to kick off, I hope I have more insights to share afterwards.

Cheers.

@privateOmega @tolu360

I m going to build this feature for my app next week. I will update this issue.

@alioguzhan I have to submit my project by end of this week and I am in a hurry. Don't forget to update the thread as soon as you find a solution.
Thanks

Btw @privateOmega , did you try @DevManny 's solution above?

@alioguzhan As @tolu360 pointed out, I need to change routes based on current location, which would make plenty of network calls to googledirections api even if I rely on that method every 50 meters or so. That seems really inefficient and might cause delay.

@privateOmega So you need navigation basicly?
In my app, users plan a route on a particular date just for one time. So i am going to call Directions API 5 or 6 times and only for the first time. But in your scenario, yes, fetching routes real time based on current location might be expensive.

And i ve found this in Google Maps Terms:

Restrictions on your Maps API Implementations.

No navigation. You will not use the Service or Content for or in connection with (a) real-time navigation or route guidance; or (b) automatic or autonomous vehicle control.

here is the source -> https://developers.google.com/maps/terms#10-license-restrictions

heloo any one implement draw a route on road..I try with polyline its not good.I want to draw map on road ....@DevManny how your method implement
plz give the guidlines.thanks

@farrukhbashir i just typed something on this with an example code. I hope it helps you:

React Native Maps with Google Directions Api

@alioguzhan Brings up a good point, you would definitely be violating Google's Terms if you implement navigation using their APIs. Also, see this.
As for using a polyline to plot the route between 2 points, that might very well fall into the "route guidance" category and also be considered a violation. Does anyone know if this is the case?

For navigation, it is probably best to use react native's Linking component to open Apple (http://maps.apple.com/?daddr=<lat>,<long>) or Google (http://maps.google.com/maps?daddr=<lat>,<long>) Maps.

@alioguzhan @gonzalezchris @DevManny @iraycd Has anyone of you found any solution or work around ?

@GeekRishabh Check my blog post please. I made it work. It may help you.

React Native Maps with Google Directions Api

The overview_polyline returned by Google Directions is not accurate. It has a limit of points.

Is there a way to generate a call with the visible coordinates on the MapView screen?

@salbatore i have used mapbox/polyline it is better

thanks GeekRishabh !!!

Great Stuff!

@tolu360 @privateOmega Found any good solution, I'm stuck on the same problem? Currently I link to maps for routing. I get coordinates from google directions api and draw them on map. Now how am I supposed to re-draw them when the driver moves. Making a network call to google and redrawing everytime someone moves looks like an absurd solution.
@alioguzhan I did not find a solution on updating polyline with movement on your blog. Am I missing something?

@rishabhbhatia Sorry but i don't understand what did you mean by saying updating polyline with movement part.

Let's say I draw a polyline from drivers current location to customers pickup point. Now as the driver moves towards customer I need to redraw the polyline. If you ever booked a ride on Uber application, this is what you see on customer application from pickup to drop off point i.e remove the covered part.
PS Sorry this is a very wrong channel to discuss about it, but it's urgent.

@rishabhbhatia Well, keeping polylines in the state may work. You can check drivers' location periodically and get the current location as they move, Then you update the polyline in the state based on latest location. Someone has asked me something like this in comments. You can see my answer here

Hope that helps.

@rishabhbhatia what I found out after a lot of research is that, its pretty useless to do that many network calls and illegal according to google's terms and conditions. they have mentioned 'maps api should not be used for routing'.

Based upon the feedback here (and in https://github.com/airbnb/react-native-maps/issues/778) I've knocked up a standalone and immediately usable component to do just that: https://github.com/bramus/react-native-maps-directions

Hope it helps out those struggling to combine all suggestions. Installation per npm/yarn: yarn add react-native-maps-directions

Is it possible to do it with customized ArcGIS routing service instead of using Google's?

@purplehank7 it should be possible as long as you can convert the ArcGIS route to a polyline, something similar to https://github.com/react-community/react-native-maps/issues/929#issuecomment-309426229

I am having trouble writing out the proper fetch call since I can't find any examples fetching an ArcGIS REST routing API to borrow an idea from.
This was as far as I could get (of course incorrect), inherited from the google map api @alioguzhan 's example above. I removed the decoding part where was unnecessary. The error messages I have been getting are ” Network request failed.” I assume it's this part that is causing the problem.

async getDirections() {
      try {
          let resp = await fetch(`...arcgis/rest/services/.../NAServer/Route/solve&stops=<start>;<stop>&f=json`);
          let respJson = await resp.json();
          return respJson.geometry.paths;
          this.setState({coords: respJson.geometry.paths})
          return respJson.features.geometry.paths
      } catch (error) {
          console.error(error);
      }
  }

One thing that I noticed is that all examples' ulr ends with .json, but mine ends with f=json. Should I mind this?

below is the returned json:

{"messages":[],"routes":{"hasM":true,"fieldAliases":{"ObjectID":"ObjectID","Name":"Name","FirstStopID":"FirstStopID","LastStopID":"LastStopID","StopCount":"StopCount","Total_Length":"Total_Length","Shape_Length":"Shape_Length"},"geometryType":"esriGeometryPolyline","spatialReference":{"wkid":32139,"latestWkid":32139},"features":[{"attributes":{"ObjectID":1,"Name":"Location 1 - Location 2","FirstStopID":1,"LastStopID":2,"StopCount":2,"Total_Length":51.087080254770434,"Shape_Length":51.082157129724521},"geometry":{"hasM":true,"paths":[[[1082936.5425999984,3112527.5207999982,0],[1082924.1206,3112517.1875,16.159614852738379],[1082916.6080000028,3112524.9992000014,26.998637841868401],[1082909.2246999964,3112528.2650999986,35.072779614877703],[1082897.0654999986,3112517.8458000012,51.087080254769326]]]}}]},"directions":[{"routeId":1,"routeName":"Location 1 - Location 2","hasM":true,"summary":{"totalLength":0.031744316115880793,"totalTime":0,"totalDriveTime":0,"envelope":{"xmin":1082897.0654992021,"ymin":3112512.6926000006,"xmax":1082936.5426274261,"ymax":3112537.2870999984,"spatialReference":{"wkid":32139,"latestWkid":32139}}},"features":[{"attributes":{"length":0,"time":0,"text":"Start at Location 1","ETA":-2209161600000,"maneuverType":"esriDMTDepart"},"compressedGeometry":"+0+1+2+lh+m7ifhe+1vt5t06+0+0|+9+0+0"},{"attributes":{"length":0.010041206497579672,"time":0,"text":"Go southwest on Sidewalk","ETA":-2209161600000,"maneuverType":"esriDMTStraight"},"compressedGeometry":"+0+1+2+lh+m7ifhe+1vt5t06-8bf-6ug|+9+0+4h"},{"attributes":{"length":0.0067351152275328882,"time":0,"text":"Turn right on Extension_Crosswalk_Imagery","ETA":-2209161600000,"maneuverType":"esriDMTTurnRight"},"compressedGeometry":"+0+1+2+lh+m7i75v+1vt5m1m-51o+586|+9+4h+32"},{"attributes":{"length":0.0050170827443695026,"time":0,"text":"Bear left on Sidewalk","ETA":-2209161600000,"maneuverType":"esriDMTBearLeft"},"compressedGeometry":"+0+1+2+lh+m7i247+1vt5r9s-4uv+26b|+9+7j+29"},{"attributes":{"length":0.0099509116463987295,"time":0,"text":"Turn left to stay on Sidewalk","ETA":-2209161600000,"maneuverType":"esriDMTTurnLeft"},"compressedGeometry":"+0+1+2+lh+m7ht58+1vt5tg7-85q-70b|+9+9s+4g"},{"attributes":{"length":0,"time":0,"text":"Finish at Location 2, on the left","ETA":-2209161600000,"maneuverType":"esriDMTStop"},"compressedGeometry":"+0+1+2+lh+m7hkve+1vt5mfs+0+0|+9+ec+0"}]}]}

@purplehank7 The ending of your URL does not matter, it's the format that's being returned which is important. As it's JSON, you're good to go.

Since your returned JSON is different from the one from Google, you'll have to manually extract the data you need. For Google the necessary data can be found in json.routes[0].overview_polyline.points … for your response you'll to look somewhere else.

Now, I don't immediately see a likewise thing directly in the JSON you provided. The data contained in respJson.features.geometry.paths only contains points, not shapes. You'll be more lucky with the compressedGeometry entries, but I don't know how they are compressed.

Furthermore I noticed that the coordinates aren't WGS84. Is it possible that you're using EPSG:32139 coordinates? You'll need to convert these to WGS84 / EPSG:3857 first.

@bramus The spatial reference defaults to the map, but I am able to specify it to EPSG:3857.
There are dozens and dozens of parameters on solving a route on arcgis which makes the work complicated. But here is the updated returned json:

{
 "messages": [
  {
   "type": 50,
   "description": "The start time was ignored because the impedance attribute is not time-based."
  }
 ],
 "routes": {
  "hasM": true,
  "fieldAliases": {
   "ObjectID": "ObjectID",
   "Name": "Name",
   "FirstStopID": "FirstStopID",
   "LastStopID": "LastStopID",
   "StopCount": "StopCount",
   "Total_Length": "Total_Length",
   "Shape_Length": "Shape_Length"
  },
  "geometryType": "esriGeometryPolyline",
  "spatialReference": {
   "wkid": 102100,
   "latestWkid": 3857
  },
  "features": [
   {
    "attributes": {
     "ObjectID": 1,
     "Name": "Location 1 - Location 2",
     "FirstStopID": 1,
     "LastStopID": 2,
     "StopCount": 2,
     "Total_Length": 167.60819580252598,
     "Shape_Length": 59.427906932599285
    },
    "geometry": {
     "hasM": true,
     "paths": [
      [
       [
        -10724322.8159,
        3583460.3137999997,
        0
       ],
       [
        -10724337.6609,
        3583448.7839000002,
        53.017003062725067
       ],
       [
        -10724346.0527,
        3583458.2084000036,
        88.57803098621369
       ],
       [
        -10724354.483999999,
        3583462.3263999969,
        115.06794445312023
       ],
       [
        -10724369.0276,
        3583450.6850999966,
        167.60819580252169
       ]
      ]
     ]
    }
   }
  ]
 },
 "stops": {
  "fieldAliases": {
   "ObjectID": "ObjectID",
   "Name": "Name",
   "RouteName": "RouteName",
   "Sequence": "Sequence",
   "TimeWindowStart": "TimeWindowStart",
   "TimeWindowEnd": "TimeWindowEnd",
   "ArriveCurbApproach": "ArriveCurbApproach",
   "DepartCurbApproach": "DepartCurbApproach",
   "LocationType": "LocationType",
   "SourceID": "SourceID",
   "SourceOID": "SourceOID",
   "PosAlong": "PosAlong",
   "SideOfEdge": "SideOfEdge",
   "CurbApproach": "CurbApproach",
   "Status": "Status",
   "Attr_Length": "Attr_Length",
   "Cumul_Length": "Cumul_Length"
  },
  "geometryType": "esriGeometryPoint",
  "spatialReference": {
   "wkid": 102100,
   "latestWkid": 3857
  },
  "features": [
   {
    "attributes": {
     "ObjectID": 1,
     "Name": "Location 1",
     "RouteName": null,
     "Sequence": 1,
     "TimeWindowStart": null,
     "TimeWindowEnd": null,
     "ArriveCurbApproach": null,
     "DepartCurbApproach": 1,
     "LocationType": 0,
     "SourceID": 1,
     "SourceOID": 2080,
     "PosAlong": 0.34780866786644066,
     "SideOfEdge": 1,
     "CurbApproach": 0,
     "Status": 0,
     "Attr_Length": 0,
     "Cumul_Length": 0
    },
    "geometry": {
     "x": -10724331.8357,
     "y": 3583472.0433999971
    }
   },
   {
    "attributes": {
     "ObjectID": 2,
     "Name": "Location 2",
     "RouteName": null,
     "Sequence": 2,
     "TimeWindowStart": null,
     "TimeWindowEnd": null,
     "ArriveCurbApproach": 2,
     "DepartCurbApproach": null,
     "LocationType": 0,
     "SourceID": 1,
     "SourceOID": 2049,
     "PosAlong": 0.4626860043293835,
     "SideOfEdge": 1,
     "CurbApproach": 0,
     "Status": 0,
     "Attr_Length": 0,
     "Cumul_Length": 167.60819580252598
    },
    "geometry": {
     "x": -10724364.1184,
     "y": 3583444.4905999973
    }
   }
  ]
 },
 "directions": [
  {
   "routeId": 1,
   "routeName": "Location 1 - Location 2",
   "hasM": true,
   "summary": {
    "totalLength": 0.03174397647775113,
    "totalTime": 0,
    "totalDriveTime": 0,
    "envelope": {
     "xmin": -10724369.242406176,
     "ymin": 3583443.0216353368,
     "xmax": -10724322.408664444,
     "ymax": 3583473.3569995621,
     "spatialReference": {
      "wkid": 102100,
      "latestWkid": 3857
     }
    }
   },
   "features": [
    {
     "attributes": {
      "length": 0,
      "time": 0,
      "text": "Start at Location 1",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTDepart"
     },
     "compressedGeometry": "+0+1+2+68-1vtg29i+lbfmiv+0+0|+3+0+0"
    },
    {
     "attributes": {
      "length": 0.010041099064911036,
      "time": 0,
      "text": "Go southwest on Sidewalk toward Extension_Crosswalk_Imagery",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTStraight"
     },
     "compressedGeometry": "+0+1+2+68-1vtg29i+lbfmiv-2sp-282|+3+0+4v",
     "strings": [
      {
       "string": "Sidewalk",
       "stringType": "esriDSTStreetName"
      },
      {
       "string": "Extension_Crosswalk_Imagery",
       "stringType": "esriDSTCrossStreet"
      }
     ]
    },
    {
     "attributes": {
      "length": 0.0067350431673275091,
      "time": 0,
      "text": "Turn right on Extension_Crosswalk_Imagery",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTTurnRight"
     },
     "compressedGeometry": "+0+1+2+68-1vtg56b+lbfkat-1kf+1qt|+3+4v+3b",
     "strings": [
      {
       "string": "Extension_Crosswalk_Imagery",
       "stringType": "esriDSTStreetName"
      }
     ]
    },
    {
     "attributes": {
      "length": 0.0050170290657016933,
      "time": 0,
      "text": "Bear left on Sidewalk",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTBearLeft"
     },
     "compressedGeometry": "+0+1+2+68-1vtg6qq+lbfm5q-1km+pn|+3+8a+2f",
     "strings": [
      {
       "string": "Sidewalk",
       "stringType": "esriDSTStreetName"
      }
     ]
    },
    {
     "attributes": {
      "length": 0.0099508051798108919,
      "time": 0,
      "text": "Turn left to stay on Sidewalk",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTTurnLeft"
     },
     "compressedGeometry": "+0+1+2+68-1vtg8fg+lbfmvh-2qt-28o|+3+ap+4u",
     "strings": [
      {
       "string": "Sidewalk",
       "stringType": "esriDSTStreetName"
      }
     ]
    },
    {
     "attributes": {
      "length": 0,
      "time": 0,
      "text": "Finish at Location 2, on the left",
      "ETA": -2209161600000,
      "maneuverType": "esriDMTStop"
     },
     "compressedGeometry": "+0+1+2+68-1vtgbad+lbfkmp+0+0|+3+fn+0"
    }
   ]
  }
 ]
}

I agree that compressedGeometry seems like the way to go. As for decoding, I found this two articles: Do you think they are the solution?
https://www.arcgis.com/home/item.html?id=feb080c524f84afebd49725d083b56ae
http://resources.esri.com/help/9.3/arcgisengine/ArcObjects/esrinetworkanalyst/INACompactStreetDirection_CompressedGeometry.htm

@purplehank7 Not doing custom routing via ArcGIS myself, so that's for you to find out ;-)

use npm package react-native-maps-directions

follow this link

import MapViewDirections from 'react-native-maps-directions';

const origin = {latitude: 37.3318456, longitude: -122.0296002};
const destination = {latitude: 37.771707, longitude: -122.4053769};
const GOOGLE_MAPS_APIKEY = '…';

<MapView initialRegion={…}>
  <MapViewDirections
    origin={origin}
    destination={destination}
    apikey={GOOGLE_MAPS_APIKEY}
  />
</MapView>

Speaking of directions api, (this may not be the best place to discuss this)

Why Google charges developers for Directions Api while Apple provides this for free?

Is this why my Im getting Error: [TypeError: undefined is not an object (evaluating 'respJson.routes[0].overview_polyline')] because the API I used is free?

@jiveRed You need to enable Billing for your API Key / Project

Hi @bramus, I just want to make sure if the free API causes the error?

@jiveRed Not that specific error, but certainly "a" error. What also could be going on is that no route was returned. Log out the response you get back to verify.

@bramus You’re right. Actually, I already availed the service base on your suggestion and now I’m getting “index=6, count=2” error on ‘coords’.

How can I make search in react-native-maps.

Here I have shown the map in my simulator. How can I search the two places(i.e) from a point to another point in react-native-maps

Well nihp I am not sure what you meant by search exactly but if you want to find direction between two points you can check my answer above link hope it helps

@saqygee I meant in google map we search for I need to go from (origin) and (destination).

Like the same can we implement this in react-native-maps package.

I don't find any example related this in this package. All examples are showing the maps and markers.

Yes you can get directions from point A to point B using that package. I have also used this package to get directions. Just make sure you have Google Map api key with Google Maps Directions API enabled. check more on their documentation.

Edit: if you meant purely implementing it react-native-maps Yes you can but it will be little complicated you will need use Google Maps Directions API and then draw polygons yourself which you get from Google Directions. similar to this link

I have another clrification.

  1. If I use google map I need to use google API with pay. Can I able to use API for some initial free of cost
  2. If I use native apple maps here. How can I add the directions from point A to point B using this package
  1. If you want to go for free please use leaflet maps they are very good. but you have to sacrifice Map component for webView. checkout Leaflet React Native and Leaflet Routing for Directions
  2. I never worked with Apple Maps but they are free too as per React-Native and Apple Maps but I am not sure about directions api if you use apple maps.

Hope it helps

Based upon the feedback here (and in #778) I've knocked up a standalone and immediately usable component to do just that: https://github.com/bramus/react-native-maps-directions

Hope it helps out those struggling to combine all suggestions. Installation per npm/yarn: yarn add react-native-maps-directions

thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AzarouAmine picture AzarouAmine  ·  52Comments

speller picture speller  ·  63Comments

thiagoterleski picture thiagoterleski  ·  55Comments

radubatori picture radubatori  ·  46Comments

l1fe picture l1fe  ·  48Comments