# leaflet.tilelayer.gl

> Apply WebGL shaders to your LeafletJS tile layers

Latest version **2.4.1** (published 2020-06-04) · Beerware license · 0 weekly downloads

## Install

```sh
npm install leaflet.tilelayer.gl
pnpm add leaflet.tilelayer.gl
yarn add leaflet.tilelayer.gl
bun add leaflet.tilelayer.gl
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.4.1 |
| Published | 2020-06-04 |
| First published | 2016-10-29 |
| Weekly downloads | 0 |
| License | Beerware |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 5.2 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Iván Sánchez Ortega |
| Maintainers | ivansanchez |
| Keywords | leaflet, webgl |

## Links

- npm: https://www.npmjs.com/package/leaflet.tilelayer.gl
- Repository: https://gitlab.com/IvanSanchez/Leaflet.TileLayer.GL
- npm.io page: https://npm.io/package/leaflet.tilelayer.gl

## Recent versions

- 2.4.1 (latest) — 2020-06-04
- 2.3.0 — 2019-01-19
- 2.2.0 — 2019-01-15
- 2.1.0 — 2019-01-14
- 2.0.0 — 2017-05-05
- 1.0.1 — 2016-10-31
- 1.0.0 — 2016-10-29

## README

# Leaflet.TileLayer.GL

A [LeafletJS](http://www.leafletjs.com) plugin to appy WebGL shaders to your tiles.

With this plugin, you can apply colour transforms to your tiles, merge two or
more tiles with a custom function, perform on-the-fly hillshading, or create synthetic
tile layers based only on the map coordinates.

## Demos


**See several examples and edit them in the [interactive editable demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/repl.html)!**

The interactive editable demo includes the code for the following, which you can also see individually:

* [Basic colour inversion](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-antitoner.html): This demo loads the ["toner" map style by Stamen](http://maps.stamen.com/toner/) and changes the colours on-the-fly.
* [Conditional colouring](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-tropical.html): This demo loads the ["toner" map style by Stamen](http://maps.stamen.com/toner/) and changes the colours on-the-fly, depending on the latitude of each pixel. This highlights the tropics and arctic circles.
* [Flood & height](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-flood.html): This demo uses [MapBox's "Terrain-RGB" tiles](https://www.mapbox.com/blog/terrain-rgb/) to play with the elevation: areas are coloured depending to the elevation (below 0 meters, between 0 and 5 meters, between 5 and 10 meters, above 10 meters).
* [Hypsometric tint](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-hypsometric.html): This demo uses [MapBox's "Terrain-RGB" tiles](https://www.mapbox.com/blog/terrain-rgb/) and applies a basic [hypsometric tint](https://en.wikipedia.org/wiki/Hypsometric_tints) colour ramp.
* [L.tileLayer.wms](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-wms.html): This demo uses [Leaflet's TileLayer.WMS](http://leafletjs.com/reference-1.3.0.html#tilelayer-wms) to load tiles from a WMS server and changes its brightness.
* [Interactive colorizer demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-sliders.html): This demo defines custom uniforms for the shaders and updates them based no the user's input.
* [Pixellated demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-pixellated.html): Forces nearest-neighbour interpolation when overzooming on a `TileLayer`.
<!-- * [Interactive NDVI demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-ndvi-sliders.html): As the previous one, but with 5 sliders to tweak a NDVI (Normalized Differential Vegetation Index) raster. -->


Besides those, the [Mandelbrot set demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-mandelbrot.html) uses a map with `L.CRS.Simple` coordinates and no tiles at all, to draw a fractal set.

The interactive demo includes the following, which doesn't have a stand-alone demo:
* Hue rotation (converts RGB colour space to HSV, modifies the hue, converts back)

## Why?

Leaflet has been lagging behind when it comes to WebGL technology. Other map libraries (such as [OpenLayers 3]() and most notably [Tangram](https://mapzen.com/products/tangram/)) can already use WebGL shaders to apply transformations to map tiles and do fancy stuff.

The inflexion point are [MapBox's "Terrain-RGB" tiles](https://www.mapbox.com/blog/terrain-rgb/). WebGL manipulation of these tiles can provide real-time terrain relief and hill shading.

This takes some inspiration from [shadertoy.com](http://www.shadertoy.com), in the sense that the shaders work on two triangles with some predefined attributes and uniforms.

## Compatibility

Leaflet 1.0.3 (or newer), and a web browser that supports both [WebGL](http://caniuse.com/#search=webgl) and [ES6 `Promise`s](http://caniuse.com/#search=promise). You can also use a `Promise` polyfill for IE11.

## Usage

Include Leaflet and Leaflet.TileLayer.GL in your HTML:

```
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css"
  integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ=="
  crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js"
  integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA=="
  crossorigin=""></script>
<script src='https://unpkg.com/leaflet.tilelayer.gl@latest/src/Leaflet.TileLayer.GL'></script>
```

Alternatively, fetch a local copy of Leaflet and Leaflet.TileLayer.GL with `npm install --save leaflet; npm install --save leaflet.tilelayer.gl` or `yarn add leaflet; yarn add leaflet.tilelayer.gl`

You can create instances of `L.TileLayer.GL` in your code. These take two new options: `fragmentShader` and `tileUrls`, e.g.:

```
	var antitoner = L.tileLayer.gl({
		fragmentShader: "// String with GLSL fragment shader code",
		tileUrls: ['http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png']
	}).addTo(map);
