Geo Series [Part 1]: Google Maps + Driving Directions + Mapbox

Geo Series Introduction

When I used to be building applications with React, the projects that involved any sort of mapping were always a pain. The moment that I thought I had mastered the package I was using (Mapbox, ArcGIS, Kepler.gl, Google) one bug or additional requirement would derail my whole understanding and I felt like I actually knew nothing. That is why I'm excited that I have Retool to do a lot of the heavy lifting for me nowadays. Even still Retool's integrations and components with geo services components are very powerful... if you know how to use them. This post will be the first of a series with the goal of giving tangible examples of how to use different geo-focused Resource Integrations and the Mapbox component so you can learn, adapt and deploy maps in your applications.

Part 1: Google Maps + Driving Directions + Mapbox

This post will explain a few key areas around:

  • Google Maps Integration beta (Retool docs) and reverse geocoding
  • REST API to make Google driving directions API calls (Google docs) and decoding the responses polyline
  • Retool's Mapbox component's and GeoJSON layer to map the decoded polyline

At the end, this post will also provide information for you to set this application up yourself. If you like what you see in this post, consider signing up for a free trial or commenting on the post.

tl;dr;

Check out with the Retool application

In this application you can change the lat,long of Origin and Destination, reverse Geocode to get address information about those points, get driving directions between the two points and visualize them in Retool's Mapbox component.

Public application here

Download the Retool application

Download the Retool application and see how we've used Google Maps, Driving Directions API, and Mapbox GeoJSON layer to build the application you see above. Connect with your own Google Maps API key and customize to your requirements.

Geo Series Part 1: Google Maps Reverse Geolocate, Driving Directions and MapBox.json (534.1 KB)

Application Walkthrough

Google Map Reverse Geocode

The origin and destination tabs are lightweight examples of taking a latitude/longitude, using the Google Maps API and retrieving the closest known address to the geolocation. Under the resource configuration, we've chosen the /maps/api/geocode/json endpoint with a query string of latlng=<lat>,<lng>; configured like the following:


In this case since we're providing the GPS location, like that of a user's device, and reverse geocoding the location information. If we had an address, like that of a user's input of their delivery address, and used this same endpoint, it would be considered geocoding to retrieve the GPS location. There are reasons to use them both and Retool supports either depending on what query string you provide. In this case, we wanted to reverse geocode because we had pre-determined latitude and longitude,

Address Components

Google Maps API returns one to many close by results and for each it has the full formatted address as well as many address components for us to parse. In the Extract Address section, we have a quick example of details you can extract from the api response, if you know what you're looking for. Here is an example of the address_components key from one of the results in the response:

[
  {
    "long_name": "415A",
    "short_name": "415A",
    "types": [
      "street_number"
    ]
  },
  {
    "long_name": "Taylor Street",
    "short_name": "Taylor St",
    "types": [
      "route"
    ]
  },
  {
    "long_name": "Tenderloin",
    "short_name": "Tenderloin",
    "types": [
      "neighborhood",
      "political"
    ]
  },
  {
    "long_name": "San Francisco",
    "short_name": "SF",
    "types": [
      "locality",
      "political"
    ]
  },
  {
    "long_name": "San Francisco County",
    "short_name": "San Francisco County",
    "types": [
      "administrative_area_level_2",
      "political"
    ]
  },
  {
    "long_name": "California",
    "short_name": "CA",
    "types": [
      "administrative_area_level_1",
      "political"
    ]
  },
  {
    "long_name": "United States",
    "short_name": "US",
    "types": [
      "country",
      "political"
    ]
  },
  {
    "long_name": "94102",
    "short_name": "94102",
    "types": [
      "postal_code"
    ]
  },
  {
    "long_name": "1701",
    "short_name": "1701",
    "types": [
      "postal_code_suffix"
    ]
  }
]

Given that this is returned as a collection of objects, to "find" a particular value (e.g., country) you're interested in, the application has a transformer that will parse out specific keys from the component. Here is the simple Javascript function we used; you provide the result from, the type and the key you are looking for, and the JS will do the rest.

const findComponent = (reverseGeo, type, key) => {
  const found = _.find(reverseGeo.address_components, (c)=>{
    return (c.types.indexOf(type) >-1)
  })
  if (found) { return found[key] }
  return ''
}

For example, if you wanted to grab the two digit country code for the reverse geocode you just performed in Retool, you could use findComponent(apiResponse.data.results[0].address_components, 'country', 'short_name') within your transformer.

Google Maps Driving Direction

Retool has not included the directions API into its Google Maps integration yet, but that doesn't stop us from being able to POST our origin and destination to retrieve the step by step directions and coordinates along the route to make a line on our map.

Retool application

Resource Page

Resource Query


