npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

leaflet.tilelayer.gl

v2.4.1

Published

Apply WebGL shaders to your LeafletJS tile layers

Downloads

912

Readme

Leaflet.TileLayer.GL

A LeafletJS 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!

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

Besides those, the Mandelbrot set demo 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) can already use WebGL shaders to apply transformations to map tiles and do fancy stuff.

The inflexion point are MapBox's "Terrain-RGB" tiles. WebGL manipulation of these tiles can provide real-time terrain relief and hill shading.

This takes some inspiration from 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 and ES6 Promises. 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/[email protected]/dist/leaflet.css"
  integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ=="
  crossorigin=""/>
<script src="https://unpkg.com/[email protected]/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 to become acquinted, or The Book Of Shaders to learn to do cool shaders, or WebGL Fundamentals 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 LatLngs).
  • 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()). 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:

// 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.

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 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": [email protected] 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.