Based on the answer given to Use OpenLayers to display local raster tiles, I have created a prototype implementation using OpenLayers (v7) with an IIIF tile source to display a set of custom tiles.
However, the scaling of the tiles is slightly wrong, as is the extent of the map.
My proof-of-concept code displays a set of tiles which are generated from a 2000x2000px image I captured of an OSM map of London with a GPX route overlay.
There are 3 zoom levels of tiles which are hosted in an S3 bucket.
I've also loaded in the actual OSM map tiles in a different layer using ol.source.OSM
.
The custom tiles containing the GPX overlay are too small and do not line up with the OSM layer.
Here's the JS code:
//sample map config
const config = {
name: 'z_london_test',
map:{
width: 2000, // Map image width in pixels
height: 2000, // Map image height in pixels
lat_top: 51.449620948774374, // Latitude of North bound of map
lon_left: -0.11981964111328126, // Longitude of West bound of map
lat_bottom: 51.42286816395111, // Latitude of South bound of map
lon_right: -0.07690429687500001, // Longitude of East bound of map
lat_centre: 51.43624455636274, //map centre
lon_centre: -0.09836196899414064
}
}
const _map = 'map',
projection = 'EPSG:3857',
minZoom = 14,
maxZoom = 16,
initialZoom = 16,
tileWidth = 256,
tileHeight = 256,
tileSize = [tileWidth,tileHeight],
content_root = 'https://workingedge-public-test-bucket.s3.eu-west-1.amazonaws.com/'+config.name+'/',
tiles_root = content_root + 'tiles/',
urlTemplate = tiles_root + '{z}/{y}/{x}.png';
let map, view, tileLayer, geolocation;
initMap = () => {
const ne_bound = ol.proj.fromLonLat([config.map.lon_right, config.map.lat_top]),
se_bound = ol.proj.fromLonLat([config.map.lon_left, config.map.lat_bottom]),
extent = se_bound.concat(ne_bound);
view = new ol.View({
projection: projection,
center: ol.proj.fromLonLat([config.map.lon_centre, config.map.lat_centre]),
minZoom: minZoom,
maxZoom: maxZoom,
zoom: initialZoom,
extent: extent
});
const source = new ol.source.IIIF({
size: [config.map.width, config.map.height],
extent: extent,
projection: projection,
tileSize: tileSize,
resolutions: [1, 2, 4],
});
source.setTileUrlFunction(function(tileCoord) {
let z = tileCoord[0],
x = tileCoord[1],
y = tileCoord[2];
const result = urlTemplate
.replace('{z}', z.toString())
.replace('{x}', x.toString())
.replace('{y}', y.toString());
return result;
});
tileLayer = new ol.layer.Tile({
title: 'Tile Layer',
type: 'overlay',
visible: true,
extent: extent,
source: source
});
const overLayLayers = [
tileLayer,
];
const overlayGroup = new ol.layer.Group({
title: 'Tile Map',
combine: true,
layers: overLayLayers
})
// Base
const osmLayer = new ol.layer.Tile({
title: 'OpenStreetMap',
type: 'base',
source: new ol.source.OSM()
})
const baseLayers = [
osmLayer,
];
const baseGroup = new ol.layer.Group({
title: 'OSM',
combine: true,
visible: true,
layers: baseLayers
})
map = new ol.Map({
target: _map,
layers: [baseGroup, overlayGroup],
view: view
});
const layerSwitcher = new ol.control.LayerSwitcher({
reverse: true,
groupSelectStyle: 'group'
});
map.addControl(layerSwitcher);
}
init = () => {
initMap();
}
$(document).ready(init);
How can I fix this so the custom tiles align with the OSM layer and the map bounds are constrained to the extent of the custom tiles?
For ease of demonstration, I've created a CodePen containing the above code to illustrate this: https://codepen.io/dpa99c/pen/jOxogqX