Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(RasterTile): partial loading for raster tiles #2489

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions src/Process/LayeredMaterialNodeProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ function buildCommand(view, layer, extentsSource, extentsDestination, requester)
requester,
priority: materialCommandQueuePriorityFunction(requester.material),
earlyDropFunction: refinementCommandCancellationFn,
partialLoading: true,
};
}

function computePitchs(textures, extentsDestination) {
return extentsDestination
.map((ext, i) => (ext.offsetToParent(textures[i].extent)));
}

export function updateLayeredMaterialNodeImagery(context, layer, node, parent) {
const material = node.material;
if (!parent || !material) {
Expand Down Expand Up @@ -139,14 +145,16 @@ export function updateLayeredMaterialNodeImagery(context, layer, node, parent) {
const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node);

return context.scheduler.execute(command).then(
(result) => {
(results) => {
// Does nothing if the layer has been removed while command was being or waiting to be executed
if (!node.layerUpdateState[layer.id]) {
return;
}
const textures = results.map((texture, index) => (texture != null ? texture :
{ isTexture: false, extent: extentsDestination[index] }));
// TODO: Handle error : result is undefined in provider. throw error
const pitchs = extentsDestination.map((ext, i) => ext.offsetToParent(result[i].extent, nodeLayer.offsetScales[i]));
nodeLayer.setTextures(result, pitchs);
const pitchs = computePitchs(textures, extentsDestination);
nodeLayer.setTextures(textures, pitchs);
node.layerUpdateState[layer.id].success();
},
err => handlingError(err, node, layer, targetLevel, context.view));
Expand Down Expand Up @@ -212,7 +220,7 @@ export function updateLayeredMaterialNodeElevation(context, layer, node, parent)
const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node);

return context.scheduler.execute(command).then(
(result) => {
(results) => {
// Does nothing if the layer has been removed while command was being or waiting to be executed
if (!node.layerUpdateState[layer.id]) {
return;
Expand All @@ -225,8 +233,8 @@ export function updateLayeredMaterialNodeElevation(context, layer, node, parent)
node.layerUpdateState[layer.id].noMoreUpdatePossible();
return;
}
const pitchs = extentsDestination.map((ext, i) => ext.offsetToParent(result[i].extent, nodeLayer.offsetScales[i]));
nodeLayer.setTextures(result, pitchs);
const pitchs = computePitchs(results, extentsDestination);
nodeLayer.setTextures(results, pitchs);
node.layerUpdateState[layer.id].success();
},
err => handlingError(err, node, layer, targetLevel, context.view));
Expand Down
18 changes: 17 additions & 1 deletion src/Provider/DataSourceProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@ export default {
const layer = command.layer;
const src = command.extentsSource;
const dst = command.extentsDestination || src;
const promises = src.map((from, i) => (layer.getData(from, dst[i])));

return Promise.all(src.map((from, i) => (layer.getData(from, dst[i]))));
// partialLoading sets the return promise as fulfilled if at least one sub-promise is fulfilled
// It waits until all promises are resolved
if (command.partialLoading) {
return Promise.allSettled(promises)
.then((results) => {
const anyFulfilledPromise = results.find(promise => promise.status === 'fulfilled');
if (!anyFulfilledPromise) {
// All promises failed -> reject
return Promise.reject(new Error('Failed to load any data'));
}
return results.map(prom => (prom.value ? prom.value : null));
});
}

// Without partialLoading, the return promise is rejected as soon as any sub-promise is rejected
return Promise.all(promises);
},
};
87 changes: 66 additions & 21 deletions src/Renderer/RasterTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@ class RasterTile extends THREE.EventDispatcher {
initFromParent(parent, extents) {
if (parent && parent.level > this.level) {
let index = 0;
for (const c of extents) {
for (const texture of parent.textures) {
if (c.isInside(texture.extent)) {
this.setTexture(index++, texture, c.offsetToParent(texture.extent));
break;
}
const sortedParentTextures = this.sortBestParentTextures(parent.textures);
for (const childExtent of extents) {
const matchingParentTexture = sortedParentTextures
.find(parentTexture => parentTexture && childExtent.isInside(parentTexture.extent));
if (matchingParentTexture) {
this.setTexture(index++, matchingParentTexture,
childExtent.offsetToParent(matchingParentTexture.extent));
}
}

Expand All @@ -78,6 +79,32 @@ class RasterTile extends THREE.EventDispatcher {
}
}

sortBestParentTextures(textures) {
const sortByValidity = (a, b) => {
if (a.isTexture === b.isTexture) {
return 0;
} else {
return a.isTexture ? -1 : 1;
}
};
const sortByZoom = (a, b) => b.extent.zoom - a.extent.zoom;

return textures.toSorted((a, b) => sortByValidity(a, b) || sortByZoom(a, b));
}

disposeRedrawnTextures(newTextures) {
const validTextureIndexes = newTextures
.map((texture, index) => (this.shouldWriteTextureAtIndex(index, texture) ? index : -1))
.filter(index => index !== -1);

if (validTextureIndexes.length === newTextures.length) {
// Dispose the whole tile when all textures are overwritten
this.dispose(false);
} else {
this.disposeAtIndexes(validTextureIndexes);
}
}

dispose(removeEvent = true) {
if (removeEvent) {
this.layer.removeEventListener('visible-property-changed', this._handlerCBEvent);
Expand All @@ -86,30 +113,43 @@ class RasterTile extends THREE.EventDispatcher {
this._listeners = {};
}
// TODO: WARNING verify if textures to dispose aren't attached with ancestor
for (const texture of this.textures) {
if (texture.isTexture) {
// Dispose all textures
this.disposeAtIndexes(this.textures.keys());
this.textures = [];
this.offsetScales = [];
this.level = EMPTY_TEXTURE_ZOOM;
}

disposeAtIndexes(indexes) {
for (const index of indexes) {
const texture = this.textures[index];
if (texture && texture.isTexture) {
texture.dispose();
}
}
this.level = EMPTY_TEXTURE_ZOOM;
this.textures = [];
this.offsetScales = [];
this.material.layersNeedUpdate = true;
}

setTexture(index, texture, offsetScale) {
this.level = (texture && texture.extent && (index == 0)) ? texture.extent.zoom : this.level;
this.textures[index] = texture || null;
this.offsetScales[index] = offsetScale;
this.material.layersNeedUpdate = true;
if (this.shouldWriteTextureAtIndex(index, texture)) {
this.level = (texture && texture.extent) ? texture.extent.zoom : this.level;
this.textures[index] = texture || null;
this.offsetScales[index] = offsetScale;
this.material.layersNeedUpdate = true;
}
}

setTextures(textures, pitchs) {
this.dispose(false);
this.disposeRedrawnTextures(textures);
for (let i = 0, il = textures.length; i < il; ++i) {
this.setTexture(i, textures[i], pitchs[i]);
}
}

shouldWriteTextureAtIndex(index, texture) {
// Do not apply noData texture if current texture is valid
return !this.textures[index] || texture && texture.isTexture;
}
}

export default RasterTile;
Expand Down Expand Up @@ -182,8 +222,12 @@ export class RasterElevationTile extends RasterTile {
}

setTextures(textures, offsetScales) {
const anyValidTexture = textures.find(texture => texture != null);
if (!anyValidTexture) {
return;
}
const currentLevel = this.level;
this.replaceNoDataValueFromTexture(textures[0]);
this.replaceNoDataValueFromTexture(anyValidTexture);
super.setTextures(textures, offsetScales);
this.updateMinMaxElevation();
if (currentLevel !== this.level) {
Expand All @@ -192,10 +236,11 @@ export class RasterElevationTile extends RasterTile {
}

updateMinMaxElevation() {
if (this.textures[0] && !this.layer.useColorTextureElevation) {
const firstValidIndex = this.textures.findIndex(texture => texture.isTexture);
if (firstValidIndex !== -1 && !this.layer.useColorTextureElevation) {
const { min, max } = computeMinMaxElevation(
this.textures[0],
this.offsetScales[0],
this.textures[firstValidIndex],
this.offsetScales[firstValidIndex],
{
noDataValue: this.layer.noDataValue,
zmin: this.layer.zmin,
Expand All @@ -214,7 +259,7 @@ export class RasterElevationTile extends RasterTile {
return;
}
// replace no data value with parent texture value or 0 (if no significant value found).
const parentTexture = this.textures[0];
const parentTexture = this.textures.find(texture => texture != null);
const parentDataElevation = parentTexture && parentTexture.image && parentTexture.image.data;
const dataElevation = texture.image && texture.image.data;

Expand Down