Skip to content
Permalink
Browse files
refactor: annotations will now only be drawn when the user is on the …
…annotations tab (#574)
  • Loading branch information
mdelez committed Nov 4, 2021
1 parent c39ca5b commit bddc2f1bddfa51182a8855cf395ae922edc494c8
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 103 deletions.
@@ -75,6 +75,7 @@ function makeRegion(geomString: string[], iri: string): ReadResource {
<app-still-image [images]="stillImageFileRepresentations"
[imageCaption]="caption"
[activateRegion]="inputActivateRegion"
[currentTab]="'annotations'"
(regionClicked)="regHovered($event)">
</app-still-image>`
})
@@ -101,14 +101,13 @@ export class StillImageComponent implements OnChanges, OnDestroy {
@Input() resourceIri: string;
@Input() project: string;
@Input() activateRegion?: string; // highlight a region

@Input() compoundNavigation?: DspCompoundPosition;
@Input() currentTab: string;

@Output() goToPage = new EventEmitter<number>();

@Output() regionClicked = new EventEmitter<string>();


@Output() regionAdded = new EventEmitter<string>();

regionDrawMode: Boolean = false; // stores whether viewer is currently drawing a region
@@ -150,13 +149,15 @@ export class StillImageComponent implements OnChanges, OnDestroy {
}
if (changes['images']) {
this._openImages();
this._renderRegions();
this._unhighlightAllRegions();
// tODO: check if this is necessary or could be handled below
// (remove the 'else' before the 'if', so changes['activateRegion'] is always checked for)
if (this.activateRegion !== undefined) {
this._highlightRegion(this.activateRegion);
}
if (this.currentTab === 'annotations') {
this.renderRegions();
}
} else if (changes['activateRegion']) {
this._unhighlightAllRegions();
if (this.activateRegion !== undefined) {
@@ -193,7 +194,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
if (!this._viewer) {
this._setupViewer();
}
this._renderRegions();
this.renderRegions();
}

/**
@@ -206,6 +207,96 @@ export class StillImageComponent implements OnChanges, OnDestroy {
this._viewer.setMouseNavEnabled(false);
}

/**
* adds a ROI-overlay to the viewer for every region of every image in this.images
*/
renderRegions(): void {
/**
* sorts rectangular regions by surface, so all rectangular regions are clickable.
* Non-rectangular regions are ignored.
*
* @param geom1 first region.
* @param geom2 second region.
*/
const sortRectangularRegion = (geom1: GeometryForRegion, geom2: GeometryForRegion) => {

if (geom1.geometry.type === 'rectangle' && geom2.geometry.type === 'rectangle') {

const surf1 = StillImageComponent.surfaceOfRectangularRegion(geom1.geometry);
const surf2 = StillImageComponent.surfaceOfRectangularRegion(geom2.geometry);

// if reg1 is smaller than reg2, return 1
// reg1 then comes after reg2 and thus is rendered later
if (surf1 < surf2) {
return 1;
} else {
return -1;
}

} else {
return 0;
}

};

this.removeOverlays();

let imageXOffset = 0; // see documentation in this.openImages() for the usage of imageXOffset

for (const image of this.images) {

const stillImage = image.fileValue as ReadStillImageFileValue;
const aspectRatio = (stillImage.dimY / stillImage.dimX);

// collect all geometries belonging to this page
const geometries: GeometryForRegion[] = [];
image.annotations.map((reg) => {

this._regions[reg.regionResource.id] = [];
const geoms = reg.getGeometries();

geoms.map((geom) => {
const geomForReg = new GeometryForRegion(geom.geometry, reg.regionResource);

geometries.push(geomForReg);
});
});

// sort all geometries belonging to this page
geometries.sort(sortRectangularRegion);

// render all geometries for this page
for (const geom of geometries) {

const geometry = geom.geometry;
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label);

}

imageXOffset++;
}

}

/**
* removes SVG overlays from the DOM.
*/
removeOverlays() {
for (const reg in this._regions) {
if (this._regions.hasOwnProperty(reg)) {
for (const pol of this._regions[reg]) {
if (pol instanceof HTMLDivElement) {
pol.remove();
}
}
}
}

this._regions = {};

this._viewer.clearOverlays();
}

/**
* opens the dialog to enter further properties for the region after it has been drawn and calls the function to upload the region after confirmation
* @param startPoint the start point of the drawing
@@ -365,27 +456,6 @@ export class StillImageComponent implements OnChanges, OnDestroy {
}
}

/**
* removes SVG overlays from the DOM.
*/
private _removeOverlays() {

for (const reg in this._regions) {
if (this._regions.hasOwnProperty(reg)) {
for (const pol of this._regions[reg]) {
if (pol instanceof HTMLDivElement) {
pol.remove();
}
}
}
}

this._regions = {};

// tODO: make this work by using osdviewer's addOverlay method
this._viewer.clearOverlays();
}

/**
* initializes the OpenSeadragon _viewer
*/
@@ -442,7 +512,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
// display only the defined range of this.images
const tileSources: object[] = this._prepareTileSourcesFromFileValues(fileValues);

this._removeOverlays();
this.removeOverlays();
this._viewer.open(tileSources);

}
@@ -492,78 +562,6 @@ export class StillImageComponent implements OnChanges, OnDestroy {
return tileSources;
}

/**
* adds a ROI-overlay to the viewer for every region of every image in this.images
*/
private _renderRegions(): void {

/**
* sorts rectangular regions by surface, so all rectangular regions are clickable.
* Non-rectangular regions are ignored.
*
* @param geom1 first region.
* @param geom2 second region.
*/
const sortRectangularRegion = (geom1: GeometryForRegion, geom2: GeometryForRegion) => {

if (geom1.geometry.type === 'rectangle' && geom2.geometry.type === 'rectangle') {

const surf1 = StillImageComponent.surfaceOfRectangularRegion(geom1.geometry);
const surf2 = StillImageComponent.surfaceOfRectangularRegion(geom2.geometry);

// if reg1 is smaller than reg2, return 1
// reg1 then comes after reg2 and thus is rendered later
if (surf1 < surf2) {
return 1;
} else {
return -1;
}

} else {
return 0;
}

};

this._removeOverlays();

let imageXOffset = 0; // see documentation in this.openImages() for the usage of imageXOffset

for (const image of this.images) {

const stillImage = image.fileValue as ReadStillImageFileValue;
const aspectRatio = (stillImage.dimY / stillImage.dimX);

// collect all geometries belonging to this page
const geometries: GeometryForRegion[] = [];
image.annotations.map((reg) => {

this._regions[reg.regionResource.id] = [];
const geoms = reg.getGeometries();

geoms.map((geom) => {
const geomForReg = new GeometryForRegion(geom.geometry, reg.regionResource);

geometries.push(geomForReg);
});
});

// sort all geometries belonging to this page
geometries.sort(sortRectangularRegion);

// render all geometries for this page
for (const geom of geometries) {

const geometry = geom.geometry;
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label);

}

imageXOffset++;
}

}

/**
* creates and adds a ROI-overlay to the viewer
* @param regionIri the Iri of the region.
@@ -5,12 +5,13 @@
<div class="representation-container center" *ngIf="representationsToDisplay.length"
[ngSwitch]="representationsToDisplay[0].fileValue.type">
<!-- still image view -->
<app-still-image class="dsp-representation" *ngSwitchCase="representationConstants.stillImage"
<app-still-image #stillImage class="dsp-representation" *ngSwitchCase="representationConstants.stillImage"
[images]="representationsToDisplay"
[imageCaption]="(incomingResource ? resource.res.label + ': ' + incomingResource.res.label : resource.res.label)"
[compoundNavigation]="compoundPosition"
[resourceIri]="incomingResource ? incomingResource.res.id : resource.res.id"
[project]="resource.res.attachedToProject"
[currentTab]="selectedTabLabel"
(goToPage)="compoundNavigation($event)"
(regionClicked)="openRegion($event)"
(regionAdded)="updateRegions($event)">
@@ -30,7 +31,7 @@
</div>

<!-- tabs -->
<mat-tab-group animationDuration="0ms" [(selectedIndex)]="selectedTab">
<mat-tab-group animationDuration="0ms" [(selectedIndex)]="selectedTab" (selectedTabChange)="tabChanged($event)">
<!-- first tab for the main resource e.g. book -->
<mat-tab [label]="resource.res.entityInfo?.classes[resource.res.type].label">
<app-properties *ngIf="resource.res" [resource]="resource" [displayProjectInfo]="true"
@@ -47,7 +48,7 @@
</mat-tab>

<!-- annotations -->
<mat-tab
<mat-tab label="annotations"
*ngIf="representationsToDisplay.length && representationsToDisplay[0].fileValue.type === representationConstants.stillImage">
<ng-template matTabLabel class="annotations">
<span [matBadge]="representationsToDisplay[0]?.annotations.length"
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Component, Inject, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
import { Component, Inject, Input, OnChanges, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { Title } from '@angular/platform-browser';
import {
ActivatedRoute,
@@ -27,7 +28,7 @@ import { DspCompoundPosition, DspResource } from './dsp-resource';
import { IncomingService } from './incoming.service';
import { PropertyInfoValues } from './properties/properties.component';
import { FileRepresentation, RepresentationConstants } from './representation/file-representation';
import { Region } from './representation/still-image/still-image.component';
import { Region, StillImageComponent } from './representation/still-image/still-image.component';
import { ValueOperationEventService } from './services/value-operation-event.service';

@Component({
@@ -38,6 +39,8 @@ import { ValueOperationEventService } from './services/value-operation-event.ser
})
export class ResourceComponent implements OnInit, OnChanges, OnDestroy {

@ViewChild('stillImage') stillImageComponent: StillImageComponent;

@Input() resourceIri: string;

// this will be the main resource
@@ -54,6 +57,8 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {

selectedTab = 0;

selectedTabLabel: string;

// list of representations to be displayed
// --> TODO: will be expanded with | MovingImageRepresentation[] | AudioRepresentation[] etc.
representationsToDisplay: FileRepresentation[] = [];
@@ -197,6 +202,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
const res = new DspResource(response);

this.resource = res;
this.selectedTabLabel = this.resource.res.entityInfo?.classes[this.resource.res.type].label;

// get information about the logged-in user, if one is logged-in
if (this._session.getSession()) {
@@ -270,6 +276,17 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
}
);
}

tabChanged(e: MatTabChangeEvent) {
if (e.tab.textLabel === 'annotations') {
this.stillImageComponent.renderRegions();
} else {
this.stillImageComponent.removeOverlays();
}

this.selectedTabLabel = e.tab.textLabel;
}

/**
* gather resource property information
*/

0 comments on commit bddc2f1

Please sign in to comment.