ol-cesium icon indicating copy to clipboard operation
ol-cesium copied to clipboard

How to use ol/layer/VectorTile?

Open yiwenzhang666999 opened this issue 6 years ago • 9 comments

ol-cesium Can be added mapbox-vector-tiles (ol/layer/VectorTile),Is there an example?

yiwenzhang666999 avatar Nov 19 '19 08:11 yiwenzhang666999

I wrote an example that can be displayed in 2D but not in 3D.

const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q';

const map = new ol.Map({
  layers: [
	new ol.layer.VectorTile({
	  declutter: true,
	  source: new ol.source.VectorTile({
		attributions: '© <a href="https://www.mapbox.com/map-feedback/">Mapbox</a> ' +
		  '© <a href="https://www.openstreetmap.org/copyright">' +
		  'OpenStreetMap contributors</a>',
		format: new ol.format.MVT(),
		url: 'https://{a-d}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/' +
			'{z}/{x}/{y}.vector.pbf?access_token=' + key
	  })//,
	  //style: createMapboxStreetsV6Style(Style, Fill, Stroke, Icon, Text)
	})
  ],
  target: 'map',
  view: new ol.View({
	center: [0, 0],
	zoom: 2
  })
});

var ol3d = new olcs.OLCesium({ map: map }); var scene = ol3d.getCesiumScene(); image image

yiwenzhang666999 avatar Nov 19 '19 08:11 yiwenzhang666999

Hi @yiwenzhang666999, no it is not possible at the moment but something I experimented with a while ago. The idea would be to create a Cesium imagery provider which would make use of OpenLayers to render the tiles.

Here is the code, bsd-3 licensed by me. Let me know if you find/implement something interesting in that direction:

const toContext = ol.render.toContext;

const format = new ol.format.MVT();


export class MVTImageryProvider {
  constructor(options) {
    this.ready = false;
    this.readyPromise = (options.readyPromise || Promise.resolve(true)).then(r => this.ready = r);
    this.tileWidth = 256;
    this.tileHeight = 256;
    this.maximumLevel = options.maximumLevel;
    this.minimumLevel = options.minimumLevel || 0;
    this.tilingScheme = options.tilingScheme || new Cesium.WebMercatorTilingScheme;
    this.rectangle = options.rectangle || Cesium.Rectangle.MAX_VALUE;
    this.errorEvent = {};
    this.credit = new Cesium.Credit(options.credit || '', false);
    this.hasAlphaChannel = Cesium.defaultValue(options.hasAlphaChannel, true);
    this.cache_ = {};
    this.url_ = options.url;
    this.styleFunction_ = options.styleFunction;
  }

  getTileCredits() {
    return [];
  }

  pickFeatures() {
  }
  
  requestImage(x, y, z, request) {
    // stupid put everything in cache strategy
    // no throttling, no subdomains
    // Caching is supposed to be handled by the ImageryLayer so why would I need that?
    const url = this.url_.replace('{x}', x).replace('{y}', y).replace('{z}', z);
    let promise = this.cache_[url];
    if (!promise) {
      promise = this.cache_[url] = rasterizeTile(url, this.styleFunction_, format);
    }
    return promise;
  }
}


export function rasterizeTile(url, styleFunction, format) {
  const canvas = document.createElement('canvas');
  const vectorContext = toContext(canvas.getContext('2d'), {size: [256, 256]});

  // FIXME: add some error handling
  return fetch(url).then(r => r.arrayBuffer().then(
    buffer => {
      const features = format.readFeatures(buffer);

      // Scale features coordinates so they fit in the 256x256 pixels tile
      const scaleFactor = 256 / 4096;
      features.forEach(f => {
        const flatCoordinates = f.getFlatCoordinates();
        for (let i = 0; i < flatCoordinates.length; ++i) {
          flatCoordinates[i] *= scaleFactor;
        }
      });

      const instructionsByZindex = {};
      features.forEach(feature => {
        const styles = styleFunction(feature);
        styles.forEach(style => {
          const zIndex = style.getZIndex() || 0;
          const instructions = instructionsByZindex[zIndex] = instructionsByZindex[zIndex] || [];
          instructions.push({
            style,
            feature
          });
        });
      });

      // Sort the instructions and draw the geometries
      const keys = Object.keys(instructionsByZindex).sort(); // ! numeric sort
      keys.forEach(key => {
        instructionsByZindex[key].forEach(instructions => {
          const {style, feature} = instructions;
          const geometry = style.getGeometry() || feature.getGeometry();
          vectorContext.setStyle(style);
          vectorContext.drawGeometry(geometry);
        });
      });

      // A layer alpha can be set at the ImageryLayer level
      return canvas;
    })
  ).finally(() => canvas)
}

gberaudo avatar Nov 21 '19 16:11 gberaudo

hi @gberaudo I have implemented loading vector tiles, the next thing to do is to read the mapbox style sheet to render the map image

yiwenzhang666999 avatar Nov 26 '19 06:11 yiwenzhang666999

hi @gberaudo I use cesium to load vector tiles and read mapbox style sheets. The map rendering method refers to ol-mapbox-style, but some annotations on the map are not displayed. Can you give me some suggestions? What could be causing this? image image

yiwenzhang666999 avatar Dec 12 '19 10:12 yiwenzhang666999

hi @gberaudo How MVTImageryProvider is added to ol-cesium?

yiwenzhang666999 avatar Jan 09 '20 11:01 yiwenzhang666999

@yiwenzhang666999, you can get inspiration on how OLImageryProvider in used. See also how custom synchronizers can be set up: https://github.com/openlayers/ol-cesium/blob/master/src/olcs/OLCesium.js#L220-L225.

gberaudo avatar Jan 10 '20 08:01 gberaudo

hi @gberaudo Thank you very much for your help, I can add MVTImageryProvider to ol-cesium through ol3d.globe_.imageryLayers.addImageryProvider method

yiwenzhang666999 avatar Jan 13 '20 10:01 yiwenzhang666999

@gberaudo the link is broken in your comment above. Here is an updated permalink: https://github.com/openlayers/ol-cesium/blob/44bb5c5c18b44e38671970e471833d00a94258c7/src/olcs/OLCesium.ts#L237-L242

matthias-ccri avatar Jun 13 '23 21:06 matthias-ccri