• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iTowns / itowns / 25318013074

04 May 2026 12:06PM UTC coverage: 88.397% (+0.002%) from 88.395%
25318013074

Pull #2756

github

web-flow
Merge e473e5a9c into 74811a183
Pull Request #2756: Feat/tms and pbf

2842 of 3662 branches covered (77.61%)

Branch coverage included in aggregate %.

55 of 75 new or added lines in 9 files covered. (73.33%)

2 existing lines in 1 file now uncovered.

28882 of 32226 relevant lines covered (89.62%)

948.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.65
/packages/Main/src/Process/LayeredMaterialNodeProcessing.js
1
import { chooseNextLevelToFetch } from 'Layer/LayerUpdateStrategy';
1✔
2
import LayerUpdateState from 'Layer/LayerUpdateState';
1✔
3
import handlingError from 'Process/handlerNodeError';
1✔
4

1✔
5
function materialCommandQueuePriorityFunction(material) {
9✔
6
    // We know that 'node' is visible because commands can only be
9✔
7
    // issued for visible nodes.
9✔
8
    // TODO: need priorization of displayed nodes
9✔
9
    // Then prefer displayed node over non-displayed one
9✔
10
    return material.visible ? 100 : 10;
9!
11
}
9✔
12

1✔
13
function refinementCommandCancellationFn(cmd) {
1✔
14
    if (!cmd.requester.parent || !cmd.requester.material) {
1!
15
        return true;
×
16
    }
×
17
    // Cancel the command if the tile already has a better texture.
1✔
18
    // This is only needed for elevation layers, because we may have several
1✔
19
    // concurrent layers but we can only use one texture.
1✔
20
    if (cmd.layer.isElevationLayer && cmd.requester.material.getElevationTile() &&
1!
21
        cmd.targetLevel <= cmd.requester.material.getElevationTile().level) {
1!
22
        return true;
×
23
    }
×
24

1✔
25
    // Cancel the command if the layer was removed between command scheduling and command execution
1✔
26
    if (!cmd.requester.layerUpdateState[cmd.layer.id]
1✔
27
        || !cmd.layer.source._featuresCaches[cmd.layer.crs]) {
1!
28
        return true;
×
29
    }
×
30

1✔
31
    return !cmd.requester.material.visible;
1✔
32
}
1✔
33

1✔
34
function buildCommand(view, layer, extentsSource, extentsDestination, requester) {
9✔
35
    return {
9✔
36
        view,
9✔
37
        layer,
9✔
38
        extentsSource,
9✔
39
        extentsDestination,
9✔
40
        requester,
9✔
41
        priority: materialCommandQueuePriorityFunction(requester.material),
9✔
42
        earlyDropFunction: refinementCommandCancellationFn,
9✔
43
        partialLoading: true,
9✔
44
    };
9✔
45
}
9✔
46

1✔
47
function computePitchs(textures, extentsDestination) {
1✔
48
    return extentsDestination
1✔
49
        .map((ext, i) => (ext.offsetToParent(textures[i].extent)));
1✔
50
}
1✔
51

