Track shipments with the JavaScript Shipment Tracking Library

The JavaScript Shipment Tracking Library lets you visualize the location of vehicles and locations of interest tracked in Fleet Engine. The library contains a JavaScript map component that is a drop-in replacement for a standard google.maps.Map entity and data components to connect with Fleet Engine. Using the JavaScript Shipment Tracking Library, you can provide a customizable, animated shipment tracking experience from your web application.

Components

The JavaScript Shipment Tracking Library provides components for visualization of the vehicle and route as it progresses to a destination, as well as raw data feeds for a driver's ETA or the remaining distance to drive.

Shipment Tracking map view

The map view component visualizes the location of vehicles and destinations. If the route for a vehicle is known, the map view component animates that vehicle as it moves along its predicted path.

Shipment location provider

A shipment location provider feeds location information for tracked objects into the shipment tracking map for first and last mile shipment tracking.

You can use the shipment location provider to track:

  • The pickup or delivery location of a shipment.
  • The location and route of the delivery vehicle.

Tracked location objects

The location provider tracks the location of objects such as vehicles and destinations.

Destination location

The destination location is the location where a journey ends. For shipment tracking, it is the planned task location.

Vehicle location

The vehicle location is the tracked location of a vehicle. It may optionally include a route for the vehicle.

Authentication token fetcher

To control access to the location data stored in Fleet Engine, you must implement a JSON Web Token (JWT) minting service for Fleet Engine on your server. Then implement an authentication token fetcher as part of your web application, using the JavaScript Journey Sharing Library to authenticate access to the location data.

Styling options

Marker and polyline styles determine the look and feel of the tracked location objects on the map. You can use custom styling options to change the default styling to match the styling of your web application.

Control the visibility of tracked locations

This section describes the visibility controls for tracked objects on the map. These rules apply to two categories of objects:

  • Location marker
  • Task data

Location marker visibility

All location markers for the origin and destination are always shown on the map. For example, a shipment delivery location is always shown on the map, regardless of the state of the delivery.

Task data visibility

This section describes the default visibility rules that apply to task data, such as vehicle location and remaining route. You can customize many tasks but not all:

  • Unavailability tasks -- You cannot customize visibility for these tasks.
  • Active vehicle tasks -- You can customize these type of tasks.
  • Inactive vehicle tasks -- You cannot customize visibility for these tasks.

Unavailability tasks

If there is at least one unavailability task (for example, if the driver is taking a break or the vehicle is being refueled) on the route to the task being tracked, the vehicle is not visible. The estimated arrival time and estimated task completion time are still available.

Active vehicle tasks

The TaskTrackingInfo object provides a number of data elements that can be made visible within the Shipment Tracking Library. By default, these fields are visible when the task is assigned to the vehicle and when the vehicle is within 5 stops of the task. The visibility ends when the task is completed or canceled. The fields are as follows:

  • Route polylines
  • Estimated time to arrival
  • Estimated task completion time
  • Remaining driving distance to the task
  • Remaining stop count
  • Vehicle location

You can customize the visibility configuration on a per task basis by setting the TaskTrackingViewConfig on a task when creating or updating the task within Fleet Engine. This creates rules of when individual data elements are available which can be based on the following criteria (referred as visibility option below):

  • Remaining stop count
  • Duration until estimated arrival time
  • Remaining driving distance
  • Always visible
  • Never visible

Note that each data element can only be associated with one visibility option. It is not possible to combine criteria using OR or AND.

An example of customization is as follows. The rules of that customization are:

  • Show the route polylines if the vehicle is within 3 stops.
  • Show the ETA if the remaining driving distance is shorter than 5000 meters.
  • Never show the remaining stop count.
  • Each other field retains the default visibility of being shown when the vehicle is within 5 stops of the task.
"taskTrackingViewConfig": {
  "routePolylinePointsVisibility": {
    "remainingStopCountThreshold": 3
  },
  "estimatedArrivalTimeVisibility": {
    "remainingDrivingDistanceMetersThreshold": 5000
  },
  "remainingStopCountVisibility": {
    "never": true
  }
}

