Skip to content

Commit

Permalink
fix(RasterTile): partial loading for raster tiles
Browse files Browse the repository at this point in the history
  • Loading branch information
airnez committed Feb 4, 2025
1 parent 6d12fde commit f977943
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 29 deletions.
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);
},
};
89 changes: 67 additions & 22 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 = null) {
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,11 +259,11 @@ 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;

if (dataElevation && !checkNodeElevationTextureValidity(dataElevation, nodatavalue)) {
if (parentDataElevation && dataElevation && !checkNodeElevationTextureValidity(dataElevation, nodatavalue)) {
insertSignificantValuesFromParent(dataElevation, parentDataElevation && dataParent(texture, parentTexture, parentDataElevation, pitch), nodatavalue);
}
}
Expand Down

0 comments on commit f977943

Please sign in to comment.