in pathology/viewer/src/components/ol-tile-viewer/ol-tile-viewer.component.ts [196:312]
flatImage() {
if (!this.slideInfo) return;
const slideInfo = this.slideInfo;
// Map views always need a projection. Here we just want to map image
// coordinates directly to map coordinates, so we create a projection that
// uses the image extent in pixels.
const extent =
[0, 0, this.slideInfo.size.x ?? 1024, this.slideInfo.size.y ?? 968];
const projection = new Projection({
code: 'flat-image',
units: 'pixels',
extent,
});
const imageLoadFunction =
async (imageWrapper: ImageWrapper, src: string) => {
const image = imageWrapper.getImage();
const slideLevelsByZoom = [...slideInfo.levelMap];
// OpenLayers most zoomed out layer is 0. Reversed of how levelMap
// stores it.
const slideLevel: Level = slideLevelsByZoom[0];
// Frame number is computed by location at X (x+1 for offset), plus number
// of tiles in previous rows (y*tilesPerWidth).
const frame = slideLevel.properties[0].frames;
const instanceUid = slideLevel?.properties[0].instanceUid ?? '';
const path = (this.slideDescriptor?.id as string) ?? '';
this.dicomwebService.getEncodedImageTile(path, instanceUid, frame)
.subscribe((imageData: string) => {
const imageUrl = this.imageDataToImageUrl(imageData);
if (image instanceof HTMLImageElement) {
image.src = imageUrl;
}
});
};
const flatImageSourceOptions = {
url: '',
projection,
imageExtent: extent,
imageLoadFunction,
};
const initialZoom = 0;
// The arbitraryMaxZoom can be set to any value, which determines how far in
// a flat image can be zoomed.
const arbitraryMaxZoom = 8;
const flatImageViewOptions = {
extent,
constrainOnlyCenter: true,
center: getCenter(extent),
zoom: initialZoom,
minZoom: initialZoom,
maxZoom: arbitraryMaxZoom,
projection,
};
const flatImageView = new View(flatImageViewOptions);
const slideOverviewControl = new OverviewMap({
collapsed: false,
view: new View(flatImageViewOptions),
layers: [
new ImageLayer({
source: new ImageStatic(flatImageSourceOptions),
}),
],
});
const loadedChangeListener =
slideOverviewControl.getOverviewMap().on('loadend', () => {
if (loadedChangeListener) {
unByKey(loadedChangeListener);
}
const [, , x, y] = extent;
this.setOverviewAspectRatio(`${x}/${y}`);
});
const flatImageLayer = new ImageLayer<ImageStatic>({
source: new ImageStatic(flatImageSourceOptions),
properties: {
name: 'flat-image-layer',
title: this.slideInfo?.slideName ?? '',
}
});
if (this.isThumbnail) {
flatImageView.setMaxZoom(initialZoom);
this.olMap = new Map({
layers: [
flatImageLayer,
],
controls: [],
interactions: [],
target: this.olMapContainer.nativeElement,
view: flatImageView,
});
} else {
this.olMap = new Map({
layers: [
flatImageLayer,
],
controls: [slideOverviewControl],
target: this.olMapContainer.nativeElement,
view: flatImageView,
});
}
this.olMap?.once('postrender', (event) => {
this.olMapLoaded.emit(this.olMap!);
});
}