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

iTowns / itowns / 15279170578

27 May 2025 03:18PM UTC coverage: 87.096% (-0.02%) from 87.111%
15279170578

Pull #2477

github

web-flow
Merge 7ed9ec5f0 into ed0980450
Pull Request #2477: refactor(LayeredMaterial): migrate to TypeScript

2805 of 3747 branches covered (74.86%)

Branch coverage included in aggregate %.

513 of 585 new or added lines in 14 files covered. (87.69%)

11 existing lines in 2 files now uncovered.

26002 of 29328 relevant lines covered (88.66%)

2209.86 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

18✔
70
        if (!layer.source.extentInsideLimit(node.extent, zoom)) {
18!
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 &&
×
NEW
76
                parent.material.getTile &&
×
NEW
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

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

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

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

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

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

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

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

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

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

30✔
131
    if ((!layer.source.isVectorSource && targetLevel <= nodeLayer.level) || targetLevel > destinationLevel) {
30!
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)) {
30!
138
        node.layerUpdateState[layer.id].noData({ targetLevel });
×
139
        context.view.notifyChange(node, false);
×
140
        return;
×
141
    }
×
142

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

14✔
147
    return context.scheduler.execute(command).then(
14✔
148
        (results) => {
14✔
149
            // Does nothing if the layer has been removed while command was being or waiting to be executed
×
150
            if (!node.layerUpdateState[layer.id]) {
×
151
                return;
×
152
            }
×
153
            const textures = results.map((texture, index) => (texture != null ? texture :
×
154
                { isTexture: false, extent: extentsDestination[index] }));
×
155
            // TODO: Handle error : result is undefined in provider. throw error
×
156
            const pitchs = computePitchs(textures, extentsDestination);
×
157
            nodeLayer.setTextures(textures, pitchs);
×
158
            node.layerUpdateState[layer.id].success();
×
159
        },
14✔
160
        err => handlingError(err, node, layer, targetLevel, context.view));
14✔
161
}
14✔
162

2✔
163
export function updateLayeredMaterialNodeElevation(context, layer, node, parent) {
2✔
164
    const material = node.material;
6✔
165
    if (!parent || !material) {
6!
166
        return;
×
167
    }
×
168

6✔
169
    // TODO: we need either
6✔
170
    //  - compound or exclusive layers
6✔
171
    //  - support for multiple elevation layers
6✔
172

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

6✔
187
    if (node.layerUpdateState[layer.id] === undefined) {
6✔
188
        node.layerUpdateState[layer.id] = new LayerUpdateState();
2✔
189

2✔
190
        const parentLayer = parent.material?.getTile(layer.id);
2!
191
        nodeLayer.initFromParent(parentLayer, extentsDestination);
2✔
192

2✔
193
        if (nodeLayer.level >= layer.source.zoom.min) {
2!
194
            context.view.notifyChange(node, false);
×
195
            return;
×
196
        }
×
197
    }
2✔
198

6✔
199
    // Possible conditions to *not* update the elevation texture
6✔
200
    if (layer.frozen ||
6✔
201
        !material.visible ||
6✔
202
        !node.layerUpdateState[layer.id].canTryUpdate()) {
6✔
203
        return;
2✔
204
    }
2✔
205

4✔
206
    const failureParams = node.layerUpdateState[layer.id].failureParams;
4✔
207
    const targetLevel = chooseNextLevelToFetch(layer.updateStrategy.type, node, extentsDestination[0].zoom, nodeLayer.level, layer, failureParams);
4✔
208

4✔
209
    if (targetLevel <= nodeLayer.level || targetLevel > extentsDestination[0].zoom) {
6!
210
        node.layerUpdateState[layer.id].noMoreUpdatePossible();
×
211
        return;
×
212
    } else if (!layer.source.extentInsideLimit(node.extent, targetLevel)) {
6!
213
        node.layerUpdateState[layer.id].noData({ targetLevel });
×
214
        context.view.notifyChange(node, false);
×
215
        return;
×
216
    }
×
217

4✔
218
    const extentsSource = extentsDestination.map(e => e.tiledExtentParent(targetLevel));
4✔
219
    node.layerUpdateState[layer.id].newTry();
4✔
220
    const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node);
4✔
221

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

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

2✔
243
export function removeLayeredMaterialNodeTile(tileId) {
2✔
244
    /**
×
245
     * @param {TileMesh} node - The node to udpate.
×
246
     */
×
NEW
247
    return function removeLayeredMaterialNodeTile(node) {
×
NEW
248
        if (node.material?.removeTile) {
×
NEW
249
            if (node.material.elevationTile !== undefined) {
×
250
                node.setBBoxZ({ min: 0, max: 0 });
×
251
            }
×
NEW
252
            node.material.removeTile(tileId);
×
253
        }
×
NEW
254
        if (node.layerUpdateState && node.layerUpdateState[tileId]) {
×
NEW
255
            delete node.layerUpdateState[tileId];
×
256
        }
×
257
    };
×
258
}
×
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