import { ajax } from './helpers';
import { Events, ButtonLocations, Themes, Controls as ControlTypes } from './definitions';
import { ol } from './definitionsOl';
import Style from 'ol/style/Style';
import Kinetic from 'ol/Kinetic';
import * as olEventsCondition from 'ol/events/condition';
import { View, Map } from 'ol';
import { Resources } from '../resources/resources';
import { PrevZoom } from './controls/prevZoom';
import { RotationControl } from './controls/rotationControl';
import { Overview } from './controls/overview';
import { AssociatedImage } from './controls/associatedImage';
import { Snapshot } from './controls/snapshot';
import { Filename } from './controls/filename';
import { DimensionSelector, isBrightfield } from './controls/dimensionSelector';
import { PathomationAttribution } from './controls/pathomationAttribution';
import { LayerSwitch } from './controls/layerSwitch';
import { ColorAdjustment } from './controls/colorAdjustment';
import { Magnifier } from './controls/magnifier';
import { PmaMouseWheelZoom } from './interactions/customMouseWheelZoom';
import { loginSupportsPost } from "./version";
import { brightnessContrastFilter, colorBalanceFilter, autoAdjust } from "./algorithms/pixelOperations"
let stateManager = {};
const allObjectiveResolutions = [1, 2, 5, 10, 20, 40, 80, 160, 320];
export function initialize() {
if (!this.imageInfo) {
if (!this.image) {
createStaticOlViewer.call(this);
} else {
findServerUrl.call(this, 0);
}
} else {
this.serviceUrl = this.imageInfo.BaseUrl + "api/json/";
this.imagesUrl = this.imageInfo.BaseUrl;
if (!this.sessionID) {
login.call(this, loadImageInfo);
} else {
loadImageInfo.call(this);
}
}
}
export function addEvent(element, eventName, fn) {
if (element.addEventListener) {
element.addEventListener(eventName, fn, false);
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
}
}
export function hasClass(element, className) {
return (" " + element.className + " ").indexOf(" " + className + " ") !== -1;
}
export function addClass(element, className) {
if (!element) {
return;
}
if (hasClass(element, className)) {
return;
}
element.className += " " + className;
}
export function removeClass(element, className) {
if (!element || !hasClass(element, className)) {
return;
}
element.className = element.className.replace(new RegExp(className, "g"), "");
}
export function parseJson(response) {
if (response === null || response === undefined || response === "") {
return null;
}
var obj = JSON.parse(response);
if (!obj) {
return null;
}
// if (obj.hasOwnProperty("d")) {
if (Object.prototype.hasOwnProperty.call(obj, "d")) {
return obj.d;
} else {
return obj;
}
}
export function tileLoad(imageTile, src) {
if (this.options.flip.horizontally !== true &&
this.options.flip.vertically !== true &&
this.imageAdjustments.brightness === 0 &&
this.imageAdjustments.contrast === 1 &&
this.imageAdjustments.gamma === 1 &&
(this.imageAdjustments.rgb === null ||
(this.imageAdjustments.rgb[0] == 1 && this.imageAdjustments.rgb[1] == 1 && this.imageAdjustments.rgb[2] == 1)) &&
(!this.imageAdjustments.tileTransformers || this.imageAdjustments.tileTransformers.length === 0)) {
imageTile.getImage().src = src;
return;
}
var tmpimg = new Image();
tmpimg.crossOrigin = '';
var self = this;
tmpimg.onload = function () {
var c = document.createElement("canvas");
c.width = tmpimg.width;
c.height = tmpimg.height;
var ctx = c.getContext('2d');
var tx = self.options.flip.horizontally ? tmpimg.width : 0;
var ty = self.options.flip.vertically ? tmpimg.height : 0;
var sx = self.options.flip.horizontally ? -1 : 1;
var sy = self.options.flip.vertically ? -1 : 1;
ctx.translate(tx, ty);
ctx.scale(sx, sy);
ctx.drawImage(tmpimg, 0, 0);
if (self.imageAdjustments.brightness !== 0 ||
self.imageAdjustments.contrast !== 1 ||
self.imageAdjustments.gamma !== 1 ||
(self.imageAdjustments.rgb !== null &&
(self.imageAdjustments.rgb[0] != 1 || self.imageAdjustments.rgb[1] != 1 || self.imageAdjustments.rgb[2] != 1)) ||
(self.imageAdjustments.tileTransformers && self.imageAdjustments.tileTransformers.length > 0)) {
var pixels = ctx.getImageData(0, 0, c.width, c.height);
if (self.imageAdjustments.tileTransformers) {
for (var i = 0; i < self.imageAdjustments.tileTransformers.length; i++) {
self.imageAdjustments.tileTransformers[i](pixels);
}
}
var brightness = self.imageAdjustments.brightness,
contrast = self.imageAdjustments.contrast;
brightnessContrastFilter(pixels.data, brightness, contrast, self.imageAdjustments.gamma);
if (self.imageAdjustments.rgb !== null &&
(self.imageAdjustments.rgb[0] != 1 || self.imageAdjustments.rgb[1] != 1 || self.imageAdjustments.rgb[2] != 1)) {
colorBalanceFilter(pixels.data, self.imageAdjustments.rgb[0], self.imageAdjustments.rgb[1], self.imageAdjustments.rgb[2]);
}
ctx.putImageData(pixels, 0, 0);
}
imageTile.getImage().src = c.toDataURL("image/jpeg");
};
tmpimg.src = src;
}
export function getTileUrl(coord, pixelRatio, projection) {
let maxZoom = this.imageInfo.MaxZoomLevel;
let pow = Math.pow(2, coord[0]);
let x = coord[1];
if (this.options.flip.horizontally === true) {
x = (pow - coord[1] - 1);
}
let y = coord[2];
if (this.options.flip.vertically === true) {
y = pow - coord[2] - 1;
}
return this.imagesUrl + "tile?sessionID=" +
encodeURIComponent(this.sessionID) +
"&channels=" + this.channelsString +
"&channelClipping=" + this.channelClippingString +
"&channelColor=" + this.channelColorString +
"&gamma=" + this.channelGammaString +
"&timeframe=" + this.selectedTimeFrame + "&layer=" + this.selectedLayer + "&pathOrUid=" + encodeURIComponent(this.image) + "&x=" + x + "&y=" + y + "&z=" + coord[0];
}
// // export function getTileUrlPattern() {
// // return this.imagesUrl + "tile?sessionID=" + encodeURIComponent(this.sessionID) + "&channels=" + this.channelsString + "&timeframe=" + this.selectedTimeFrame + "&layer=" + this.selectedLayer + "&pathOrUid=" + encodeURIComponent(this.image) + "&x={x}&y={y}&z={z}";
// // }
export function getAnnotatedTileUrlPattern(layerName) {
return this.imagesUrl + "annotatedTile?sessionID=" + encodeURIComponent(this.sessionID) + "&pathOrUid=" + encodeURIComponent(this.image) + "&type=" + encodeURIComponent(layerName) + "&x={x}&y={y}&z={z}";
}
export function getAnnotatedTileUrl(layerName, coord, pixelRatio, projection) {
var maxZoom = this.imageInfo.MaxZoomLevel;
var pow = Math.pow(2, coord[0]);
let x = coord[1];
if (this.options.flip.horizontally === true) {
x = (pow - coord[1] - 1);
}
let y = coord[2];
if (this.options.flip.vertically === true) {
y = pow - coord[2] - 1;
}
return this.imagesUrl + "annotatedTile?sessionID=" + encodeURIComponent(this.sessionID) + "&pathOrUid=" + encodeURIComponent(this.image) + "&type=" + encodeURIComponent(layerName) + "&x=" + x + "&y=" + y + "&z=" + coord[0];
}
export function annotatedTileLoad(imageTile, src) {
if (this.options.flip.horizontally !== true &&
this.options.flip.vertically !== true) {
imageTile.getImage().src = src;
return;
}
var tmpimg = new Image();
tmpimg.crossOrigin = '';
var self = this;
tmpimg.onload = function () {
var c = document.createElement("canvas");
c.width = tmpimg.width;
c.height = tmpimg.height;
var ctx = c.getContext('2d');
var tx = self.options.flip.horizontally ? tmpimg.width : 0;
var ty = self.options.flip.vertically ? tmpimg.height : 0;
var sx = self.options.flip.horizontally ? -1 : 1;
var sy = self.options.flip.vertically ? -1 : 1;
ctx.translate(tx, ty);
ctx.scale(sx, sy);
ctx.drawImage(tmpimg, 0, 0);
imageTile.getImage().src = c.toDataURL("image/png");
};
tmpimg.src = src;
}
export function findServerUrl(index) {
var _this = this;
ajax.call(this, this.serverUrls[index] + "api/json/GetVersionInfo", "GET", null, function (http) {
if (http.status == 200) {
var s = this.serverUrls[index];
if (s[s.length - 1] != "/") {
s = s + "/";
}
const pmaCoreVersion = JSON.parse(http.responseText);
this.serviceUrl = s + "api/json/";
this.imagesUrl = s;
if (!this.sessionID) {
login.call(this, loadImageInfo, pmaCoreVersion);
} else {
loadImageInfo.call(this);
}
} else if (index < this.serverUrls.length - 1) {
findServerUrl.call(this, index + 1);
} else {
if (typeof _this.failCallback === "function") {
_this.failCallback();
}
throw "No accessible server URL found.";
}
});
}
export function login(callback, pmaCoreVersion) {
var _this = this;
_this.userInfo = null;
let usePost = loginSupportsPost(pmaCoreVersion);
ajax.call(this, this.serviceUrl + "Authenticate", usePost ? "POST" : "GET", { username: this.username, password: this.password, caller: this.options.caller },
function (http) {
if (http.status == 200) {
var response = parseJson(http.responseText);
if (response && response.Success === true) {
_this.sessionID = response.SessionId;
_this.userInfo = response;
if (callback) {
callback.call(_this);
}
} else {
if (typeof _this.failCallback === "function") {
_this.failCallback();
}
throw "Login failed. " + response.Reason;
}
} else {
if (typeof _this.failCallback === "function") {
_this.failCallback();
}
console.log(http);
throw "Login failed with status " + http.status + " " + http.statusText;
}
}, { contentType: "application/json", dataEncodeCallback: usePost ? JSON.stringify : null });
}
export function loadImageInfo() {
if (this.imageInfo) {
// skip getting image info, since it was already provided
getRenderingOptions.call(this, createOlViewer);
} else {
var _this = this;
ajax.call(this, this.serviceUrl + "GetImageInfo", "GET", { sessionID: this.sessionID, pathOrUid: this.image }, function (http) {
if (http.status == 200) {
var response = parseJson(http.responseText);
if (response) {
_this.imageInfo = response;
getRenderingOptions.call(_this, createOlViewer);
} else {
if (typeof _this.failCallback === "function") {
_this.failCallback();
}
_this.fireEvent(Events.SlideLoadError, _this);
console.error("Server found but could not get image info");
}
} else {
if (typeof _this.failCallback === "function") {
_this.failCallback();
}
var errorCode = 0; // Unknown error
var errorMessage = Resources.translate("Unknown Error");
try {
if (http.responseText && http.responseText.length !== 0) {
var errorResponse = parseJson(http.responseText);
// if (errorResponse && errorResponse.hasOwnProperty("Code")) {
if (errorResponse && Object.prototype.hasOwnProperty.call(errorResponse, "Code")) {
errorCode = errorResponse.Code;
errorMessage = Resources.translate(errorResponse.Message);
}
}
} catch (e) { } // eslint-disable-line no-empty
if (this.element) {
this.element.innerHTML = "Cannot load slide. " + errorMessage;
}
_this.Error = { Code: errorCode, Message: errorMessage };
_this.fireEvent(Events.SlideLoadError, _this);
console.error("Server responded with status " + http.status + " and code " + errorCode);
console.log(http);
}
});
}
}
export function annotationTransform(input, output, dimension) {
for (var i = 0; i < input.length; i += dimension) {
var x = input[i];
var y = input[i + 1];
if (this.flip.vertically !== true) {
y = this.extent[3] - y;
}
if (this.flip.horizontally === true) {
x = this.extent[2] - x;
}
output[i] = x;
output[i + 1] = y;
}
}
export function loadAnnotations(vectorSource, projection) {
if (typeof this.options.annotations === "object" && "loadAnnotationsByFingerprint" in this.options.annotations) {
if (this.options.annotations.loadAnnotationsByFingerprint) {
getFingerprint.call(this, (fingerprint) => {
getAnnotationsServer.call(this, vectorSource, projection, fingerprint, this.options.annotations.filter, this.readyCallback);
});
return;
}
}
getAnnotationsServer.call(this, vectorSource, projection, null, this.options.annotations.filter, this.readyCallback);
}
export function getAnnotationsServer(vectorSource, projection, fingerprint, filterCb, readyCallback) {
var _this = this;
var data = {
sessionID: this.sessionID,
pathOrUid: this.image,
currentUserOnly: this.options.annotations.currentUserOnly === true ? true : false,
refresh: Math.random(),
};
Object.assign(data, fingerprint ? { fingerprint: fingerprint } : {});
Object.assign(data, this.options.annotations.contexts ? { contexts: this.options.annotations.contexts } : {});
ajax.call(this, this.serviceUrl + "GetAnnotations", "GET", data, function (http) {
if (http.status == 200) {
let response = parseJson(http.responseText);
if (response) {
let annotations = response;
if (typeof filterCb === "function") {
annotations = [];
response.forEach(annot => {
if (filterCb(annot)) {
annotations.push(annot);
}
});
}
const features = _this.initializeFeatures(annotations, projection);
vectorSource.addFeatures(features);
}
}
if (typeof readyCallback === "function") {
readyCallback();
}
});
}
export function getFingerprint(callback) {
ajax.call(this, this.serviceUrl + "GetFingerprint", "GET", { sessionID: this.sessionID, pathOrUid: this.image }, function (http) {
if (http.status == 200) {
var response = parseJson(http.responseText);
if (response) {
callback(response);
} else {
callback(null);
}
} else {
callback(null);
}
});
}
export function getRenderingOptions(callback) {
ajax.call(this, this.serviceUrl + "GetRenderingOptions", "GET", { sessionID: this.sessionID, pathOrUid: this.image }, function (http) {
if (http.status == 200) {
var response = parseJson(http.responseText);
if (response) {
response.supportsRenderingOptions = true;
callback.call(this, response);
} else {
callback.call(this, { supportsRenderingOptions: true });
}
} else {
callback.call(this, { supportsRenderingOptions: false });
}
});
}
export function saveRenderingOptions() {
const renderingOptions = this.getChannelRenderingOptions.call(this);
const data = {
SessionID: this.sessionID,
PathOrUid: this.image,
Contrast: this.imageAdjustments.contrast,
Brightness: this.imageAdjustments.brightness,
Gamma: this.imageAdjustments.gamma,
Channels: this.imageInfo.TimeFrames[0].Layers[0].Channels.map((x, i) => {
return {
Visible: x.Active,
Clipping: renderingOptions[i].clipping ? renderingOptions[i].clipping.map(y => parseFloat(y)) : null,
ChannelColor: renderingOptions[i].color ? renderingOptions[i].color : null,
Gamma: renderingOptions[i].gamma ? renderingOptions[i].gamma : null,
}
}),
};
ajax.call(this, this.serviceUrl + "SetRenderingOptions", "POST", data,
function (http) {
return http.status == 200;
},
{
contentType: "application/json",
dataEncodeCallback: function (data) {
return JSON.stringify(data);
},
}
);
}
export function getKeyboardPanDelta(factor, viewportWidth) {
var pxDelta = factor * viewportWidth;
if (isNaN(pxDelta) || pxDelta < 0 || !isFinite(pxDelta)) {
pxDelta = 50;
}
return pxDelta;
}
export function addOrUpdateArrowPanInteraction() {
if (this.arrowPanInteraction !== null) {
this.map.removeInteraction(this.arrowPanInteraction);
}
this.arrowPanInteraction = new ol.interaction.KeyboardPan({ pixelDelta: getKeyboardPanDelta(this.options.keyboardPanFactor, this.element.offsetWidth), duration: this.options.panAnimations ? this.options.animationDuration : 0 });
this.map.addInteraction(this.arrowPanInteraction);
}
export function getPadding() {
var maxZoom = this.imageInfo.MaxZoomLevel;
var pow = Math.pow(2, maxZoom);
var xPadding = 0,
yPadding = 0;
var boxSize = pow * this.imageInfo.TileSize;
if (this.options.flip.horizontally === true) {
xPadding = boxSize - this.imageInfo.Width;
}
if (this.options.flip.vertically !== true) {
yPadding = boxSize - this.imageInfo.Height;
}
return { xPadding: xPadding, yPadding: yPadding };
}
export function createProjection() {
var padding = getPadding.call(this);
var pixelPerUMeter = 1;
if (this.imageInfo.MicrometresPerPixelX && this.imageInfo.MicrometresPerPixelX > 0) {
pixelPerUMeter = this.imageInfo.MicrometresPerPixelX;
}
var pixelProjection = new ol.proj.Projection({
code: 'pixel',
units: 'pixels',
extent: [padding.xPadding, padding.yPadding, padding.xPadding + this.imageInfo.Width, padding.yPadding + this.imageInfo.Height],
metersPerUnit: pixelPerUMeter * 0.000001,
getPointResolution: function (resolution, coord) {
return resolution;
}
});
return pixelProjection;
}
export function createMainView(projection, center, zoom, rotation) {
var tilesPerBoxSide = Math.pow(2, this.imageInfo.MaxZoomLevel);
var digitalZoomLevels = parseInt(this.options.digitalZoomLevels);
if (isNaN(digitalZoomLevels) || digitalZoomLevels <= 0) {
digitalZoomLevels = 0;
}
digitalZoomLevels = Math.min(digitalZoomLevels, 3);
this.options.digitalZoomLevels = digitalZoomLevels;
var startZoom = this.imageInfo.MaxZoomLevel;
var iw = this.imageInfo.Width;
var ih = this.imageInfo.Height;
// find a zoom level that fits the whole image inside the viewport - or stop at zoom level 0
while (startZoom > 0 && (iw > this.element.offsetWidth || ih > this.element.offsetHeight)) {
iw /= 2;
ih /= 2;
startZoom--;
}
let allowedResolutions = [];
const minDigitalResolution = 1 / Math.pow(2, digitalZoomLevels);
if (this.imageInfo.MicrometresPerPixelX) {
let maxResolution = tilesPerBoxSide;
let mmpx = this.imageInfo.MicrometresPerPixelX;
allowedResolutions = allObjectiveResolutions.map(function (v) {
return { objective: v, resolution: 10 / mmpx / v };
});
while (allowedResolutions[0].resolution < maxResolution) {
allowedResolutions = [{ objective: 0, resolution: 2 * allowedResolutions[0].resolution }].concat(allowedResolutions);
}
if (allowedResolutions[allowedResolutions.length - 1] > minDigitalResolution) {
allowedResolutions.push({ objective: "MAX", resolution: minDigitalResolution });
}
}
const viewResolutions = allowedResolutions.length == 0 ? undefined : allowedResolutions.filter(x => x.resolution >= minDigitalResolution).map(x => x.resolution);
var view = new View({
projection: projection,
center: center ? center : ol.extent.getCenter(projection.getExtent()),
// extent: projection.getExtent(),
showFullExtent: true,
maxResolution: tilesPerBoxSide,
minResolution: 1 / Math.pow(2, digitalZoomLevels),
zoom: zoom ? zoom : startZoom,
rotation: rotation ? rotation : 0,
resolutions: viewResolutions,
constrainResolution: false,
zoomFactor: 2
});
var self = this;
function fireEvent() {
self.fireEvent(Events.ViewChanged, self);
}
view.on("change:center", fireEvent.bind(this));
view.on("change:resolution", fireEvent.bind(this));
view.on("change:rotation", fireEvent.bind(this));
return view;
}
export function createMagnifierControl(element, collapsed) {
return new Magnifier({ target: element /* or null */, collapsed: collapsed });
}
export function createOverviewControl() {
if (this.imageInfo) {
var tilesPerBoxSide = Math.pow(2, this.imageInfo.MaxZoomLevel);
this.element.querySelector(".ol-overview") && this.element.querySelector(".ol-overview").remove();
return new Overview({
maxResolution: tilesPerBoxSide,
tipLabel: Resources.translate("Overview"),
collapsed: this.options.overview && this.options.overview.collapsed === true,
tracking: this.options.overview && this.options.overview.tracking === true,
stateManager: stateManager,
pmaViewport: this,
});
} else {
this.element.querySelector(".ol-overview") && this.element.querySelector(".ol-overview").remove();
return new Overview({
tipLabel: Resources.translate("Overview"),
collapsed: this.options.overview && this.options.overview.collapsed === true,
tracking: this.options.overview && this.options.overview.tracking === true,
stateManager: stateManager,
pmaViewport: this,
});
}
}
export function createOlViewer(renderingOptions) {
// PHP image info case, if there is just one time frame, PHP encodes it as an object and not as an array
if (this.imageInfo.TimeFrames && this.imageInfo.TimeFrames.TimeFrame) {
this.imageInfo.TimeFrames = [this.imageInfo.TimeFrames.TimeFrame];
}
if (!this.imageInfo.TimeFrames || this.imageInfo.TimeFrames.length === 0) {
this.imageInfo.TimeFrames = [];
this.imageInfo.TimeFrames.push({ Layers: [{ LayerID: 0, Channels: [{ ChannelID: 0, Color: "ffffffff", Name: "Default" }] }], TimeID: 0 });
}
// PHP image info case, if there is just one layer, PHP encodes it as an object and not as an array
for (var i = 0; i < this.imageInfo.TimeFrames.length; i++) {
var tf = this.imageInfo.TimeFrames[i];
if (tf.Layers.ImageLayer) {
tf.Layers = [tf.Layers.ImageLayer];
}
// PHP image info case, if there is just one channel, PHP encodes it as an object and not as an array
for (var j = 0; j < this.imageInfo.TimeFrames[i].Layers.length; j++) {
var l = this.imageInfo.TimeFrames[i].Layers[j];
if (l.Channels.Channel) {
l.Channels = [l.Channels.Channel];
}
}
}
this.selectedTimeFrame = this.options.fov && this.options.fov.timeframe ? this.options.fov.timeframe : 0;
this.selectedLayer = this.options.fov && this.options.fov.layer ? this.options.fov.layer : 0;
for (i = 0; i < this.imageInfo.TimeFrames[0].Layers[0].Channels.length; i++) {
var c = this.imageInfo.TimeFrames[0].Layers[0].Channels[i];
if (this.options.fov && this.options.fov.channels) {
c.Active = this.options.fov.channels.includes(i);
}
// else if (!c.hasOwnProperty("Active")) {
else if (!Object.prototype.hasOwnProperty.call(c, "Active")) {
c.Active = true;
}
if (!c.DefaultGamma) {
c.Gamma = 1.0;
} else {
c.Gamma = c.DefaultGamma;
}
if (renderingOptions && renderingOptions.Channels && renderingOptions.Channels[i]) {
var roc = renderingOptions.Channels[i];
c.UserOptions = {
Clipping: roc.Clipping,
Gamma: roc.Gamma,
Color: roc.ChannelColor
}
if (roc.Visible !== null) {
c.Active = roc.Visible;
}
}
}
this.channelsString = this.getActiveChannels().join(",");
this.channelGammaString = this.getChannelGammaString();
this.channelColorString = this.getChannelColorString();
this.channelClippingString = this.getChannelClippingString();
addClass(this.element, "pma-ui-viewport-container");
if (this.element.tabIndex < 0) {
this.element.tabIndex = 0;
}
// if (this.element.tabIndex) {
// this.element.removeAttribute("tabindex");
// }
// remove any previously added theme classes
for (var key in Themes) {
// if (!Themes.hasOwnProperty(key)) {
if (!Object.prototype.hasOwnProperty.call(Themes, key)) {
continue;
}
removeClass(this.element, Themes[key]);
}
addClass(this.element, this.options.theme);
this.element.innerHTML = "";
if (!this.imageInfo.BackgroundColor) {
this.imageInfo.BackgroundColor = "ffffff";
}
if (this.imageInfo.TimeFrames.length === 1 && this.imageInfo.TimeFrames[0].Layers.length === 1 && isBrightfield(this.imageInfo.TimeFrames[0].Layers[0])) {
this.options.dimensions = false;
}
this.element.style.backgroundColor = "#" + this.imageInfo.BackgroundColor;
if (this.imageInfo.NumberOfZoomLevels) {
this.imageInfo.MaxZoomLevel = this.imageInfo.NumberOfZoomLevels;
}
var maxZoom = this.imageInfo.MaxZoomLevel;
if (!this.imageInfo.MicrometresPerPixelX || this.imageInfo.MicrometresPerPixelX <= 0) {
this.options.scaleLine = false;
}
var pixelProjection = createProjection.call(this);
var tilesPerBoxSide = Math.pow(2, maxZoom);
var layer = new ol.layer.Tile({
source: new ol.source.XYZ({
tileUrlFunction: getTileUrl.bind(this),
tileLoadFunction: tileLoad.bind(this),
projection: pixelProjection,
wrapX: false,
attributions: "",
crossOrigin: "PMA.UI",
cacheSize: Infinity,
tileGrid: ol.tilegrid.createXYZ({
tileSize: [this.imageInfo.TileSize, this.imageInfo.TileSize],
extent: [0, 0, tilesPerBoxSide * this.imageInfo.TileSize, tilesPerBoxSide * this.imageInfo.TileSize],
maxZoom: maxZoom
}),
}),
preload: Infinity,
className: "ol-layer main-layer",
extent: [0, 0, tilesPerBoxSide * this.imageInfo.TileSize, tilesPerBoxSide * this.imageInfo.TileSize]
});
this.mainLayer = layer;
this.loading = 0;
this.loaded = 0;
this.progressEl = null;
var olViewer = this;
// Add progress bar if user requested
if (this.options.loadingBar) {
this.loading = 0;
this.loaded = 0;
this.progressEl = document.createElement('div');
this.progressEl.className = "ol-progress";
if (this.imageInfo.TimeFrames[0].Layers[0].Channels.length > 1) {
this.progressEl.className = "ol-progress dark";
}
this.element.appendChild(this.progressEl);
layer.getSource().on('tileloadstart', function () {
if (olViewer.loading === 0) {
olViewer.progressEl.style.visibility = 'visible';
}
++olViewer.loading;
updateProgress.call(olViewer);
});
layer.getSource().on('tileloadend', function () {
setTimeout(function () {
++olViewer.loaded;
updateProgress.call(olViewer);
}, 100);
});
layer.getSource().on('tileloaderror', function () {
setTimeout(function () {
++olViewer.loaded;
updateProgress.call(olViewer);
}, 100);
});
}
layer.getSource().on('tileloaderror', function () {
olViewer.fireEvent(Events.TilesError, olViewer);
});
var dragZoom = new ol.interaction.DragZoom({ condition: olEventsCondition.shiftKeyOnly, duration: this.options.panAnimations ? this.options.animationDuration : 0 });
var dragRotate = new ol.interaction.DragRotate({ condition: olEventsCondition.altKeyOnly });
var controls = [
new ol.control.Zoom({ zoomInTipLabel: Resources.translate("Zoom in"), zoomOutTipLabel: Resources.translate("Zoom out") }),
new PrevZoom({ tipLabel: Resources.translate("Previous view"), dragZoom: dragZoom }),
];
controls.push(new ol.control.FullScreen({ tipLabel: Resources.translate("Toggle fullscreen"), source: this.options.fullScreenElement, label: '\u2195', labelActive: '\u2195' }));
var layerList = [layer];
// Initialize server side annotation layers if any
if (this.options.annotationsLayers && this.imageInfo.AnnotationsLayers && this.imageInfo.AnnotationsLayers.length > 0) {
var groupList = [];
for (var a = 0; a < this.imageInfo.AnnotationsLayers.length; a++) {
var aLayerName = this.imageInfo.AnnotationsLayers[a];
var alayer = new ol.layer.Tile({
displayInLayerSwitcher: true,
visible: this.options.annotationsLayers.loadLayers === true,
title: aLayerName,
source: new ol.source.XYZ({
tileUrlFunction: getAnnotatedTileUrl.bind(this, aLayerName),
tileLoadFunction: annotatedTileLoad.bind(this),
projection: pixelProjection,
wrapX: false,
attributions: '',
crossOrigin: "PMA.UI"
}),
extent: [0, 0, tilesPerBoxSide * this.imageInfo.TileSize, tilesPerBoxSide * this.imageInfo.TileSize],
className: "ol-layer annotations-layer"
});
alayer.getSource().tileGrid = ol.tilegrid.createXYZ({
tileSize: [this.imageInfo.TileSize, this.imageInfo.TileSize],
extent: [0, 0, tilesPerBoxSide * this.imageInfo.TileSize, tilesPerBoxSide * this.imageInfo.TileSize],
maxZoom: maxZoom
});
groupList.push(alayer);
}
if (groupList.length > 0) {
var group = new ol.layer.Group({
title: Resources.translate("Annotation layers"),
layers: groupList
});
layerList.push(group);
}
}
// try get the annotations
if (this.options.annotations) {
var vectorSource = new ol.source.Vector({
projection: pixelProjection
});
this.annotationsLayer = new ol.layer.Vector({ source: vectorSource, updateWhileAnimating: true, updateWhileInteracting: true, className: "ol-layer annotations-layer" });
loadAnnotations.call(this, vectorSource, pixelProjection);
layerList.push(this.annotationsLayer);
this.showAnnotations(this.options.annotations.visible !== false);
}
var measureVectorSource = new ol.source.Vector({
projection: pixelProjection
});
this.measureLayer = new ol.layer.Vector({
source: measureVectorSource,
updateWhileAnimating: true,
updateWhileInteracting: true,
style: new Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: 2
}),
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({ color: 'rgba(255, 255, 255, 0.2)' }),
stroke: new ol.style.Stroke({ color: '#ff0000', width: 2 }),
}),
text: new ol.style.Text({
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({ color: '#000' }),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
}),
})
})
});
this.measureLayer.setZIndex(10000);
this.gridLayer = new ol.layer.Vector({
updateWhileAnimating: true,
updateWhileInteracting: true,
source: new ol.source.Vector({
projection: pixelProjection
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: (this.options && this.options.grid && this.options.grid.color) ? this.options.grid.color : '#c0c0c0',
width: 1
}),
})
});
this.measureTooltips = [];
layerList.push(this.measureLayer);
layerList.push(this.gridLayer);
if (!this.options.keyboardPanFactor && this.options.keyboardPanFactor !== 0.0) {
this.options.keyboardPanFactor = 0.5;
}
var dragPanInteraction = new ol.interaction.DragPan({ kinetic: new Kinetic(-0.005, 0.05, 100), onFocusOnly: false });
this.mouseWheelInteraction = new PmaMouseWheelZoom({
duration: this.options.mouseWheelZoomAnimations ? this.options.animationDuration : 0,
maxDelta: 1,
deltaPerZoom: 100,
timeout: 0,
onFocusOnly: false,
condition: olEventsCondition.noModifierKeys,
constrainResolution: false
});
let kbzd = 1;
if (this.options.keyboardZoomDelta == 0.0) {
kbzd = 0;
}
else if (this.options.keyboardZoomDelta) {
kbzd = this.options.keyboardZoomDelta;
}
this.map = new Map({
interactions: [
dragPanInteraction,
dragRotate,
dragZoom,
this.mouseWheelInteraction,
new ol.interaction.PinchZoom({ constrainResolution: false }),
new ol.interaction.PinchRotate(),
new ol.interaction.DoubleClickZoom({ duration: this.options.panAnimations ? this.options.animationDuration : 0 }),
new ol.interaction.KeyboardZoom({ delta: kbzd })
],
loadTilesWhileAnimating: this.options.highQuality === true,
loadTilesWhileInteracting: this.options.highQuality === true,
layers: layerList,
target: this.element,
controls: controls,
view: createMainView.call(this, pixelProjection)
});
setControlVisibility.call(this, this.element.querySelector(".ol-full-screen"), this.options.fullscreenControl);
addOrUpdateArrowPanInteraction.call(this);
if (this.options.customButtons) {
createCustomButtons.call(this, this.options.customButtons);
}
if (this.options.position) {
this.setPosition(this.options.position);
}
if (this.options.fov && this.options.fov.extent) {
this.fitToExtent(this.options.fov.extent, this.options.fov.constrainResolution == true ? true : false);
}
if (this.options.fov && this.options.fov.rotation) {
this.setPosition({ rotation: this.options.fov.rotation });
}
initializeControls.call(this, renderingOptions ? renderingOptions.supportsRenderingOptions : false);
if (this.options.digitalZoomLevels > 0) {
var totalLevels = this.options.digitalZoomLevels + maxZoom;
var percent = 100 - Math.round(this.options.digitalZoomLevels * 100 / totalLevels);
if (percent > 0 && percent <= 100) {
var zsElement = this.element.querySelector(".ol-zoomslider");
if (zsElement) {
var digitalLevelsBgColor = "rgba(127, 127, 127, 0.4)";
var zsStyle = window.getComputedStyle(zsElement, null);
var testContainer = document.createElement("div");
testContainer.className = "pma-ui-viewport-container " + this.options.theme;
var testEl = document.createElement("div");
testEl.className = this.options.theme + " ol-zoomslider digital-zoom-levels";
testContainer.appendChild(testEl);
zsElement.appendChild(testContainer);
digitalLevelsBgColor = window.getComputedStyle(testEl, null).getPropertyValue('background-color');
zsElement.removeChild(testContainer);
zsElement.style.background = "linear-gradient(to top, " + zsStyle.getPropertyValue('background-color') + " " + percent + "%, " + digitalLevelsBgColor + " " + percent + "%, " + digitalLevelsBgColor + " 100%)";
}
}
}
if (this.options.grid) {
this.showGrid(this.options.grid.size, this.options.grid.color);
}
if (this.map) {
this.map.on("change:size", mapResize.bind(this));
setMapSizeClass.call(this);
}
if (!isNaN(this.imageInfo.DefaultGamma)) {
this.imageAdjustments.gamma = this.imageInfo.DefaultGamma;
}
if (renderingOptions) {
if (renderingOptions.Brightness) {
this.imageAdjustments.brightness = renderingOptions.Brightness;
}
if (renderingOptions.Contrast) {
this.imageAdjustments.contrast = renderingOptions.Contrast;
}
if (renderingOptions.Gamma) {
this.imageAdjustments.gamma = renderingOptions.Gamma;
}
}
applyImageAdjustments.call(this, false);
// fire ready callback only if we are not going to load annotations
// otherwise let the annotations load export function to do it
if (!this.options.annotations && typeof this.readyCallback === "function") {
this.readyCallback();
}
}
export function createStaticOlViewer() {
var imageWidth = this.options.referenceImage.width;
var imageHeight = this.options.referenceImage.height;
var backgroundColor = "#ffffff";
if (this.options.referenceImage) {
backgroundColor = this.options.referenceImage.backgroundColor;
this.referenceImage = this.options.referenceImage.src;
}
this.imageInfo = {
Filename: "Pathomation",
MicrometresPerPixelX: 1,
MicrometresPerPixelY: 1,
MaxZoomLevel: 4,
Width: imageWidth * 32,
Height: imageHeight * 32,
TimeFrames: [
{
TimeID: 0,
Layers: [
{
LayerID: 0,
Channels: [
{
ChannelID: 0,
Color: "ffffffff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
{
ChannelID: 1,
Color: "ff0000ff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
]
},
{
LayerID: 1,
Channels: [
{
ChannelID: 0,
Color: "ffffffff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
{
ChannelID: 1,
Color: "ff0000ff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
]
}
]
},
{
TimeID: 1,
Layers: [
{
LayerID: 0,
Channels: [
{
ChannelID: 0,
Color: "ffffffff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
{
ChannelID: 1,
Color: "ff0000ff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
]
},
{
LayerID: 1,
Channels: [
{
ChannelID: 0,
Color: "ffffffff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
{
ChannelID: 1,
Color: "ff0000ff",
Name: "Default",
DefaultGamma: 1,
ExposureTime: 0,
EmmissionWaveLength: 0
},
]
}
]
}
],
};
this.selectedTimeFrame = this.options.fov && this.options.fov.timeframe ? this.options.fov.timeframe : 0;
this.selectedLayer = this.options.fov && this.options.fov.layer ? this.options.fov.layer : 0;
for (var i = 0; i < this.imageInfo.TimeFrames[0].Layers[0].Channels.length; i++) {
var c = this.imageInfo.TimeFrames[0].Layers[0].Channels[i];
if (this.options.fov && this.options.fov.channels) {
c.Active = this.options.fov.channels.includes(i);
// } else if (!c.hasOwnProperty("Active")) {
} else if (!Object.prototype.hasOwnProperty.call(c, "Active")) {
c.Active = true;
}
if (!c.DefaultGamma) {
c.Gamma = 1.0;
} else {
c.Gamma = c.DefaultGamma;
}
}
this.channelsString = this.getActiveChannels().join(",");
this.channelGammaString = this.getChannelGammaString();
addClass(this.element, "pma-ui-viewport-container");
if (this.element.tabIndex < 0) {
this.element.tabIndex = 0;
}
// remove any previously added theme classes
for (var key in Themes) {
// if (!Themes.hasOwnProperty(key)) {
if (!Object.prototype.hasOwnProperty.call(Themes, key)) {
continue;
}
removeClass(this.element, Themes[key]);
}
addClass(this.element, this.options.theme);
this.element.innerHTML = "";
this.element.style.backgroundColor = backgroundColor;
var pixelPerUMeter = 1;
if (this.imageInfo.MicrometresPerPixelX && this.imageInfo.MicrometresPerPixelX > 0) {
pixelPerUMeter = this.imageInfo.MicrometresPerPixelX;
}
var extent = [imageWidth * -2, imageHeight * -2, imageWidth * 2, imageHeight * 2];
var projection = new ol.proj.Projection({
code: 'pixel',
units: 'pixels',
extent: extent,
metersPerUnit: pixelPerUMeter * 0.000001,
getPointResolution: function (resolution, coord) {
return resolution;
}
});
var layer = new ol.layer.Tile({
source: new ol.source.XYZ({
tileUrlFunction: () => { return this.referenceImage },
projection: projection,
wrapX: false,
attributions: "",
crossOrigin: "PMA.UI",
cacheSize: Infinity,
tileGrid: ol.tilegrid.createXYZ({
tileSize: [imageWidth * 32, imageHeight * 32],
extent: extent,
maxZoom: 4
}),
}),
preload: Infinity,
className: "ol-layer main-layer",
extent: extent
});
this.mainLayer = layer;
this.loading = 0;
this.loaded = 0;
this.progressEl = null;
var olViewer = this;
// Add progress bar if user requested
if (this.options.loadingBar) {
this.loading = 0;
this.loaded = 0;
this.progressEl = document.createElement('div');
this.progressEl.className = "ol-progress";
if (this.imageInfo.TimeFrames[0].Layers[0].Channels.length > 1) {
this.progressEl.className = "ol-progress dark";
}
this.element.appendChild(this.progressEl);
layer.getSource().on('tileloadstart', function () {
if (olViewer.loading === 0) {
olViewer.progressEl.style.visibility = 'visible';
}
++olViewer.loading;
updateProgress.call(olViewer);
});
layer.getSource().on('tileloadend', function () {
setTimeout(function () {
++olViewer.loaded;
updateProgress.call(olViewer);
}, 100);
});
layer.getSource().on('tileloaderror', function () {
setTimeout(function () {
++olViewer.loaded;
updateProgress.call(olViewer);
}, 100);
});
}
var dragZoom = new ol.interaction.DragZoom({ condition: olEventsCondition.shiftKeyOnly, duration: this.options.panAnimations ? this.options.animationDuration : 0 });
var dragRotate = new ol.interaction.DragRotate({ condition: olEventsCondition.altKeyOnly });
var controls = [
new ol.control.Zoom({ zoomInTipLabel: Resources.translate("Zoom in"), zoomOutTipLabel: Resources.translate("Zoom out") }),
new PrevZoom({ tipLabel: Resources.translate("Previous view"), dragZoom: dragZoom }),
];
controls.push(new ol.control.FullScreen({ tipLabel: Resources.translate("Toggle fullscreen"), source: this.options.fullScreenElement, label: '\u2195', labelActive: '\u2195' }));
var layerList = [layer];
if (!this.options.keyboardPanFactor && this.options.keyboardPanFactor !== 0.0) {
this.options.keyboardPanFactor = 0.5;
}
var dragPanInteraction = new ol.interaction.DragPan({ kinetic: new Kinetic(-0.005, 0.05, 100), onFocusOnly: false });
this.mouseWheelInteraction = new PmaMouseWheelZoom({
duration: this.options.mouseWheelZoomAnimations ? this.options.animationDuration : 0,
maxDelta: 1,
deltaPerZoom: 100,
timeout: 0,
onFocusOnly: false,
condition: olEventsCondition.always,
constrainResolution: false
});
let kbzd = 1;
if (this.options.keyboardZoomDelta == 0.0) {
kbzd = 0;
}
else if (this.options.keyboardZoomDelta) {
kbzd = this.options.keyboardZoomDelta;
}
this.map = new Map({
interactions: [
dragPanInteraction,
dragRotate,
dragZoom,
this.mouseWheelInteraction,
new ol.interaction.PinchZoom({ constrainResolution: false }),
new ol.interaction.PinchRotate(),
new ol.interaction.DoubleClickZoom({ duration: this.options.panAnimations ? this.options.animationDuration : 0 }),
new ol.interaction.KeyboardZoom({ delta: kbzd })
],
layers: layerList,
target: this.element,
controls: controls,
view: new View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 1,
maxZoom: 4,
rotation: 0,
}),
});
setControlVisibility.call(this, this.element.querySelector(".ol-full-screen"), this.options.fullscreenControl);
addOrUpdateArrowPanInteraction.call(this);
if (this.options.customButtons) {
createCustomButtons.call(this, this.options.customButtons);
}
if (this.options.position) {
this.setPosition(this.options.position);
}
if (this.options.fov && this.options.fov.extent) {
this.fitToExtent(this.options.fov.extent, this.options.fov.constrainResolution == true ? true : false);
}
if (this.options.fov && this.options.fov.rotation) {
this.setPosition({ rotation: this.options.fov.rotation });
}
initializeStaticControls.call(this);
if (this.map) {
this.map.on("change:size", mapResize.bind(this));
setMapSizeClass.call(this);
}
// fire ready callback only if we are not going to load annotations
// otherwise let the annotations load export function to do it
if (!this.options.annotations && typeof this.readyCallback === "function") {
this.readyCallback();
}
}
export function mapResize() {
if (!this.map.getSize()) {
return;
}
setMapSizeClass.call(this);
addOrUpdateArrowPanInteraction.call(this);
}
export function setMapSizeClass() {
var size = this.map.getSize();
if (!size) {
return;
}
this.element.className = this.element.className.replace(/ xlg\b| lg\b| md\b| sm\b| xs\b/g, '');
addClass(this.element.querySelector(".ol-scale-line"), "ol-control");
if (size[0] > 1200) {
this.element.className += ' xlg';
removeClass(this.element.querySelector(".ol-scale-line"), "collapsed");
removeClass(this.element.querySelector(".ol-filename"), "ol-collapsed");
} else if (size[0] > 992) {
this.element.className += ' lg';
removeClass(this.element.querySelector(".ol-scale-line"), "collapsed");
removeClass(this.element.querySelector(".ol-filename"), "ol-collapsed");
} else if (size[0] > 768) {
this.element.className += ' md';
removeClass(this.element.querySelector(".ol-scale-line"), "collapsed");
removeClass(this.element.querySelector(".ol-filename"), "ol-collapsed");
} else if (size[0] > 360) {
this.element.className += ' sm';
// addClass(this.element.querySelector(".ol-scale-line"), "collapsed");
// addClass(this.element.querySelector(".ol-filename"), "ol-collapsed");
} else {
this.element.className += ' xs';
// addClass(this.element.querySelector(".ol-scale-line"), "collapsed");
// addClass(this.element.querySelector(".ol-filename"), "ol-collapsed");
}
this.element.className = this.element.className.replace(/ xlgh| lgh| mdh| smh| xsh/g, '');
if (size[1] > 1200) {
this.element.className += ' xlgh';
} else if (size[1] > 992) {
this.element.className += ' lgh';
} else if (size[1] > 768) {
this.element.className += ' mdh';
} else if (size[1] > 360) {
this.element.className += ' smh';
} else {
this.element.className += ' xsh';
}
if (this.overviewControl) {
// 300-500px: resize overview image 1/6 of the number of horizontal pixels in the viewport
// 500-750px: resize overview image 1/7 of the number of horizontal pixels in the viewport
// 750-1000px: resize overview image 1/8 of the number of horizontal pixels in the viewport
// 1000-1250px:resize overview image 1/9 of the number of horizontal pixels in the viewport
// 1250-1500px: resize overview image 1/10 of the number of horizontal pixels in the viewport
// 1500-1750px:resize overview image 1/11 of the number of horizontal pixels in the viewport
// 1750-2000px:resize overview image 1/12 of the number of horizontal pixels in the viewport
// 2000-3000px:resize overview image 1/13 of the number of horizontal pixels in the viewport
// 3000-4000px: resize overview image 1/14 of the number of horizontal pixels in the viewport
if (size[0] < 300 || size[1] < 300) {
// this.overviewControl.setCollapsed(true);
} else {
var factor = 1 / 14;
if (size[0] < 500) {
factor = 1 / 6;
} else if (size[0] < 750) {
factor = 1 / 7;
} else if (size[0] < 1000) {
factor = 1 / 8;
} else if (size[0] < 1250) {
factor = 1 / 9;
} else if (size[0] < 1500) {
factor = 1 / 10;
} else if (size[0] < 1750) {
factor = 1 / 11;
} else if (size[0] < 2000) {
factor = 1 / 12;
} else if (size[0] < 3000) {
factor = 1 / 13;
}
this.overviewControl.changeOverviewSizePx(Math.sqrt(size[0] * size[0]) * factor);
}
}
}
export function printObjectivesInZoomBar() {
var zsElement = this.element.querySelector(".ol-zoomslider");
if (!zsElement) {
return;
}
var oldEl = zsElement.querySelector(".objectives-scale");
if (oldEl) {
zsElement.removeChild(oldEl);
}
var digitalLevelsBgColor = "rgba(127, 127, 127, 0.4)";
var zsStyle = window.getComputedStyle(zsElement, null);
if (this.imageInfo.MicrometresPerPixelX !== 0) {
// eslint-disable-next-line no-unused-vars
var verticalOffset = parseFloat(zsStyle.getPropertyValue("padding-top")) +
parseFloat(zsStyle.getPropertyValue("margin-top")) +
parseFloat(zsStyle.getPropertyValue("border-top-width")) +
parseFloat(zsStyle.getPropertyValue("padding-bottom")) +
parseFloat(zsStyle.getPropertyValue("margin-bottom")) +
parseFloat(zsStyle.getPropertyValue("border-bottom-width"));
var thumbButton = this.element.querySelector(".ol-zoomslider-thumb");
var thumbButtonStyle = window.getComputedStyle(thumbButton, null);
var thumbButtonHeight = thumbButton.offsetHeight +
parseFloat(thumbButtonStyle.getPropertyValue("padding-top")) +
parseFloat(thumbButtonStyle.getPropertyValue("margin-top")) +
parseFloat(thumbButtonStyle.getPropertyValue("border-top-width")) +
parseFloat(thumbButtonStyle.getPropertyValue("padding-bottom")) +
parseFloat(thumbButtonStyle.getPropertyValue("margin-bottom")) +
parseFloat(thumbButtonStyle.getPropertyValue("border-bottom-width"));
var zoomBarHeight = zsElement.offsetHeight - thumbButtonHeight;
zoomBarHeight += (thumbButtonHeight - thumbButton.offsetHeight);
////var factor = (this.imageInfo.MicrometresPerPixelX - 0.75) / -0.0125;
var factor = 10 / this.imageInfo.MicrometresPerPixelX;
var totalLevels = this.options.digitalZoomLevels + this.imageInfo.MaxZoomLevel;
var zoomLevelHeight = zoomBarHeight / totalLevels;
var objectiveHeights = [];
for (var obji = 0; obji <= totalLevels; obji++) {
var curObj = factor / Math.pow(2, (totalLevels - obji - this.options.digitalZoomLevels));
curObj = displayObjective(curObj);
if (curObj !== false) {
objectiveHeights.push({ name: curObj + "X", height: zoomLevelHeight * obji });
}
}
if (objectiveHeights.length > 0) {
var objectivesEl = document.createElement("div");
objectivesEl.className = "objectives-scale";
zsElement.appendChild(objectivesEl);
var html = "";
for (var t = 0; t < objectiveHeights.length; t++) {
html += '<div style="bottom: ' + objectiveHeights[t].height + 'px"><div>' + objectiveHeights[t].name + '</div></div>';
}
objectivesEl.innerHTML = html;
objectivesEl.style.height = zoomBarHeight + "px";
objectivesEl.style.top = (thumbButtonHeight / 2.0) + "px";
}
}
}
export function displayObjective(objective) {
if (objective > 1) {
// objective |= 0; // convert to int
// if (objective > 10) {
// objective = (Math.round(objective / 10.0) * 10.0) | 0; // round to closest 10 multiple
// }
objective = findClosestObjectiveValue(objective);
for (var i = 0; i < allObjectiveResolutions.length; i++) {
if (allObjectiveResolutions[i] === objective) {
return allObjectiveResolutions[i];
}
}
}
return false;
}
export function findClosestObjectiveValue(v) {
return allObjectiveResolutions[allObjectiveResolutions.map(function (av) {
return Math.abs(1 - av / v);
}).reduce(function (iMin, x, i, arr) {
return x < arr[iMin] ? i : iMin;
}, 0)];
}
export function calculateObjective(element) {
var maxPhysicalObjective = findClosestObjectiveValue(10 / this.imageInfo.MicrometresPerPixelX);
var resolution = this.map.getView().getResolution();
var objective = maxPhysicalObjective / resolution;
if (objective > 1) {
objective = findClosestObjectiveValue(objective);
element.innerHTML = objective + "X";
element.style.display = "block";
if (resolution < 1) {
element.innerHTML = objective + "X / " + maxPhysicalObjective + "X";
element.style.display = "block";
}
} else {
element.style.display = "none";
}
}
export function updateProgress() {
var w = this.loaded / this.loading * 100;
if (w > 100) {
w = 100;
}
var width = w.toFixed(1) + '%';
if (this.loaded == 0) {
this.startLoadTime = performance.now();
}
this.progressEl.style.width = width;
if (this.loading <= this.loaded) {
// time taken to load in milliseconds
this.lastLoadTime = performance.now() - this.startLoadTime;
this.startLoadTime = 0;
this.fireEvent(Events.ViewLoaded, { time: this.lastLoadTime, tiles: this.loaded });
this.loading = 0;
this.loaded = 0;
var this_ = this;
setTimeout(function () {
if (this_.loading === this_.loaded) {
this_.progressEl.style.visibility = 'hidden';
this_.progressEl.style.width = 0;
}
}, 500);
}
}
export function applyImageAdjustments(shouldSave = true) {
if (this.colorAdjustmentsControl != null) {
this.colorAdjustmentsControl.update(this.imageAdjustments.brightness, this.imageAdjustments.contrast, this.imageAdjustments.gamma);
}
if (shouldSave) {
this.debouncedSetRenderingOptions.call(this);
}
this.redraw();
}
export function drawScalebar(canvasCtx, imageInfo, scale, location, font, fontColor) {
/** @type {CanvasRenderingContext2D} */
var ctx = canvasCtx;
if (imageInfo.MicrometresPerPixelX == 0) {
return;
}
if (!font) {
font = '24px serif';
}
if (!fontColor) {
fontColor = 'black';
}
if (!location) {
location = "TopLeft";
}
var pixelsPerUnit = scale / imageInfo.MicrometresPerPixelX;
var maxWidth = 80;
var totalUnits = 0,
pow = 1;
var width = 0;
let iw = ctx.canvas.width;
let ih = ctx.canvas.height;
while (width < maxWidth) {
var firstChar = totalUnits / pow;
if (firstChar == 0) {
totalUnits = 1;
} else if (firstChar == 1) {
totalUnits = 2;
} else if (firstChar == 2) {
totalUnits = 5;
} else {
totalUnits = 1;
pow *= 10;
}
totalUnits *= pow;
width = totalUnits * pixelsPerUnit;
}
var units = "";
if (totalUnits % 1000 == 0) {
units = " mm";
totalUnits /= 1000;
} else {
units = " μm";
}
var height = 20;
var startpixelyOr = location.toLowerCase().startsWith("bottom") ? (ih - (2 * height) - 20) : 20;
var startpixelx = location.toLowerCase().endsWith("right") ? (iw - (2 * maxWidth) - 20) : 20;
var rectdiff = 0.2 * 20;
ctx.fillStyle = '#ffffff77';
ctx.fillRect(startpixelx - rectdiff, startpixelyOr - rectdiff, width + (2 * rectdiff), (2 * height) + (2 * rectdiff));
ctx.lineWidth = 2;
var startpixely = startpixelyOr + height;
ctx.beginPath();
ctx.moveTo(startpixelx, startpixely);
ctx.lineTo(startpixelx, startpixely + height);
ctx.moveTo(startpixelx, startpixely + (height / 2));
ctx.lineTo(startpixelx + width, startpixely + (height / 2));
ctx.moveTo(startpixelx + width, startpixely + height);
ctx.lineTo(startpixelx + width, startpixely);
ctx.stroke();
ctx.font = font;
ctx.fillStyle = fontColor;
var text = ctx.measureText(`${totalUnits} ${units}`);
let x = startpixelx + (width / 2.0) - (text.width / 2.0);
x = x < startpixelx ? startpixelx : x;
ctx.textBaseline = "top";
ctx.fillText(`${totalUnits} ${units}`, x, startpixelyOr);
}
/**
*
* @param {CanvasRenderingContext2D} canvasCtx
* @param {string} title
* @ignore
*/
export function drawTitle(canvasCtx, title, location, font, fontColor) {
if (!title || !canvasCtx) {
return;
}
if (!font) {
font = '32px serif';
}
if (!fontColor) {
fontColor = 'black';
}
if (!location) {
location = "Top";
}
let ih = canvasCtx.canvas.height;
canvasCtx.font = font;
canvasCtx.fillStyle = fontColor;
canvasCtx.strokeStyle = 'white';
canvasCtx.lineWidth = 4;
var text = canvasCtx.measureText(title);
let x = (canvasCtx.canvas.width / 2.0) - (text.width / 2.0);
let y = location.toLowerCase().startsWith("bottom") ? (ih - 20) : 20;
let textBaseline = location.toLowerCase();
canvasCtx.textBaseline = textBaseline;
canvasCtx.strokeText(title, x, y);
canvasCtx.fillText(title, x, y);
}
/**
*
* @param {CanvasRenderingContext2D} canvasCtx
* @param {Viewport} viewport
* @param {0 | 90 | 180 | 270} [rotation] - The rotation of the barcode
* @ignore
*/
export function drawBarcode(canvasCtx, viewport, rotation) {
return new Promise((resolve, reject) => {
let imageInfo = viewport && viewport.imageInfo;
if (!viewport || !canvasCtx || !imageInfo) {
resolve();
return;
}
if (imageInfo.AssociatedImageTypes.indexOf("Barcode") < 0) {
resolve();
return;
}
var img = new Image;
img.crossOrigin = "anonymous";
img.onload = function () {
let fw = canvasCtx.canvas.width * 0.12;
let fh = fw * (img.height / img.width);
let x = canvasCtx.canvas.width - fw - 20;
let y = 20;
canvasCtx.lineWidth = 2;
canvasCtx.drawImage(img, x, y, fw, fh);
canvasCtx.strokeStyle = 'black';
canvasCtx.strokeRect(x, y, fw + 2, fh + 2);
canvasCtx.strokeStyle = 'white';
canvasCtx.strokeRect(x + 2, y + 2, fw - 2, fh - 2);
resolve();
};
img.onerror = reject
img.src = getBarcodeUrl(viewport, rotation);
});
}
/**
*
* @param {CanvasRenderingContext2D} canvasCtx
* @param {Viewport} viewport
* @ignore
*/
export function drawOverview(canvasCtx, viewport) {
return new Promise((resolve, reject) => {
let scale = (canvasCtx.canvas.width * 0.2) / viewport.imageInfo.Width;
var url =
viewport.imagesUrl +
"region?pathOrUid=" +
encodeURIComponent(viewport.image) +
"&format=jpg" +
"&timeframe=" +
viewport.selectedTimeFrame +
"&layer=" +
viewport.selectedLayer +
"&channels=" +
viewport.channelsString +
"&channelClipping=" +
viewport.channelClippingString +
"&channelColor=" +
viewport.channelColorString +
"&sessionID=" +
encodeURIComponent(viewport.sessionID) +
"&drawScaleBar=false" +
"&x=0" + "&y=0" +
"&width=" + viewport.imageInfo.Width + "&height=" + viewport.imageInfo.Height +
"&scale=" + scale;
var img = new Image;
img.crossOrigin = "anonymous";
img.onload = function () {
let fw = canvasCtx.canvas.width * 0.2;
let fh = fw * (img.height / img.width);
let x = canvasCtx.canvas.width - fw - 20;
let y = canvasCtx.canvas.height - fh - 20;
canvasCtx.drawImage(img, x, y, fw, fh);
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'black';
canvasCtx.strokeRect(x, y, fw + 2, fh + 2);
canvasCtx.strokeStyle = 'white';
canvasCtx.strokeRect(x + 2, y + 2, fw - 2, fh - 2);
resolve();
};
img.onerror = reject
img.src = url;
});
}
/**
* @param {Viewport} viewport - The main viewport
* @param {0 | 90 | 180 | 270} [rotation] - The rotation of the barcode
* @returns {string} - The barcode url
* @ignore
*/
export function getBarcodeUrl(viewport, rotation) {
return viewport.getActiveServerUrl() + "barcode" +
"?sessionID=" + encodeURIComponent(viewport.getSessionID()) +
"&pathOrUid=" + encodeURIComponent(viewport.imageInfo.Filename) +
(rotation ? ("&rotation=" + rotation) : "");
}
export function createCustomButtons(buttons) {
if (buttons != null && buttons.length > 0 && this.element) {
var defaultLocation = ButtonLocations.S;
var groupByLocations = {};
// groupByLocations[defaultLocation] = [];
for (var i = 0; i < buttons.length; i++) {
if (!buttons[i].location) {
// groupByLocations[defaultLocation].push(buttons[i]);
(groupByLocations[defaultLocation] = groupByLocations[defaultLocation] || []).push(buttons[i]);
}
else {
var l = buttons[i].location;
(groupByLocations[l] = groupByLocations[l] || []).push(buttons[i]);
}
}
for (var location in groupByLocations) {
// if (groupByLocations.hasOwnProperty(location)) {
if (Object.prototype.hasOwnProperty.call(groupByLocations, location)) {
var div = document.createElement("div");
div.className = "ol-unselectable ol-control ol-pma-custom-btn ol-custom-control" + " " + location.toString();
for (i = 0; i < groupByLocations[location].length; i++) {
var b = groupByLocations[location][i];
var btn = document.createElement("button");
btn.title = b.title ? b.title : "";
btn.innerHTML = b.content ? b.content : "";
btn.className = b.class ? b.class : "";
if (typeof b.callback === "function") {
btn.addEventListener("click", b.callback.bind(this));
}
div.appendChild(btn);
}
this.element.querySelector(".ol-overlaycontainer-stopevent").appendChild(div);
}
}
}
}
export function setControlVisibility(controlDiv, visible) {
if (controlDiv) {
if (visible === false) {
addClass(controlDiv, "ol-hidden");
} else {
if (hasClass(controlDiv, "ol-dimension-selector") && isBrightfield(this.imageInfo.TimeFrames[0].Layers[0])) {
return;
}
removeClass(controlDiv, "ol-hidden");
}
}
}
export function getControlVisibility(controlDiv) {
if (controlDiv) {
return !(hasClass(controlDiv, "ol-hidden"));
}
return false;
}
export function setScaleLineConfiguration(conf) {
var scaleLineDiv = this.element.querySelector(".ol-scale-line");
if (scaleLineDiv) {
setControlVisibility.call(this, scaleLineDiv, conf.visible);
if (conf.collapsed === true) {
addClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = true;
} else {
removeClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = false;
}
}
}
export function setControlsConfiguration(configuration) {
for (var i = 0; i < configuration.length; i++) {
var conf = configuration[i];
if (conf.control == ControlTypes.ZoomSlider) {
setControlVisibility.call(this, this.element.querySelector(".ol-zoomslider"), conf.visible);
} else if (conf.control == ControlTypes.ScaleLine) {
if (this.imageInfo && (this.imageInfo.MicrometresPerPixelY && this.imageInfo.MicrometresPerPixelY !== 0) && (this.imageInfo.MicrometresPerPixelX && this.imageInfo.MicrometresPerPixelX !== 0)) {
setScaleLineConfiguration.call(this, conf);
}
} else if (conf.control == ControlTypes.Overview) {
setControlVisibility.call(this, this.element.querySelector(".ol-overview"), conf.visible);
if (this.overviewControl) {
this.overviewControl.setCollapsed(conf.collapsed);
}
} else if (conf.control == ControlTypes.Barcode) {
setControlVisibility.call(this, this.element.querySelector(".ol-associated-image"), conf.visible);
if (this.barcodeControl) {
this.barcodeControl.setCollapsed(conf.collapsed);
this.barcodeControl.setRotation(conf.rotation);
}
} else if (conf.control == ControlTypes.Magnifier) {
if (this.magnifierControl) {
this.magnifierControl.setCollapsed(conf.visible);
}
} else if (conf.control == ControlTypes.ColorAdjustments) {
setControlVisibility.call(this, this.element.querySelector(".ol-brightness-contrast"), conf.visible);
} else if (conf.control == ControlTypes.LayerSwitch) {
setControlVisibility.call(this, this.element.querySelector(".ol-layerswitch"), conf.visible);
if (this.layerSwitcher) {
this.layerSwitcher.setCollapsed(conf.collapsed);
}
} else if (conf.control == ControlTypes.DimensionSelector) {
setControlVisibility.call(this, this.element.querySelector(".ol-dimension-selector"), conf.visible);
if (this.dimensionsControl) {
this.dimensionsControl.setCollapsed(conf.collapsed);
}
} else if (conf.control == ControlTypes.Filename) {
setControlVisibility.call(this, this.element.querySelector(".ol-filename"), conf.visible);
if (this.filenameControl) {
this.filenameControl.setCollapsed(conf.collapsed);
}
} else if (conf.control == ControlTypes.Snapshot) {
setControlVisibility.call(this, this.element.querySelector(".ol-snapshot"), conf.visible);
} else if (conf.control == ControlTypes.Rotation) {
setControlVisibility.call(this, this.element.querySelector(".ol-rotation"), conf.visible);
if (this.rotationControl) {
this.rotationControl.setCollapsed(conf.collapsed);
}
} else if (conf.control == ControlTypes.Attribution) {
setControlVisibility.call(this, this.element.querySelector(".ol-attr"), conf.visible);
} else if (conf.control == ControlTypes.Fullscreen) {
setControlVisibility.call(this, this.element.querySelector(".ol-full-screen"), conf.visible);
}
}
}
export function getControlsConfiguration() {
var scaleLineDiv = this.element.querySelector(".ol-scale-line");
return [{
control: ControlTypes.ZoomSlider,
visible: getControlVisibility.call(this, this.element.querySelector(".ol-zoomslider"))
},
{
control: ControlTypes.ScaleLine,
visible: this.scaleLineControl && getControlVisibility.call(this, this.element.querySelector(".ol-scale-line")),
collapsed: scaleLineDiv ? hasClass(scaleLineDiv, "collapsed") : false
},
{
control: ControlTypes.Overview,
visible: getControlVisibility.call(this, this.element.querySelector(".ol-overview")),
collapsed: this.overviewControl && this.overviewControl.getCollapsed()
},
{
control: ControlTypes.Barcode,
visible: getControlVisibility.call(this, this.element.querySelector(".ol-associated-image")),
collapsed: this.barcodeControl && this.barcodeControl.getCollapsed(),
rotation: this.barcodeControl ? this.barcodeControl.rotation : 0
},
{
control: ControlTypes.Magnifier,
visible: this.magnifierControl && this.magnifierControl.getCollapsed()
},
{
control: ControlTypes.ColorAdjustments,
visible: getControlVisibility.call(this, this.element.querySelector(".ol-brightness-contrast"))
},
{
control: ControlTypes.LayerSwitch,
visible: this.layerSwitcher ? getControlVisibility.call(this, this.element.querySelector(".ol-layerswitch")) : false,
collapsed: this.layerSwitcher ? this.layerSwitcher.getCollapsed() : false
},
{
control: ControlTypes.DimensionSelector,
visible: this.dimensionsControl && getControlVisibility.call(this, this.element.querySelector(".ol-dimension-selector")),
collapsed: this.dimensionsControl ? this.dimensionsControl.getCollapsed() : false
},
{
control: ControlTypes.Filename,
visible: this.filenameControl && getControlVisibility.call(this, this.element.querySelector(".ol-filename")),
collapsed: this.filenameControl ? this.filenameControl.getCollapsed() : false,
filename: null // {string|PMA.UI.View.Viewport~filenameCallback}
},
{
control: ControlTypes.Snapshot,
visible: this.snapShotControl && getControlVisibility.call(this, this.element.querySelector(".ol-snapshot"))
},
{
control: ControlTypes.Rotation,
visible: this.rotationControl && getControlVisibility.call(this, this.element.querySelector(".ol-rotation")),
collapsed: this.rotationControl ? this.rotationControl.getCollapsed() : false
},
{
control: ControlTypes.Attribution,
visible: this.attributionControl ? getControlVisibility.call(this, this.element.querySelector(".ol-attr")) : false,
options: null // {boolean|PMA.UI.View.Viewport~attributionOptions}
}
];
}
export function initializeControls(supportsRenderingOptions) {
this.zoomSliderControl = new ol.control.ZoomSlider({ duration: this.options.panAnimations ? this.options.animationDuration : 0 });
this.map.addControl(this.zoomSliderControl);
this.zoomSliderControl.element.title = "Drag to zoom in and out";
if (this.options.zoomSlider === false) {
setControlVisibility.call(this, this.element.querySelector(".ol-zoomslider"), false);
}
var meta = this.imageInfo.MetaData;
var isJp2k = false;
for (var m = 0; m < meta.length; m++) {
if (meta[m].Name === "compression") {
if (meta[m].Value === "Jpeg2000YCbCr" ||
meta[m].Value === "Jpeg2000RGB" ||
meta[m].Value === "Jpeg2000" ||
meta[m].Value.indexOf("JPEG 2000") !== -1 ||
meta[m].Value === "JP2") {
isJp2k = true;
}
break;
}
}
isJp2k = false; // Disables powered by Kakadu message
if (isJp2k) {
this.attributionControl = new PathomationAttribution({ className: "ol-attr-kakadu", html: '<a href="http://kakadusoftware.com/" target="_blank"> </a>' });
this.map.addControl(this.attributionControl);
} else if (this.options.attributions) {
this.attributionControl = new PathomationAttribution(this.options.attributions);
this.map.addControl(this.attributionControl);
}
this.colorAdjustmentsControl = new ColorAdjustment({ layer: this.mainLayer, viewer: this });
this.map.addControl(this.colorAdjustmentsControl);
setControlVisibility.call(this, this.element.querySelector(".ol-brightness-contrast"), this.options.colorAdjustments);
if (this.options.annotationsLayers && this.imageInfo.AnnotationsLayers && this.imageInfo.AnnotationsLayers.length > 0) {
this.layerSwitcher = new LayerSwitch({
pmaViewport: this,
tipLabel: Resources.translate("Layers"),
collapsed: this.options.annotationsLayers && this.options.annotationsLayers.collapsed ? true : false,
stateManager: stateManager
});
this.map.addControl(this.layerSwitcher);
}
this.overviewControl = createOverviewControl.call(this);
this.map.addControl(this.overviewControl);
setControlVisibility.call(this, this.element.querySelector(".ol-overview"), this.options.overview);
if (this.imageInfo.AssociatedImageTypes) {
for (var k = 0; k < this.imageInfo.AssociatedImageTypes.length; k++) {
if (this.imageInfo.AssociatedImageTypes[k].toLowerCase() == "barcode") {
let rotation = 0;
let collapsed = false;
if (this.options.barcode) {
if (this.options.barcode.rotation) {
rotation = this.options.barcode.rotation;
}
if (this.options.barcode.collapsed) {
collapsed = this.options.barcode.collapsed;
}
}
this.barcodeControl = new AssociatedImage({ pmaViewport: this, rotation: rotation, collapsed: collapsed, imageType: "barcode", tipLabel: Resources.translate("Barcode"), stateManager: stateManager });
this.map.addControl(this.barcodeControl);
setControlVisibility.call(this, this.element.querySelector(".ol-associated-image"), this.options.barcode);
break;
}
}
}
this.dimensionsControl = new DimensionSelector({ pmaViewport: this, supportsRenderingOptions: supportsRenderingOptions === true, tipLabel: Resources.translate("Channels"), collapsed: this.options.dimensions && this.options.dimensions.collapsed, stateManager: stateManager });
this.map.addControl(this.dimensionsControl);
this.dimensionsControl.renderSliders();
setControlVisibility.call(this, this.element.querySelector(".ol-dimension-selector"), this.options.dimensions);
var self = this;
var filenameOptions = {
//filename: (typeof this.options.filename === "string" ? this.options.filename : this.imageInfo.Filename),
onClick: function () {
self.fireEvent(Events.FilenameClick, self);
},
stateManager: stateManager
};
if (typeof this.options.filename === "string") {
filenameOptions.filename = self.options.filename;
}
else if (typeof this.options.filename === "function") {
filenameOptions.filename = self.options.filename.call(self, { serverUrl: self.getActiveServerUrl(), path: self.imageInfo.Filename });
}
else {
filenameOptions.filename = self.imageInfo.Filename;
}
this.filenameControl = new Filename(filenameOptions);
this.map.addControl(this.filenameControl);
setControlVisibility.call(this, this.element.querySelector(".ol-filename"), this.options.filename);
if (!stateManager.scaleLine) {
stateManager.scaleLine = {};
}
this.scaleLineControl = new ol.control.ScaleLine();
this.map.addControl(this.scaleLineControl);
var scaleLineDiv = this.element.querySelector(".ol-scale-line");
if (scaleLineDiv) {
if (this.options.scaleLine == false) {
setScaleLineConfiguration.call(this, { visible: false, collapsed: stateManager.scaleLine.collapsed });
}
if (stateManager.scaleLine.collapsed === true) {
addClass(scaleLineDiv, "collapsed");
}
var objectiveIndicator = document.createElement("div");
objectiveIndicator.className = 'scanResolution';
calculateObjective.call(this, objectiveIndicator);
scaleLineDiv.appendChild(objectiveIndicator);
var _this = this;
this.map.getView().on("change:resolution", function () {
calculateObjective.call(_this, objectiveIndicator);
});
addEvent(scaleLineDiv, "click", function () {
if (hasClass(scaleLineDiv, "collapsed")) {
removeClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = false;
} else {
addClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = true;
}
});
}
this.snapShotControl = new Snapshot({ pmaViewport: this, tipLabel: Resources.translate("Snapshot") });
this.map.addControl(this.snapShotControl);
setControlVisibility.call(this, this.element.querySelector(".ol-snapshot"), this.options.snapshot == true);
let rotCollapsed;
try {
if (this.options.rotationControl.collapsed !== true) {
rotCollapsed = false;
} else {
rotCollapsed = true;
}
} catch {
rotCollapsed = false;
}
this.rotationControl = new RotationControl({ pmaViewport: this, resetTipLabel: Resources.translate("Reset rotation"), flipHorizontallyTipLabel: Resources.translate("Flip horizontally"), flipVerticallyTipLabel: Resources.translate("Flip vertically"), collapsed: rotCollapsed });
this.map.addControl(this.rotationControl);
setControlVisibility.call(this, this.element.querySelector(".ol-rotation"), !(this.options.rotationControl === false));
this.magnifierControl = createMagnifierControl.call(this, null, !this.options.magnifier || (this.options.magnifier && this.options.magnifier.collapsed === true));
this.map.addControl(this.magnifierControl);
printObjectivesInZoomBar.call(this);
}
export function initializeStaticControls() {
this.zoomSliderControl = new ol.control.ZoomSlider({ duration: this.options.panAnimations ? this.options.animationDuration : 0 });
this.map.addControl(this.zoomSliderControl);
if (this.options.zoomSlider === false) {
setControlVisibility.call(this, this.element.querySelector(".ol-zoomslider"), false);
}
this.colorAdjustmentsControl = new ColorAdjustment({ layer: this.mainLayer, viewer: this });
this.map.addControl(this.colorAdjustmentsControl);
setControlVisibility.call(this, this.element.querySelector(".ol-brightness-contrast"), this.options.colorAdjustments);
this.overviewControl = createOverviewControl.call(this);
this.map.addControl(this.overviewControl);
setControlVisibility.call(this, this.element.querySelector(".ol-overview"), this.options.overview);
let rotation = 0;
let collapsed = false;
if (this.options.barcode) {
if (this.options.barcode.rotation) {
rotation = this.options.barcode.rotation;
}
if (this.options.barcode.collapsed) {
collapsed = this.options.barcode.collapsed;
}
}
this.barcodeControl = new AssociatedImage({ pmaViewport: this, rotation: rotation, collapsed: collapsed, imageType: "barcode", tipLabel: Resources.translate("Barcode"), stateManager: stateManager, url: this.referenceImage });
this.map.addControl(this.barcodeControl);
setControlVisibility.call(this, this.element.querySelector(".ol-associated-image"), this.options.barcode);
this.dimensionsControl = new DimensionSelector({ pmaViewport: this, tipLabel: Resources.translate("Channels"), collapsed: this.options.dimensions && this.options.dimensions.collapsed, stateManager: stateManager });
this.map.addControl(this.dimensionsControl);
this.dimensionsControl.renderSliders();
setControlVisibility.call(this, this.element.querySelector(".ol-dimension-selector"), this.options.dimensions);
var self = this;
var filenameOptions = {
//filename: (typeof this.options.filename === "string" ? this.options.filename : this.imageInfo.Filename),
onClick: function () {
self.fireEvent(Events.FilenameClick, self);
},
stateManager: stateManager
};
if (typeof this.options.filename === "string") {
filenameOptions.filename = self.options.filename;
} else if (typeof this.options.filename === "function") {
filenameOptions.filename = self.options.filename.call(self, { serverUrl: self.getActiveServerUrl(), path: self.imageInfo.Filename });
} else {
filenameOptions.filename = self.imageInfo.Filename;
}
this.filenameControl = new Filename(filenameOptions);
this.map.addControl(this.filenameControl);
setControlVisibility.call(this, this.element.querySelector(".ol-filename"), this.options.filename);
if (!stateManager.scaleLine) {
stateManager.scaleLine = {};
}
this.scaleLineControl = new ol.control.ScaleLine();
this.map.addControl(this.scaleLineControl);
var scaleLineDiv = this.element.querySelector(".ol-scale-line");
if (scaleLineDiv) {
if (this.options.scaleLine == false) {
setScaleLineConfiguration.call(this, { visible: false, collapsed: stateManager.scaleLine.collapsed });
}
if (stateManager.scaleLine.collapsed === true) {
addClass(scaleLineDiv, "collapsed");
}
var objectiveIndicator = document.createElement("div");
objectiveIndicator.className = 'scanResolution';
calculateObjective.call(this, objectiveIndicator);
scaleLineDiv.appendChild(objectiveIndicator);
var _this = this;
this.map.getView().on("change:resolution", function () {
calculateObjective.call(_this, objectiveIndicator);
});
addEvent(scaleLineDiv, "click", function () {
if (hasClass(scaleLineDiv, "collapsed")) {
removeClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = false;
} else {
addClass(scaleLineDiv, "collapsed");
stateManager.scaleLine.collapsed = true;
}
});
}
this.snapShotControl = new Snapshot({ pmaViewport: this, tipLabel: Resources.translate("Snapshot") });
this.map.addControl(this.snapShotControl);
setControlVisibility.call(this, this.element.querySelector(".ol-snapshot"), this.options.snapshot == true);
let rotCollapsed;
try {
if (this.options.rotationControl.collapsed !== true) {
rotCollapsed = false;
} else {
rotCollapsed = true;
}
} catch {
rotCollapsed = false;
}
this.rotationControl = new RotationControl({ pmaViewport: this, resetTipLabel: Resources.translate("Reset rotation"), flipHorizontallyTipLabel: Resources.translate("Flip horizontally"), flipVerticallyTipLabel: Resources.translate("Flip vertically"), collapsed: rotCollapsed });
this.map.addControl(this.rotationControl);
setControlVisibility.call(this, this.element.querySelector(".ol-rotation"), !(this.options.rotationControl === false));
this.magnifierControl = createMagnifierControl.call(this, null, !this.options.magnifier || (this.options.magnifier && this.options.magnifier.collapsed === true));
this.map.addControl(this.magnifierControl);
printObjectivesInZoomBar.call(this);
}
//#endregion
//#region documentation objects
/**
* Receives pixel data and applies an image transformation to it
* @callback PMA.UI.View.tileTransformer
* @param {ImageData} pixels - Represents the underlying pixel data of an area of a canvas element
* @ignore
*/
/**
* Viewport position
* @typedef {Object} Viewport~position
* @property {Array} center - The x and y coordinates of the center point
* @property {number} zoom - The zoom level. Can be omitted if resolution is specified
* @property {number} [resolution] - Can be omitted if zoom is specified
* @property {number} [rotation=0]
*/
/**
* Viewport Field of view
* @typedef {Object} Viewport~fov
* @property {Array} extent - The extent of the viewport [minx, miny, maxx, maxy]
* @property {number} rotation - The rotation of the viewport
* @property {bool} [constrainResolution] - Whether to contain the resolution to allowed values when fitting to extent
* @property {Array} [channels] - The selected channels
* @property {number} [layer=0] - The selected layer
* @property {number} [timeframe=0] - The selected timeframe
*/
/**
* Annotation display options
* @typedef {Object} Viewport~annotationOptions
* @property {boolean} [visible=true] - Whether or not to display the loaded annotations
* @property {boolean} [labels=false] - Whether or not to render the text label of each annotation in the viewer
* @property {boolean} [imageBaseUrl=""] - The base URL from which to load images
* @property {number} [imageScale=NaN] - Scale factor for images
* @property {boolean} [alwaysDisplayInMicrons=false] - Whether or not to automatically select the appropriate units for annotations (μm(^2) or mm(^2)) depending on the value
* @property {boolean} [showMeasurements=true] - Whether to show the length and area of an annotation
* @property {boolean} [loadAnnotationsByFingerprint=false] - Whether to load annotations based on image's fingerprint
* @property {function} [filter] - A function that takes a PMA.core annotation and returns true if annotation should load or false if shouldn't
*/
/**
* Attribution display options
* @typedef {Object} Viewport~attributionOptions
* @property {string} html - The HTML contents to add inside the attribution container element
* @property {string} [className="ol-attr"] - The CSS class to assign to the attribution container element
*/
/**
* Image flip options
* @typedef {Object} Viewport~flipOptions
* @property {boolean} [horizontally=false] - Whether or not to flip the image horizontally
* @property {boolean} [vertically=false] - Whether or not to flip the image vertically
*/
/**
* export function that returns a file name to display in the viewer
* @callback Viewport~filenameCallback
* @param {Object} options
* @param {string} options.serverUrl - The URL of the current PMA.core server
* @param {string} options.fileName - The full path of the currently loaded image
* @returns {string}
*/
/**
* A custom button to be added to the viewer
* @typedef {Object} Viewport~customButton
* @property {string} title - The title of the button
* @property {string} content - The inner html of the button
* @property {string} class - The class of the button
* @property {PMA.UI.View.ButtonLocations} [location=PMA.UI.View.ButtonLocations.S] - The location in the viewport of the custom button
* @property {function} callback - The callback to call when the button is clicked with this referring to the viewer
*/
/**
* An annotation as returned by pma.core
* @typedef Viewport~annotation
* @property {Number} AnnotationID - The annotation id
* @property {Number} LayerID - The layer id
* @property {string} Geometry - The annotation geometry in wkt format
* @property {string} [Notes] - Optional notes for the annotation
* @property {string} [Classification] - Optional classification string (Necrosis, tumor etc)
* @property {string} [Color] - Optional color
* @property {string} [CreatedBy] - Optional created by string
* @property {string} [UpdateInfo] - Optional update info
* @property {string} [Updatedby] - Optional updated by info
* @property {string} [FillColor] - Optional fill color
* @property {Number} [Dimensions] - Optional dimensionality of the annotation
*/
/**
* An object for configuring the visible and collapse state of a control
* @typedef Viewport~ControlConfiguration
* @property {PMA.UI.Types.Controls} control - The control to configure
* @property {boolean} - visible - The visibility of the control
* @property {boolean} - collapsed - Whether the control is collapsed or not
*/
/**
* An object for configuring the visible and collapse state of a control
* @typedef Viewport~SnapshotCoordinates
* @property {Integer} x - The x coordinate
* @property {Integer} y - The y coordinate
* @property {Integer} w - The width
* @property {Integer} h - The height
* @property {Number} scale - The scale of the current view in radians
* @property {Number} rotation - The rotation of the current view in radians
* @property {Viewport~flipOptions} flip - Whether the image is flipped
*/