You can also customize the default visibility configuration for your project by contacting the support team.

Route polylines and vehicle location visibility rules:

When route polylines are visible, the vehicle location must also be visible, otherwise the vehicle location can be indicated by the end of polylines. That means route polylines cannot have a less restrictive visibility option.

These rules must be followed to provide a valid route polylines / vehicle location visibility combination:

  • When route polylines and vehicle location have the same visibility option type:

    • If the visibility option is remaining stop count, duration until ETA, or remaining driving distance, route polylines must provide a value that is less than or equal to the value set for this visibility option for the vehicle location. An example is as follows:
    "taskTrackingViewConfig": {
      "routePolylinePointsVisibility": {
        "remainingStopCountThreshold": 3
      },
      "vehicleLocationVisibility": {
        "remainingStopCountThreshold": 5
      },
    }
    
    • If route polylines has an always visible visibility option, vehicle location must also provide an always visible visibility option.
    • If vehicle location has a never visible visibility option, route polylines must also provide a never visible visibility option.
  • When route polylines and vehicle location have different visibility option types, vehicle location is only visible when both of their visibility options are satisfied.

    An example is as follows:

    "taskTrackingViewConfig": {
      "routePolylinePointsVisibility": {
        "remainingStopCountThreshold": 3
      },
      "vehicleLocationVisibility": {
        "remainingDrivingDistanceMetersThreshold": 3000
      },
    }
    

    In this example, the vehicle location is only visible if the remaining stop count is at least 3 AND the remaining driving distance is at least 3000 meters.

Get started with the JavaScript Journey Sharing Library

Before using the JavaScript Journey Sharing Library make sure you are familiar with Fleet Engine and with getting an API key.

To track a delivery, first create a tracking ID claim.

Create a tracking ID claim

To track a shipment using the shipment location provider, create a JSON Web Token (JWT) with a tracking ID claim.

To create the JWT payload, add an additional claim in the authorization section with the key trackingid. Set its value to the shipment tracking ID.

The following example shows how to create a token for tracking by tracking ID:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_consumer_service_account"
}
.
{
  "iss": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "sub": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "scope": "https://www.googleapis.com/auth/xapi",
  "authorization": {
     "trackingid": "tid_54321",
   }
}

Create an authentication token fetcher

You can create an authentication token fetcher to retrieve a token minted with the appropriate claims on your servers using a service account certificate for your project. It is important to only mint tokens on your servers and never share your certificates on any clients. Otherwise, you will compromise the security of your system.

The fetcher must return a data structure with two fields, wrapped in a Promise:

  • A string token.
  • A number expiresInSeconds. A token expires in this amount of time after fetching.

The JavaScript Shipment Tracking Library requests a token via the authentication token fetcher when any of the following is true:

  • It does not have a valid token, such as when it hasn't called the fetcher on a fresh page load, or when the fetcher hasn't returned with a token.
  • The token it fetched previously has expired.
  • The token it fetched previously is within one minute of expiring.

Otherwise, the library uses the previously issued, still valid token and does not call the fetcher.

The following example shows how to create an authentication token fetcher:

JavaScript

function authTokenFetcher(options) {
  // options is a record containing two keys called
  // serviceType and context. The developer should
  // generate the correct SERVER_TOKEN_URL and request
  // based on the values of these fields.
  const response = await fetch(SERVER_TOKEN_URL);
  if (!response.ok) {
    throw new Error(response.statusText);
  }
  const data = await response.json();
  return {
    token: data.Token,
    expiresInSeconds: data.ExpiresInSeconds
  };
}

TypeScript

function authTokenFetcher(options: {
  serviceType: google.maps.journeySharing.FleetEngineServiceType,
  context: google.maps.journeySharing.AuthTokenContext,
}): Promise<google.maps.journeySharing.AuthToken> {
  // The developer should generate the correct
  // SERVER_TOKEN_URL based on options.
  const response = await fetch(SERVER_TOKEN_URL);
  if (!response.ok) {
    throw new Error(response.statusText);
  }
  const data = await response.json();
  return {
    token: data.token,
    expiresInSeconds: data.expiration_timestamp_ms - Date.now(),
  };
}

