Skip to content

Repository files navigation

ngx-maplibre-gl

Angular wrapper for maplibre-gl. It exposes a bunch of components meant to be simple to use with Angular.

npm version

Demo site

Can be found here (based on the generated gh-pages in this repo): https://maplibre.org/ngx-maplibre-gl/

Attribution

This is a fork of ngx-mapbox-gl and I would like to thank the maintainers there for their amazing work to build this up. It's truly a great piece of software!

API Documentation

The API documentation can be found here.

How to start

npm install @maplibre/ngx-maplibre-gl maplibre-gl
yarn add @maplibre/ngx-maplibre-gl maplibre-gl

There might be a need to add the following configuration to tsconfig.json file

"compilerOptions": {
    ...
    "strictNullChecks": false,
    "skipLibCheck": true,
}

Load the CSS of maplibre-gl

For example, with angular-cli add this in angular.json:

"styles": [
  ...,
  "./node_modules/maplibre-gl/dist/maplibre-gl.css"
],

Or in the global CSS file (called styles.css for example in angular-cli):

@import 'maplibre-gl/dist/maplibre-gl.css';

Serve the MapLibre GL JS web worker

MapLibre GL JS v6 is ESM-only and loads its web worker from a separate file at runtime. Bundlers cannot rewrite that URL, so every bundler-based app must point MapLibre at it before the first map is created — see setWorkerUrl() is bundler-only. Without it the worker 404s and no tiles render. Here is the Angular setup.

Add both files to the assets of your build target in angular.json. The worker imports maplibre-gl-shared.mjs as a sibling at runtime, so they must be copied together, into the same directory:

"assets": [
  ...,
  {
    "glob": "maplibre-gl-worker.mjs",
    "input": "node_modules/maplibre-gl/dist",
    "output": "/"
  },
  {
    "glob": "maplibre-gl-shared.mjs",
    "input": "node_modules/maplibre-gl/dist",
    "output": "/"
  }
]

Then provide the worker URL with provideMaplibreWorker in your application config:

import { ApplicationConfig } from '@angular/core';
import { provideMaplibreWorker } from '@maplibre/ngx-maplibre-gl/config';

export const appConfig: ApplicationConfig = {
  providers: [provideMaplibreWorker('maplibre-gl-worker.mjs')],
};

The URL is resolved against document.baseURI, so it stays correct when your app is deployed under a sub-path via --base-href. It can also be provided in a component's providers to scope it to a subtree. The URL is applied lazily, right before the first map is created — unlike calling setWorkerUrl() in main.ts, this keeps maplibre-gl out of your initial bundle.

If you cannot use the provider, call setWorkerUrl() once yourself before the first map is created instead:

import { setWorkerUrl } from 'maplibre-gl';

setWorkerUrl(new URL('maplibre-gl-worker.mjs', document.baseURI).href);

If you also run browser-based unit tests, do the same in your test setup file. When tests are served by Vite, a ?url import resolves the worker straight out of node_modules:

import { setWorkerUrl } from 'maplibre-gl';
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?url';

setWorkerUrl(new URL(workerUrl, document.baseURI).href);

Upgrading MapLibre GL JS

Upgrading from v5? Follow the v5 to v6 migration guide.

Breaking change: camera inputs are plain values

[zoom], [bearing], [pitch] and [roll] took a single-element array, so that re-assigning the same number still counted as a change. They are now plain numbers, and two-way capable:

- <mgl-map [zoom]="[9]" [pitch]="[45]" [bearing]="[-17.6]" />
+ <mgl-map [zoom]="9" [pitch]="45" [bearing]="-17.6" />

Updating one axis has never dragged the others back with it - that is handled by only sending the axes that actually changed - so nothing else changes for one-way bindings.

If you relied on the array to return the map to a position the user had since moved away from, bind two-way instead. The camera models follow the map, so the earlier value is a real change again:

<mgl-map [(zoom)]="zoom" [(center)]="center" />

Then, in your app's main module (or in any other module), import the MapComponent:

import { Component } from '@angular/core';
import { MapComponent } from '@maplibre/ngx-maplibre-gl';

@Component({
  template: `
    <mgl-map
      [mapStyle]="'https://demotiles.maplibre.org/style.json'"
      [zoom]="9"
      [center]="[-74.5, 40]"
    >
    </mgl-map>
  `,
  styles: [
    `
      mgl-map {
        height: 100%;
        width: 100%;
      }
    `,
  ],
  imports: [MapComponent],
})
export class AppComponent {}

If you use several components, it will be convenient to import NgxMapLibreGLModule instead:

import { Component } from '@angular/core';
import { NgxMapLibreGLModule } from '@maplibre/ngx-maplibre-gl';

@Component({
  template: `
    <mgl-map
      [mapStyle]="'https://demotiles.maplibre.org/style.json'"
      [zoom]="9"
      [center]="[-74.5, 40]"
    >
      <mgl-control
        mglNavigation
        [visualizePitch]="true"
    />
    </mgl-map>
  `,
  styles: [
    `
      mgl-map {
        height: 100%;
        width: 100%;
      }
    `,
  ],
  imports: [NgxMapLibreGLModule]
})
export class AppComponent {}

About

Angular binding of maplibre-gl

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

117 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages