The RaQuet viewer is a fully client-side application that renders RaQuet files directly in the browser using HTTP range requests. No server-side processing is required.
JavaScript Parquet reader that supports HTTP range requests for efficient partial file access
WebGL-powered mapping library for high-performance tile rendering
JavaScript zlib/gzip implementation for decompressing band data
When you load a RaQuet file, the viewer first fetches the Parquet footer (last 8 bytes + metadata section) using HTTP range requests:
GET /file.parquet
Range: bytes=-8 # Get footer size
Range: bytes=X-Y # Get metadata based on footer size
The metadata contains:
RaQuet stores its metadata in the last row of the file (where block=0). The viewer reads this row to get:
{
"version": "0.1.0",
"bounds": [-122.5, 37.5, -122.0, 38.0],
"minresolution": 10,
"maxresolution": 14,
"block_width": 256,
"block_height": 256,
"bands": [
{"name": "band_1", "type": "uint8"},
{"name": "band_2", "type": "uint8"},
{"name": "band_3", "type": "uint8"}
]
}
The viewer builds an in-memory index of row groups with their QUADBIN block ID ranges. This enables efficient tile lookups:
Row Group 0: blocks 5270201491262341119 - 5270201491262406655
Row Group 1: blocks 5270201491262472191 - 5270201491262537727
...
When you pan/zoom the map, deck.gl's TileLayer calculates which tiles (z/x/y) are visible and requests them.
Each tile coordinate is converted to a QUADBIN cell ID:
function tileToQuadbin(x, y, z) {
// Mode 9 (tile mode) header
const mode = 9n << 59n;
const resolution = BigInt(z) << 52n;
// Interleave x,y bits to create spatial index
let index = 0n;
for (let i = 0; i < z; i++) {
index |= ((BigInt(x) >> BigInt(i)) & 1n) << BigInt(2 * i);
index |= ((BigInt(y) >> BigInt(i)) & 1n) << BigInt(2 * i + 1);
}
return mode | resolution | index;
}
Using the row group index, the viewer identifies which row groups might contain the requested block. This is fast because QUADBIN IDs are sorted, so only row groups with matching min/max ranges need to be checked.
For efficiency, the viewer uses a two-phase approach:
block column from the row group to find the exact row containing our tile. This is a small read (~8 bytes per row).
band_1, band_2, band_3). This fetches just the pixel data for that specific tile.
The band data is decoded:
Row group size significantly affects HTTP request efficiency. Our testing shows:
| Row Group Size | HTTP Requests/Tile | Reduction |
|---|---|---|
| 1 row/group | ~11.3 | baseline |
| 4 rows/group | ~7.4 | 35% fewer |
| 8 rows/group | ~5.3 | 54% fewer |
| 16 rows/group | ~5.1 | 55% fewer |
Recommendation: Use --row-group-size 8 when creating RaQuet files optimized for web viewing.
Tile requests are batched with a 50ms delay to combine multiple visible tiles into fewer operations.
Loaded tiles are cached in memory to avoid re-fetching when panning back to previously viewed areas.
Using min/max statistics on the block column, the viewer skips row groups that can't contain the requested tile.
Only the columns needed (block ID + band data) are read, not the entire row.
Access-Control-Allow-Origin: *Access-Control-Expose-Headers: Content-Length, Content-RangeThe viewer source code is available in the RaQuet repository. It's a single HTML file with inline JavaScript that can be easily customized or integrated into other applications.