developmentseed / developmentseed/deck.gl-raster
COG tiles near ±180° longitude cause RasterReprojector mesh divergence in Web Mercator mode
- Dominant language
- TypeScript
- Stars
- 228
- Forks
- 29
- Avg merge
- 12h 27m
- Merged PRs (30d)
- 4
Description
## Description
When rendering an EPSG:4326 COG that spans the antimeridian (±180° longitude) in a Web Mercator viewport, tiles near the dateline cause `RasterReprojector` mesh refinement to diverge, producing mislocated rectangles across the map.
The root cause: `forwardTo3857` maps nearby source coordinates on opposite sides of ±180° to EPSG:3857 x-values ~40 million meters apart (+20M vs -20M). The mesh triangles connecting these vertices span the entire map, and the reprojection error never converges (error=43200 pixels after 10001 iterations).
## Reproduction
Load a global EPSG:4326 COG (e.g. WorldPop population data):
```ts
new COGLayer({
geotiff: 's3://wherobots-examples/data/ppp_2020_1km_Aggregated.tif',
// ...
})
```
Console shows:
```text
RasterReprojector: mesh refinement did not converge after 10001 iterations
(maxError=0.125, currentError=43200.00017279999)
```
## Analysis
In `_renderSubLayers`, for Web Mercator viewports, `reprojectionFns.forwardReproject` is set to `forwardTo3857`. For tiles at the antimeridian, this projection has a discontinuity — longitude values like +179° and -179° (which are 2° apart geographically) map to x-values ~40M meters apart in EPSG:3857. The RasterReprojector mesh subdivision cannot resolve this discontinuity no matter how many iterations it runs.
## Workaround
We're patching `_renderSubLayers` to detect antimeridian-crossing tiles by checking if the tile's corner x-coordinates in EPSG:3857 span more than half the world circumference. When detected, `forwardTo3857` is wrapped to shift negative x-values by +circumference, making the mesh continuous:
```ts
const HALF_CIRCUMFERENCE = WEB_MERCATOR_METER_CIRCUMFERENCE / 2;
const cornerXs = /* compute tile corners in EPSG:3857 */;
if (Math.max(...cornerXs) - Math.min(...cornerXs) > HALF_CIRCUMFERENCE) {
tileForwardTo3857 = (x, y) => {
const [px, py] = forwardTo3857(x, y);
return [px < 0 ? px + WEB_MERCATOR_METER_CIRCUMFERENCE : px, py];
};
tileInverseFrom3857 = (x, y) => {
const unwrapped = x > HALF_CIRCUMFERENCE ? x - WEB_MERCATOR_METER_CIRCUMFERENCE : x;
return inverseFrom3857(unwrapped, y);
};
}
```
## Environment
`@developmentseed/deck.gl-geotiff`: `0.4.0`
`@developmentseed/raster-reproject`: `0.4.0`
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in _renderSubLayers, where Web Mercator uses forwardTo3857 and inverseFrom3857, and trace how tile corner coordinates reach RasterReprojector mesh refinement. Reproduce with the global EPSG:4326 COG from the issue, then verify that antimeridian-crossing tiles use continuous coordinates and no longer report mesh non-convergence or mislocated rectangles.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- computer-graphics, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100