Matrix Routing with ReactJS to Optimize a Shopping Plan
Tags: javascript, geocoding, routing
Published: 2021-11-03 05:31:17

---
title: Matrix Routing with ReactJS to Optimize a Shopping Plan
published: true
description: solution for matrix routing to optimizing a route with multiple stops and minimize distance traveled
tags: javascript,geocoding,routing
cover_image: https://cdn2.hubspot.net/hubfs/2174253/HEREblog_newrealitystory1_body1-1.jpg
canonical_url: https://developer.here.com/blog/here-matrix-routing-with-reactjs-to-optimize-your-black-friday
---
The annual tradition known as Black Friday leads many to go shopping at retail stores. Getting a route from your current location to another is a straight-forward navigation tool we’ve used many times. If we have multiple stops, how do we plan the most optimal route between them? That’s a job for Matrix Routing.

# Project

*Morpheus: Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.*

For this project, we’ll demonstrate using the HERE [Places](https://developer.here.com/documentation#places) and [Routing](https://developer.here.com/documentation#routing) APIs with ReactJS to plan a series of stops.

If you need to go to multiple stores such as Walmart, H-E-B, Gamestop and Kohl’s it may not matter which location or order you need to make the stops. To make the best use of your time as seen in the following screen capture, we can search for these stores and see the closest option highlighted in green when taking driving times and traffic into account. As we select a waypoint, the next closest store is highlighted from the remaining groups.

![matrix demo](https://cdn2.hubspot.net/hubfs/2174253/matrix-routing.gif)

Selecting multiple waypoints leads to an optimized shopping plan. As you build applications, taking into account where somebody is and where they are going can provide a much richer user experience by considering the context of location.

# Getting Started

As with other ReactJS tutorials, we’ll start with `create-react-app` as a project structure.

We’ll use a few other libraries as well, **axios** for making HTTP requests, **react-bootstrap** for ready-made react components of the Bootstrap library, and **styled-components** because what I’ve always felt like I was missing in my life was the ability to set CSS properties more easily in JavaScript (🖤 web development).

Here are the commands to create the app, install the dependencies, and then start the development web server:

```
create-react-bootstrap app
cd app
npm install --save styled-components axios react-bootstrap
npm start
```

* https://github.com/facebook/create-react-app
* https://react-bootstrap.github.io/
* https://www.styled-components.com/

# Components

We will develop a few React components that encapsulate the view and behavior of our user interface.

* **StartLocation.js** is a component that will display a form for describing the starting location. We can change the latitude and longitude in the form or click on the globe icon to use our current location.

* **PlaceSearch.js** is a component that displays a form to search for places near our starting location. The results are displayed in a list by distance so that the user can select one.

* **PlacePlanner.js** is a component that is used for planning a route across multiple place searches. It uses the Matrix Routing algorithm to find the next nearest waypoint.

* **App.js** is a component to wrap up everything into an application that includes the *StartLocation* and *PlacePlanner*.

# App

Starting from the top most component, we define our App which is composed from the *StartLocation* and *PlacePlanner* components. We maintain the geocordinates of our origin as state in the App so that when it is changed by the StartLocation component we can keep things consistent in the PlacePlanner by passing them down as properties. You can see the view being defined by the render method:

```javascript

render() {
    return (
      <div className="App">
        <StartLocation
            lat={this.state.start.lat}
            lng={this.state.start.lng}
            key="MyLocator"
            onChange={this.onLocationChanged}
            onLocate={this.onLocate}
            />

        <Wrapper>
          <p>Search for nearby places.</p>
          <PlacePlanner
            app_id={this.state.here.app_id}
            app_code={this.state.here.app_code}
            lat={this.state.start.lat}
            lng={this.state.start.lng}
          />
        </Wrapper>
      </div>
    );
  }

```

The `onLocationChanged()` method passed to the *StartLocation* component is used for any changes made to the text forms.

```javascript

onLocationChanged(e) {
    e.preventDefault();

    let state = this.state;
    state['start'][e.target.id] = e.target.value;
    this.setState(state);
  }

```

The `onLocate()` method is there for handling HTML5 geolocation API to use the current location detected by the browser.

```javascript

onLocate(e) {
      e.preventDefault();

      const self = this;
      navigator.geolocation.getCurrentPosition(function(position) {
        self.setState({
          start : {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
            }
        });
      });
  }

```

# StartLocation

The *StartLocation* is not much more than a simple Bootstrap form for collecting user input since the behaviors are passed in as properties.

```javascript

render() {
  return (
    <Wrapper>

      <Grid>

        <Row>

        <Col xs={4} md={4}>
          <ControlLabel>Latitude</ControlLabel>
          <FormControl
            type="text"
            bsSize="sm"
            id="lat"
            key="lat"
            value={this.props.lat}
            onChange={ this.onChange }
          />
        </Col>

        <Col xs={4} md={4}>
          <ControlLabel>Longitude</ControlLabel>
          <FormControl
            type="text"
            bsSize="sm"
            id="lng"
            key="lng"
            value={this.props.lng}
            onChange={ this.onChange }
          />
        </Col>

        <Col xs={4} md={4}>
          <br/>
          <Button onClick={this.onLocate}>
            <Glyphicon glyph="globe"/>
          </Button>
        </Col>

        </Row>

        <Row>
        <FormControl.Feedback />
        </Row>

        </Grid>
    </Wrapper>
  );
}
```

In order to render the `Glyphicon` you will need to update the **public/index.html** to pull in the bootstrap css from a CDN. The addition of the `<Wrapper>` was just a simple styled-component for additional presentation customization.

```javascript

const Wrapper = styled.section`
  padding: 1em;
  background: papayawhip;
`;
```

# PlaceSearch

We’re going to skip over the *PlacePlanner* component for a moment to take a closer look at the *PlaceSearch* component first. In this component we start making use of the HERE Location services to search for places.

Digging into the `render()` method, we need a form that allows us to enter a search query. The `onChange()` and `onKeyPress()` are typical behavior of form entry so that state is maintained and the user can either click the search button or press `return` to trigger a places search.

```javascript

<FormGroup><InputGroup>
        <FormControl
          type="text"
          bsSize="sm"
          id={"destination" + this.props.idx}
          key={"destination" + this.props.idx}
          placeholder="Store Name"
          onChange={ this.onChange }
          onKeyPress={ e => { if (e.key === 'Enter') { this.onSearch(e); }}}
        />
        <InputGroup.Addon>
          <Glyphicon glyph="search" onClick={ this.onSearch } />
        </InputGroup.Addon>
    </InputGroup></FormGroup>

```

Additionally in the `render()` method we are displaying the search results but we’ll come back to that. The Places API can be used for finding specific places with a text string. Unlike with the HERE Geocoder, this is not matching by address but by the name of a place and returning a set of results. You can use the HERE Maps API for JS which includes functions for displaying places. You’d use the same trick of `window.H` as described in the tutorial on how to Use HERE Interactive Maps with ReactJS to make it work. Since this project isn’t displaying a map and is just a simple GET request, I am using **axios**.

```javascript

onSearch(e) {
        const self = this;
        axios.get(
          'https://places.api.here.com/places/v1/discover/search',
          {'params': {
            'app_id': self.props.app_id,
            'app_code': self.props.app_code,
            'q': self.state.q,
            'size': 10,
            'at': self.props.lat + ',' + self.props.lng
          }}).then(function (response) {
            self.setState({results: response.data.results.items});
            self.addPlaces(self.props.idx, response.data.results.items, self.props.lat, self.props.lng);
          });
    }
```

A few notes about the parameters. You need the *app_id* and *app_code* typically used with any HERE developer account. The text query is given by the q parameter. I’ve limited the size to the 10 closest matches based on distance and given the at as the location from which to do a proximity search.

Once the results are fetched from the request, we call `setState` which triggers the component to re-render as part of the typical React lifecycle. The rest of the render() method will use these results to display the search result listings.

We have a helper method called `decodeVicinity()` to help process our Places response. The attribute typically has a HTML `<br/>` element which we don’t want so can strip it out.

```javascript

const decodeVicinity = function(raw) {
  var e = document.createElement('div');
  e.innerHTML = raw;
  return e.childNodes[0].nodeValue;
}
```

In our `render()` implementation we also loop over the results to make a list of items. Each item represents a place from our search in a list. In addition to simply listing all the matches there are two cases to handle. First, if the user has made a selection the **className** can be set to active which will cause it to be highlighted by Boostrap. Second, if the item is the next closest destination we will color it green by using the **bsStyle** attribute and setting it to *success*.

```javascript

// Build up listing of locations that match query
let destinations = [];
if (self.state.results.length > 0) {
  self.state.results.forEach(function(item) {
    let option = (
      <ListGroupItem
          id={item.id}
          key={item.id}
          onClick={self.onSelect}
          header={item.title}
          className={self.state.selected === item.id ? "active" : ""}
          bsStyle={self.props.nearest === item.id ? "success" : "info" }
          >
      <Fragment>{decodeVicinity(item.vicinity)}</Fragment>
      </ListGroupItem>
    );
    destinations.push(option);
    // ({item.position[0]}, {item.position[1]})
  });
}
```

These items are then simply included in a `<ListGroup>`.

```javascript

<ListGroup>
  { destinations }
</ListGroup>
```

When a user selects one of the destinations, in addition to highlighting it making it *active* there is some behavior. By making a selection of a waypoint, this should trigger a new search for the next closest destination among remaining groups.

```javascript

onSelect(e) {
        this.setState({'selected': e.currentTarget.id});
        this.props.findNearest(this.props.idx);
    }
```

The `findNearest()` method is defined in the *PlacePlanner* component and passed down as a property so we’ll look at that component next.

# PlacePlanner

The *PlacePlanner* component handles the optimization of our path by using the Matrix Routing algorithm across multiple places.

The `render()` method makes use of our reusable `<PlaceSearch/>` component. A few properties are passed down for its behaviors as well as the methods defined in this component for identifying the next nearest destination and maintaining state of which places have been selected. The full definition of this method can be found in the full source code listing from the github repository.

```javascript

<PlaceSearch
        idx={0}
        app_id={ this.state.app_id }
        app_code={ this.state.app_code }
        lat={ this.props.lat}
        lng={ this.props.lng}
        nearest={ this.state.nearest.id }
        addPlaces={ this.addPlaces }
        findNearest={ this.findNearest }
        />
```

The `addPlaces()` method is called from the *PlaceSearch* component to maintain a list of all potential destinations. That is, from our starting location by searching for “Walmart” we found 10 possible locations of Walmart we could go to. As we search for “H-E-B” we identify 10 more possible locations for a total of 20 different options to choose from. The selected state is being maintained because once we’ve identified a single Walmart to go to, we limit our list of remaining options to the 10 H-E-B locations. That’s what the following snippet demonstrates.

```javascript

addPlaces(idx, results, lat, lng) {

        // Update places with new search results
        let places = this.state.places;
        places[idx] = results;

        // Combine all results across searched places where a selection has
        // not yet been made as our options for the next destination
        let options = [];
        for (var p in places) {
            if (typeof this.state.selected[p] === 'undefined') {
                for (var o in places[p]) {
                    options.push({
                        lat: places[p][o].position[0],
                        lng: places[p][o].position[1],
                        id: places[p][o].id,
                    });
                }
            }
        }

        // If there are no more options then we are done searching
        if (options.length === 0) {
            return;
        }

        // To be continued
        ...
    }

```

Given a list of 40 potential options, how do I get started? The HERE Routing API supports Requesting a Matrix of Routes. This means that given N starting locations and M destination locations we can query the cost factor of going to each as a matrix. The cost factor is a representation in our case of the time it would take by driving in traffic to get to a given location. From the destination with the lowest cost factor, we can make a recommendation for the optimized path across multiple places. Once at that next destination, we can further compute from the remaining options the next best location to route to.

In our case, we are looking at a 1:M query as demonstrated in the next snippet:

```javascript

addPlaces(idx, results, lat, lng) {

        ...
        // continuing from above


        // Will build parameters including all of the potential destinations
        let params = {
            'app_id': this.state.app_id,
            'app_code': this.state.app_code,
            'mode': 'fastest;car;traffic:enabled',
            'matrixAttributes': 'ix,su',
            'summaryattributes': 'all',
            'start0': lat + ',' + lng,
        }
        for (var i = 0; i < options.length; i++) {
            params['destination' + i] = options[i].lat + ',' + options[i].lng;
        }

        // Calculate matrix routing among options to make a recommendation
        const self = this;
        axios.get(
            'https://matrix.route.api.here.com/routing/7.2/calculatematrix.json',
            {'params': params}).then(function(response) {
                const matrix = response.data.response.matrixEntry;

                let nearest = matrix[0].summary;
                nearest['id'] = options[0].id;

                for (var i = 0; i < matrix.length; i++) {
                    if (matrix[i].summary.costFactor < nearest.costFactor) {
                        nearest = matrix[i].summary;
                        nearest.id = options[i].id;
                    }
                }
                self.setState({
                    nearest: nearest
                })
            });
      this.setState({places: places});
    }
```

We are using the location id from our options so that the property can be used in our PlaceSearch component for highlighting.

# Summary

*Neo: I’m going to show them a world without you. A world without rules and controls, without borders or boundaries. A world where anything is possible. Where we go from there is a choice I leave to you.*

This is not a ready-made production application but hopefully gives you a flavor of how and why to start using some of the HERE Location Services like Places and Routing in your applications. An obvious next step would be to display a map like described in Use HERE Interactive Maps with ReactJS to place markers and the route.

You can find [source code](https://github.com/j12y/HERE-react-matrix-routing) listings for the components mentioned in this post on GitHub.