When implementing the server-side endpoint for minting the tokens, keep the following in mind:

  • The endpoint must return an expiry time for the token; in the example above, it is given as data.ExpiresInSeconds.
  • The authentication token fetcher must pass the expiry time (in seconds, from time of fetching) to the library, as shown in the example.
  • The SERVER_TOKEN_URL depends on your backend implementation, these are the URLs for the sample app backend:
    • https://SERVER_URL/token/delivery_driver/DELIVERY_VEHICLE_ID
    • https://SERVER_URL/token/delivery_consumer/TRACKING_ID
    • https://SERVER_URL/token/fleet_reader

Load a map from HTML

The following example shows how to load the JavaScript Shipment Tracking Library from a specified URL. The callback parameter executes the initMap function after the API loads. The defer attribute lets the browser continue rendering the rest of your page while the API loads.

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=journeySharing" defer></script>

Follow a shipment

This section shows how to use the JavaScript Shipment Tracking Library to follow a shipment pickup or delivery. Make sure to load the library from the callback function specified in the script tag before running your code.

Instantiate a shipment location provider

The JavaScript Shipment Tracking Library predefines a location provider for the Fleet Engine Deliveries API. Use your project ID and a reference to your token factory to instantiate it.

JavaScript

locationProvider =
    new google.maps.journeySharing
        .FleetEngineShipmentLocationProvider({
          projectId: 'your-project-id',
          authTokenFetcher: authTokenFetcher, // the token fetcher defined in the previous step

          // Optionally, you may specify tracking ID to
          // immediately start tracking.
          trackingId: 'your-tracking-id',
});

TypeScript

locationProvider =
    new google.maps.journeySharing
        .FleetEngineShipmentLocationProvider({
          projectId: 'your-project-id',
          authTokenFetcher: authTokenFetcher, // the token fetcher defined in the previous step

          // Optionally, you may specify tracking ID to
          // immediately start tracking.
          trackingId: 'your-tracking-id',
});

Initialize the map view

After loading the JavaScript Journey Sharing library, initialize the map view and add it to the HTML page. Your page should contain a <div> element that holds the map view. In the following example, the <div> element is named map_canvas.

To avoid race conditions, set the tracking ID for the location provider in the callback that is invoked after the map is initialized.

JavaScript

const mapView = new 
    google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'), 
  locationProviders: [locationProvider],
  vehicleMarkerSetup: vehicleMarkerSetup,
  anticipatedRoutePolylineSetup:
      anticipatedRoutePolylineSetup,
  // Any undefined styling options will use defaults.
});

// If you did not specify a tracking ID in the location
// provider constructor, you may do so here.
// Location tracking will start as soon as this is set.
locationProvider.trackingId = 'your-tracking-id';

// Give the map an initial viewport to allow it to 
// initialize; otherwise the 'ready' event above may 
// not fire. The user also has access to the mapView
// object to customize as they wish.
mapView.map.setCenter({lat: 37.2, lng: -121.9});
mapView.map.setZoom(14);

TypeScript

const mapView = new 
    google.maps.journeySharing.JourneySharingMapView({
  document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  vehicleMarkerSetup: vehicleMarkerSetup,
  anticipatedRoutePolylineSetup:
      anticipatedRoutePolylineSetup,
 // Any undefined styling options will use defaults.
});

// If you did not specify a tracking ID in the location
// provider constructor, you may do so here.
// Location tracking will start as soon as this is set.
locationProvider.trackingId = 'your-tracking-id';

// Give the map an initial viewport to allow it to
// initialize; otherwise the 'ready' event above may 
// not fire. The user also has access to the mapView
// object to customize as they wish.
mapView.map.setCenter({lat: 37.2, lng: -121.9});
mapView.map.setZoom(14);

Tracking ID

