Skip to content

Commit 15494cb

Browse files
committed
feat(pmtiles): add streaming tile size retrieval and corresponding tests, close #178
1 parent 4b478de commit 15494cb

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

  • versatiles_container/src/container/pmtiles

versatiles_container/src/container/pmtiles/reader.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,33 @@ impl TileSource for PMTilesReader {
420420
))
421421
}
422422

423+
#[context("streaming tile sizes for bbox {:?}", bbox)]
424+
async fn get_tile_size_stream(&self, bbox: TileBBox) -> Result<TileStream<'static, u32>> {
425+
let mut tile_sizes: Vec<(TileCoord, u32)> = Vec::new();
426+
427+
let coords: Vec<TileCoord> = bbox.iter_coords().collect();
428+
for coord in coords {
429+
let Ok(tile_id) = coord.get_hilbert_index() else {
430+
continue;
431+
};
432+
if let Some(range) = Self::resolve_tile_range(
433+
tile_id,
434+
Arc::clone(&self.root_entries),
435+
&self.leaves_cache,
436+
&self.leaves_bytes,
437+
self.header.tile_data.offset,
438+
self.internal_compression,
439+
)
440+
.await?
441+
&& let Ok(size) = u32::try_from(range.length)
442+
{
443+
tile_sizes.push((coord, size));
444+
}
445+
}
446+
447+
Ok(TileStream::from_vec(tile_sizes))
448+
}
449+
423450
// deep probe of container meta
424451
#[cfg(feature = "cli")]
425452
/// Adds PMTiles‑specific container metadata (the v3 header) to the CLI probe output.
@@ -489,6 +516,60 @@ mod tests {
489516
Ok(())
490517
}
491518

519+
#[tokio::test]
520+
async fn tile_size_stream_matches_tile_reads() -> Result<()> {
521+
let reader = PMTilesReader::open_path(&PATH, TilesRuntime::default()).await?;
522+
523+
let bbox = TileBBox::from_min_and_max(9, 274, 167, 275, 168)?;
524+
let compression = reader.metadata().tile_compression;
525+
526+
let mut sizes: Vec<(TileCoord, u32)> = reader.get_tile_size_stream(bbox).await?.to_vec().await;
527+
sizes.sort_by_key(|(c, _)| (c.y, c.x));
528+
529+
assert_eq!(sizes.len(), 4);
530+
531+
for (coord, size) in &sizes {
532+
let blob = reader
533+
.get_tile(coord)
534+
.await?
535+
.expect("tile should exist")
536+
.into_blob(compression)?;
537+
assert_eq!(
538+
*size,
539+
blob.len() as u32,
540+
"size mismatch at {coord:?}"
541+
);
542+
}
543+
544+
Ok(())
545+
}
546+
547+
#[tokio::test]
548+
async fn tile_size_stream_single_tile() -> Result<()> {
549+
let reader = PMTilesReader::open_path(&PATH, TilesRuntime::default()).await?;
550+
551+
let bbox = TileBBox::from_min_and_max(0, 0, 0, 0, 0)?;
552+
let sizes: Vec<(TileCoord, u32)> = reader.get_tile_size_stream(bbox).await?.to_vec().await;
553+
554+
assert_eq!(sizes.len(), 1);
555+
assert_eq!(sizes[0].0, TileCoord::new(0, 0, 0)?);
556+
assert_eq!(sizes[0].1, 20);
557+
558+
Ok(())
559+
}
560+
561+
#[tokio::test]
562+
async fn tile_size_stream_empty_for_missing_zoom() -> Result<()> {
563+
let reader = PMTilesReader::open_path(&PATH, TilesRuntime::default()).await?;
564+
565+
let bbox = TileBBox::from_min_and_max(20, 0, 0, 3, 3)?;
566+
let sizes: Vec<(TileCoord, u32)> = reader.get_tile_size_stream(bbox).await?.to_vec().await;
567+
568+
assert!(sizes.is_empty());
569+
570+
Ok(())
571+
}
572+
492573
#[tokio::test]
493574
async fn tile_stream_matches_individual_reads() -> Result<()> {
494575
let reader = PMTilesReader::open_path(&PATH, TilesRuntime::default()).await?;

0 commit comments

Comments
 (0)