adaguc-webmapjs
Version:
Interactive maps library, capable of parsing a OGC WMS getcapabilities and display geographical layers
198 lines (188 loc) • 9.2 kB
JavaScript
export default class WMJSTileRenderer {
render (currentBBOX, newBBOX, srs, width, height, ctx, bgMapImageStore, tileOptions, layerName) {
let renderedURLs = {};
const imagesToRender = [];
bgMapImageStore.stopLoading();
if (!layerName) {
console.error('layerName not defined');
return;
}
/* Temporal mappings from bgmaps.cgi service names to names defined here */
if (layerName === 'streetmap') layerName = 'OSM';
if (layerName === 'pdok') layerName = 'OSM';
if (layerName === 'naturalearth2') layerName = 'NaturalEarth2';
let tileLayer = tileOptions[layerName];
if (!tileLayer) {
console.error('Tiled layer with name ' + layerName + ' not found');
return;
}
let tileSettings = tileLayer[srs];
/* If current map projection is missing in the tilesets, try to find an alternative */
if (!tileSettings) {
for (var tileOption in tileOptions) {
if (tileOptions.hasOwnProperty(tileOption)) {
for (var epsgCode in tileOptions[tileOption]) {
if (tileOptions[tileOption].hasOwnProperty(epsgCode)) {
if (epsgCode === srs) {
// console.log('Projection not supported by tileserver: Falling back to ', tileOption, epsgCode);
tileSettings = tileOptions[tileOption][epsgCode];
}
}
}
}
}
}
let pi = Math.PI;
/* Default settings for OSM Mercator */
let tileSize = 256;
let initialResolution = 2 * pi * 6378137 / tileSize;
let originShiftX = -2 * pi * 6378137 / 2.0;
let originShiftY = 2 * pi * 6378137 / 2.0;
if (tileSettings.tileSize) tileSize = tileSettings.tileSize;
if (tileSettings.resolution) initialResolution = tileSettings.resolution;
if (tileSettings.origX) originShiftX = tileSettings.origX;
if (tileSettings.origY) originShiftY = tileSettings.origY;
let screenWidth = width;
let bboxw = currentBBOX.right - currentBBOX.left;
let originShiftX2 = initialResolution * tileSize + originShiftX;
let originShiftY2 = originShiftY - initialResolution * tileSize;
let tileSetWidth = originShiftX2 - originShiftX;
let tileSetHeight = originShiftY - originShiftY2;
let levelF = Math.log((Math.abs(originShiftX2 - originShiftX)) / ((bboxw / screenWidth) * tileSize)) / Math.log(2);
let level = parseInt(levelF + 0.5);
let getAttribution = (textileLayer) => {
if (!textileLayer || !srs || !tileLayer[srs] || !tileLayer[srs].copyRight) return null;
return tileLayer[srs].copyRight;
};
let drawBGTiles = (level) => {
let home = tileSettings.home;
let tileServerType = tileSettings.tileServerType; // 'osm' or 'argisonline'
let tileServerFormat = tileSettings.tileServerFormat || 'png';
let tmsEnabled = tileSettings.tms || false; // 'osm' or 'argisonline'
if (level < tileSettings.minLevel) level = tileSettings.minLevel;
if (level > tileSettings.maxLevel) level = tileSettings.maxLevel;
let numTilesAtLevel = Math.pow(2, level);
let numTilesAtLevelX = tileSetWidth / ((initialResolution / numTilesAtLevel) * tileSize);// / Math.abs(originShiftY / originShiftX);
let numTilesAtLevelY = tileSetHeight / ((initialResolution / numTilesAtLevel) * tileSize);
let tilenleft = parseInt(Math.round((((((currentBBOX.left - originShiftX) / (tileSetWidth)) * (numTilesAtLevelX))) / 1) + 0.5));
let tilenright = parseInt(Math.round((((((currentBBOX.right - originShiftX) / (tileSetWidth)) * (numTilesAtLevelX))) / 1) + 0.5));
let tilentop = parseInt(Math.round((numTilesAtLevelY - ((((currentBBOX.bottom - originShiftY2) / tileSetHeight) * numTilesAtLevelY))) + 0.5));
let tilenbottom = parseInt(Math.round((numTilesAtLevelY - ((((currentBBOX.top - originShiftY2) / tileSetHeight) * numTilesAtLevelY))) + 0.5));
let tileXYZToMercator = function (level, x, y) {
let tileRes = initialResolution / Math.pow(2, level);
let p = { x: x * tileRes + (originShiftX), y: originShiftY - y * tileRes };
return p;
};
let getTileBounds = function (level, x, y) {
let p1 = tileXYZToMercator(level, (x) * tileSize, (y) * tileSize);
let p2 = tileXYZToMercator(level, (x + 1) * tileSize, (y + 1) * tileSize);
return { left:p1.x, bottom:p1.y, right:p2.x, top: p2.y };
};
let getPixelCoordFromGeoCoord = function (coordinates, b, w, h) {
var x = (w * (coordinates.x - b.left)) / (b.right - b.left);
var y = (h * (coordinates.y - b.top)) / (b.bottom - b.top);
return { x:parseFloat(x), y:parseFloat(y) };
};
let drawTile = function (ctx, level, x, y, loadImage = true) {
let bounds = getTileBounds(level, x, y);
let bl = getPixelCoordFromGeoCoord({ x: bounds.left, y: bounds.bottom }, newBBOX, width, height);
let tr = getPixelCoordFromGeoCoord({ x: bounds.right, y: bounds.top }, newBBOX, width, height);
let imageURL;
if (tileServerType === 'osm') {
if (tmsEnabled) {
imageURL = home + level + '/' + x + '/' + ((numTilesAtLevelY - 1) - y) + '.' + tileServerFormat;
} else {
imageURL = home + level + '/' + x + '/' + (y) + '.' + tileServerFormat;
}
} else if (tileServerType === 'arcgisonline' || tileServerType === 'wmst') {
imageURL = home + level + '/' + y + '/' + (x);
} else if (tileServerType === 'skyvector') {
imageURL = home + 2 * (11 - Math.round(level)) + '/' + x + '/' + (y) + '.' + tileServerFormat;
}
if (renderedURLs[imageURL]) {
return;
}
renderedURLs[imageURL] = true;
let image = bgMapImageStore.getImage(imageURL);
// let image = bgMapImageStore.getImageForSrc(imageURL);
if (loadImage === false) {
/* Here we display lower resolution images, if not available switch to an even higher resolution */
if (image.isLoaded() && !image.hasError()) {
try {
ctx.drawImage(image.getElement()[0], parseInt(bl.x), parseInt(bl.y), parseInt(tr.x - bl.x) + 1, parseInt(tr.y - bl.y) + 1);
} catch (e) {
}
} else {
/* If desired image is not yet loaded, try to load a higher resolution variant instead */
if (level > 1) {
drawTile(ctx, level - 1, parseInt(x / 2), parseInt(y / 2), false);
}
}
} else {
/* Not all images need to load, as we can switch to lower resolutions for the time being */
if (loadImage) {
if (image.isLoaded() === false && image.hasError() === false && image.isLoading() === false) {
image.load();
}
}
/* Here we display the images we like to have at the desired resolution */
if (image.isLoaded() && !image.hasError()) {
imagesToRender.push({
image: image,
i: image.getElement()[0],
x: parseInt(bl.x),
y: parseInt(bl.y),
w: parseInt(tr.x - bl.x) + 1,
h: parseInt(tr.y - bl.y) + 1,
level: level
});
} else {
/* If desired image is not yet loaded, try to load a higher resolution variant instead */
if (level > 1) {
drawTile(ctx, level - 1, parseInt(x / 2), parseInt(y / 2), false);
}
}
}
};
if (srs === 'EPSG:4326' || srs === 'EPSG:4258') {
numTilesAtLevelX *= 2;
}
if (tilenbottom < 1)tilenbottom = 1; if (tilenbottom > numTilesAtLevelY)tilenbottom = numTilesAtLevelY;
if (tilenleft < 1)tilenleft = 1; if (tilenleft > numTilesAtLevelX)tilenleft = numTilesAtLevelX;
if (tilentop < 1)tilentop = 1; if (tilentop > numTilesAtLevelY)tilentop = numTilesAtLevelY;
if (tilenright < 1)tilenright = 1; if (tilenright > numTilesAtLevelX)tilenright = numTilesAtLevelX;
if (tilentop - tilenbottom > 20) { console.error('Too many tiles in vertical', tilentop - tilenbottom); return; }
if (tilenright - tilenleft > 20) { console.error('Too many tiles in horizontal', tilentop - tilenbottom); return; }
for (let ty = tilenbottom - 1; ty < tilentop; ty++) {
for (let tx = tilenleft - 1; tx < tilenright; tx++) {
drawTile(ctx, level, tx, ty);
}
}
};
drawBGTiles(level);
imagesToRender.sort((imageA, imageB) => {
if (imageA.level < imageB.level) return -1;
if (imageA.level > imageB.level) return 1;
return 0;
});
for (let i = 0; i < imagesToRender.length; i++) {
const image = imagesToRender[i];
if (image.image.isLoaded() && !image.image.hasError()) {
try {
ctx.drawImage(image.i,
image.x,
image.y,
image.w,
image.h
);
} catch (e) {
}
}
// ctx.fillText(i, imagesToRender[i].x + 20, imagesToRender[i].y + 20);
};
const attributionText = getAttribution(tileLayer);
return {
attributionText: attributionText
};
}
};