1✔
52
export function updateLayeredMaterialNodeImagery(context, layer, node, parent) {
1✔
53
    const material = node.material;
15✔
54
    if (!parent || !material) {
15!
55
        return;
×
56
    }
×
57
    const extentsDestination = node.getExtentsByProjection(layer.crs);
15✔
58

15✔
59
    const zoom = extentsDestination[0].zoom;
15✔
60
    if (zoom > layer.zoom.max || zoom < layer.zoom.min) {
15!
61
        return;
×
62
    }
×
63

15✔
64
    let nodeLayer = material.getTile(layer.id);
15✔
65

15✔
66
    // Initialisation
15✔
67
    if (node.layerUpdateState[layer.id] === undefined) {
15✔
68
        node.layerUpdateState[layer.id] = new LayerUpdateState();
9✔
69

9✔
70
        if (!layer.source.extentInsideLimit(node.extent, zoom)) {
9!
71
            // we also need to check that tile's parent doesn't have a texture for this layer,
×
72
            // because even if this tile is outside of the layer, it could inherit it's
×
73
            // parent texture
×
74
            if (!layer.noTextureParentOutsideLimit &&
×
75
                parent.material &&
×
76
                parent.material.getTile &&
×
77
                parent.material.getTile(layer.id)) {
×
78
                // ok, we're going to inherit our parent's texture
×
79
            } else {
×
80
                node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
81
                return;
×
82
            }
×
83
        }
×
84

9✔
85
        if (!nodeLayer) {
9✔
86
            // Create new raster node
2✔
87
            nodeLayer = layer.setupRasterNode(node);
2✔
88

2✔
89
            // Init the node by parent
2✔
90
            const parentLayer = parent.material?.getTile(layer.id);
2!
91
            nodeLayer.initFromParent(parentLayer, extentsDestination);
2✔
92
        }
2✔
93

9✔
94
        // Proposed new process, two separate processes:
9✔
95
        //      * FIRST PASS: initNodeXXXFromParent and get out of the function
9✔
96
        //      * SECOND PASS: Fetch best texture
9✔
97

9✔
98
        // The two-step allows you to filter out unnecessary requests
9✔
99
        // Indeed in the second pass, their state (not visible or not displayed) can block them to fetch
9✔
100
        if (nodeLayer.level >= layer.source.zoom?.min) {
9✔
101
            context.view.notifyChange(node, false);
4✔
102
            return;
4✔
103
        }
4✔
104
    }
9✔
105

11✔
106
    // Node is hidden, no need to update it
11✔
107
    if (!material.visible) {
15!
108
        return;
×
109
    }
✔
110

11✔
111
    // An update is pending / or impossible -> abort
11✔
112
    if (!layer.visible || !node.layerUpdateState[layer.id].canTryUpdate()) {
15✔
113
        return;
4✔
114
    }
4✔
115

7✔
116
    if (nodeLayer.level >= extentsDestination[0].zoom) {
15!
117
        // default decision method
×
118
        node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
119
        return;
×
120
    }
✔
121

7✔
122
    // is fetching data from this layer disabled?
7✔
123
    if (layer.frozen) {
15!
124
        return;
×
125
    }
✔
126

7✔
127
    const failureParams = node.layerUpdateState[layer.id].failureParams;
7✔
128
    const destinationLevel = extentsDestination[0].zoom || node.level;
15!
129
    const targetLevel = chooseNextLevelToFetch(layer.updateStrategy, destinationLevel, nodeLayer.level, failureParams, layer.source.zoom);
15✔
130

15✔
131
    if ((!layer.source.isVectorSource && targetLevel <= nodeLayer.level) || targetLevel > destinationLevel) {
15!
132
        if (failureParams.lowestLevelError != Infinity) {
×
133
            // this is the highest level found in case of error.
×
134
            node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
135
        }
×
136
        return;
×
137
    } else if (!layer.source.extentInsideLimit(node.extent, targetLevel)) {
15!
138
        node.layerUpdateState[layer.id].noData({ targetLevel });
×
139
        context.view.notifyChange(node, false);
×
140
        return;
×
141
    }
×
142

7✔
143
    const extentsSource = extentsDestination.map(e => e.tiledExtentParent(targetLevel));
7✔
144
    node.layerUpdateState[layer.id].newTry();
7✔
145
    const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node);
7✔
146

7✔
147
    return context.scheduler.execute(command)
7✔
148
        .then(
7✔
149
            (results) => {
7✔
NEW
150
                // Does nothing if the layer has been removed while command was being or waiting to be executed
×
NEW
151
                if (!node.layerUpdateState[layer.id]) {
×
NEW
152
                    return;
×
NEW
153
                }
×
NEW
154
                const textures = results.map((texture, index) => (texture != null ? texture :
×
NEW
155
                    { isTexture: false, extent: extentsDestination[index] }));
×
NEW
156

×
NEW
157
                // TODO: Handle error : result is undefined in provider. throw error
×
NEW
158
                const pitchs = computePitchs(textures, extentsDestination);
×
NEW
159
                nodeLayer.setTextures(textures, pitchs);
×
NEW
160
                node.layerUpdateState[layer.id].success();
×
161
            })
7✔
162
        .catch((err) => {
7✔
163
            handlingError(err, node, layer, targetLevel, context.view);
1✔
164
        });
7✔
165
}
7✔
166

