我参与了一个项目,可以在Potree中将地图投影到点云上。
我们正在从OpenStreetMap的tile API加载地图,并在用户放大点云时加载更多的图块,以便为用户提供更详细的体验。我们使用Three.js纹理将瓷砖数据从瓷砖投影到点云,我们从画布元素中提取瓷砖。
我们已经完成所有代码以从OSM获取切片,将它们切割成正确的大小并将它们放在画布中。对于较小的点云,该解决方案非常有效,但随着区域的增长,需要更多的图块/图像,并且画布元素变得更大(我们正在缩放画布以跟随缩放级别以提供更好的分辨率)我们使用大量内存会遇到麻烦(在任务管理器中大约10 GB,但后来我调查了chrome dev工具中的内存使用情况,内存使用率要低很多)。大内存使用将导致浏览器无响应并最终死亡。
我的问题是,在这段代码中是否存在某种内存泄漏,或者在使用图像和画布时必须非常小心?
代码:
Potree.MapTextureManager = class MapTextureManager {
constructor(projection, bbMin, bbMax) {
this.projection = projection;
this.bbMin = bbMin;
this.bbMax = bbMax;
this._mapCanvas = document.getElementById("texture");
let ratio = (bbMax[0] - bbMin[0]) / (bbMax[1] - bbMin[1]);
let minHeight = 256;
this._mapCanvas.width = minHeight * ratio;
this._mapCanvas.height = minHeight;
this._minWeb = proj4(swiss, WGS84, [this.bbMin[0], this.bbMin[1]]);
this._maxWeb = proj4(swiss, WGS84, [this.bbMax[0], this.bbMax[1]]);
this.updateTexture(this._minWeb, this._maxWeb);
this._cachedTexture = null;
this._drawnImages = [];
this.geometryNodeIds = new Set();
this._cachedTileImages = [];
this._currentMaxZoom = this.getTiles(this._minWeb, this._maxWeb)[0].zoom;
}
updateTextureFor(visibleNodes, matrixWorld) {
visibleNodes.forEach(visibleNode => {
if (!this.geometryNodeIds.has(visibleNode.geometryNode.id)) {
this.geometryNodeIds.add(visibleNode.geometryNode.id);
var swiss = proj4.defs("test");
var WGS84 = proj4.defs("WGS84");
let nodeBox = Potree.utils.computeTransformedBoundingBox(visibleNode.geometryNode.boundingBox, matrixWorld);
let minWeb = proj4(swiss, WGS84, [nodeBox.min.x, nodeBox.min.y]);
let maxWeb = proj4(swiss, WGS84, [nodeBox.max.x, nodeBox.max.y]);
this.updateTexture(minWeb, maxWeb);
}
});
}
updateTexture(minWeb, maxWeb) {
let canvasEl = this._mapCanvas;
let tiles = this.getTiles(minWeb, maxWeb);
let tilePromises = this.tilePromisesFor(tiles);
tilePromises.forEach(tilePromise => {
tilePromise.then(tileImage => {
if (tileImage.tile.zoom > this._currentMaxZoom) {
this.resizeCanvasTo(tileImage.tile.zoom);
}
this._cachedTileImages.push(tileImage);
this._cachedTileImages.sort((tileImage1, tileImage2) => {
if (tileImage1.tile.zoom >= tileImage2.tile.zoom) {
return 1;
} else {
return -1;
}
});
let myArray = this._cachedTileImages.filter((el) => !this._drawnImages.includes(el));
myArray.forEach(tileImage => {
// if (this._drawnImages.indexOf(tileImage) === -1) {
this.drawTileOnCanvas(canvasEl, tileImage.image, tileImage.tile);
this._drawnImages.push(tileImage);
// }
});
if (this._cachedTexture) {
this._cachedTexture.dispose();
this._cachedTexture = null;
}
});
});
}
get mapTexture() {
if (this._cachedTexture) {
return this._cachedTexture;
}
let texture = new THREE.CanvasTexture(this._mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
this._cachedTexture = texture;
return texture;
}
getTiles(minCoord, maxCoord, zoom = 1) {
let maxZoom = 18;
let minNumberOfTiles = 4;
let minX = this.long2tile(minCoord[0], zoom);
let minY = this.lat2tile(minCoord[1], zoom);
let maxX = this.long2tile(maxCoord[0], zoom);
let maxY = this.lat2tile(maxCoord[1], zoom);
let arrayX = [minX, maxX].sort();
let arrayY = [minY, maxY].sort();
let tiles = [];
for (var x = arrayX[0]; x <= arrayX[1]; x++) {
for (var y = arrayY[0]; y <= arrayY[1]; y++) {
tiles.push({ X: x, Y: y, zoom: zoom });
}
}
// We want at least minNumberOfTiles tiles per pointcloud node
if (tiles.length >= minNumberOfTiles || zoom === maxZoom) {
return tiles;
} else {
return this.getTiles(minCoord, maxCoord, zoom + 1);
}
}
tilePromisesFor(Tiles) {
return Tiles.map(function (tile, i) {
return new Promise((resolve, reject) => {
let image = new Image(256, 256);
image.crossOrigin = "Anonymous";
image.onload = function () {
let data = { tile: tile, image: image };
resolve(data);
}
image.src = "https://tile.openstreetmap.org" + "/" + tile.zoom + "/" + tile.X + "/" + tile.Y + ".png";
})
});
}
drawTileOnCanvas(canvas, image, tile) {
let ctx = canvas.getContext("2d");
ctx.drawImage(image, sX, sY, imageWidthToBeDrawn, imageHeightToBeDrawn, dX, dY, drawingWidth, drawingHeight);
image.src = "";
image = null;
}
resizeCanvasTo(zoom) {
let canvas = this._mapCanvas;
let multiplier = Math.pow(2, zoom - this._currentMaxZoom);
let ctx = canvas.getContext("2d");
// create a temporary canvas obj to cache the pixel data //
var temp_cnvs = document.createElement('canvas');
var temp_cntx = temp_cnvs.getContext('2d');
// set it to the new width & height and draw the current canvas data into it //
temp_cnvs.width = canvas.width * multiplier;;
temp_cnvs.height = canvas.height * multiplier;;
temp_cntx.drawImage(canvas, 0, 0);
// resize & clear the original canvas and copy back in the cached pixel data //
canvas.width = canvas.width * multiplier;
canvas.height = canvas.height * multiplier;
ctx.scale(multiplier, multiplier);
ctx.drawImage(temp_cnvs, 0, 0);
this._currentMaxZoom = zoom;
temp_cnvs = null;
temp_cntx = null;
}
};
我试图删除一些不必要的代码。如果你遗失了什么,请说出来。