Skip to content

Commit ccd0fc1

Browse files
authored
feat: DZI fetch function (#162)
1 parent b0d5818 commit ccd0fc1

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

packages/dzi/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { getVisibleTiles, type DziImage, type DziTile } from './loader';
1+
export { fetchDziMetadata, getVisibleTiles, type DziImage, type DziTile } from './loader';
22
export {
33
buildDziRenderer,
44
buildAsyncDziRenderer,

packages/dzi/src/loader.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
import { logger } from '@alleninstitute/vis-core';
12
import { Box2D, type Interval, Vec2, type box2D, type vec2 } from '@alleninstitute/vis-geometry';
23

34
type DziTilesRoot = `${string}_files/`;
5+
type DziFormat = 'jpeg' | 'png' | 'jpg' | 'JPG' | 'PNG';
6+
const isDziFormat = (format: string): format is DziFormat => ['jpeg', 'png', 'jpg', 'JPG', 'PNG'].includes(format);
7+
48
// see https://learn.microsoft.com/en-us/previous-versions/windows/silverlight/dotnet-windows-silverlight/cc645077(v=vs.95)?redirectedfrom=MSDN
59
// TODO find a less ancient spec...
610
export type DziImage = {
711
imagesUrl: DziTilesRoot; // lets say you found a dzi at http://blah.com/deepzoom.dzi
812
// imagesUrl would be the path which contains all the files for the actual image tiles:
913
// in this example:
1014
// http://blah.com/deepzoom_files/
11-
format: 'jpeg' | 'png' | 'jpg' | 'JPG' | 'PNG';
15+
format: DziFormat;
1216
overlap: number; // in pixels, ADDED every side of any given tile (for example, with overlap=1 and tilesize=256, you could see a jpeg of size 258x258).
1317
// note that tiles on the edge wont have padding (on a per edge basis!)
1418
tileSize: number;
@@ -27,6 +31,67 @@ export type DziTile = {
2731
relativeLocation: box2D;
2832
layer: number;
2933
};
34+
35+
/**
36+
* Fetches the metadata for a Deep Zoom Image (DZI) from a given URL.
37+
*
38+
* @param url The URL to a DZI metadata file, which should be an XML file containing the metadata for a Deep Zoom Image
39+
* @returns A DZI image object containing the metadata for the Deep Zoom Image
40+
*/
41+
export async function fetchDziMetadata(url: string): Promise<DziImage | undefined> {
42+
return fetch(url)
43+
.then((response) => response.text())
44+
.then((xmlString) => decodeDzi(xmlString, url));
45+
}
46+
47+
function decodeDzi(xmlString: string, url: string): DziImage | undefined {
48+
const parser = new DOMParser();
49+
const doc = parser.parseFromString(xmlString, 'text/xml');
50+
const err = doc.querySelector('parsererror');
51+
if (err) {
52+
logger.error(`Failed to parse DZI XML from ${url} with content:`, xmlString);
53+
return undefined;
54+
}
55+
56+
const img = doc.getElementsByTagName('Image')[0];
57+
const size = doc.getElementsByTagName('Size')?.[0];
58+
const [format, overlap, tileSize] = [
59+
img.getAttribute('Format'),
60+
img.getAttribute('Overlap'),
61+
img.getAttribute('TileSize'),
62+
];
63+
64+
if (!size || !format || !overlap || !tileSize) {
65+
logger.error(`Failed to parse DZI XML from ${url}: Missing required attributes`);
66+
return undefined;
67+
}
68+
69+
const width = size.getAttribute('Width');
70+
const height = size.getAttribute('Height');
71+
const splits = url.split('.dzi');
72+
73+
if (!width || !height || !splits || splits.length < 1) {
74+
logger.error(`Failed to parse DZI XML from ${url}: Missing size or URL splits`);
75+
return undefined;
76+
}
77+
78+
if (!isDziFormat(format)) {
79+
logger.error(`Failed to parse DZI XML from ${url}: Invalid format "${format}"`);
80+
return undefined;
81+
}
82+
83+
return {
84+
imagesUrl: `${splits[0]}_files/`,
85+
format: format,
86+
overlap: Number.parseInt(overlap, 10),
87+
tileSize: Number.parseInt(tileSize, 10),
88+
size: {
89+
width: Number.parseInt(width, 10),
90+
height: Number.parseInt(height, 10),
91+
},
92+
};
93+
}
94+
3095
function tileUrl(dzi: DziImage, level: number, tile: TileIndex): string {
3196
return `${dzi.imagesUrl}${level.toFixed(0)}/${tile.col.toFixed(0)}_${tile.row.toFixed(0)}.${dzi.format}`;
3297
}

0 commit comments

Comments
 (0)