OpenLayers 7.0 令人兴奋的特性

2,208 阅读2分钟

OpenLayer 非常优秀、悠久的前端地图框架,10 年前OpenLayers 是 Web 地图库的第一选择。随着 2012 年的重构以及 Leaflet 和 Mapbox GL JS 竞争的加剧,该项目有一个阶段失去了人气。如今,它已成为一个功能齐全、灵活且高性能的地理空间 JavaScript 库,用户可以长期依赖它,尤其是当他们的地图需求变得更加复杂时。

国外地图相关的可视库如火如荼,在竞争中不断成长,国内开源领域近几年也不断涌现后起之秀,AntV L7 (github.com/antvis/l7) 是蚂蚁集团开源的地理可视化引擎,L7 侧重地理数据可视化表达,作为新生代的可视化引擎,站在巨人的肩膀上更快的成长,少走很多弯路, 同时 L7 在技术上也不断创新发展,可视化能力也越来越丰富,满足各种复杂的地图需求。

new in ol@7 🎉

  • new webgl vector renderer
  • untranspiled modules
    (package size dropped 5 MB)
  • other goodies

new webgl vector renderer 🧪

  • points, lines and polygons
  • for untiled vector data

土地覆盖 2018

土地覆盖 2022

\

low level style api with default shaders for common use cases

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,
        },
      },
    });
  }
}
  

\

default vertex shader (fill)

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;
  }`;

low level style api building blocks

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
});
    

graticule 🌍

import Graticule from 'ol/Graticule.js';

const graticule = new Graticule({ showLabels: true });
map.addLayer(graticule);

vector editing

  • interactions for drawing, modifying, translating, and snapping
  • new trace option for the draw interaction 💥

\

style

old stylenew style 🌱
```
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, }), }) );
``````
layer.setStyle({ 'fill-color': 'orange', 'stroke-color': 'red', 'stroke-width': 5, });

#### link interaction 🌱

import Link from 'ol/interaction/Link';

// sync map state with the URL map.addInteraction(new Link());


#### vector tiles single vectortile source

import MapboxVectorLayer from 'ol/layer/MapboxVector';

new MapboxVectorLayer({ styleUrl: 'api.maptiler.com/maps/bright…', });


#### ol-mapbox-style vector, geojson and raster

import {apply} 'ol-mapbox-style'; import LayerGroup from 'ol/layer/Group.js';

apply(map, 'api.maptiler.com/maps/topo/s…');


#### label and symbol decluttering also per-style 🌱

import VectorTileLayer from 'ol/layer/VectorTile';

new VectorTileLayer({ declutter: true, style: { 'icon-declutter-mode': 'none', // or 'obstacle' 'icon-src': '/data/icon.png', }; });


#### rich text style 🌱

textStyle.setText([ feature.getId(), 'bold 13px Calibri,sans-serif', ${feature.get('name')}, '', '\n', '', ${feature.get('kWp')} kWp, 'italic 11px Calibri,sans-serif', ])


![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/28ed12726151469a984eea43637dce2b~tplv-k3u1fbpfcp-zoom-1.image)

\


#### webgl tiles (geotiff sources)

import GeoTIFF from 'ol/source/GeoTIFF';

const source = new GeoTIFF({ sources: [ {url: 'example.com/world.tif'}, ] });

import GeoTIFF from 'ol/source/GeoTIFF';

const source = new GeoTIFF({ sources: [ {url: 'example.com/B08.tif'}, {url: 'example.com/B04.tif'}, {url: 'example.com/B02.tif'}, ] });


#### webgl tiles (band math)

const red = ['band', 3]; const nir = ['band', 4]; const diff = ['-', nir, red]; const sum = ['+', nir, red];

const ndvi = ['/', diff, sum];


#### webgl tiles(style expressions)

const layer = new TileLayer({ source: new GeoTIFF(options), style: { color: [ 'interpolate', ['linear'], ndvi, -1, 'rgb(100, 100, 100)', 1, 'rgb(0, 69, 0)' ], } })


#### webgl tiles (variables)

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) }); });


#### resolving views 🪄

import GeoTIFF from 'ol/source/GeoTIFF';

const source = new GeoTIFF(options);

const map = new Map({ target: 'container', layers: [new TileLayer({source})], view: source.getView(), });


#### pixel data 🌱

map.on('pointermove', event => {

// this next line is the new part! layer.getData(event.pixel);

// do something with the pixel data console.log(data); });


#### more new webgl tile features 🌱

-   expressions for band arguments (6.10)
-   support a palette operator in style expressions (6.10)
-   webgl context in render events (6.10)
-   non-square tiles (6.15)
-   load geotiff from blob (6.15)
-   geotiff nodata handling (6.9, 6.15)
-   geotiff normalize option (6.9)
-   multiple sources for webgl tile layers (6.13)
-   canvas reuse for webgl layers (6.10)

#### map composition history

-   until v5: everything rendered to a single canvas
-   early v6: each layer rendered to its own canvas\
    (because we introduced layer-level webgl renderers)
-   mid v6: combine canvas2d outputs when fully opaque, same resolution and same rotation technique
-   late v6: combine all adjacent canvas2d outputs

#### dom element structure

```

custom renderer 🚀 interactive svg

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;
  },
});
    

webgl points layer style expressions

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],
    },
  },
})