How to use Map Libraries in Angular

Leaflet, Mapbox GL/MapLibre GL, OpenLayers in Angular
Leaflet, Mapbox GL/MapLibre GL, OpenLayers in Angular

When it comes to using maps on your website, several JavaScript libraries can help you create interactive maps. These libraries have a lot in common, with a good code behind them and many code examples.

However, integrating them into an Angular component creates more questions than answers. It seems a bit complicated, so people decide to use map library wrappers or decision-making libraries, such as the popular npm package – angular-leaflet-directive, ngx-mapbox-gl, or ngx-openlayers.

In this tutorial, we want to show that it’s easy to use map libraries in an Angular application. We will guide you on using Leaflet, MapLibre GL (an open fork of Mapbox GL), OpenLayers map libraries with no additional wrapper libraries:

What is wrong with the wrapper libraries for maps?

This is the question you probably have in mind. Using those libraries adds a new dependency to your project that doesn't bring new functionality. Even if the integration looks very straightforward initially, it may become a nightmare in the future. For example, when the wrapper library is not being updated as frequently as Angular does.

Angular maps examples

We are going to use Geoapify Map Tiles as a baselayer for the map examples. Geoapify offers both raster and vector map tiles. We will use raster maps for Leaflet and OpenLayer map examples and vector maps for MapLibre GL.

Common part for all the libraries

As all of our map libraries use HTML elements as Map containers, the integration is very straightforward. To add a map, you will have to perform two main steps.

First, add a visual container to an angular component and create access to it in the code. You can run this command in your terminal to generate a new component:

ng generate component myMap

Now create a <div> element for your future map and provide it as a view child in the component:

my-map.component.html
<div class="map-container" #map></div>
my-map.component.scss
.map-container {
    height: 100%;
}
my-map.component.ts
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  selector: 'app-my-map',
  templateUrl: './my-map.component.html',
  styleUrls: ['./my-map.component.scss']
})
export class MyMapComponent implements OnInit, AfterViewInit {

  @ViewChild('map')
  private mapContainer: ElementRef<HTMLElement>;

  constructor() { }

  ngOnInit() { }

  ngAfterViewInit() { }
}

To make the map fullscreen, add to styles.scss:

styles.scss
html,
body {
  height: 100%;
  margin: 0;
}

Then, provide the container to the map library and create a map. All of the following samples implement exactly the same logic, using different libraries.

Angular + Leaflet: perfect for fast rendering and browsing

The Leaflet is an awesome library for building interactive maps. The Leaflet has excellent documentation and a lot of usage examples. It's open-source and distributed under a commercial-friendly license.

However, it doesn't have native support for vector maps, so we use raster map tiles to build a map.

Install Leaflet and its TypeSript types

You can install the Leaflet map library and its types to the Angular project with the NPM package manager:

npm i leaflet
npm i --save-dev @types/leaflet

Note the type definitions are better to install as devDependencies.

Then, add Leaflet Styling to angular.json:

angular.json
{
  ...
  "projects": {
    "angular-project": {
      ...
      "architect": {
        "build": {
          "options": {
            ...
            "styles": [
              "src/styles.scss",
              "node_modules/leaflet/dist/leaflet.css"
            ],
            ...
          },
          ...
        },
        ...
      }
    }
  },
}

Create a Leaflet Angular directive

Let's extend Leaflet Angular component with a map and add a tile layer with the OSM-Bright map style.

To view Geoapify maps, you must obtain an API key. Go to MyProjects page, sign up for free, and get your API key!

my-map.component.ts
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Browser, Map, map, tileLayer } from 'leaflet';

@Component({
  selector: 'app-my-map',
  templateUrl: './my-map.component.html',
  styleUrls: ['./my-map.component.scss']
})
export class MyMapComponent implements OnInit, AfterViewInit {

  @ViewChild('map')
  private mapContainer: ElementRef<HTMLElement>;

  constructor() { }

  ngOnInit() {
  }

