Using LDS XYZ services in OpenLayers

A step-by-step guide to setting up an OpenLayers map using LDS XYZ tiles.

This guide also provides hints and tips when using LDS XYZ services in OpenLayers, and includes examples using XYZ and WMTS style requests.

This document is not a guide to using OpenLayers and assumes a good understanding of JavaScript. Refer to the official OpenLayers documentation for help.

This guide focuses on use of the LDS XYZ tile service. An example of using the WMTS functionality is also included. This method means one more request for the web application (GetCapabilities request to determine the tilematrix definition), but reduces the client-side configuration. 

See Using LINZ basemaps in Javascript for information on available map services, URL syntax, domains and response formats.

Notes before you begin

  • The code snippets in this guide and referenced examples use OpenLayers version 4.6.5.
  • The majority of examples in this guide assume use of the LDS Web Mercator (EPSG:3857) tile set. Specific guidance is also provided for utilising the NZTM tile set.
  • OpenLayers 4.6.5 does not yet support the Get FeatureInfo function for vector-based tile services.
  • OpenLayers 4.6.5 supports OGC WMTS GetCapabilities requests. An example is included below.

Read the guide

Adding an LDS tiled layer to your map

To add a single LDS tiled layer the simple format is: 

new ol.layer.Tile({       source: new ol.source.XYZ({ url: 'http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=YOUR_API_KEY/tiles/v4/layer=LAYER_ID/EPSG:3857/{z}/{x}/{y}.png')

Note the use of the syntax {a-d}. This will allow requests to use our a, b, c, d subdomains to support more simultaneous tile requests in your browser.

Adding multiple tiled layers

You can return multiple layers at once by compositing layers in a single request. The request is composited server-side then returned as a single image, reducing bandwidth and processing overhead. This method is not suitable if you require the ability to toggle individual layers on and off.

http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=YOUR_API_KEY/tiles/v4/layer=LAYER_ID;layer=LAYER_ID/EPSG:3857/{z}/{x}/{y}.png

Vector Query API

The LDS Vector Query API lets you overlay vector data into your web map client to highlight points of interest, specific attributes or selected features.

Read the full Vector Query API documentation

How to attribute LINZ

All LDS tile service supported layers are available under a Creative Commons Attribution 4.0 International license, meaning you are free to use, reuse and share data as long as you attribute the work to Toitū Te Whenua as the original source of the data. 

Find out about Toitū Te Whenua data licensing and attribution

Use the Tile layer option attribute in the LINZ Data Service as the original source of the data:

attributions: [ new ol.Attribution({ html: ['<a href="http://data.linz.govt.nz">LINZ. CC BY 4.0</a>'] }) ]

Toitū Te Whenua does not own the copyright of the National Imagery data and requires a variation to the standard Toitū Te Whenua data attribution statement. You can make the attribution in the following way, which changes the URL to point to the page that applies to aerial imagery.

Find out about the variation to the standard Toitū Te Whenua data attribution statement

attributions: [ new ol.Attribution({ html: ['<a href="//www.linz.govt.nz/data/licensing-and-using-data/attributing-aerial-imagery-data">Sourced from LINZ. CC BY 4.0</a>'] }) ]

Using a Custom Map Projection - NZTM tile set (XYZ request) 

OpenLayers does not support most local projections out of the box, so if you are integrating LDS NZTM tile layers into your service, you will need to add additional code. This includes calling additional scripts to support custom projections, specifying the projection the tiles are in, the origin and resolutions when creating your map object.

Enabling custom projection support

Custom projections within OpenLayers must be defined manually. This can be done by using the Proj4js library and including the Proj4js definition for the NZTM2000 (ESPG:2193) the header:

<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script> <script src="http://epsg.io/2193.js"></script>

Specify the custom coordinate reference system

You then need to manually define the parameters of the NZTM2000 tile grid, specifying the extents, tile matrix origin and resolution. Much of this information can be found in the LDS tile set definitions document. The required functions are:

var projection = ol.proj.get('EPSG:2193'); projection.setExtent([827933.23, 3729820.29, 3195373.59, 7039943.58]); var origin = [-1000000, 10000000]; var resolutions = [8960, 4480, 2240, 1120, 560, 280, 140, 70, 28, 14, 7, 2.8, 1.4, 0.7, 0.28, 0.14, 0.07]; var matrixIds = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

The first step is to call the Proj4 definition for NZTM2000 (EPSG:2193) and describe the extents of the coordinate system:

var projection = ol.proj.get('EPSG:2193'); projection.setExtent([827933.23, 3729820.29, 3195373.59, 7039943.58]);

Then define the origin of the LDS NZTM tile grid:

var origin = [-1000000, 10000000];

Then define the pre-determined resolutions and matrix ids (zoom levels) for the LDS NZTM tile set:

var resolutions = [8960, 4480, 2240, 1120, 560, 280, 140, 70, 28, 14, 7, 2.8, 1.4, 0.7, 0.28, 0.14, 0.07]; var matrixIds = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

Use a WMTS style GetTile URL

Now that you have manually defined the NZTM coordinate system, you need to use WMTS GetTile URLs. This will let OpenLayers determine the x, y and z parameters itself by drawing from the projection definition you have provided.

The WMTS URL syntax is:

http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=YOUR_API_KEY/tiles/v4/set=4702/EPSG:2193/{TileMatrix}/{TileCol}/{TileRow}.png

This URL is calling the NZ Aerial Imagery service, identified by the ‘set=4702’ element.

You will also notice that the default EPSG value has been changed to the value for NZTM2000 (ESPG:2193).

Specify Layer options

OpenLayers will only understand custom projections if you specify a number of Tile layer options. This includes identifying the layer type, source, encoding, projection and tileGrid.

var layer = new ol.layer.Tile  source: new ol.source.WMTS     url: urlTemplate,     requestEncoding: 'REST',     projection: projection,     tileGrid: new ol.tilegrid.WMTS       origin: origin,       resolutions: resolutions,       matrixIds: matrixIds     })   }) });

