Skip to content

Commit bddc2f1

Browse files
authored
refactor: annotations will now only be drawn when the user is on the annotations tab (#574)
1 parent c39ca5b commit bddc2f1

4 files changed

Lines changed: 120 additions & 103 deletions

File tree

‎src/app/workspace/resource/representation/still-image/still-image.component.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ function makeRegion(geomString: string[], iri: string): ReadResource {
7575
<app-still-image [images]="stillImageFileRepresentations"
7676
[imageCaption]="caption"
7777
[activateRegion]="inputActivateRegion"
78+
[currentTab]="'annotations'"
7879
(regionClicked)="regHovered($event)">
7980
</app-still-image>`
8081
})

‎src/app/workspace/resource/representation/still-image/still-image.component.ts‎

Lines changed: 96 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,13 @@ export class StillImageComponent implements OnChanges, OnDestroy {
101101
@Input() resourceIri: string;
102102
@Input() project: string;
103103
@Input() activateRegion?: string; // highlight a region
104-
105104
@Input() compoundNavigation?: DspCompoundPosition;
105+
@Input() currentTab: string;
106106

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

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

111-
112111
@Output() regionAdded = new EventEmitter<string>();
113112

114113
regionDrawMode: Boolean = false; // stores whether viewer is currently drawing a region
@@ -150,13 +149,15 @@ export class StillImageComponent implements OnChanges, OnDestroy {
150149
}
151150
if (changes['images']) {
152151
this._openImages();
153-
this._renderRegions();
154152
this._unhighlightAllRegions();
155153
// tODO: check if this is necessary or could be handled below
156154
// (remove the 'else' before the 'if', so changes['activateRegion'] is always checked for)
157155
if (this.activateRegion !== undefined) {
158156
this._highlightRegion(this.activateRegion);
159157
}
158+
if (this.currentTab === 'annotations') {
159+
this.renderRegions();
160+
}
160161
} else if (changes['activateRegion']) {
161162
this._unhighlightAllRegions();
162163
if (this.activateRegion !== undefined) {
@@ -193,7 +194,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
193194
if (!this._viewer) {
194195
this._setupViewer();
195196
}
196-
this._renderRegions();
197+
this.renderRegions();
197198
}
198199

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

210+
/**
211+
* adds a ROI-overlay to the viewer for every region of every image in this.images
212+
*/
213+
renderRegions(): void {
214+
/**
215+
* sorts rectangular regions by surface, so all rectangular regions are clickable.
216+
* Non-rectangular regions are ignored.
217+
*
218+
* @param geom1 first region.
219+
* @param geom2 second region.
220+
*/
221+
const sortRectangularRegion = (geom1: GeometryForRegion, geom2: GeometryForRegion) => {
222+
223+
if (geom1.geometry.type === 'rectangle' && geom2.geometry.type === 'rectangle') {
224+
225+
const surf1 = StillImageComponent.surfaceOfRectangularRegion(geom1.geometry);
226+
const surf2 = StillImageComponent.surfaceOfRectangularRegion(geom2.geometry);
227+
228+
// if reg1 is smaller than reg2, return 1
229+
// reg1 then comes after reg2 and thus is rendered later
230+
if (surf1 < surf2) {
231+
return 1;
232+
} else {
233+
return -1;
234+
}
235+
236+
} else {
237+
return 0;
238+
}
239+
240+
};
241+
242+
this.removeOverlays();
243+
244+
let imageXOffset = 0; // see documentation in this.openImages() for the usage of imageXOffset
245+
246+
for (const image of this.images) {
247+
248+
const stillImage = image.fileValue as ReadStillImageFileValue;
249+
const aspectRatio = (stillImage.dimY / stillImage.dimX);
250+
251+
// collect all geometries belonging to this page
252+
const geometries: GeometryForRegion[] = [];
253+
image.annotations.map((reg) => {
254+
255+
this._regions[reg.regionResource.id] = [];
256+
const geoms = reg.getGeometries();
257+
258+
geoms.map((geom) => {
259+
const geomForReg = new GeometryForRegion(geom.geometry, reg.regionResource);
260+
261+
geometries.push(geomForReg);
262+
});
263+
});
264+
265+
// sort all geometries belonging to this page
266+
geometries.sort(sortRectangularRegion);
267+
268+
// render all geometries for this page
269+
for (const geom of geometries) {
270+
271+
const geometry = geom.geometry;
272+
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label);
273+
274+
}
275+
276+
imageXOffset++;
277+
}
278+
279+
}
280+
281+
/**
282+
* removes SVG overlays from the DOM.
283+
*/
284+
removeOverlays() {
285+
for (const reg in this._regions) {
286+
if (this._regions.hasOwnProperty(reg)) {
287+
for (const pol of this._regions[reg]) {
288+
if (pol instanceof HTMLDivElement) {
289+
pol.remove();
290+
}
291+
}
292+
}
293+
}
294+
295+
this._regions = {};
296+
297+
this._viewer.clearOverlays();
298+
}
299+
209300
/**
210301
* 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
211302
* @param startPoint the start point of the drawing
@@ -365,27 +456,6 @@ export class StillImageComponent implements OnChanges, OnDestroy {
365456
}
366457
}
367458

368-
/**
369-
* removes SVG overlays from the DOM.
370-
*/
371-
private _removeOverlays() {
372-
373-
for (const reg in this._regions) {
374-
if (this._regions.hasOwnProperty(reg)) {
375-
for (const pol of this._regions[reg]) {
376-
if (pol instanceof HTMLDivElement) {
377-
pol.remove();
378-
}
379-
}
380-
}
381-
}
382-
383-
this._regions = {};
384-
385-
// tODO: make this work by using osdviewer's addOverlay method
386-
this._viewer.clearOverlays();
387-
}
388-
389459
/**
390460
* initializes the OpenSeadragon _viewer
391461
*/
@@ -442,7 +512,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
442512
// display only the defined range of this.images
443513
const tileSources: object[] = this._prepareTileSourcesFromFileValues(fileValues);
444514

445-
this._removeOverlays();
515+
this.removeOverlays();
446516
this._viewer.open(tileSources);
447517

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

495-
/**
496-
* adds a ROI-overlay to the viewer for every region of every image in this.images
497-
*/
498-
private _renderRegions(): void {
499-
500-
/**
501-
* sorts rectangular regions by surface, so all rectangular regions are clickable.
502-
* Non-rectangular regions are ignored.
503-
*
504-
* @param geom1 first region.
505-
* @param geom2 second region.
506-
*/
507-
const sortRectangularRegion = (geom1: GeometryForRegion, geom2: GeometryForRegion) => {
508-
509-
if (geom1.geometry.type === 'rectangle' && geom2.geometry.type === 'rectangle') {
510-
511-
const surf1 = StillImageComponent.surfaceOfRectangularRegion(geom1.geometry);
512-
const surf2 = StillImageComponent.surfaceOfRectangularRegion(geom2.geometry);
513-
514-
// if reg1 is smaller than reg2, return 1
515-
// reg1 then comes after reg2 and thus is rendered later
516-
if (surf1 < surf2) {
517-
return 1;
518-
} else {
519-
return -1;
520-
}
521-
522-
} else {
523-
return 0;
524-
}
525-
526-
};
527-
528-
this._removeOverlays();
529-
530-
let imageXOffset = 0; // see documentation in this.openImages() for the usage of imageXOffset
531-
532-
for (const image of this.images) {
533-
534-
const stillImage = image.fileValue as ReadStillImageFileValue;
535-
const aspectRatio = (stillImage.dimY / stillImage.dimX);
536-
537-
// collect all geometries belonging to this page
538-
const geometries: GeometryForRegion[] = [];
539-
image.annotations.map((reg) => {
540-
541-
this._regions[reg.regionResource.id] = [];
542-
const geoms = reg.getGeometries();
543-
544-
geoms.map((geom) => {
545-
const geomForReg = new GeometryForRegion(geom.geometry, reg.regionResource);
546-
547-
geometries.push(geomForReg);
548-
});
549-
});
550-
551-
// sort all geometries belonging to this page
552-
geometries.sort(sortRectangularRegion);
553-
554-
// render all geometries for this page
555-
for (const geom of geometries) {
556-
557-
const geometry = geom.geometry;
558-
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label);
559-
560-
}
561-
562-
imageXOffset++;
563-
}
564-
565-
}
566-
567565
/**
568566
* creates and adds a ROI-overlay to the viewer
569567
* @param regionIri the Iri of the region.

‎src/app/workspace/resource/resource.component.html‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
<div class="representation-container center" *ngIf="representationsToDisplay.length"
66
[ngSwitch]="representationsToDisplay[0].fileValue.type">
77
<!-- still image view -->
8-
<app-still-image class="dsp-representation" *ngSwitchCase="representationConstants.stillImage"
8+
<app-still-image #stillImage class="dsp-representation" *ngSwitchCase="representationConstants.stillImage"
99
[images]="representationsToDisplay"
1010
[imageCaption]="(incomingResource ? resource.res.label + ': ' + incomingResource.res.label : resource.res.label)"
1111
[compoundNavigation]="compoundPosition"
1212
[resourceIri]="incomingResource ? incomingResource.res.id : resource.res.id"
1313
[project]="resource.res.attachedToProject"
14+
[currentTab]="selectedTabLabel"
1415
(goToPage)="compoundNavigation($event)"
1516
(regionClicked)="openRegion($event)"
1617
(regionAdded)="updateRegions($event)">
@@ -30,7 +31,7 @@
3031
</div>
3132

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

4950
<!-- annotations -->
50-
<mat-tab
51+
<mat-tab label="annotations"
5152
*ngIf="representationsToDisplay.length && representationsToDisplay[0].fileValue.type === representationConstants.stillImage">
5253
<ng-template matTabLabel class="annotations">
5354
<span [matBadge]="representationsToDisplay[0]?.annotations.length"

‎src/app/workspace/resource/resource.component.ts‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @typescript-eslint/member-ordering */
2-
import { Component, Inject, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
2+
import { Component, Inject, Input, OnChanges, OnDestroy, OnInit, ViewChild } from '@angular/core';
3+
import { MatTabChangeEvent } from '@angular/material/tabs';
34
import { Title } from '@angular/platform-browser';
45
import {
56
ActivatedRoute,
@@ -27,7 +28,7 @@ import { DspCompoundPosition, DspResource } from './dsp-resource';
2728
import { IncomingService } from './incoming.service';
2829
import { PropertyInfoValues } from './properties/properties.component';
2930
import { FileRepresentation, RepresentationConstants } from './representation/file-representation';
30-
import { Region } from './representation/still-image/still-image.component';
31+
import { Region, StillImageComponent } from './representation/still-image/still-image.component';
3132
import { ValueOperationEventService } from './services/value-operation-event.service';
3233

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

42+
@ViewChild('stillImage') stillImageComponent: StillImageComponent;
43+
4144
@Input() resourceIri: string;
4245

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

5558
selectedTab = 0;
5659

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

199204
this.resource = res;
205+
this.selectedTabLabel = this.resource.res.entityInfo?.classes[this.resource.res.type].label;
200206

201207
// get information about the logged-in user, if one is logged-in
202208
if (this._session.getSession()) {
@@ -270,6 +276,17 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
270276
}
271277
);
272278
}
279+
280+
tabChanged(e: MatTabChangeEvent) {
281+
if (e.tab.textLabel === 'annotations') {
282+
this.stillImageComponent.renderRegions();
283+
} else {
284+
this.stillImageComponent.removeOverlays();
285+
}
286+
287+
this.selectedTabLabel = e.tab.textLabel;
288+
}
289+
273290
/**
274291
* gather resource property information
275292
*/

0 commit comments

Comments
 (0)