  ngAfterViewInit() {
    const initialState = { lng: 11, lat: 49, zoom: 4 };

    const lefletMap: Map = map(this.mapContainer.nativeElement).setView([initialState.lat, initialState.lng], initialState.zoom);

    const isRetina = Browser.retina;
    const baseUrl = "https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}.png?apiKey={apiKey}";
    const retinaUrl = "https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}@2x.png?apiKey={apiKey}";
    
    tileLayer(isRetina ? retinaUrl : baseUrl, {
      attribution: 'Powered by <a href="https://www.geoapify.com/" target="_blank">Geoapify</a> | <a href="https://openmaptiles.org/" target="_blank">© OpenMapTiles</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap</a> contributors',
      apiKey: 'YOUR_API_KEY',
      maxZoom: 20,
      id: 'osm-bright',
    } as any).addTo(lefletMap);
  }
}

Make sure to specify the initial view and zoom level with setView() method.

You will need to set the maximal zoom level for the Leaflet map. Geoapify provides map tiles up to the 20th zoom level.

Additionally, be sure to cite Geoapify, OpenStreetMap, and OpenMapTiles when using our tile layer. Of course, you can still skip the Geoapify attribution on the paid plans.

Geoapify offers both standard and high-resolution maps, so your users will be able to see an accurate map, even when they have a high-resolution (retina) screen. You can decide which screen type your customer have by reading the property Browser.retina.

The leafletMap is the Leaflet map object that lets you listen for events, adds markers, draws geometries, etc.

This Stackblitz project is a working application that demonstrates Angular and Leaflet working together:

Angular + MapLibre GL (Mapbox GL): vector maps for your flexibility

Mapbox GL is a powerful and modern map library to render maps. It has been developed by the Mapbox company and is one of the most popular tools in the last few years.

The library was open-source and worked in integration with different service providers until December 2020. The library was open-source and worked in integration with varying providers of service until December 2020. Then, it was released under a proprietary license that requires using Mapbox as a service provider.

MapLibre GL is a community-driven fork of the Mapbox GL library. MapLibre GL became one of the most popular projects in the OpenStreetMap ecosystem. This project is still actively developed and finding new and active users and contributors.

Install MapLibre GL and required GeoJSON types

Add the Maplibre-GL package and GeoJSON types to your Angular project with the command below:

npm i [email protected]
npm i --save-dev @types/geojson

Use --save-dev flag for type definitions to keep them in devDependencies. We used the first version of the MapLibre GL for this tutorial, while the 2.x version was pre-release when we wrote this article.

Add MapLibre GL styles to angular.json:

angular.json
{
  ...
  "projects": {
    "angular-project": {
      ...
      "architect": {
        "build": {
          "options": {
            ...
            "styles": [
              "src/styles.scss",
              "node_modules/maplibre-gl/dist/maplibre-gl.css"
            ],
            ...
          },
          ...
        },
        ...
      }
    }
  },
}

Create a MapLibre Angular component

Now we will make use of the angular component we prepared before to show the MapLibre map.

To view Geoapify maps, go to MyProjects page, sign up for free, and get your API key!

my-map.component.ts
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Map, NavigationControl } from 'maplibre-gl';

@Component({
  selector: 'app-my-map',
  templateUrl: './my-map.component.html',
  styleUrls: ['./my-map.component.scss']
})
export class MyMapComponent implements OnInit, AfterViewInit {

  @ViewChild('map')
  private mapContainer: ElementRef<HTMLElement>;

  constructor() { }

  ngOnInit() {
  }

  ngAfterViewInit() {
    const myAPIKey = 'YOUR_API_KEY'; 
    const mapStyle = 'https://maps.geoapify.com/v1/styles/positron/style.json';

    const initialState = { lng: 11, lat: 49, zoom: 9 };

    const map = new Map({
      container: this.mapContainer.nativeElement,
      style: `${mapStyle}?apiKey=${myAPIKey}`,
      center: [initialState.lng, initialState.lat],
      zoom: initialState.zoom
    });

    map.addControl(new NavigationControl());
  }
}