See the full example below to view the complete variables for layer within context.

Also, remember also to transform coordinates to ESPG:2193 when setting the map centre or bounds, and to use WGS84 lat/long coordinates:

center: ol.proj.transform([172.762057, -40.852931], 'EPSG:4326', 'EPSG:2193'),zoom: 2

Working examples of LDS XYZ tile services

LDS tile services OpenLayers example in Web Mercator (XYZ)

With the substitution of the API key placeholder with your own, the Web Mercator example will run in the browser.  

<html>
  <head>
    <title>NZ Aerial Imagery basemap EPSG:3857 Demo (OL4)</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script>
  </head>
  <body> 
    <div id="map"></div>
  </body>
<style>
html, body {
  height:100%;
  padding:0;
  margin:0;
}
#map {
  height: 100%;
}
</style>
<script>
//Enter your API key in the placeholder
var APIKey = 'YOUR_API_KEY';

var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.XYZ({
        url: 'http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=' + APIKey + '/tiles/v4/set=4702/EPSG:3857/{z}/{x}/{y}.png',
        attributions: [
          new ol.Attribution({ html: ['<a href="http://data.linz.govt.nz">LINZ. CC BY 4.0</a>'] })
        ]
      })
    })
  ],
  view: new ol.View({
    center: ol.proj.transform([172.762057, -40.852931], 'EPSG:4326', 'EPSG:3857'),
    zoom: 6
  })
});
</script>
</html>

LDS tile services OpenLayers example in NZTM2000 (XYZ)

The NZTM200 example will require you to call the Proj4 related resources to enable custom projections.

<html>
  <head>
    <meta name="description" content="NZ Topo50 NZTM demo (OL4)" />
    <meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
    <script src="http://epsg.io/2193.js"></script>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script>
  </head>
  <body>
    <div id="map"></div>   
  </body>
<style>
html, body {
  height:100%;
  padding:0;
  margin:0;
}
#map {
  height: 100%;
}
</style>
<script>
// set NZTM projection extent so OL can determine zoom level 0 extents.
var projection = ol.proj.get('EPSG:2193');
projection.setExtent([827933.23, 3729820.29, 3195373.59, 7039943.58]); 

// NZTM tile matrix origin, resolution and matrixId definitions. See the LDS tile set definition document for more information
var origin = [-1000000, 10000000];
var resolutions = [8960, 4480, 2240, 1120, 560, 280, 140, 70, 28, 14, 7, 2.8, 1.4, 0.7, 0.28, 0.14, 0.07];
var matrixIds = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

// LDS API key. Please use your own API key
var APIKey = 'YOUR_API_KEY';
// LDS tile service URL. Please use your own API key
var urlTemplate = 'http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=' + APIKey + '/tiles/v4/layer=50767/EPSG:2193/{TileMatrix}/{TileCol}/{TileRow}.png';

var layer = new ol.layer.Tile({
  source: new ol.source.WMTS({ 
    url: urlTemplate,
    requestEncoding: 'REST',
    attributions: [
      new ol.Attribution({ html: ['<a href="http://data.linz.govt.nz">LINZ. CC BY 4.0</a>'] })
    ],
    projection: projection,
    tileGrid: new ol.tilegrid.WMTS({
      origin: origin,
      resolutions: resolutions,
      matrixIds: matrixIds
    })
  })
});

