Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: only show image regions when on the annotations tab (DEV-146) #574

Merged
merged 1 commit into from Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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>`
})
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -193,7 +194,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
if (!this._viewer) {
this._setupViewer();
}
this._renderRegions();
this.renderRegions();
}

/**
Expand All @@ -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
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);

}
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions src/app/workspace/resource/resource.component.html
Expand Up @@ -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)">
Expand All @@ -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"
Expand All @@ -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"
Expand Down
21 changes: 19 additions & 2 deletions src/app/workspace/resource/resource.component.ts
@@ -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,
Expand Down Expand Up @@ -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({
Expand All @@ -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
Expand All @@ -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[] = [];
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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
*/
Expand Down