feat(core): apply local meter scales in custom projections - #10745
Pessimistress wants to merge 2 commits into
Conversation
|
| for (let y = 0; y < size; y++) { | ||
| for (let x = 0; x < size; x++) { | ||
| const common = [((x + 0.5) * 512) / size, ((y + 0.5) * 512) / size]; | ||
| const output = [ | ||
| (common[0] - 256) / normalization + (minX + maxX) / 2, | ||
| (common[1] - 256) / normalization + (minY + maxY) / 2, | ||
| 0 | ||
| ]; | ||
| try { | ||
| if (output[0] < minX || output[0] > maxX || output[1] < minY || output[1] > maxY) | ||
| continue; | ||
| const input = projection.inverse(output); | ||
| if (!input || input.length < 2 || !input.every(Number.isFinite)) continue; | ||
| if ( | ||
| fromBounds && | ||
| (input[0] < fromBounds[0] || | ||
| input[0] > fromBounds[2] || | ||
| input[1] < fromBounds[1] || | ||
| input[1] > fromBounds[3]) | ||
| ) | ||
| continue; |
There was a problem hiding this comment.
Narrow domains lose meter scale When a valid projection occupies less than one grid cell, no cell center may pass these checks. For example, an identity converter with
toBounds: [0, 0, 512, 512] and fromBounds: [1, 0, 2, 512] produces no valid scale records, even though positions inside fromBounds are valid. Padding has no valid neighbor to copy, so the shader samples zero and collapses meter-sized geometry and altitude.
| vec3 project_external_size_scale_at(vec2 commonPosition) { | ||
| if (any(lessThan(commonPosition, vec2(0.0))) || any(greaterThan(commonPosition, vec2(512.0)))) return vec3(0.0); |
There was a problem hiding this comment.
Positions outside extent lose scale Projection bounds define normalization, not clipping, so valid data can still render outside the normalized
[0, 512] extent. With an identity converter and toBounds: [0, 0, 512, 512], a position at x=513 remains projectable, but this lookup returns zero. Its meter-sized radius and altitude collapse. The WGSL path has the same behavior.
| previous?.resource.destroy(); | ||
| this.resources.set(viewport.id, {signature: customViewport.sizeScaleSignature, resource}); |
There was a problem hiding this comment.
Removed views retain GPU resources The cache destroys an old resource only when another viewport reuses its ID; otherwise entries remain until the LayerManager is finalized. An application that repeatedly removes custom-projection views and creates new ones with different IDs therefore accumulates textures or buffers after those views stop rendering. Releasing entries for removed views would prevent that GPU memory growth.
Knowledge Base Used: Core rendering engine
| gpuTest.each( | ||
| [ | ||
| { | ||
| name: 'scalar XY and independent Z', |
There was a problem hiding this comment.
WebGPU scale math lacks checks These numeric GPU cases, and the numeric altitude case, are skipped on WebGPU. Shader assembly checks that the storage-buffer binding exists, and a render image checks visual output, but neither checks calculated scale values. A WebGPU numeric case with a nonconstant scale or altitude would catch binding or indexing errors that could still produce a plausible image.
Knowledge Base Used: Distribution and quality
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
8039c42 to
1a80405
Compare
1a80405 to
be5c334
Compare
| this.signature = JSON.stringify([fromCrs, toCrs, resolution]); | ||
| this.scaleOptions = opts; | ||
| this.metersPerUnitCallback = metersPerUnitCallback; | ||
| this.sizeScaleSignature = JSON.stringify([fromCrs, toCrs]); |
There was a problem hiding this comment.
View changes leave scales stale When a view keeps the same ID and CRS but changes
toBounds or getMetersPerUnit, these signatures do not change, although both prepared positions and the scale grid depend on those values. Existing layers keep positions from the old configuration, and the GPU resource cache reuses the old meter scale. For example, changing an identity projection’s toBounds from [0, 0, 512, 512] to [0, 0, 1024, 512] leaves positions in the old normalization and renders meter sizes at the old scale.
Knowledge Base Used: Layer lifecycle and state
For #10739 (PR 6 of 6)
Account for local projection distortion when rendering meter-sized objects and altitude.
Changed list
Method
Generation
Generate a 64×64 scale grid over the projection’s normalized extent. At each valid sample, inverse-project its position and estimate the local projection derivatives using one-meter displacements in world coordinates.
Use the square root of the absolute Jacobian determinant to obtain an area-equivalent scalar scale. Applying the same scale in both horizontal directions preserves the aspect ratio of anchored objects rather than reproducing directional distortion.
Compute scale slopes from neighboring samples, then extend valid values and slopes one cell into unsampled regions to cover valid positions near curved projection boundaries. Reuse the generated resource across camera changes; regenerate when its projection configuration changes.
Encoding
Each grid cell stores four floats:
[scale, dScale/dX, dScale/dY, altitudeScale]A zero scale marks an invalid sample. WebGL stores float bit patterns in an integer texture; WebGPU uses a
vec4<f32>storage buffer with direct address lookup.Interpolation
Fetch the nearest cell and reconstruct the local scale using its slopes:
Altitude scale receives the same relative adjustment. This requires one record lookup without bilinear filtering. Four-cell blending and higher-order interpolation remain documented alternatives if greater continuity or precision is needed.
Measured approximation error
Current harness results, in percent. “Center” covers a 128×128 common-unit square; “whole” covers valid positions within the 512×512 extent. Discontinuity measures the scale jump across nearest-cell boundaries, relative to the reference scale.
Test domains: Web Mercator through ±85.0511° latitude; Equal Earth worldwide; north-polar stereographic from 60°S to 90°N; Albers over
[-135, 30, -45, 75].All sampled valid positions retrieved valid scale values. Tests enforce error and discontinuity below 1%. These are sampled maxima for these configurations, not universal bounds. Equal Earth’s exact poles are excluded from relative-error calculations because the numerical reference is singular there, but sample validity is still checked.