The tracking ID supplied to the location provider may correspond to several tasks; for example, a pickup and a delivery task for the same package, or several failed delivery attempts. One task is selected to be shown on the shipment tracking map. The task to be shown is determined as follows:

  1. If there is exactly one open pickup task, it is shown. An error is generated if there are multiple open pickup tasks.
  2. If there is exactly one open delivery task, it is shown. An error is generated if there are multiple open delivery tasks.
  3. If there are closed delivery tasks:
    • If there is exactly one closed delivery task, it is shown.
    • If there are multiple closed delivery tasks, the one with the most recent outcome time is shown.
    • If there are multiple closed delivery tasks, none of which have an outcome time, an error is generated.
  4. If there are closed pickup tasks:
    • If there is exactly one closed pickup task, it is shown.
    • If there are multiple closed pickup tasks, the one with the most recent outcome time is shown.
    • If there are multiple closed pickup tasks, none of which have an outcome time, an error is generated.
  5. Otherwise, no task is shown.

Listen to change events

You can retrieve meta information about a task from the task tracking info object using the location provider. The meta information includes the ETA, number of remaining stops, and remaining distance before pickup or delivery. Changes to the meta information trigger an update event. The following example shows how to listen to these change events.

JavaScript

locationProvider.addListener('update', e => {
  // e.taskTrackingInfo contains data that may be useful
  // to the rest of the UI.
  console.log(e.taskTrackingInfo.remainingStopCount);
});

TypeScript

locationProvider.addListener('update',
    (e: google.maps.journeySharing.FleetEngineShipmentLocationProviderUpdateEvent) => {
  // e.taskTrackingInfo contains data that may be useful
  // to the rest of the UI.
  console.log(e.taskTrackingInfo.remainingStopCount);
});

Handle errors

Errors that arise asynchronously from requesting shipment information trigger error events. The following example shows how to listen for these events in order to handle errors.

JavaScript

locationProvider.addListener('error', e => {
  // e.error is the error that triggered the event.
  console.error(e.error);
});

TypeScript

locationProvider.addListener('error', (e: google.maps.ErrorEvent) => {
  // e.error is the error that triggered the event.
  console.error(e.error);
});

Note: Make sure to wrap library calls in try...catch blocks to handle unanticipated errors.

Stop tracking

To stop the location provider from tracking the shipment, remove the tracking ID from the location provider.

JavaScript

locationProvider.trackingId = '';

TypeScript

locationProvider.trackingId = '';

Remove the location provider from the map view

The following example shows how to remove a location provider from the map view.

JavaScript

mapView.removeLocationProvider(locationProvider);

TypeScript

mapView.removeLocationProvider(locationProvider);

Customize the look and feel of the base map

To customize the look and feel of the maps component, style your map using cloud-based tooling or by setting options directly in code.

Use cloud-based map styling

Cloud-based maps styling lets you create and edit map styles for any of your apps that use Google Maps from the Google Cloud console, without requiring any changes to your code. The map styles are saved as map IDs in your Cloud project. To apply a style to your JavaScript Shipment Tracking map, specify a mapId when you create the JourneySharingMapView. The mapId field cannot be changed or added after the JourneySharingMapView has been instantiated. The following example shows how to enable a previously created map style with a map ID.

JavaScript

const mapView = new google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  mapOptions: {
    mapId: 'YOUR_MAP_ID'
  }
  // Any other styling options.
});

TypeScript

const mapView = new google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  mapOptions: {
    mapId: 'YOUR_MAP_ID'
  }
  // Any other styling options.
});

Use code-based map styling

Another way of customizing map styling is to set mapOptions when you create the JourneySharingMapView.

JavaScript

const mapView = new google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  mapOptions: {
    styles: [
      {
        "featureType": "road.arterial",
        "elementType": "geometry",
        "stylers": [
          { "color": "#CCFFFF" }
        ]
      }
    ]
  }
});

TypeScript

const mapView = new google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  mapOptions: {
    styles: [
      {
        "featureType": "road.arterial",
        "elementType": "geometry",
        "stylers": [
          { "color": "#CCFFFF" }
        ]
      }
    ]
  }
});