var map = new ol.Map({
  target: 'map',
  layers: [layer],
  view: new ol.View({
    projection: projection,
    center: ol.proj.transform([172.762057, -40.852931], 'EPSG:4326', 'EPSG:2193'),
    zoom: 2
  })
});
</script>
</html>

Using a Custom Map Projection - NZTM tile set (WMTS GetCapabilities) 

You can access our tile services through an OGC WMTS GetCapabilities request, rather than a XYZ style request. In this example for using the NZTM2000 tile set, your web client will use the WMTS Get Capabilities functionality to determine the tile matrix definition. This method means one more request for the web application, but reduces client-side configuration. 

Enabling custom projection support with WMTS

As with the XYZ example, you will need to add additional code to your application to be able to use a WMTS-styled custom projection within OpenLayers.

To begin, you need to add the Proj4js library to the header:

<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js" type="text/javascript"></script>

Specify the custom coordinate reference system

You then need to specify the definition and the extents of the NZTM2000 tile grid so OpenLayers can determine the zoom level 0 extents. This information can be found in the LDS tile set definitions document. The required details are:

proj4.defs("EPSG:2193","+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +axis=neu"); var projection = ol.proj.get('EPSG:2193'); projection.setExtent([827933.23, 3729820.29, 3195373.59, 7039943.58]);

Use an OGC WMTS GetCapabilities URL

Add the OGC WMTS GetCapabilities URL to your code:

var capabilitiesURL = 'https://data.linz.govt.nz/services;key=YOUR_API_KEY/wmts/1.0.0/WMTSCapabilities.xml';

Sign into LDS to get the complete WMTS URL for the basemap or layer of your choice.

Describe the GetCapabilities function

Add code to enable OpenLayers to understand how to use the GetCapabilities function:

var parser = new ol.format.WMTSCapabilities(); $.ajax(capabilitiesURL).then(function(response) {   var capabilities = parser.read(response);   var options = ol.source.WMTS.optionsFromCapabilities( capabilities, { layer: 'layer-50767', matrixSet: 'EPSG:2193'});   var layer = new ol.layer.Tile({     source: new ol.source.WMTS(options)   });   map.addLayer(layer); });    

Working example of LDS tile services - WTMS GetCapabilities request

See below for an example of LDS tile services in OpenLayers using a WMTS GetCapabilities style request. This calls the NZ Topo50 map tiles as a demonstration.

LDS tile services OpenLayers example in NZTM2000 using WMTS GetCapabilities

This example will require you to call the related Proj4 resources. The WMTS GetCapabilities method means one more request for the web application, but reduces client-side configuration.

<html>
  <head>
    <meta name="description" content="NZ Topo50 NZTM demo (OL4.6.5 WMTS GetCapabilities)" />
    <meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
    <script src="http://epsg.io/2193.js"></script>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div id="map"></div> 
  </body>
<style>
html, body {
  height:100%;
  padding:0;
  margin:0;
}
#map {
  height: 100%;
}
</style>
<script>
// set NZTM projection extent so OL can determine zoom level 0 extents.
proj4.defs("EPSG:2193","+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +axis=neu");
var projection = ol.proj.get('EPSG:2193');
projection.setExtent([827933.23, 3729820.29, 3195373.59, 7039943.58]); 

// LDS API key. Please use your own API key
var APIKey = 'YOUR_API_KEY';

var map = new ol.Map({
  target: 'map',
  layers: [],
  view: new ol.View({
    projection: projection,
    center: ol.proj.transform([172.762057, -40.852931], 'EPSG:4326', 'EPSG:2193'),
    zoom: 2
  })
});

var capabilitiesURL = 'https://data.linz.govt.nz/services;key=' + APIKey + '/wmts/1.0.0/layer/50767/WMTSCapabilities.xml';
var parser = new ol.format.WMTSCapabilities();
$.ajax(capabilitiesURL).then(function(response) {
  var capabilities = parser.read(response);
  var options = ol.source.WMTS.optionsFromCapabilities( capabilities, { layer: 'layer-50767', matrixSet: 'EPSG:2193'});
  var layer = new ol.layer.Tile({
    source: new ol.source.WMTS(options)
  });
  map.addLayer(layer);
});
</script>
</html>

Working example of using GetFeatureInfo functionality to identify features

If you want to let your customers view information about individual parcels from the NZ Parcel Boundaries map service, you can enable support for GetFeatureInfo functionality. This lets you click on a parcel on the map and view attribute information about that parcel. 

OpenLayers 4.6.5 does not support GetFeatureInfo functionality out of the box, but you can build support for it.

This example requires a modified version of OpenLayers 4.6.5 that includes the functionality provided by a pull request.

Various elements are included that control the style and behaviour of the popup we have used to display the feature attribute information.

