cesium-geoserverterrainprovider
Version:
plugin to create a Cesium terrain provider from Geoserver
212 lines (190 loc) • 10.2 kB
text/typescript
import { basicAssignResult, CRS, FormatImage, ICRS, IDescription, IFormatImage, IResult } from "./utils";
const C: typeof import('cesium') = (window as any).Cesium
const { fetchXML: loadXML } = C.Resource;
export async function generate(description: IDescription) {
let resultat: IResult;
if (description.url) {
let urlofServer = description.url;
const index = urlofServer.lastIndexOf("?");
if (index > -1) { urlofServer = urlofServer.substring(0, index) }
let urlGetCapabilities = urlofServer + '/gwc/service/wmts?REQUEST=GetCapabilities';
if (description.proxy) {
urlGetCapabilities = description.proxy.getURL(urlGetCapabilities);
}
description.xml = await loadXML({ url: urlGetCapabilities });
resultat = getMetaDatafromXML(description);
} else if (description.xml) {
resultat = getMetaDatafromXML(description);
} else {
throw new Error('either description.url or description.xml are required.');
}
return resultat;
};
function getMetaDatafromXML(description: IDescription): IResult {
const xml = description.xml;
if (!(xml instanceof XMLDocument)) { throw new Error('xml must be a XMLDocument'); }
const resultat: Partial<IResult> = {};
const layerName = description.layerName;
basicAssignResult(description, resultat);
const maxLevel = description.maxLevel;
const proxy = description.proxy;
let styleName = description.styleName;
let template: string = null;
let listTileMatrixSetLinkNode: Element[] = [];
let urlKVP: string = null, urlRESTful: string = null;
let formatImage: IFormatImage = null;
//KVP support for now
Array.from(xml.querySelectorAll('Operation[name="GetTile"] HTTP Get'))
.map((elt) => ({ node: elt, type: elt.querySelector("Value").textContent }))
.forEach(item => {
if (item.type === "RESTful" && urlRESTful === null) {
urlRESTful = item.node.getAttribute("xlink:href");
if (proxy) { urlRESTful = proxy.getURL(urlRESTful); }
}
if (item.type === "KVP" && urlKVP === null) {
urlKVP = item.node.getAttribute("xlink:href");
if (proxy) { urlKVP = proxy.getURL(urlKVP); }
}
});
const nodeIdentifiers = xml.querySelectorAll("Contents>Layer>Identifier");
let layerNode: ParentNode = null;
for (let i = 0; i < nodeIdentifiers.length && layerNode === null; i++) {
if (layerName === nodeIdentifiers[i].textContent) {
layerNode = nodeIdentifiers[i].parentNode;
}
}
if (layerNode !== null) {
//optionality of style in geoserver is not compliant with OGC requirements!!
let defaultStyle: string;
let selectedStyle: string;
Array.from(layerNode.querySelectorAll("Style")).forEach(item => {
const style = item.querySelector("Identifier").textContent;
if (item.getAttribute("isDefault") != null) { defaultStyle = style; }
if (style === styleName) { selectedStyle = style; }
});
//Work with attribute isDefault when no style was defined!!
if (!styleName || styleName !== selectedStyle) { styleName = defaultStyle || ''; }
//format
const nodeFormats = Array.from(layerNode.querySelectorAll("Format"));
for (let l = 0; l < FormatImage.length && formatImage === null; l++) {
const validFormats = nodeFormats.filter(elt => elt.textContent === FormatImage[l].format);
if (validFormats.length > 0) { formatImage = FormatImage[l]; }
}
//TileMatrixSetLink =>TileMatrixSet
listTileMatrixSetLinkNode = Array.from(layerNode.querySelectorAll("TileMatrixSetLink"));
}
const nodeMatrixSetIds = Array.from(xml.querySelectorAll("TileMatrixSet>Identifier"));
for (let a = 0; a < listTileMatrixSetLinkNode.length && !resultat.ready; a++) {
const matrixSetLinkNode = listTileMatrixSetLinkNode[a];
const tileMatrixSetLinkName = matrixSetLinkNode.querySelector("TileMatrixSet").textContent;
let tileMatrixSetNode: ParentNode = null;
let CRSSelected: ICRS = null;
for (let i = 0; i < nodeMatrixSetIds.length && tileMatrixSetNode === null; i++) {
if (nodeMatrixSetIds[i].textContent === tileMatrixSetLinkName) { tileMatrixSetNode = nodeMatrixSetIds[i].parentNode; }
}
const supportedCRS = tileMatrixSetNode.querySelector("SupportedCRS").textContent;
for (let n = 0; n < CRS.length && CRSSelected === null; n++) {
if (CRS[n].supportedCRS === supportedCRS) { CRSSelected = CRS[n]; }
}
if (CRSSelected !== null) {
const tileSets: {
id: string,
maxWidth: number,
maxHeight: number,
scaleDenominator: number,
complete: boolean,
tileWidth: number,
tileHeight: number,
bbox?: {
minTileRow: number,
maxTileRow: number,
minTileCol: number,
maxTileCol: number
}
}[] = Array.from(tileMatrixSetNode.querySelectorAll("TileMatrix"))
.map(function (noeud) {
let id = noeud.querySelector("Identifier").textContent;
let maxWidth = parseInt(noeud.querySelector("MatrixWidth").textContent, 10);
let maxHeight = parseInt(noeud.querySelector("MatrixHeight").textContent, 10);
let tileWidth = parseInt(noeud.querySelector("TileWidth").textContent, 10);
let tileHeight = parseInt(noeud.querySelector("TileHeight").textContent, 10);
let scaleDenominator = parseFloat(noeud.querySelector("ScaleDenominator").textContent);
return {
id,
maxWidth,
maxHeight,
scaleDenominator,
complete: false,
tileWidth,
tileHeight
};
})
.sort((a, b) => b.scaleDenominator - a.scaleDenominator);
const listTileMatrixLimits = Array.from(matrixSetLinkNode.querySelectorAll("TileMatrixSetLimits>TileMatrixLimits"))
.map(nodeLink => ({
id: nodeLink.querySelector("TileMatrix").textContent,
bbox: {
minTileRow: parseInt(nodeLink.querySelector("MinTileRow").textContent, 10),
maxTileRow: parseInt(nodeLink.querySelector("MaxTileRow").textContent, 10),
minTileCol: parseInt(nodeLink.querySelector("MinTileCol").textContent, 10),
maxTileCol: parseInt(nodeLink.querySelector("MaxTileCol").textContent, 10)
}
}));
tileSets.forEach(tile => {
listTileMatrixLimits.forEach(nodeLink => {
if (tile.id === nodeLink.id) {
tile.bbox = nodeLink.bbox;
tile.complete = true;
}
});
});
if (tileSets.length > 0) {
resultat.tilingScheme = new CRSSelected.tilingScheme({
ellipsoid: CRSSelected.ellipsoid,
numberOfLevelZeroTilesX: tileSets[0].maxWidth,
numberOfLevelZeroTilesY: tileSets[0].maxHeight
});
const resourceURL = layerNode.querySelector("ResourceURL[format='" + formatImage.format + "']");
if (resourceURL !== null) {
template = resourceURL.getAttribute("template").replace("{TileRow}", "{y}").replace("{TileCol}", "{x}").replace("{style}", styleName).replace("{Style}", styleName).
replace("{TileMatrixSet}", tileMatrixSetLinkName).replace("{layer}", layerName).replace("{infoFormatExtension}", formatImage.extension);
} else if (urlKVP !== null) {
template = urlKVP + "service=WMTS&request=GetTile&version=1.0.0&layer=" + layerName + "&style=" + styleName + "&format=" + formatImage.format + "&TileMatrixSet=" + tileMatrixSetLinkName + "&TileMatrix={TileMatrix}&TileRow={y}&TileCol={x}"
}
if (template !== null) {
resultat.getTileDataAvailable = (x, y, level) => {
let retour = false;
if (level < maxLevel && level < tileSets.length) {
const tile = tileSets[level];
const bbox = tile.bbox;
if (tile.complete) {
retour = (y <= bbox.maxTileRow && y >= bbox.minTileRow) && (x <= bbox.maxTileCol && x >= bbox.minTileCol);
} else {
retour = x < tile.maxWidth && y < tile.maxHeight;
}
}
return retour;
};
resultat.URLtemplateImage = function (x, y, level) {
let retour = "";
if (resultat.getTileDataAvailable(x, y, level)) {
let tile = tileSets[level];
retour = template.replace("{TileMatrix}", tile.id);
}
return retour;
};
const imageSize = {
width: tileSets[0].tileWidth,
height: tileSets[0].tileHeight
};
const checkSize = tileSets.filter(elt => elt.tileWidth != imageSize.width || elt.tileHeight != imageSize.height);
if (checkSize.length === 0) {
resultat.imageSize = imageSize;
}
resultat.ready = true;
}
}
}
}
return resultat as IResult;
};