基于带有 openlayers 的 WMS `GetTile` 请求的 TileWMS -> TileLayer -> Map -> View 仅复制地图上的相同图块

An TileWMS -> TileLayer -> Map -> View based on a WMS `GetTile` request with openlayers only duplicates the same tile on the map

直到 2020 年,我一直在使用 Openlayers 显示 IGN 地图,代码如下:

import { Component, OnInit, Input } from '@angular/core';

import Map from 'ol/Map';
import View from 'ol/View';
import TileWMS from 'ol/source/TileWMS';
import * as olProj from 'ol/proj';
import TileLayer from 'ol/layer/Tile';

@Component({
  selector: 'carte',
  templateUrl: './carte.component.html',
  styleUrls: ['./carte.component.css']
})
export class CarteComponent implements OnInit {
  /** Carte Openlayers. */
  map: Map;
      
  /* Longitude. */
  @Input() longitude: number;
  
  /* Latitude. */
  @Input() latitude: number;
  
  /** Niveau de zoom initial. */
  @Input() zoom: number;

  constructor() {
  }

  ngOnInit(): void {
    var ign_source = new TileWMS({
      url: 'http://wxs.ign.fr/cdw20cou9clqe59fvn39rmcu/geoportail/r/wms',
      params: {'LAYERS': 'GEOGRAPHICALGRIDSYSTEMS.PLANIGN', 'TILED': false}
    });
    
    var ign = new TileLayer({
        source: ign_source
    });

    this.map = new Map({
        target: "carte_principale",
        
        view: new View({
            center: olProj.fromLonLat([this.longitude, this.latitude]),
            zoom: this.zoom
        })
    });
    
    this.map.addLayer(ign);
  }
}

然后 IGN geoportail 做了几个月的大改动,当它返回时,我不得不调整我的代码才能再次使用它。

它的文档看起来使用 GetTile 请求来显示地图,而不是之前建议使用的 GetMap
根据它,我以这种方式更改了我的代码:

ngOnInit(): void {
  const planIGN =  new TileWMS( {
    url: 'https://wxs.ign.fr/decouverte/geoportail/wmts',

    params: {
      REQUEST: 'GetTile',
      VERSION: '1.0.0',
      LAYER: 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2',
      STYLE: 'normal',
      FORMAT: 'image/png',
      SERVICE: 'WMTS',
      TILEMATRIX: 14,
      TILEMATRIXSET: 'PM',
      TILECOL: 8180,
      TILEROW: 5905
    }});

  const ign = new TileLayer({
     source: planIGN
  });

  this.map = new Map({
     target: 'carte_principale',

     view: new View({
        center: olProj.fromLonLat([this.longitude, this.latitude]),
        zoom: this.zoom
     })
  });

  this.map.addLayer(ign);
}

有效...但显示效果与预期不符:

最后两点,因为我不知道如何正确地参数化请求。

当我尝试使用其网络工具显示 QGis(地图显示正常)时,它看到交换了那种请求,所以我还没有到现在:

https://wxs.ign.fr/decouverte/geoportail/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2&STYLE=normal&FORMAT=image/png&TILEMATRIXSET=PM&TILEMATRIX=13&TILEROW=2946&TILECOL=4093

Openlayers 很强大,但对我来说常常很棘手:
我应该改变什么以获得更好的行为?


结语:在@Solomon 和其他人回答了我的问题后,这里是我最终生成并有效的代码:

import { Component, OnInit, Input } from '@angular/core';

import Map from 'ol/Map';
import View from 'ol/View';
import Tile from 'ol/layer/Tile';
import TileLayer from 'ol/layer/Tile';
import WMTS from 'ol/source/WMTS';
import WMTSTileGrid from 'ol/tilegrid/WMTS';

import {fromLonLat, get as getProjection} from 'ol/proj';
import {getWidth, getTopLeft} from 'ol/extent';

@Component({
  selector: 'carte',
  templateUrl: './carte.component.html',
  styleUrls: ['./carte.component.css']
})
export class CarteComponent implements OnInit {
  /** Carte Openlayers. */
  map: Map;

  /* Longitude. */
  @Input() longitude: number;

  /* Latitude. */
  @Input() latitude: number;

  /** Niveau de zoom initial. */
  @Input() zoom: number;

  /** Couche Plan IGN V2 */
  planIGNV2: TileLayer<WMTS> = this.couche('https://wxs.ign.fr/decouverte/geoportail/wmts', 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2',
    'EPSG:3857', 'image/png');

  /* Couche : Orthophotos
     orthoPhotos: TileLayer<WMTS> = this.couche('https://wxs.ign.fr/decouverte/geoportail/wmts', 'ORTHOIMAGERY.ORTHOPHOTOS',
     'EPSG:3857', 'image/jpeg')
  */

  ngOnInit() {
    const map = new Map({
      layers: [ this.planIGNV2 ],
      target: 'carte_principale', // id de l'élément HTML
      view: new View({
        center: fromLonLat([this.longitude, this.latitude]),
        zoom: this.zoom
      })
    });
  }

  /**
   * Crée une couche tuilée WMTS
   * @param url URL
   * @param layer Nom de la couche
   * @param projection Projection à utiliser pour calculer la grille de tuiles (exemple : EPSG:3857)
   * @param format Format d'image à utiliser.
   */
  couche(url: string, layer: string, projection: string, format: string): TileLayer<WMTS> {
    return new Tile({
      source : new WMTS({
        url,
        layer,
        matrixSet: 'PM',
        format,
        style: 'normal',
        tileGrid: this.grilleTuiles(projection)
      })
    });
  }

  /**
   * Renvoie une grille de tuiles
   * @param projection Projection (exemple : EPSG:3857)
   */
  grilleTuiles(projection: string): WMTSTileGrid {
    const proj = getProjection(projection);
    const maxResolution = getWidth(proj.getExtent()) / 256;
    const projectionExtent = proj.getExtent();

    const resolutions = [];
    const matrixIds = [];

    for (let i = 0; i < 20; i++) {
      matrixIds[i] = i.toString();
      resolutions[i] = maxResolution / Math.pow(2, i);
    }

    return new WMTSTileGrid( {
      origin: getTopLeft(projectionExtent), // coin supérieur gauche
      resolutions,
      matrixIds
    });
  }
}

我相信你根本没有做对。 您只需要一块地砖,位于 TILECOL:8180,TILEROW:5905

的地砖

使用此代码:

        const projection = ol.proj.get('EPSG:4326');
        const projectionExtent = projection.getExtent();
        // The matrixes start at 2 tiles wide, 1 tile high (OR WHATEVER YOU HAVE IN THE API DOCUMENTATION)
        const size = ol.extent.getWidth(projectionExtent) / 256 / 2; // or ol.extent.getHeight(projectionExtent) / 256
        const resolutions = new Array(12);
        const matrixIds = new Array(12);

        for (let z = 0; z < 12; ++z) {
            // generate resolutions and matrixIds arrays for this WMTS
            resolutions[z] = size / Math.pow(2, z);
            matrixIds[z] = "EPSG:4326:" + z;
        }

        map.addLayer(new ol.layer.Tile({
            source: new ol.source.WMTS({
                url: <your_url_tile_provider>
                projection: projection,
                layer: <your layer name>,
                matrixSet: 'EPSG:4326',
                format: <your format>,
                style: <your style>,
                tileGrid : new ol.tilegrid.WMTS({
                    origin: ol.extent.getTopLeft(projectionExtent), // topLeftCorner
                    resolutions: resolutions,
                    matrixIds: matrixIds
                }),
            }),
            opacity: 0.7, <whatever you want>
        }));