-
Notifications
You must be signed in to change notification settings - Fork 126
Integrating Plugins
One of the awesome things about Leaflet is that it is extensible and customizable. There are hundreds of plugins that add custom layers, controls, and utilities. This is a large part of why Leaflet is so popular and why we built ngx-leaflet.
We get a lot of questions about Leaflet plugin integration on GitHub and Stack Overflow. Often, we'll include a ton of example code and configuration in the answers, but it isn't centrally located or easy to find. As a result, we made a project on GitHub as a central location for integration examples. This article provides an intro to the ngx-leaflet-tutorial-plugins project and describes the process of Leaflet plugin integration with ngx-leaflet.
In the ngx-leaflet-tutorial-plugins project, you'll find several projects that show how to integrate ngx-leaflet with different Leaflet plugins. Each project has a working Angular CLI example as well as a README with instructions. The included projects are listed below. If we haven't covered the specific plugin you're interested in, you can still review this article and the example projects for helpful information.
Leaflet.Coordinates is a custom control that displays the coordinates of the mouse on the map. In addition, the user can drop markers at specific locations and see the coordinates of the position.
Leaflet.Path.Transform extends existing Leaflet functionality. It allows users to drag, rotate, and scale vector features on the map.
Leaflet.TimeDimension is a really cool plugin that allows you to present time-based animated map features (e.g., time-based weather radar). And, it provides controls so the user can start and stop playback.
heatmap.js adds custom heatmap layers to Leaflet.
Leaflet plugins extend the functionality of Leaflet. Often, they add custom controls, layers, or utilities. Using Leaflet plugins outside of Angular is simple when Leaflet is exposed as the L
variable. But, it gets more complicated with module systems (e.g., UMD, ES6 Modules, etc.), module bundlers (e.g., Webpack, Rollup, etc.), and Typescript. The rest of this article presents the general process of integrating Leaflet plugins into Angular CLI projects that use ngx-leaflet.
The first step is installing the code for the plugin. Often, it's available via npm. If it is, you can simply install it:
# Install via npm
$ npm install leaflet-timedimension
# Install via yarn
$ yarn add leaflet-timedimension
Unfortunately, not all dependencies are available as packages on npm. Some are only available via Bower.io or only as a download. In those cases, you need to download/install the files and place them directly in your project (i.e., in a directory).
Next, you need to get the plugin code into your project. Leaflet plugins vary widely in how they are packaged. Some use ES6 modules. Most use a specific module system like UMD. But others are simply scripts that modify the global L
variable.
Angular CLI is pretty flexible about supporting libraries that are exported using various module systems (or no module system at all). So, it's usually just a matter of figuring out the right method of importing the library.
These are the easiest because you can perform named imports of Leaflet and of the plugin you want to use. Nothing special is necessary, so it's not very exciting, eh?
In this case, you must import the whole Leaflet module into L
. Then, you import the plugin library for side effects. This is illustrated in the following snippet taken from the leaflet-path-transform
example:
import { Component } from '@angular/core';
// Import Leaflet into L
import * as L from 'leaflet';
// Import the plugin libraries so they will modify L
import 'leaflet-path-transform';
import 'leaflet-path-drag';
@Component({
...
Sometimes plugins aren't npm packages, or don't name a main file in package.json
. As a result, you may need to reference the file using a relative file path. This will force Webpack to bundle the file, allowing it to modify L
. This is the case with leaflet-timedimension
:
import { Component } from '@angular/core';
// Import Leaflet into L
import * as L from 'leaflet';
// Import the plugin library file directly, so it will change L
import '../../node_modules/leaflet-timedimension/dist/leaflet.timedimension.src.js';
@Component({
...
Finally, some plugins (or their dependencies) need to be loaded as global scripts. In these cases, you will need to configure Angular CLI to load them this way. In these cases, you have to modify the scripts
property in angular.json like this example from heatmap.js
:
...
"scripts": [
"./node_modules/leaflet/dist/leaflet.js",
"./node_modules/heatmap.js/build/heatmap.js",
"./node_modules/leaflet-heatmap/leaflet-heatmap.js"
],
...
The last thing you need to set up is type information. Fortunately, a lot of typings already exist. If they don't, you'll need to create your own.
First, you should see if they are already included in the plugin library. Look in the plugin's package.json
file for a typings
property. If it's there, you should be good.
Second, check the DefinitelyTyped project for existing typings for the plugin. If they exist, install them using npm and you're done. Angular CLI will automatically include them in your project.
If you can't find existing typings, you'll need to create your own. But don't worry, it isn't hard. Most plugins (for better or worse) modify the existing Leaflet types. This means that most of the time, you'll add some type info to the existing Leaflet module. You can do this by adding or modifying the /src/typings.d.ts
file in your Angular CLI project. The following is an example for Leaflet.Coordinates
:
// Import Leaflet into L in case you want to reference Leaflet types
import * as L from 'leaflet';
// Declare the leaflet module so we can modify it
declare module 'leaflet' {
// We want to alter the control namespace
namespace control {
// Define minimal type information for the coordinates function
function coordinates(v: any);
}
}
In this example, we're keeping the typings simple so we can get up and running quickly. But, they are not as easy to work with in the long run. In this example, we exposed the coordinates
function but didn't type the input parameter to the function.
If you put in the effort to develop a really good set of type definitions for a Leaflet plugin, you should consider contributing it back to DefinitelyTyped
At this point, you can actually do the work to get the plugin working. This step varies a lot depending on the plugin. Some are layers that you can just create and add to the layers that get added to the map. This is the case with heatmap.js
:
heatmapLayer = new HeatmapOverlay({
radius: 2,
maxOpacity: 0.8,
scaleRadius: true,
useLocalExtrema: true,
latField: 'lat',
lngField: 'lng',
valueField: 'count'
});
options = {
layers: [
L.tileLayer(...),
this.heatmapLayer
],
zoom: 4,
center: L.latLng([ 46.879966, -121.726909 ])
};
In other cases, you have to add a custom control to the map, like with Leaflet.Coordinates
. To do this, simply bind a function (in this case onMapReady
) to the (leafletMapReady)
output binding, create the control, and add it to the map:
onMapReady(map: L.Map) {
L.control.coordinates({}).addTo(map);
}
Every plugin has its nuances, so it will take a little investigation to get each to work. But, the plugin tutorials project provides several examples to get you started. If you have specific requests or questions, we monitor the ngx-leaflet tag on Stack Overflow. Otherwise, we welcome contributions on Github.