OpenLayers
Feature Frenzy
npm create ol-app my-app
cd my-app
npm start
Let's dive in! ๐
import Layer from 'ol/layer/Layer.js';
import VectorLayerRenderer from 'ol/renderer/webgl/VectorLayer.js';
import {packColor} from 'ol/renderer/webgl/shaders.js';
class WebGLLayer extends Layer {
createRenderer() {
return new VectorLayerRenderer(this, {
fill: {
attributes: {
color: (feature) => packColor(feature.get('color')),
opacity: () => 0.6,
},
},
stroke: {
attributes: {
color: () => packColor([0, 0, 0, 1]),
width: () => 3,
},
},
});
}
}
const DECODE_COLOR_EXPRESSION = `vec3(
fract(floor(a_color / 256.0 / 256.0) / 256.0),
fract(floor(a_color / 256.0) / 256.0),
fract(a_color / 256.0)
);`;
const FILL_VERTEX_SHADER = `
precision mediump float;
uniform mat4 u_projectionMatrix;
attribute vec2 a_position;
attribute float a_color;
attribute float a_opacity;
varying vec3 v_color;
varying float v_opacity;
void main(void) {
gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0);
v_color = ${DECODE_COLOR_EXPRESSION}
v_opacity = a_opacity;
}`;
import VectorLayerRenderer from 'ol/renderer/webgl/VectorLayer.js';
new VectorLayerRenderer(this, {
fill: {
vertexShader: fillVertexShader,
fragmentShader: fillFragmentShader,
attributes: fillAttributes,
},
stroke: {
vertexShader: strokeVertexShader,
fragmentShader: strokeFragmentShader,
attributes: strokeAttributes,
},
point: {
vertexShader: poiontVertexShader,
fragmentShader: pointFragmentShader,
attributes: pointAttributes,
},
uniforms,
postProcesses
});
import Graticule from 'ol/Graticule.js';
const graticule = new Graticule({ showLabels: true });
map.addLayer(graticule);
trace
option for the draw interaction ๐ฅ
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
// previously
layer.setStyle(
new Style({
fill: new Fill({
color: 'orange',
}),
stroke: new Stroke({
color: 'red',
width: 5,
}),
})
);
// for static styles
layer.setStyle({
'fill-color': 'orange',
'stroke-color': 'red',
'stroke-width': 5,
});
import Link from 'ol/interaction/Link';
// sync map state with the URL
map.addInteraction(new Link());
import MapboxVectorLayer from 'ol/layer/MapboxVector';
new MapboxVectorLayer({
styleUrl:
'https://api.maptiler.com/maps/bright/style.json',
});
import {apply} 'ol-mapbox-style';
import LayerGroup from 'ol/layer/Group.js';
apply(map, 'https://api.maptiler.com/maps/topo/style.json');
can also apply
to an ol/layer/Group
๐ฑ
import VectorTileLayer from 'ol/layer/VectorTile';
new VectorTileLayer({
declutter: true,
style: {
'icon-declutter-mode': 'none', // or 'obstacle'
'icon-src': '/data/icon.png',
};
});
textStyle.setText([
feature.getId(),
'bold 13px Calibri,sans-serif',
` ${feature.get('name')}`,
'',
'\n',
'',
`${feature.get('kWp')} kWp`,
'italic 11px Calibri,sans-serif',
])
import GeoTIFF from 'ol/source/GeoTIFF';
const source = new GeoTIFF({
sources: [
{url: 'https://example.com/world.tif'},
]
});
import GeoTIFF from 'ol/source/GeoTIFF';
const source = new GeoTIFF({
sources: [
{url: 'https://example.com/B08.tif'},
{url: 'https://example.com/B04.tif'},
{url: 'https://example.com/B02.tif'},
]
});
const red = ['band', 3];
const nir = ['band', 4];
const diff = ['-', nir, red];
const sum = ['+', nir, red];
const ndvi = ['/', diff, sum];
const layer = new TileLayer({
source: new GeoTIFF(options),
style: {
color: [
'interpolate', ['linear'], ndvi,
-1, 'rgb(100, 100, 100)',
1, 'rgb(0, 69, 0)'
],
}
})
const style = {
variables: {
seaLevel: 0,
},
color: [
'case',
['<=', elevation, ['var', 'seaLevel']],
[139, 212, 255, 1],
[139, 212, 255, 0],
],
}
slider.addEventListener('input', () => {
layer.updateStyleVariables({
seaLevel: parseFloat(slider.value)
});
});
import GeoTIFF from 'ol/source/GeoTIFF';
const source = new GeoTIFF(options);
const map = new Map({
target: 'container',
layers: [new TileLayer({source})],
view: source.getView(),
});
map.on('pointermove', event => {
// this next line is the new part!
layer.getData(event.pixel);
// do something with the pixel data
console.log(data);
});
const element = map.getTargetElement();
map.on('loadstart', () => {
element.classList.add('spinner');
});
map.on('loadend', () => {
element.classList.remove('spinner');
});
className
configured on adjacent layers splits outputs
<div class="ol-layers"></div>
<div class="ol-layer"></div>
<div class="svg-layer"></div>
<canvas class="webgl-layer"></canvas>
</div>
import Layer from 'ol/layer/Layer.js';
const container = document.createElement('div');
fetch(url)
.then(response => response.text())
.then(svg => {
container.innerHTML = svg;
});
const svgLayer = new Layer({
render(frameState) {
// transform container to match frameState
// (center, resolution, size)
return container;
},
});
import WebGLPointsLayer from 'ol/layer/WebGLPoints.js';
new WebGLPointsLayer({
className: 'webgl-layer',
source: source,
style: {
variables, // contains the current year of the animation
filter, // only points within 10 years back from current year,
symbol: {
symbolType: 'circle',
size, // size based on mass of the meteorite and decay
color: 'rgb(255, 0, 0)',
opacity: ['*', 0.5, decay],
},
},
});