Getting Started with Here Maps and Web Components using Lit

Originally published at labs.thisdot.co

Aug 5, 2021 · 6 minutes read · [TypeScript] [Lit] [Here Maps] [Web Components]

In the past months, I have been actively working on the development of geolocation-oriented solutions in web applications. My experience has been good so far, taking into account the availability of modern tools and libraries, with a wide range of features that even enable the use of interactive maps and location services.

In this article, I’ll explain how to integrate Here Maps through web components, using Lit and TypeScript, in just a few steps.

Introduction

Let’s start by explaining the technologies that we will be integrating into this post, step by step.

Lit

Lit is the next-generation library for creating simple, fast, and lightweight web components.

One of the benefits of using Lit is the power of interoperability, which means that your web components can be integrated within any framework, or can even be used on their own. This is possible since we’re talking about native web components that can be rendered in any modern web browser.

If you’re interested in learning more about this technology, you can take a look at the LitElement Series that has been published in this blog.

Here Maps

Let’s say your application requires real-time navigation, the creation of custom maps, or the ability to visualize datasets over a map. HERE Maps meet these requirements without too much effort.

In fact, there is a Maps API for JavaScript, which can be used to build web applications using either JavaScript or TypeScript.

Note

Although this guide provides an API key for testing purposes only, you may need to register your app, and get your API Key to move forward with your applications.

Creating the Project

Project Scaffolding

An easy way to initialize an application is through the project generator from the Open Web Components initiative. They provide a set of tools and guides to create a web components application, adding the testing, linting, and build support in the beginning.

Let’s create the project using a command-line interface

npm init @open-wc
# Select "Scaffold a new project" (What would you like to do today?)
# Select "Application" (What would you like to scaffold?)
# You can select any option for (What would you like to add?)
# Yes (Would you like to use TypeScript?)
# Mark/Select "Testing", "Demoing" and "Building" (Would you like to scaffold examples files for?)
# spa-heremaps-lit (What is the tag name of your application/web component?)
# Yes (Do you want to write this file structure to disk?)
# Yes, with npm (Do you want to install dependencies?)

Pay attention to the project structure that is generated. Also, take a look at your package.json file to review the dependencies and scripts that have been added.

Update to Lit

In case you generated a project based on LitElement, it shouldn’t be complicated to replace dependencies with lit:

  • Install the last version of Lit
npm install --save lit
  • Uninstall LitElement and lit-html library
npm uninstall lit-element lit-html

Also, you can make sure to update the imports as follows to make it work:

import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

Next, make sure the preview of the application is working fine using the following command:

npm run start

Creating a Map Component

Installing Here Maps

Before creating a web component that will render a map, you are required to load the API code libraries in our index.html file:

<link
  rel="stylesheet"
  type="text/css"
  href="https://js.api.here.com/v3/3.1/mapsjs-ui.css"
/>
<script
  src="https://js.api.here.com/v3/3.1/mapsjs-core.js"
  type="text/javascript"
  charset="utf-8"
></script>
<script
  src="https://js.api.here.com/v3/3.1/mapsjs-service.js"
  type="text/javascript"
  charset="utf-8"
></script>
<script
  src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"
  type="text/javascript"
  charset="utf-8"
></script>
<script
  src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"
  type="text/javascript"
  charset="utf-8"
></script>

You can see HERE Maps API for JavaScript Modules for more information about each imported script.

Since the initial project is based on TypeScript, we may need to install the type definitions for the HERE Maps API.

npm install --save-dev @types/heremaps

Now we’re ready to go and code the web component using TypeScript.

The Map Component Implementation

Let’s create a src/map/map.ts file with the following content:

// map.ts

import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('spa-map')
export class Map extends LitElement {
  static styles = css`
    :host {
      display: block;
      width: 100%;
      height: 100vh;
    }
  `;

  @property({ type: Object }) location: H.geo.IPoint = {
    lat: 40.71427,
    lng: -74.00597,
  };

  render() {
    return html`
      <link
        rel="stylesheet"
        type="text/css"
        href="https://js.api.here.com/v3/3.1/mapsjs-ui.css"
      />
      <div id="spa-map" style="width:100%; height:100vh"></div>
    `;
  }
}

The above code snippet allows the definition of a new custom element through the @customElement decorator. Also, it defines the location value as a component property so that it can be configured later as:

<spa-map .location="${VALUE}"></spa-map>

Also, the render method is ready to return a TemplateResult object which contains a div element that will be rendered in all the available space. The <div> element is the HTML container in which the map will be rendered later.

To finish the component implementation, we’ll need to write the firstUpdated method.

// map.ts

@customElement('spa-map')
export class Map extends LitElement {
  // Other methods/properties defined above...

  firstUpdated() {
    const platform = new H.service.Platform({
      apikey: '<API_KEY>',
    });

    const defaultLayers = platform.createDefaultLayers();

    const divContainer = this.shadowRoot?.getElementById(
      'spa-map'
    ) as HTMLDivElement;
    const map = new H.Map(divContainer, defaultLayers.vector.normal.map, {
      center: this.location,
      zoom: 10,
      pixelRatio: window.devicePixelRatio || 1,
    });

    const behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    const ui = H.ui.UI.createDefault(map, defaultLayers);

    window.addEventListener('resize', () => map.getViewPort().resize());
  }
}

Before creating the map object, it’s required to initialize the communication with the back-end services provided by HERE maps. In that way, an H.service.Platform object is created using the <API_Key> value you got in previous steps.

However, most of the code in the previous block has to do with the initialization of the map:

  • An instance of H.Map class is created specifying the container element, the map type to use, the geographic coordinates to be used to center the map, and a zoom level.
  • In case you need to make your map interactive, an H.mapevents.Behavior object can be created to enable the event system through a MapEvents instance.
  • On other hand, the default UI components for the map can be added using the H.ui.UI.createDefault method.
  • The resize listener can be used to make sure the map is rendered in the entire container.

Using the Map Component

As a final step, we’ll need to update the spa-heremaps-lit.ts file:

import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

import './map/map';

@customElement('spa-heremaps-lit')
export class SpaHeremapsLit extends LitElement {
  static styles = css`
    :host {
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      font-size: calc(10px + 2vmin);
      color: #1a2b42;
      max-width: 960px;
      margin: 0 auto;
      text-align: center;
    }
  `;

  render() {
    return html` <spa-map></spa-map> `;
  }
}

Since this class defines a new custom element as a container for the map component, it’s required to import the previous component definition using import './map/map'.

Then, inside the class definition we can find:

  • The static styles attribute defines the styles for the component using a tagged template literal(css)
  • The render method returns the HTML content through a template literal(html).
    • The HTML content uses the previous custom element we created: <spa-map></spa-map>
HERE maps example using Lit for Web Components and TypeScript

The screenshot shows the map rendered in the main component of the application.

Source Code of the Project

Find the latest version of this project in this GitHub repository: spa-heremaps-lit. If you want to see the final code for this article, take a look at 01-setup-lit-heremaps tag. Do not forget to give it a star ⭐️ and play around with the code.


Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work.

tweet Share