The MapLibre GL has native support for vector maps. The style definition contains all required information, such as maximal zoom level, required attributions, etc. In addition, you don't need to care about retina screens, while the vector maps are always rendered with the highest possible resolution.

The map object is a member of MapLibre Map class which you can use according to MapLibreGL documentation in your code to create beautiful map visualizations.

This Stackblitz project explores integration between Angular and MapLibre GL:

Angular + OpenLayers: build geographic applications with more power

OpenLayers is an open-source JavaScript library for developing interactive maps. It's highly customizable, so you can use it to develop both simple and professional GIS applications.

OpenLayers supports both vector and raster maps. In this tutorial, we will show how to draw vector maps with OpenLayers.

Install OpenLayers and type definitions

To install OpenLayers in an Angular app, run the following command:

npm i ol
npm i --save-dev @types/openlayers

The type definitions are required only during the compilation phase, so they should be installed with --save-dev flag.

Add OpenLayers styles to angular.json:

angular.json
{
  ...
  "projects": {
    "angular-project": {
      ...
      "architect": {
        "build": {
          "options": {
            ...
            "styles": [
              "src/styles.scss",
              "node_modules/ol/ol.css"
            ],
            ...
          },
          ...
        },
        ...
      }
    }
  },
}

Create a OpenLayers Angular component

Now, we want to display the OpenLayers map using the Angular component that we've prepared earlier.

You will need an API key to use Geoapify map tiles. Don't have an API key? Register and get one from the MyProjects page.

my-map.component.ts
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Map, View } from 'ol';
import FullScreen from 'ol/control/FullScreen';
import olms from 'ol-mapbox-style';
import {transform} from 'ol/proj';

@Component({
  selector: 'app-my-map',
  templateUrl: './my-map.component.html',
  styleUrls: ['./my-map.component.scss']
})
export class MyMapComponent implements OnInit, AfterViewInit {

  @ViewChild('map')
  private mapContainer: ElementRef<HTMLElement>;

  constructor() { }

  ngOnInit() {
  }

  ngAfterViewInit() {
    const initialState = { lng: 11, lat: 49, zoom: 9 };

    const myAPIKey = 'YOUR_API_KEY';
    const mapStyle = 'https://maps.geoapify.com/v1/styles/toner-grey/style.json';

    olms(this.mapContainer.nativeElement, `${mapStyle}?apiKey=${myAPIKey}`).then((map: Map) => {
      map.setView(new View({
        center: transform([initialState.lng, initialState.lat], 'EPSG:4326', 'EPSG:3857'),
        zoom: initialState.zoom
      }));

      map.addControl(new FullScreen());
    });
  }
}

The map object returned by the olms method represents an OpenLayers map.

Note: OpenLayers deals with different map projections. The default projection is Web Mercator ( EPSG:3857 ). You can use the transform method to transform Latitude and Longitude coordinates to another projection.

Check OpenLayers Angular component working example on the Stackblitz.

FAQ

How to include Angular with Leaflet?

Add the leaflet package and its type definitions to the Angular project with NPM or another package manager - npm i leaflet, npm i --save-dev @types/leaflet. Then add the Leaflet styles to angular.json - node_modules/leaflet/dist/leaflet.css. Now you can leaflet in Angular components - import { Map, map, tileLayer, Browser } from 'leaflet'.

How to use Leaflet Maps in Angular?

After adding the Leaflet and types to your Angular project and styling the Leaflet objects and methods become available in your components. So all you need to create a map is to call map(this.mapContainer.nativeElement) with an HTML container as a parameter and add a tile layer to the map - tileLayer("https://maps.geoapify.com/v1/tile/osm-carto/{z}/{x}/{y}.png?apiKey=YOU_API_KEY").addTo(map).

How to import Mapbox GL with Angular?

Just install Mapbox GL packages (or its open-source fork MapLibre GL) to your Angular project - npm i maplibre-gl, npm i --save-dev @types/geojson. Don't forget to add stylings, for example, to angular.json. Then all Mapbox objects and methods become available in typescript files - import { Map, NavigationControl } from 'maplibre-gl'.

What's next