Use marker customizations

With the JavaScript Shipment Tracking Library, you can customize the look and feel of markers added to the map. You do this by specifying marker customizations, which the Shipment Tracking Library then applies before adding markers to the map and with every marker update.

The simplest customization is to specify a MarkerOptions object that will be applied to all markers of the same type. The changes specified in the object are applied after each marker has been created, overwriting any default options.

A more advanced option is to specify a customization function. Customization functions allow for styling of the markers based on data, as well as adding interactivity to markers, such as click handling. Specifically, Shipment Tracking passes data to the customization function about the type of object the marker represents: vehicle or destination. This then allows marker styling to change based on the current state of the marker element itself; for example, the number of planned stops remaining until the destination. You can even join against data from sources outside Fleet Engine and style the marker based on that information.

The Shipment Tracking library provides the following customization parameters in FleetEngineShipmentLocationProviderOptions:

Change the styling of markers using MarkerOptions

The following example shows how to configure a vehicle marker's styling with a MarkerOptions object. Follow this pattern to customize the styling of any marker using any of the marker customizations listed above.

JavaScript

deliveryVehicleMarkerCustomization = {
  cursor: 'grab'
};

TypeScript

deliveryVehicleMarkerCustomization = {
  cursor: 'grab'
};

Change the styling of markers using customization functions

The following example shows how to configure a vehicle marker's styling. Follow this pattern to customize the styling of any marker using any of the marker customization parameters listed above.

JavaScript

deliveryVehicleMarkerCustomization =
  (params) => {
    var stopsLeft = params.taskTrackingInfo.remainingStopCount;
    params.marker.setLabel(`${stopsLeft}`);
  };

TypeScript

deliveryVehicleMarkerCustomization =
  (params: ShipmentMarkerCustomizationFunctionParams) => {
    const stopsLeft = params.taskTrackingInfo.remainingStopCount;
    params.marker.setLabel(`${stopsLeft}`);
  };

Add click handling to markers

The following example shows how to add click handling to a vehicle marker. Follow this pattern to add click handling to any marker using any of the marker customization parameters listed above.

JavaScript

deliveryVehicleMarkerCustomization =
  (params) => {
    if (params.isNew) {
      params.marker.addListener('click', () => {
        // Perform desired action.
      });
    }
  };

TypeScript

deliveryVehicleMarkerCustomization =
  (params: ShipmentMarkerCustomizationFunctionParams) => {
    if (params.isNew) {
      params.marker.addListener('click', () => {
        // Perform desired action.
      });
    }
  };

Use polyline customizations

With the Shipment Tracking Library, you can also customize the look and feel of the shipment's route on the map. The library creates a google.maps.Polyline object for each pair of coordinates in the shipment's active or remaining path. You can style the Polyline objects by specifying polyline customizations. The library then applies these customizations in two situations: before adding the objects to the map, and when the data used for the objects have changed.

Similar to marker customization, you can specify a set of PolylineOptions to be applied to all of the matched Polyline objects when they are created or updated.

Likewise, you can specify a customization function. Customization functions allow for individual styling of the objects based on data sent by Fleet Engine. The function can change the styling of each object based on the current state of the shipment; for example, coloring the Polyline object a deeper shade, or making it thicker when the vehicle is moving slower. You can even join against from sources outside Fleet Engine and style the Polyline object based on that information.

You can specify the customizations using parameters provided in FleetEngineShipmentLocationProviderOptions. You can set customizations for different path states in the vehicle's journey--already traveled, actively traveling, or not yet traveled. The parameters are as follows:

Change the styling of Polyline objects using PolylineOptions

The following example shows how to configure the styling for a Polyline object with PolylineOptions. Follow this pattern to customize the styling of any Polyline object using any of the polyline customizations listed earlier.

JavaScript

activePolylineCustomization = {
  strokeWidth: 5,
  strokeColor: 'black',
};

TypeScript

