Coffee To Go

The following section describes a more complex application that involves more advanced features of HNOD SDK, namely:

  • Map View API
  • POI search
  • Routing and guidance


The goal of this example is to allow user to search for a coffee place and order coffee from inside an HNOD application. Once a coffee has been selected, the user should be routed to a pickup location.

Package Structure

In this section, we will outline the folder structure of this example. The Coffee application will be simple enough to be implemented as a single bundle, so we scaffold a new service package named coffee .

The src of the new service package will have the following structure:

  • components where all react components referenced in app are defined.
  • models where state modelling classes are defined. There classes are by convention suffixed by Model.
  • bundles where a class extending the WebRunnableBundle interface resides.
  • theme for some constants that are used by the theme of the application.
  • utils contains the helper methods.

Bundles loaded by the HNOD application framework on the UI side are expected to expose an instance of the bundle’s WebRunnableBundle implementation as a default export:

import { WebRunnableBundle } from "@here/hnod-sdk";

const runnableBundle: WebRunnableBundle = {
  // implement

export default runnableBundle;

Coffee Model

The methods of the CoffeeModel implement most of the application logic and can be classified into three main categories:

  1. State methods allow to manipulate with the CurrentCoffeeOrder that captures the current coffee order, if one has been made.

     public async updateCoffeeOrder(order: CurrentCoffeeOrder): Promise<void>
     public async getCoffeeOrder(): Promise<CurrentCoffeeOrder | null>
     public async clearCoffeeState(): Promise<void>
  2. The application flow methods implement the main logic including search, routing and guidance:

     public recommendCoffee(): Observable<hnodApi.core.v1.plugin.SearchResponse>

    Implements search for a coffee place around the current location if there are no active routes or search along the route otherwise.

     public calculateRoute(
         destination: hnodApi.core.v1.common.GeoCoordinate
     ): Observable<hnodKmwaApi.core.v1.user.state.RouteDigestResultResponse>

    Calculates a route to a given destination (i.e. to a POI returned in a search response in the previous method), if there are no active routes the result will be a route from the current location to the destination, otherwise the active route will be updated with an extra stopover waypoint that represents the POI's location.

     public startGuidance(routeResponse: hnodKmwaApi.core.v1.user.state.RouteDigestResultResponse): void

    Updates the active route to the one given as a parameter and starts the guidance mode in the application.

  3. Map View methods enable rendering something on the map, in this particular case we need to display markers and custom routes on the map and this is implemented by the following methods:

     private setMarkers(pois: hnodApi.core.v1.common.Marker[]): void
     private setRouteShape(route?: hnodApi.core.v1.common.Route): void

Application Screens

The Coffee application requires two screens to be implemented: CoffeeMenuScreen , and CoffeeOrderScreen , both are React components extending CardStackScreen and implemented in the corresponding classes under app folder.

CoffeeMenuScreen displays POI info (name, distance) along with the menu of the drinks available at the place. CoffeeList is predefined at the moment to simplify the example.

CoffeeOrderScreen shows the ETA to the POI and has a pickup button that starts the guidance mode routing to the POI.

Runnable Bundle

CoffeeBundle implements the WebRunnableBundle interface which is an entry point of our application. This will allow us to install our bundle in HNOD and display UI elements from our bundle on the application. In the components() method we need to register all the cards that our application consists of:

public components(): AppComponent[] {
  return [
      name: CoffeeBundle.coffeeMenuCard,
      component: CoffeeMenuScreen
      name: CoffeeBundle.coffeeOrderCard,
      component: CoffeeOrderScreen

menuItems() method returns an array of a single item: the coffee button, which is registered in the HNOD application and starts the example from the Service Launcher. The callback field contains the action registered to the click event.

public menuItems(): MenuItem[] {
  return [
      id: "Coffee_MenuItem",
      menuId: MenuIdentifier.ServicesLauncher,
      title: "Coffee Example",
      testId: "Coffee_testId",
      position: 1,
      svgIcon: createLogoButton,
      callback: async props => {
        return CardStackModel.get(props.user).push(CoffeeBundle.coffeeMenuCard);

discoverables() returns a list of named observables that are used by the HNOD application to receive updates on the map elements than need to be rendered (markers and routes in this case).

public discoverables(): NamedTypeObservableMap {
  const map = new NamedTypeObservableMap();
  map.setNamedTypeObservable(hnodApi.core.v1.common.CustomLayer$Name, (entity: Entity) => [
  map.setNamedTypeObservable(hnodKmwaApi.core.v1.user.state.RouteShapeArray$Name, (entity: Entity) => [

  return map;

How to work with static resources

Static resources in this examples are processed in two ways: as assets and as resources.

Assets will be processed by by url-loader by default. This means that the file contents will be transformed into base64-encoded data-URIs and loaded as part of the code. It is good to use this way for small resources like the logo of the service package, as they will be already available when the code is executed. In order to do so, the static resources should be put into the assets folder and all the corresponding file extensions should be listed in assetsExtensions field in hnod.bundler.json. Then in CoffeeLogo and CoffeeMenuScreen the star_logo.png is used:

import * as logo from "../../assets/star_logo.png";

const LogoImage = styled(Image)(({ theme }) => ({
   height: theme.sizeOf.headerIconLarge,
   width: theme.sizeOf.headerIconLarge

logo: <LogoImage src={logo} />

To make TypeScipt compiler allow to import non-source files, a definition in the assets.d.ts in the src is added:

declare module "*.png";

Resources will be loaded at runtime from the portal. In order to be used as resources, corresponding files should be listed in resources field in hnod.bundler.json, so they will be copied to the server. Then to use it in the code, a base url has to be defined. It will be injected by the application as resourceUrl.
CoffeesListItem and CoffeeModel uses button icons from folder resources:

declare let resourceUrl: string;

const buttonUrl = (size: CoffeeSize, isActive: boolean) => {
  const postfix = isActive ? "active" : "idle";
  return `${resourceUrl}/buttons/star_${size}_${postfix}.png`;
interface CoffeeImageProps {
  sizeFactor: number;

const CoffeeImage = styled(Image)<CoffeeImageProps>(({ theme, sizeFactor }) => ({
    height: theme.sizeOf.iconSmall * sizeFactor,
    width: theme.sizeOf.iconSmall * sizeFactor

const imageMiniSource = buttonUrl("mini", isActive);

<CoffeeImage src={imageMiniSource} sizeFactor={0.6} />

(see explanation for the styled component in Hello World example)

Performance Considerations

This specific example is implemented such that is will run only in the UI process. Due to the nature of the product, the core part of the application runs in a Nodejs instance, and not in the UI process.

Previous implemenatation of this example packaged used to trigger duplication of route data in the UI process. This is unnecessary for most cases. In case you need route data, except for route geometry, you are advied to make use of GuidanceModel.routeDigest, instead of GuidanceModel.route. GuidanceModel.route is formally deprecated, and will be removed in future releases.

Build the Example

Once implementation is done, service package can be built using HNOD bundler with the following command:

yarn build

It outputs a single ZIP archive containing all the code and other resources that application requires.

Run the Example

When we have the ZIP file we can run the application in the runner using the command:

yarn start

Once HNOD application is running you can start the newly created service package by clicking on the Coffee To Go button in the Service Launcher.

alt text
Figure 1. Coffee To Go Launcher

Coffee Menu Card will pop up with some information and coffee selection.

alt text
Figure 2. Coffee To Go Menu Card

When the coffee has been selected route to the POI will be displayed and Coffee Order Card will appear with the pickup button that will start the guidance.

alt text
Figure 3. Coffee To Go Order Card

results matching ""

    No results matching ""