In this example we create lat,lng pairs for origin and destination url parameters, and POST the request to https://maps.googleapis.com/maps/api/directions/json with an additional URL parameter of key inserted on our request from the resource page which you'll create during setup. Nothing needs to go in the request body for this POST.

Polyline Decode

The Google Maps directions API returns a lot of data about all the routes you can take along with the step by step directions. Each route also contains a line that we can draw on the map, however the first problem you'll see is the data format is encoded and looks like:

scseFjncjVi_IinUinb@w`Ek~z@}hn@anm@euLilw@mx@}z|Ax~o@}cvC~oCsp~A`zZwop@qsBmi_@dlKkmg@kjXkqg@vv\}uz@vpf@{_m@}eDm_Yjo@}u\loa@q~\fw_@eoC`ij@}|Jnec@av[nyC_sh@ygZy|f@xjQu|o@|c@w`y@mrW}eZskGs}_@io_@}wyC|}Fkrc@}_LizhAglh@w~u@kwQ}d^c{Cozq@vn_@{joAdeOygjAxkQipr@cnVa}Jkqr@{_Ywj]mj\{q_@_es@jgE{lr@e`Uihc@szCwkj@|rZekbA~|GgfLv{Qke]}fQ}oY{mTmyXo}tA_jg@isq@ixEsgTidUovFycYqi@wja@p{Bs~rAjlXucJcx_@s~`@slPwbe@~pHqv\vWq|QlnJuo^~ja@upk@_ef@sucAirR{jd@~|bAwh`Axe~@szSlyj@k|b@ldm@kceAsdAyd[tgPwpSojWmoc@zbTy`}@fmb@a{s@vkLzxFt{W}mKfr{BymY~f~CkoF|izBw_\zznCyhh@`yzAolQlks@z`Vp{y@w_j@l|XqqMjde@gtj@|nk@wcu@bjX_rGl`l@v_SjaX|gEb`t@s|t@lrKk|i@dd|Asno@hvhA}kg@dzy@i}k@`bQ_pMmlKqxR|`Romd@fu~@avWj|m@wvZh|d@q`_@rtUs{c@hfAoto@cwOmqn@sij@__ZlfOyuS{mGag_@e~\seaAhga@cag@luGuse@uwL{eIw{\}{XkeSyhJnhNkkL_uPukImyt@kpMgcz@_ul@nzKskU_}NsyWmo@qgT{fFmbRj}Ia~\nv{CdcS||nCpyIxobAtdOryd@rgGhg`AuqTls|Byi_@v~qBwrn@nqnA}|Jtib@|lN~y~@zaMxrl@}iV|ry@kwUrpvAwk_@lkrAysTxjqAp|P`hmBohJhyyClvHlncC{lk@zsqBuUjnx@yaIzxHw|m@tdeAqv`Ajd}C_|dAxqjDw`q@heY}fU~c`@{p\zrGcn^ru^sxP`o^sxk@dn}Amg~@xmnD_`@|p^lbEzco@tpN|zCtyJfce@tyJhiLhrX|lJhtDnxl@rgUdtW`qRxlu@ntNzikBlk_AzrkCflUxp`@dqNj_TbP|`r@j}A`_{AdlKhm_AbnQd|mBpgYd|pA|TphjCfuUnrlCfhV`bVhqQd~]f}m@dyuAbaJdmPntO_yCb}Lm{Wh|N{neA|_Badn@|mLgdUgGbjM~aUzag@adHb{^fxk@va]lcM}uh@dbRgtBl`u@|oP

Our javascript transformer in this app takes care of the heavy lifting for you and it uses a pattern from jhermsmeier/node-google-polyline repository. The transformer decodes it into something a bit more useful that we can than create GeoJSON out of:

[ 
  [ -122.41142, 37.78634 ],
  [ -122.29633, 37.83759 ],
  [ -122.26533, 38.01924 ],
  [ -122.0231, 38.32634 ],
  [ -121.95299, 38.56427 ],
  [ -121.9438, 38.85312 ],
  [ -122.19465, 39.33375 ],
  [ -122.21785, 40.10766 ],
  [ -122.36042, 40.59688 ],
  [ -122.34177, 40.85044 ],
  [ -122.40532, 41.01595 ],
  [ -122.2755, 41.22305 ],
  [ -122.42778, 41.43079 ],
  [ -122.6303, 41.73654 ],
  [ -122.60359, 41.9722 ],
  [ -122.61133, 42.10539 ],
  [ -122.78804, 42.25754 ],
  [ -122.95576, 42.41107 ],
  [ -123.17753, 42.43414 ],
  [ -123.36289, 42.49525 ],
  [ -123.38761, 42.6423 ],
  [ -123.24796, 42.85542 ],
  [ -123.34201, 43.05987 ],
  [ -123.34792, 43.31038 ],
  [ -123.22193, 43.60762 ],
  [ -123.17895, 43.74697 ],
  [ -123.0125, 43.91571 ],
  [ -123.05329, 44.70818 ],
  [ -122.98658, 44.8956 ],
  [ -122.77454, 45.27373 ],
  [ -122.67848, 45.55529 ],
  [ -122.6535, 45.71496 ],
  [ -122.81986, 45.97536 ],
  [ -122.90277, 46.38686 ],
  [ -122.99698, 46.77227 ],
  [ -122.8768, 47.03616 ],
  [ -122.61274, 47.09729 ],
  [ -122.45726, 47.23055 ],
  [ -122.2904, 47.38086 ],
  [ -122.32246, 47.64806 ],
  [ -122.20963, 47.9114 ],
  [ -122.18473, 48.09721 ],
  [ -122.32616, 48.31941 ],
  [ -122.37192, 48.6644 ],
  [ -122.46868, 48.73212 ],
  [ -122.37525, 48.88674 ],
  [ -122.26535, 49.02257 ],
  [ -121.82527, 49.1548 ],
  [ -121.56602, 49.36136 ],
  [ -121.45712, 49.39613 ],
  [ -121.41752, 49.50962 ],
  [ -121.41071, 49.64351 ],
  [ -121.43064, 49.81947 ],
  [ -121.56078, 50.24949 ],
  [ -121.39292, 50.30656 ],
  [ -121.3037, 50.48058 ],
  [ -121.35266, 50.67574 ],
  [ -121.35662, 50.82799 ],
  [ -121.41541, 50.92488 ],
  [ -121.59141, 51.08627 ],
  [ -121.39077, 51.31438 ],
  [ -121.2904, 51.66616 ],
  [ -121.63824, 51.8575 ],
  [ -121.96189, 52.19186 ],
  [ -122.18628, 52.29868 ],
  [ -122.42267, 52.48258 ],
  [ -122.41153, 52.84168 ],
  [ -122.49996, 52.98597 ],
  [ -122.37524, 53.09121 ],
  [ -122.48338, 53.27816 ],
  [ -122.66486, 53.59589 ],
  [ -122.73346, 53.86662 ],
  [ -122.86093, 53.82664 ],
  [ -123.49889, 53.89047 ],
  [ -124.31425, 54.02596 ],
  [ -124.94576, 54.06442 ],
  [ -125.68238, 54.21302 ],
  [ -126.15247, 54.42451 ],
  [ -126.4207, 54.51883 ],
  [ -126.72223, 54.40077 ],
  [ -126.85494, 54.62105 ],
  [ -127.05036, 54.6957 ],
  [ -127.27819, 54.91926 ],
  [ -127.40797, 55.1965 ],
  [ -127.6386, 55.2405 ],
  [ -127.76698, 55.13798 ],
  [ -128.03852, 55.10583 ],
  [ -128.10307, 55.38193 ],
  [ -128.58006, 55.60167 ],
  [ -128.95755, 55.84993 ],
  [ -129.25886, 56.0568 ],
  [ -129.35151, 56.28693 ],
  [ -129.28792, 56.36133 ],
  [ -129.38551, 56.4627 ],
  [ -129.71163, 56.65446 ],
  [ -129.95185, 56.78103 ],
  [ -130.14598, 56.92307 ],
  [ -130.26208, 57.08716 ],
  [ -130.27349, 57.27606 ],
  [ -130.18771, 57.52526 ],
  [ -129.96585, 57.76885 ],
  [ -130.04896, 57.90709 ],
  [ -130.00562, 58.01314 ],
  [ -129.85215, 58.17827 ],
  [ -130.02756, 58.51725 ],
  [ -130.07211, 58.72239 ],
  [ -130.0016, 58.92026 ],
  [ -129.84852, 58.97256 ],
  [ -129.7451, 59.10519 ],
  [ -129.82342, 59.16308 ],
  [ -129.73286, 59.23162 ],
  [ -129.45727, 59.28485 ],
  [ -129.15451, 59.35931 ],
  [ -129.22035, 59.59323 ],
  [ -129.13875, 59.70789 ],
  [ -129.131, 59.83503 ],
  [ -129.0939, 59.94392 ],
  [ -129.14996, 60.04175 ],
  [ -129.95244, 60.1952 ],
  [ -130.68939, 60.09213 ],
  [ -131.03512, 60.03668 ],
  [ -131.22882, 59.95385 ],
  [ -131.56295, 59.91151 ],
  [ -132.20622, 60.02202 ],
  [ -132.79498, 60.18759 ],
  [ -133.20242, 60.43139 ],
  [ -133.38333, 60.4925 ],
  [ -133.71021, 60.41347 ],
  [ -133.94378, 60.34133 ],
  [ -134.24393, 60.46084 ],
  [ -134.69219, 60.57738 ],
  [ -135.11914, 60.74326 ],
  [ -135.54087, 60.85411 ],
  [ -136.10552, 60.76234 ],
  [ -136.89821, 60.82018 ],
  [ -137.57652, 60.77035 ],
  [ -138.16354, 60.99785 ],
  [ -138.45784, 61.00148 ],
  [ -138.50806, 61.05313 ],
  [ -138.86737, 61.29341 ],
  [ -139.67719, 61.62998 ],
  [ -140.55572, 61.9879 ],
  [ -140.68985, 62.24418 ],
  [ -140.85961, 62.35809 ],
  [ -140.90375, 62.50943 ],
  [ -141.06609, 62.67057 ],
  [ -141.22738, 62.76171 ],
  [ -141.71109, 62.99109 ],
  [ -142.60946, 63.315 ],
  [ -142.77105, 63.32028 ],
  [ -143.01759, 63.28901 ],
  [ -143.04254, 63.20938 ],
  [ -143.23778, 63.14879 ],
  [ -143.30599, 63.0882 ],
  [ -143.36454, 62.95711 ],
  [ -143.59902, 62.9281 ],
  [ -143.72529, 62.81408 ],
  [ -144.00398, 62.71391 ],
  [ -144.55868, 62.63367 ],
  [ -145.27866, 62.304 ],
  [ -145.45047, 62.18924 ],
  [ -145.55805, 62.10953 ],
  [ -145.81948, 62.10679 ],
  [ -146.29053, 62.09169 ],
  [ -146.6205, 62.02814 ],
  [ -147.18837, 61.93356 ],
  [ -147.60776, 61.79907 ],
  [ -148.32097, 61.79556 ],
  [ -149.04601, 61.67936 ],
  [ -149.16426, 61.56012 ],
  [ -149.32285, 61.46503 ],
  [ -149.76736, 61.22467 ],
  [ -149.85667, 61.16801 ],
  [ -149.83203, 61.08265 ],
  [ -149.7046, 61.01127 ],
  [ -149.34366, 60.92978 ],
  [ -149.10221, 60.91427 ],
  [ -148.98873, 60.84532 ],
  [ -149.06219, 60.84664 ],
  [ -149.26745, 60.73352 ],
  [ -149.43067, 60.78041 ],
  [ -149.58471, 60.55109 ],
  [ -149.37112, 60.4787 ],
  [ -149.35236, 60.38091 ],
  [ -149.44211, 60.1042 ]
]

Mapbox GeoJSON layer

Retool's Mapbox component has the option to place an additional layer onto the map via GeoJSON, an industry standard for encoding geographic data. Using the decoded polyline from above and creating a geoJSON object, we can tell Retool's Mapbox component to draw it with an object like this:

{
  type: 'FeatureCollection',
  {
    type: "Feature",
    geometry: {
      type: "LineString",
      coordinates: decodedPolyLine
    }
  }
}

This will create a new layer on the map (outside of the normal points that are drawn), which can also be controlled by styling like this:

{ 
  type: "line",
  paint: { 
    "line-width": 2,
    "line-color": "navy"
  } 
}

It's important to note that Retool only supports one new layer (as of writing this article), however my next post will be showing how you can do dynamic styling of that new layer. With all of these pieces, you can go from lat,long to a map with a route in Retool.

Setup in your own environment

  1. Login to your Retool environment or start a free trial
  2. Download the application.json from the top of the article
  3. Import the application
    image
  4. The app comes preloaded with mock API calls and you can stop here if you're just investigating; if you'd like to set it up completely
  5. Go get a Google Maps developer key
  6. Head over to /settings/beta if you're an admin, turn on the Google Maps Beta. If not ask your admin to turn on the beta
  7. Head over to /resources and create a new resource, the first being the Google Maps integration
    6a. Paste in your API key from step 4 and save
  8. Create another new resource, this time for a REST endpoint
    7a. Place https://maps.googleapis.com/maps/api/directions in base url
    7b. In URL parameters, create key and paste in your key from Step 4 and save.
  9. Open up the application you imported in Step 2
  10. Update the resources to your newly created resources
    9a. Change resource query lookupPoint1 to the resource in Step 6, save changes
    9b. Change resource query lookupPoint2 to the resource in Step 6, save changes
    9c. Change resource query lookupDirections to the resource in Step 7, save changes
  11. Edit origin or destination lat long and click the buttons to Get Address and Get Directions
  12. Head over to the map and see your points and polyline

Walkthrough Wrap-up

Above we covered quite a few topics at a high level, gave you tangible assets to use in case you have a similar use case; I hope they were useful. Feel free to download the application above, install in your Retool environment, setup your own resources and post any questions you may have. When Part 2 is coming soon and will feature more learning around the GeoJSON layer + database GIS functions.

8 Likes