activePolylineCustomization = {
  strokeWidth: 5,
  strokeColor: 'black',
};

Change the styling of Polyline objects using customization functions

The following example shows how to configure an active Polyline object's styling. Follow this pattern to customize the styling of any Polyline object using any of the polyline customization parameters listed earlier.

JavaScript

// Color the Polyline objects in green if the vehicle is nearby.
activePolylineCustomization =
  (params) => {
    const distance = params.taskTrackingInfo.remainingDrivingDistanceMeters;
    if (distance < 1000) {

      // params.polylines contains an ordered list of Polyline objects for
      // the path.
      for (const polylineObject of params.polylines) {
        polylineObject.setOptions({strokeColor: 'green'});
      }
    }
  };

TypeScript

// Color the Polyline objects in green if the vehicle is nearby.
activePolylineCustomization =
  (params: ShipmentPolylineCustomizationFunctionParams) => {
    const distance = params.taskTrackingInfo.remainingDrivingDistanceMeters;
    if (distance < 1000) {

      // params.polylines contains an ordered list of Polyline objects for
      // the path.
      for (const polylineObject of params.polylines) {
        polylineObject.setOptions({strokeColor: 'green'});
      }
    }
  };

Control the visibility of Polyline objects

By default, all Polyline objects are visible. To make a Polyline object invisible, set its visible property:

JavaScript

remainingPolylineCustomization = {visible: false};

TypeScript

remainingPolylineCustomization = {visible: false};

Display an InfoWindow for a vehicle or location marker

You can use an InfoWindow to display additional information about a vehicle or location marker.

The following example shows how to create an InfoWindow and attach it to a vehicle marker:

JavaScript

// 1. Create an info window.
const infoWindow = new google.maps.InfoWindow(
    {disableAutoPan: true});

locationProvider.addListener('update', e => {
  const stopsCount =
      e.taskTrackingInfo.remainingStopCount;
  infoWindow.setContent(
      `Your vehicle is ${stopsCount} stops away.`);

  // 2. Attach the info window to a vehicle marker.
  // This property can return multiple markers.
  const marker = mapView.vehicleMarkers[0];
  infoWindow.open(mapView.map, marker);
});

// 3. Close the info window.
infoWindow.close();

TypeScript

// 1. Create an info window.
const infoWindow = new google.maps.InfoWindow(
    {disableAutoPan: true});

locationProvider.addListener('update', (e: google.maps.journeySharing.FleetEngineShipmentLocationProviderUpdateEvent) => {
  const stopsCount =
      e.taskTrackingInfo.remainingStopCount;
  infoWindow.setContent(
      `Your vehicle is ${stopsCount} stops away.`);

  // 2. Attach the info window to a vehicle marker.
  // This property can return multiple markers.
  const marker = mapView.vehicleMarkers[0];
  infoWindow.open(mapView.map, marker);
});

// 3. Close the info window.
infoWindow.close();

Disable automatic fitting

You can stop the map from automatically fitting the viewport to the vehicle and anticipated route by disabling automatic fitting. The following example shows how to disable automatic fitting when you configure the journey sharing map view.

JavaScript

const mapView = new
    google.maps.journeySharing.JourneySharingMapView({
  element: document.getElementById('map_canvas'),
  locationProviders: [locationProvider],
  automaticViewportMode:
      google.maps.journeySharing
          .AutomaticViewportMode.NONE,
  ...
});

TypeScript

// 1. Create an info window.
const infoWindow = new google.maps.InfoWindow(
    {disableAutoPan: true});

locationProvider.addListener('update', (e: google.maps.journeySharing.FleetEngineShipmentLocationProviderUpdateEvent) => {
  const stopsCount =
      e.taskTrackingInfo.remainingStopCount;
  infoWindow.setContent(
      `Your vehicle is ${stopsCount} stops away.`);

  // 2. Attach the info window to a vehicle marker.   
  // This property can return multiple markers.
  const marker = mapView.vehicleMarkers[0];
  infoWindow.open(mapView.map, marker);
});