1✔
167
export function updateLayeredMaterialNodeElevation(context, layer, node, parent) {
1✔
168
    const material = node.material;
3✔
169
    if (!parent || !material) {
3!
170
        return;
×
171
    }
×
172

3✔
173
    // TODO: we need either
3✔
174
    //  - compound or exclusive layers
3✔
175
    //  - support for multiple elevation layers
3✔
176

3✔
177
    // Elevation is currently handled differently from color layers.
3✔
178
    // This is caused by a LayeredMaterial limitation: only 1 elevation texture
3✔
179
    // can be used (where a tile can have N textures x M layers)
3✔
180
    const extentsDestination = node.getExtentsByProjection(layer.crs);
3✔
181
    const zoom = extentsDestination[0].zoom;
3✔
182
    if (zoom > layer.zoom.max || zoom < layer.zoom.min) {
3!
183
        return;
×
184
    }
×
185
    // Init elevation layer, and inherit from parent if possible
3✔
186
    let nodeLayer = material.getElevationTile();
3✔
187
    if (!nodeLayer) {
3!
188
        nodeLayer = layer.setupRasterNode(node);
×
189
    }
×
190

3✔
191
    if (node.layerUpdateState[layer.id] === undefined) {
3✔
192
        node.layerUpdateState[layer.id] = new LayerUpdateState();
1✔
193

1✔
194
        const parentLayer = parent.material?.getTile(layer.id);
1!
195
        nodeLayer.initFromParent(parentLayer, extentsDestination);
1✔
196

1✔
197
        if (nodeLayer.level >= layer.source.zoom.min) {
1!
198
            context.view.notifyChange(node, false);
×
199
            return;
×
200
        }
×
201
    }
1✔
202

3✔
203
    // Possible conditions to *not* update the elevation texture
3✔
204
    if (layer.frozen ||
3✔
205
        !material.visible ||
3✔
206
        !node.layerUpdateState[layer.id].canTryUpdate()) {
3✔
207
        return;
1✔
208
    }
1✔
209

2✔
210
    const failureParams = node.layerUpdateState[layer.id].failureParams;
2✔
211
    const targetLevel = chooseNextLevelToFetch(layer.updateStrategy, extentsDestination[0].zoom, nodeLayer.level, failureParams, layer.source.zoom);
2✔
212

2✔
213
    if (targetLevel <= nodeLayer.level || targetLevel > extentsDestination[0].zoom) {
3!
214
        node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
215
        return;
×
216
    } else if (!layer.source.extentInsideLimit(node.extent, targetLevel)) {
3!
217
        node.layerUpdateState[layer.id].noData({ targetLevel });
×
218
        context.view.notifyChange(node, false);
×
219
        return;
×
220
    }
×
221

2✔
222
    const extentsSource = extentsDestination.map(e => e.tiledExtentParent(targetLevel));
2✔
223
    node.layerUpdateState[layer.id].newTry();
2✔
224
    const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node);
2✔
225

2✔
226
    return context.scheduler.execute(command).then(
2✔
227
        (results) => {
2✔
228
            // Does nothing if the layer has been removed while command was being or waiting to be executed
1✔
229
            if (!node.layerUpdateState[layer.id]) {
1!
230
                return;
×
231
            }
×
232

1✔
233
            // Do not apply the new texture if its level is < than the current
1✔
234
            // one.  This is only needed for elevation layers, because we may
1✔
235
            // have several concurrent layers but we can only use one texture.
1✔
236
            if (targetLevel <= nodeLayer.level) {
1!
237
                node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
238
                return;
×
239
            }
×
240
            const pitchs = computePitchs(results, extentsDestination);
1✔
241
            nodeLayer.setTextures(results, pitchs);
1✔
242
            node.layerUpdateState[layer.id].success();
1✔
243
        },
2✔
244
        err => handlingError(err, node, layer, targetLevel, context.view));
2✔
245
}
2✔
246

1✔
247
export function removeLayeredMaterialNodeTile(tileId) {
1✔
248
    /**
×
249
     * @param {TileMesh} node - The node to udpate.
×
250
     */
×
251
    return function removeLayeredMaterialNodeTile(node) {
×
252
        if (node.material?.removeTile) {
×
253
            if (node.material.elevationTile !== undefined) {
×
254
                node.setBBoxZ({ min: 0, max: 0 });
×
255
            }
×
256
            node.material.removeTile(tileId);
×
257
        }
×
258
        if (node.layerUpdateState && node.layerUpdateState[tileId]) {
×
259
            delete node.layerUpdateState[tileId];
×
260
        }
×
261
    };
×
262
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc