title: An TileWMS -> TileLayer -> Map -> View based on a WMS `GetTile` request with openlayers only duplicates the same tile on the map
body: <p>Until 2020, I was displaying an <em>IGN</em> map with <em>Openlayers</em> with this code:</p>
  <pre class="lang-js prettyprint-override"><code>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: &quot;carte_principale&quot;,
          
          view: new View({
              center: olProj.fromLonLat([this.longitude, this.latitude]),
              zoom: this.zoom
          })
      });
      
      this.map.addLayer(ign);
    }
  }
  </code></pre>
  <p>Then IGN geoportail made big changes for months, and when it went back, I had to adapt my code to use it again.</p>
  <p>It documentation looks to use <code>GetTile</code> requests to display the map, instead of <code>GetMap</code> it was suggesting to use before.<br />
  And according to it, I changed my code that way:</p>
  <pre class="lang-js prettyprint-override"><code>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);
  }
  </code></pre>
  <p>It works... but the display isn't the one expected:</p>
  <ul>
  <li>the same tile is duplicated along the map.</li>
  <li>the latitude/longitude isn't resolved</li>
  <li>the zoom doesn't work</li>
  </ul>
  <p>The two last points, because I don't know how to parametrize the request correctly.</p>
  <p><a href="https://i.stack.imgur.com/lY7zj.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/lY7zj.jpg" alt="enter image description here" /></a></p>
  <p>When I attempt that displaying with <em>QGis</em> (where the map is shown fine) using its network tool, it see that kind of requests exchanged, so I'm not so far:</p>
  <pre><code>https://wxs.ign.fr/decouverte/geoportail/wmts?SERVICE=WMTS&amp;REQUEST=GetTile&amp;VERSION=1.0.0&amp;LAYER=GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2&amp;STYLE=normal&amp;FORMAT=image/png&amp;TILEMATRIXSET=PM&amp;TILEMATRIX=13&amp;TILEROW=2946&amp;TILECOL=4093
  </code></pre>
  <p><em>Openlayers</em> is powerful, but often tricky for me:<br />
  what should I change to have a better behavior?</p>
  <hr />
  <p>Epilog: after @Solomon and others replied to my question, here the code I've eventually produced and that worked:</p>
  <pre class="lang-js prettyprint-override"><code>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&lt;WMTS&gt; = this.couche('https://wxs.ign.fr/decouverte/geoportail/wmts', 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2',
      'EPSG:3857', 'image/png');
  
    /* Couche : Orthophotos
       orthoPhotos: TileLayer&lt;WMTS&gt; = 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&lt;WMTS&gt; {
      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 &lt; 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
      });
    }
  }
  </code></pre>