AWS

Using JavaScript to Simulate Transport in Supply Chain Scenario

By Michael Palermo | 28 September 2020

Try HERE Maps

Create a free API key to build location-aware apps and services.

Get Started

In a recent post, we announced the AWS Connections – Supply Chain series. In this post, we are going to explore how we built the transportation component of the supply chain scenario. Each delivery operator logs in, and movement is captured via drag-n-drop. Each time the icon/marker representing the transportation vehicle is ‘dropped’ on the map, it’s coordinates are communicated via MQTT to AWS IoT and on to any subscribed to the topic. Let’s examine each feature one at a time. 

Building a Web Page per Delivery Operator 

The base HTML for transportation is as follows: 

<html>
<head>
<title>Transportation</title>
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0,maximum-scale=1.0,user-scalable=yes">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<!-- HERE JS V3.1 -->
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<!-- Resources for AWS IoT -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/hmac-min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/sha256-min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
<style>
body {padding: 0; margin: 0;}
#transportId {z-index: 99;position: absolute; top: 10px;left:10px;}
#mapContainer {width: 100vw; height: 100vh; display:none;}
</style>
</head>
<body>
<select id="transportId">
<option value="00">Select Transport Vehicle...</option>
<option value="delivery01">Transport Vehicle 01</option>
<option value="delivery02">Transport Vehicle 02</option>
<option value="delivery03">Transport Vehicle 03</option>
</select>
<div id="mapContainer"></div>
<script type="module" src="./appTransport.js"></script>
</body>
</html>

Notice the initial style of the map is set in style as display:none, which prevents the map from initially being viewed. A drop down select element prompts the user to choose between three delivery operator options. When a selection is made, the following code is invoked: 

 

let deliveryOp = "";
const tranSelect = document.getElementById("transportId");
tranSelect.addEventListener('change', () => {
    deliveryOp = tranSelect.value;
    const iconTransport = new H.map.Icon(`./${deliveryOp}.png`);
    const dragMarker = new H.map.Marker(initPoint,{icon:iconTransport}, {volatility:true}); 
    dragMarker.draggable = true;
    map.addObject(dragMarker);
    container.style.display = "initial";
    map.getViewPort().resize();
    console.log("end change");
});

 

Once a selection is made, the map is now visible by resetting it's style to "initial". Also, each delivery operator has a unique icon used to represent it on the map, which is determined by the user selection. Here are the names/images of the icons which are loaded into the marker: 

delivery01.png delivery01

delivery02.png delivery02

delivery03.png delivery03

Note when the marker is added to the map, it is enabled for drag-n-drop behavior. However, more code is needed to make it work. Let’s see how this is implemented. 

Adding Drag-n-Drop Behavior 

Three methods need to be added to the code to successfully drag-n-drop the marker around. The first method handles the initial “pick up” of the marker as seen here: 

map.addEventListener('dragstart', function(ev) {
        var target = ev.target,
            pointer = ev.currentPointer;
        if (target instanceof H.map.Marker) {
        var targetPosition = map.geoToScreen(target.getGeometry());
        target['offset'] = new H.math.Point(pointer.viewportX - targetPosition.x, pointer.viewportY - targetPosition.y);
        behavior.disable();
        }
    }, false);

The next method is called for every movement made by the user while the marker is being dragged as seen here: 

 

map.addEventListener('drag', function(ev) {
    var target = ev.target,
        pointer = ev.currentPointer;
    if (target instanceof H.map.Marker) {
        target.setGeometry(map.screenToGeo(pointer.viewportX - target['offset'].x, pointer.viewportY - target['offset'].y));
    }
}, false);

The final method is called when the user “let’s go” of the marker, thus dropping it on map as seen here: 

map.addEventListener('dragend', function(ev) {
    let target = ev.target;
    if (target instanceof H.map.Marker) {
        processMarker(target);
    }
}, false);

 As shown above, when the marker is dropped, it calls another method to process the information. Let’s explore that in more detail. 

Processing Marker Location Data  

Here is the code for the processMarker method: 

function processMarker(marker) {
    behavior.enable();
    const markerPoint = marker.getGeometry();
    let message = getGeoLabel(markerPoint)}
    const bubble =  new H.ui.InfoBubble(markerPoint,{content: message});
    ui.addBubble(bubble);
    client.publish(`sc/transport/${deliveryOp}`, 
    {operator: deliveryOp, geo: markerPoint});
    map.setCenter(markerPoint,true);
}

Because the code validates the event target to be an instance of a marker, we can now call the getGeometry method to receive the geolocation as a Point object. Another helper function is called to create a friendly viewing of the coordinates. Here is the code for that method: 

 

function getGeoLabel(point) {
    return "" + Math.abs(point.lat.toFixed(4)) +
    ((point.lat > 0) ? 'N' : 'S') +
    '
' + Math.abs(point.lng.toFixed(4)) + ((point.lng > 0) ? 'E' : 'W'); }

Another aspect of the processMarker method is publishing the data via MQTT to the topic "sc/transport/delivery##" where the ## is the number of the delivery operator who has logged in. What is listening to or subscribed to that data? Find out in an upcoming blog post! 

To see the code above in action, don’t miss our episode on this topic below!