// 3. Close the info window.
infoWindow.close();

Replace an existing map

You can use the JavaScript Shipment Tracking Library to replace an existing map that includes markers or other customizations without losing those customizations.

For example, suppose you have a web page with a standard google.maps.Map entity on which a marker is shown:

<!DOCTYPE html>
<html>
  <head>
    <style>
       /* Set the size of the div element that contains the map */
      #map {
        height: 400px;  /* The height is 400 pixels */
        width: 100%;  /* The width is the width of the web page */
       }
    </style>
  </head>
  <body>
    <h3>My Google Maps Demo</h3>
    <!--The div element for the map -->
    <div id="map"></div>
    <script>
// Initialize and add the map
function initMap() {
  // The location of Uluru
  var uluru = {lat: -25.344, lng: 131.036};
  // The map, initially centered at Mountain View, CA.
  var map = new google.maps.Map(document.getElementById('map'));
  map.setOptions({center: {lat: 37.424069, lng: -122.0916944}, zoom: 14});

  // The marker, now positioned at Uluru
  var marker = new google.maps.Marker({position: uluru, map: map});
}
    </script>
    <!-- Load the API from the specified URL.
       * The async attribute allows the browser to render the page while the API loads.
       * The key parameter will contain your own API key (which is not needed for this tutorial).
       * The callback parameter executes the initMap() function.
    -->
    <script defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
    </script>
  </body>
</html>

To add the JavaScript Journey Sharing library:

  1. Add code for the authentication token factory.
  2. Initialize a location provider in the initMap() function.
  3. Initialize the map view in the initMap() function. The view contains the map.
  4. Move your customization into the callback function for the map view initialization.
  5. Add the location library to the API loader.

The following example shows the changes to be made:

<!DOCTYPE html>
<html>
  <head>
    <style>
       /* Set the size of the div element that contains the map */
      #map {
        height: 400px;  /* The height is 400 pixels */
        width: 100%;  /* The width is the width of the web page */
       }
    </style>
  </head>
  <body>
    <h3>My Google Maps Demo</h3>
    <!--The div element for the map -->
    <div id="map"></div>
    <script>
let locationProvider;

// (1) Authentication Token Fetcher
function authTokenFetcher(options) {
  // options is a record containing two keys called 
  // serviceType and context. The developer should
  // generate the correct SERVER_TOKEN_URL and request
  // based on the values of these fields.
  const response = await fetch(SERVER_TOKEN_URL);
      if (!response.ok) {
        throw new Error(response.statusText);
      }
      const data = await response.json();
      return {
        token: data.Token,
        expiresInSeconds: data.ExpiresInSeconds
      };
}

// Initialize and add the map
function initMap() {
  // (2) Initialize location provider.
  locationProvider = new google.maps.journeySharing.FleetEngineShipmentLocationProvider({
    YOUR_PROVIDER_ID,
    authTokenFetcher,
  });

  // (3) Initialize map view (which contains the map).
  const mapView = new google.maps.journeySharing.JourneySharingMapView({
    element: document.getElementById('map'),
    locationProviders: [locationProvider],
    // any styling options
  });

  locationProvider.trackingId = TRACKING_ID;

    // (4) Add customizations like before.

    // The location of Uluru
    var uluru = {lat: -25.344, lng: 131.036};
    // The map, initially centered at Mountain View, CA.
    var map = mapView.map;
    map.setOptions({center: {lat: 37.424069, lng: -122.0916944}, zoom: 14});
    // The marker, now positioned at Uluru
    var marker = new google.maps.Marker({position: uluru, map: map});
  };

    </script>
    <!-- Load the API from the specified URL
      * The async attribute allows the browser to render the page while the API loads
      * The key parameter will contain your own API key (which is not needed for this tutorial)
      * The callback parameter executes the initMap() function
      *
      * (5) Add the journey sharing library to the API loader.
    -->
    <script defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=journeySharing">
    </script>
  </body>
</html>

If you have a tracked package with the specified ID near Uluru, it will now be rendered on the map.