```

Using this plugin requires some knowledge of WebGL and GLSL shaders. If you've never heard the terms "vertex shader" or "fragment shader", read [this WebGL tutorial](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL) to become acquinted, or [The Book Of Shaders](https://thebookofshaders.com/) to learn to do cool shaders, or [WebGL Fundamentals](http://webglfundamentals.org/webgl/lessons/webgl-image-processing.html) to see some WebGL image processing techniques.

The `fragmentShader` option contain shader code, in a string. For every map tile, two triangles are created, a simple vertex shader runs to copy data and fill the values for the _varyings_, and the fragment shader runs once on every pixel (to be precise, on every _fragment_). This plugin does not allow you to create more triangles, and does not allow to create animations.

The fragment shader receives the following **varyings**:

* `vLatLngCoords`: a `vec2` containing the *map data* coordinates for the vertices (with values like `LatLng`s).
* `vCRSCoords`: a `vec2` containing the *map display* coordinates for the vertices (with values for the map CRS).
* `vTextureCoords`: a `vec2` containing the *texture* coordinates for the vertices. Use this for fetching texels.

It also receives the following **uniforms**:

* `uNow`: a `float` with the number of microseconds since page load (as per [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now)). If this uniform is not used, tiles will be rendered only once. If it is, then they will be re-rendered at each frame.
* `uTexture0`: a `sampler2D` referring to the first loaded tile image. This exists only if the `tileUrls` option is not empty.
* `uTexture1`..`uTexture7`: texture samplers for the 2nd through 8th image.
* `uTileCoords`: a `vec3` containing the *tile* coordinates, as used in the tile URLs. Use only when the tile coordinates (or their zoom level) are relevant.


Alternatively, provide one or more instances of `L.TileLayer` in the `tileLayers` option. This is useful for using WMS data sources, e.g.:

```
	var layer = L.tileLayer.gl({
		fragmentShader: "// String with GLSL fragment shader code",
		tileLayers: [
			L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png'),
			L.tileLayer.wms('http://path.to/wms/service')
		]
	}).addTo(map);
```

In the previous example, the `L.TileLayer` will be in `uTexture0` and the `L.TileLayer.WMS` in `uTexture1`.


## Demo shaders

This is the code used in the "antitoner" demo, commented and explained:

```js
// Create the fragment shader as a multi-line string. Note the "`" character, valid only in ES6 JavaScript.
// Shaders can be defined elsewhere, or loaded from other files or from the network,
// but they must be strings when used in a TileLayer.GL.

// You need to *not* define the varyings and uniforms. L.TileLayer.GL does that for you.
// // precision highp float;
// // uniform sampler2D uTexture0;	// This contains a reference to the tile image loaded from the network
// // varying vec2 vTextureCoords;	// This is the interpolated texel coords for this fragment

var antiTonerFragmentShader = `
	void main(void) {
		// Classic texel look-up (fetch the texture "pixel" color for this fragment)
		vec4 texelColour = texture2D(uTexture0, vec2(vTextureCoords.s, vTextureCoords.t));

		// If uncommented, this would output the image "as is"
		// gl_FragColor = texelColour;

		// Let's mix the colours a little bit, inverting the red and green channels.
		gl_FragColor = vec4(1.0 - texelColour.rg, texelColour.b, 1.0);
	}
`

// Instantiate our L.TileLayer.GL...
var antitoner = L.tileLayer.gl({
	// ... with the shader we just wrote above...
	fragmentShader: antiTonerFragmentShader,

	// ...and loading tile images from Stamen Toner as "uTexture0".
	// If this array contained more than one tile template string,
	// there would be "uTexture1", "uTexture2" and so on.
	tileUrls: ['http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png']
}).addTo(map);
```

Find more examples in the [interactive demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/repl.html).


## Custom uniforms

It is possible to specify custom uniforms, update them from the JS side and trigger re-renders every time they change. This is done with the `uniforms` property, and the `setUniform()` and `reRender()` methods, e.g.:

```
var layer = L.tileLayer.gl({
	tileUrls: ['http://tileserver/{z}/{x}/{y}.png'],
	fragmentShader: "gl_FragColor = vec4(uRGB, uAlpha);",
	uniforms: {
		uRGB: [0.5, 1.0, 0.2],
		uAlpha: 1.0
	}
}).addTo(map);

layer.setUniform(uAlpha, 0.8);
layer.setUniform(uRGB, [0.6, 0.9, 0.3]);
layer.reRender();
```

The [interactive colorizer demo](http://ivansanchez.gitlab.io/Leaflet.TileLayer.GL/demo/demo-sliders.html) offers a more complete example of this feature set.

## Cool things that should be doable, but nobody has yet shown interest in asking about, much less in implementing them

* Updating the shaders
* Reusing the same WebGL context for more than one `TileLayer.GL` (as the render
  calls are sync)
* Render stuff off the main thread

## Legalese

----------------------------------------------------------------------------

"THE BEER-WARE LICENSE":
<ivan@sanchezortega.es> wrote this file. As long as you retain this notice you
can do whatever you want with this stuff. If we meet some day, and you think
this stuff is worth it, you can buy me a beer in return.

----------------------------------------------------------------------------

---
_Source: https://npm.io/package/leaflet.tilelayer.gl · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
