OpenLayers – Working with GeoTIFF COG Mosaic and Custom Projections

cloud-optimized-geotiffgeotiff-tiffmosaicopenlayers

TL;DR

I have a collection of 50 COGs in NZTM projection. The COGs are aligned in a seamless grid. I'd like to use these COGs in a similar method to mosaicJSON on like a VRT online, wherein, I can make single call to a JSON type docoument and load the COGs at once.


I am using OpenLayers and can easily get an individual COG to work well using the GeoTIFF and TileLayer modules. The COGs are served statically via S3.

I'd like to use something like the tileJSON or mosaicJSON formats to bring these COGs together to act as a single unit.  I might be wrong, but I am thinking that while tileJSON can use custom projection, a GeoTIFF cannot be used in the document.  The mosaicJSON would be the tool for this, yes?; but this seems to not allow for a projection other than 3857.

I know there must be a way to get around this, but I cannot seem to figure out how.  Using OpenLayers, I did run a test in using tileJSON and the GeoTIFF link and it looks like I do get a return from the TIFF, however nothing is returned to the screen. Regardless, my assumption is tileJSON cannot be used in this manner but it was worth a shot.

Here is the experiment using one COG as an example:

import './style.css';
import {Map, View} from 'ol';
import TileLayer from 'ol/layer/WebGLTile';
import GeoTIFF from 'ol/source/GeoTIFF';
import proj4 from 'proj4';
import {register} from 'ol/proj/proj4';
import {get as getProjection} from 'ol/proj';
import {fromLonLat} from 'ol/proj';
import TileJSON from 'ol/source/TileJSON';

// set NZTM projection 
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");
register(proj4)
const nztmProjection = getProjection('EPSG:2193');


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

var nztmTileJSON = {
   "tilejson": "2.0.0",
   "name": "compositing",
   "description": "Trying tileJSON with COG",
   "version": "1.0.0",
   "attribution": "TBD",
   "scheme": "tms",
   "tiles": [
       "https://tile-service-raster.s3.us-east-1.amazonaws.com/cogs/as-raster-tile/HM_COG.tif",
       "https://tile-service-raster.s3.us-east-1.amazonaws.com/cogs/as-raster-tile/HN_COG.tif"
   ],
   "minzoom": 6,
   "maxzoom": 10,
   "projected_bounds": [ 827933.23, 3729820.29, 3195373.59, 7039943.58 ],
   "center": origin,
   "crs": "EPSG:2193",
   "projection": "+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",
   "scales": resolutions,
 };

// cog file loads
const cog = new TileLayer({
   source: new TileJSON({
     tileJSON: nztmTileJSON,
     crossOrigin: 'anonymous',
   }),
})

// draw map
const map = new Map ({
  layers: [cog],
  target: 'map',
  view: new View({
    projection: nztmProjection,
    center: fromLonLat([176.0,-38.68], nztmProjection),
    zoom: 6,
    maxZoom: 6,
    minZoom: 10,
    resolutions: resolutions,
    matrixIds: matrixIds,
    constrainResolution: true,
    smoothResolutionConstraint: true
  })
});

Does anyone know of a good example of how to do a mosaicJSON with a custom projection that works through OpenLayers or how to use the tileJSON format with GeoTIFF in OpenLayers? Preferably, this would be done without Lamda in the mix. Maybe there is even some way to leverage WMTS?

Best Answer

Not the ideal VRT solution, but the workaround to load multiple urls.:

// URL to COG tile
const urls = [
  'https://tile-service-raster.s3.us-east-1.amazonaws.com/cogs/as-raster-tile/HM_COG.tif',
  'https://tile-service-raster.s3.us-east-1.amazonaws.com/cogs/as-raster-tile/HN_COG.tif'
]

function getSourceURLs(urls) {
  var urlArray = [];
  urls.forEach(address => urlArray.push(
    new GeoTIFF({
        sources: [
          {
            url:address,
            tileSize: 256,
          },
        ],
        convertToRGB: true,
        interpolate: false,
      }), 
    ))
    return urlArray
}

const cog = new TileLayer({
  crossOrigin: 'anonymous',
  sources: getSourceURLs(urls)
})
Related Question