developmentseed / developmentseed/deck.gl-raster

COG tiles near ±180° longitude cause RasterReprojector mesh divergence in Web Mercator mode

Open
#366 3 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.