Remember to replace the YOUR_API_KEY placeholders with your own API key.<html>   <head>     <meta name="description" content="NZ Parcel Boundaries Wireframe GetFeatureInfo demo (OL4)" />     <meta charset="utf-8" />     <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">     <script src="http://d1z0glnzqlaae1.cloudfront.net/ol3/ol-debug.js" type="text/javascript"></script>     <script src="https://code.jquery.com/jquery-3.3.1.min.js" type="text/javascript"></script>   </head>   <body>     <div id="map">       <div id="popup" class="ol-popup">             <a href="//www.linz.govt.nz/" id="popup-closer" class="ol-popup-closer"></a>             <div id="popup-content"></div>         </div>     </div>    </body> <style>  html, body {   height:100%;   padding:0;   margin:0; } #map {   height: 100%; } .ol-popup {   position: absolute;   background-color: white;   -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));   filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));   padding: 15px;   border-radius: 10px;   border: 1px solid #cccccc;   bottom: 12px;   left: -50px; } .ol-popup:after, .ol-popup:before {   top: 100%;   border: solid transparent;   content: " ";   height: 0;   width: 0;   position: absolute;   pointer-events: none; } .ol-popup:after {   border-top-color: white;   border-width: 10px;   left: 48px;   margin-left: -10px; } .ol-popup:before {   border-top-color: #cccccc;   border-width: 11px;   left: 48px;   margin-left: -11px; } .ol-popup-closer {   text-decoration: none;   position: absolute;   top: 2px;   right: 8px; } .ol-popup-closer:after {   content: "✖"; } </style> <script> // LDS API key. Please use your own API key var APIKey = 'YOUR_API_KEY'; // LDS tile service URL. Please use your own API key var urlTemplate = 'http://tiles-{a-d}.data-cdn.linz.govt.nz/services;key=' + APIKey + '/tiles/v4/set=4769/EPSG:3857/{TileMatrix}/{TileCol}/{TileRow}.png'; var wmtsSource = new ol.source.WMTS({      url: urlTemplate,     requestEncoding: 'REST',     attributions: [       new ol.Attribution({ html: ['<a href="http://data.linz.govt.nz">LINZ. CC BY 4.0</a>'] })     ],     projection: 'EPSG:3857',     getFeatureInfoOptions: {       url: 'http://data.linz.govt.nz/services;key=' + APIKey + '/wmts/1.0.0/set/4769/featureinfo/EPSG:3857/{TileMatrix}/{TileCol}/{TileRow}/{I}/{J}.html',       requestEncoding: 'REST',       infoFormat: 'text/html'     },     tileGrid: new ol.tilegrid.WMTS({       origin: [-20037508.3428, 20037508.3428],         resolutions: [         559082264.029 * 0.28E-3,         279541132.015 * 0.28E-3,         139770566.007 * 0.28E-3,         69885283.0036 * 0.28E-3,         34942641.5018 * 0.28E-3,         17471320.7509 * 0.28E-3,         8735660.37545 * 0.28E-3,         4367830.18773 * 0.28E-3,         2183915.09386 * 0.28E-3,         1091957.54693 * 0.28E-3,         545978.773466 * 0.28E-3,         272989.386733 * 0.28E-3,         136494.693366 * 0.28E-3,         68247.3466832 * 0.28E-3,         34123.6733416 * 0.28E-3,         17061.8366708 * 0.28E-3,         8530.91833540 * 0.28E-3,         4265.45916770 * 0.28E-3,         2132.72958385 * 0.28E-3,         1066.36479193 * 0.28E-3       ],       matrixIds: [         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19       ]     })   }); var map = new ol.Map({   target: 'map',   layers: [     new ol.layer.Tile({       source: wmtsSource     })   ],   view: new ol.View({     center: ol.proj.transform([172.762057, -40.852931], 'EPSG:4326', 'EPSG:3857'),     zoom: 6   }) }); var container = document.getElementById('popup'); var content = document.getElementById('popup-content'); var closer = document.getElementById('popup-closer'); //Add a click handler to hide the popup. closer.onclick = function() {   container.style.display = 'none';   closer.blur();   return false; }; // Create an overlay to anchor the popup to the map. var overlay = new ol.Overlay({   element: container }); map.addOverlay(overlay); // Show FeatureInfo on click map.on('click', function(evt) {   var viewResolution = /** @type {number} */ (map.getView().getResolution());   var url = wmtsSource.getGetFeatureInfoUrl(     evt.coordinate, viewResolution, map.getView().getProjection());   if (url) {     overlay.setPosition(evt.coordinate);     content.innerHTML =       '<iframe seamless src="https://www.linz.govt.nz/%27%20%2B%20url%20%2B%20%27"></iframe>';     container.style.display = 'block';   } }); </script> </html>