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

iTowns / itowns / 13988543204

21 Mar 2025 09:22AM UTC coverage: 87.163% (+0.05%) from 87.11%
13988543204

Pull #2522

github

web-flow
Merge 568faa3a3 into 3c1bb1c81
Pull Request #2522: fix(subdivision): Remove enough texture check and don't update if subdividing

2796 of 3737 branches covered (74.82%)

Branch coverage included in aggregate %.

8 of 12 new or added lines in 3 files covered. (66.67%)

247 existing lines in 6 files now uncovered.

25757 of 29021 relevant lines covered (88.75%)

1115.85 hits per line

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

63.52
/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.getElevationLayer() &&
1!
21
        cmd.targetLevel <= cmd.requester.material.getElevationLayer().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
    if (node.pendingSubdivision) {
15!
NEW
54
        return;
×
NEW
55
    }
×
56
    const material = node.material;
15✔
57
    if (!parent || !material) {
15!
58
        return;
×
59
    }
×
60
    const extentsDestination = node.getExtentsByProjection(layer.crs);
15✔
61

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

15✔
67
    let nodeLayer = material.getLayer(layer.id);
15✔
68

15✔
69
    // Initialisation
15✔
70
    if (node.layerUpdateState[layer.id] === undefined) {
15✔
71
        node.layerUpdateState[layer.id] = new LayerUpdateState();
9✔
72

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

9✔
88
        if (!nodeLayer) {
9✔
89
            // Create new raster node
2✔
90
            nodeLayer = layer.setupRasterNode(node);
2✔
91

2✔
92
            // Init the node by parent
2✔
93
            const parentLayer = parent.material?.getLayer(layer.id);
2!
94
            nodeLayer.initFromParent(parentLayer, extentsDestination);
2✔
95
        }
2✔
96

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

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

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

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

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

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

7✔
130
    const failureParams = node.layerUpdateState[layer.id].failureParams;
7✔
131
    const destinationLevel = extentsDestination[0].zoom || node.level;
15!
132
    const targetLevel = chooseNextLevelToFetch(layer.updateStrategy.type, node, destinationLevel, nodeLayer.level, layer, failureParams);
15✔
133

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

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

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

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

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

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

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

1✔
196
        const parentLayer = parent.material?.getLayer(layer.id);
1!
197
        nodeLayer.initFromParent(parentLayer, extentsDestination);
1✔
198

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

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

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

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

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

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

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

1✔
249
export function removeLayeredMaterialNodeLayer(layerId) {
1✔
250
    /**
×
251
     * @param {TileMesh} node - The node to udpate.
×
252
     */
×
253
    return function removeLayeredMaterialNodeLayer(node) {
×
254
        if (node.material?.removeLayer) {
×
255
            if (node.material.elevationLayerIds.indexOf(layerId) > -1) {
×
256
                node.setBBoxZ({ min: 0, max: 0 });
×
257
            }
×
258
            node.material.removeLayer(layerId);
×
259
        }
×
260
        if (node.layerUpdateState && node.layerUpdateState[layerId]) {
×
261
            delete node.layerUpdateState[layerId];
×
262
        }
×
263
    };
×
264
}
×
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