You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/04/07 19:36:22 UTC

[incubator-streampipes] branch dev updated: [STREAMPIPES-528] Improve support for images in data explorer

This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new d26d88ecd [STREAMPIPES-528] Improve support for images in data explorer
d26d88ecd is described below

commit d26d88ecd79c92cecf0ed8772c958850fe722904
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Thu Apr 7 21:36:13 2022 +0200

    [STREAMPIPES-528] Improve support for images in data explorer
---
 ui/src/app/core-ui/core-ui.module.ts               | 212 ++++++++--------
 .../image-bar-preview.component.html}              |   2 +
 .../image-bar-preview.component.scss}              |  12 +-
 .../image-bar-preview.component.ts}                |  38 +--
 .../components/image-bar/image-bar.component.css   |   7 -
 .../components/image-bar/image-bar.component.html  |  58 +++--
 .../image-container/image-container.component.html |   2 +-
 .../image-container/image-container.component.ts   | 278 ++-------------------
 .../image-labeling/image-labeling.component.ts     |   4 +-
 .../image/image-viewer/image-viewer.component.html |   2 +-
 .../image/image-viewer/image-viewer.component.ts   |  11 +-
 ...-explorer-visualisation-settings.component.html |   5 +
 .../config/image-widget-config.component.html      |  10 +
 .../image/config/image-widget-config.component.ts  |  13 +-
 .../widgets/image/image-widget.component.html      |  21 +-
 .../widgets/image/image-widget.component.ts        |  78 +++---
 16 files changed, 284 insertions(+), 469 deletions(-)

diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts
index 94f3a00ba..aed2bfa03 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -84,114 +84,114 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
 import { StaticRuntimeResolvableTreeInputComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input.component';
 import { MatTreeModule } from '@angular/material/tree';
 import { PlatformServicesModule } from '@streampipes/platform-services';
-import { BarchartWidgetComponent } from '../pipeline-details/components/monitoring/widget/barchart/barchart-widget.component';
-import { StatusWidgetComponent } from '../pipeline-details/components/monitoring/widget/status/status-widget.component';
+import { ImageBarPreviewComponent } from './image/components/image-bar/image-bar-preview/image-bar-preview.component';
 
 @NgModule({
-    imports: [
-        CommonModule,
-        ColorPickerModule,
-        FlexLayoutModule,
-        CodemirrorModule,
-        CustomMaterialModule,
-        ReactiveFormsModule,
-        FormsModule,
-        CdkTableModule,
-        MatAutocompleteModule,
-        MatSnackBarModule,
-        MatProgressSpinnerModule,
-        MatDatepickerModule,
-        MatNativeDateModule,
-        NgxChartsModule,
-        PlotlyViaWindowModule,
-        MatSliderModule,
-        MatSlideToggleModule,
-        MatChipsModule,
-        MatTreeModule,
-        PlatformServicesModule,
-        PortalModule,
-        OverlayModule,
-        QuillModule.forRoot(),
-        MatTreeModule
-    ],
-    declarations: [
-        ConfigureLabelsComponent,
-        ConfirmDialogComponent,
-        DisplayRecommendedPipe,
-        ImageComponent,
-        ImageContainerComponent,
-        ImageLabelingComponent,
-        SelectLabelComponent,
-        ImageBarComponent,
-        ImageAnnotationsComponent,
-        ImageViewerComponent,
-        ObjectPermissionDialogComponent,
-        StandardDialogComponent,
-        PanelDialogComponent,
-        SplitSectionComponent,
-        StaticAnyInput,
-        StaticPropertyComponent,
-        StaticFreeInputComponent,
-        StaticSecretInputComponent,
-        StaticFileInputComponent,
-        StaticMappingNaryComponent,
-        StaticMappingUnaryComponent,
-        StaticGroupComponent,
-        StaticAlternativesComponent,
-        StaticCollectionComponent,
-        StaticColorPickerComponent,
-        StaticCodeInputComponent,
-        StaticOneOfInputComponent,
-        StaticRuntimeResolvableAnyInputComponent,
-        StaticRuntimeResolvableOneOfInputComponent,
-        StaticRuntimeResolvableTreeInputComponent,
-        StaticSlideToggleComponent,
-        LabelListItemComponent,
-        ErrorHintComponent,
-        AddToCollectionComponent,
-        PipelineStartedStatusComponent,
-    ],
-    providers: [
-        MatDatepickerModule,
-        ColorService,
-        DisplayRecommendedPipe,
-        ReactLabelingService,
-        PolygonLabelingService,
-        BrushLabelingService,
-        CocoFormatService,
-        LabelingModeService,
-        DialogService,
-        RuntimeResolvableService,
-    ],
-    exports: [
-        ConfigureLabelsComponent,
-        ImageComponent,
-        ImageLabelingComponent,
-        SelectLabelComponent,
-        StandardDialogComponent,
-        PanelDialogComponent,
-        ConfirmDialogComponent,
-        StaticAnyInput,
-        StaticPropertyComponent,
-        StaticFreeInputComponent,
-        StaticSecretInputComponent,
-        StaticFileInputComponent,
-        StaticMappingNaryComponent,
-        StaticMappingUnaryComponent,
-        StaticGroupComponent,
-        StaticAlternativesComponent,
-        StaticCollectionComponent,
-        StaticColorPickerComponent,
-        StaticCodeInputComponent,
-        StaticOneOfInputComponent,
-        StaticRuntimeResolvableAnyInputComponent,
-        StaticRuntimeResolvableOneOfInputComponent,
-        StaticSlideToggleComponent,
-        ImageViewerComponent,
-        ErrorHintComponent,
-        PipelineStartedStatusComponent,
-        SplitSectionComponent,
-    ]
+  imports: [
+    CommonModule,
+    ColorPickerModule,
+    FlexLayoutModule,
+    CodemirrorModule,
+    CustomMaterialModule,
+    ReactiveFormsModule,
+    FormsModule,
+    CdkTableModule,
+    MatAutocompleteModule,
+    MatSnackBarModule,
+    MatProgressSpinnerModule,
+    MatDatepickerModule,
+    MatNativeDateModule,
+    NgxChartsModule,
+    PlotlyViaWindowModule,
+    MatSliderModule,
+    MatSlideToggleModule,
+    MatChipsModule,
+    MatTreeModule,
+    PlatformServicesModule,
+    PortalModule,
+    OverlayModule,
+    QuillModule.forRoot(),
+    MatTreeModule
+  ],
+  declarations: [
+    ConfigureLabelsComponent,
+    ConfirmDialogComponent,
+    DisplayRecommendedPipe,
+    ImageBarPreviewComponent,
+    ImageComponent,
+    ImageContainerComponent,
+    ImageLabelingComponent,
+    SelectLabelComponent,
+    ImageBarComponent,
+    ImageAnnotationsComponent,
+    ImageViewerComponent,
+    ObjectPermissionDialogComponent,
+    StandardDialogComponent,
+    PanelDialogComponent,
+    SplitSectionComponent,
+    StaticAnyInput,
+    StaticPropertyComponent,
+    StaticFreeInputComponent,
+    StaticSecretInputComponent,
+    StaticFileInputComponent,
+    StaticMappingNaryComponent,
+    StaticMappingUnaryComponent,
+    StaticGroupComponent,
+    StaticAlternativesComponent,
+    StaticCollectionComponent,
+    StaticColorPickerComponent,
+    StaticCodeInputComponent,
+    StaticOneOfInputComponent,
+    StaticRuntimeResolvableAnyInputComponent,
+    StaticRuntimeResolvableOneOfInputComponent,
+    StaticRuntimeResolvableTreeInputComponent,
+    StaticSlideToggleComponent,
+    LabelListItemComponent,
+    ErrorHintComponent,
+    AddToCollectionComponent,
+    PipelineStartedStatusComponent,
+  ],
+  providers: [
+    MatDatepickerModule,
+    ColorService,
+    DisplayRecommendedPipe,
+    ReactLabelingService,
+    PolygonLabelingService,
+    BrushLabelingService,
+    CocoFormatService,
+    LabelingModeService,
+    DialogService,
+    RuntimeResolvableService,
+  ],
+  exports: [
+    ConfigureLabelsComponent,
+    ImageComponent,
+    ImageLabelingComponent,
+    SelectLabelComponent,
+    StandardDialogComponent,
+    PanelDialogComponent,
+    ConfirmDialogComponent,
+    StaticAnyInput,
+    StaticPropertyComponent,
+    StaticFreeInputComponent,
+    StaticSecretInputComponent,
+    StaticFileInputComponent,
+    StaticMappingNaryComponent,
+    StaticMappingUnaryComponent,
+    StaticGroupComponent,
+    StaticAlternativesComponent,
+    StaticCollectionComponent,
+    StaticColorPickerComponent,
+    StaticCodeInputComponent,
+    StaticOneOfInputComponent,
+    StaticRuntimeResolvableAnyInputComponent,
+    StaticRuntimeResolvableOneOfInputComponent,
+    StaticSlideToggleComponent,
+    ImageViewerComponent,
+    ErrorHintComponent,
+    PipelineStartedStatusComponent,
+    SplitSectionComponent,
+  ]
 })
 export class CoreUiModule {
 }
diff --git a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.html
similarity index 85%
copy from ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html
copy to ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.html
index fb99b649e..0854d62a9 100644
--- a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.html
@@ -15,3 +15,5 @@
   ~ limitations under the License.
   ~
   -->
+
+<img [ngClass]="focus ? 'imagePreview imageFocus' : 'imagePreview'" [style.height.px]="imagePreviewHeight" *ngIf="showImage" [src]="imagePath">
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.scss
similarity index 88%
copy from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
copy to ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.scss
index 54647dc83..aba112fec 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.scss
@@ -16,15 +16,13 @@
  *
  */
 
-.imageBar {
-    height: 70px;
-    /*width: 640px;*/
-}
-
 .imagePreview {
-    /*height: 65px*/
+  height: 65px;
+  margin-left: 10px;
+  margin-right: 10px;
 }
 
 .imageFocus {
-    border: 5px solid #39b54a
+  border: 3px solid #39b54a
 }
+
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.ts
similarity index 55%
copy from ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
copy to ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.ts
index 21bebcac6..b49e6d18d 100644
--- a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.ts
@@ -6,7 +6,7 @@
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,33 +15,35 @@
  * limitations under the License.
  */
 
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
+import { SafeUrl } from '@angular/platform-browser';
+import { Observable } from 'rxjs';
 
 @Component({
-  selector: 'sp-image-viewer',
-  templateUrl: './image-viewer.component.html',
-  styleUrls: ['./image-viewer.component.css']
+  selector: 'sp-image-bar-preview',
+  templateUrl: './image-bar-preview.component.html',
+  styleUrls: ['./image-bar-preview.component.scss']
 })
-export class ImageViewerComponent {
+export class ImageBarPreviewComponent implements OnInit {
 
   @Input()
-  public imagesRoutes;
+  imagePreviewHeight: number;
 
   @Input()
-  public canvasHeight = 500;
+  focus = false;
 
-  @Input()
-  public canvasWidth = 800;
+  imagePath: SafeUrl;
+  showImage = false;
 
   @Input()
-  public imagePreviewHeight = 65;
-
-
-  public imagesIndex = 0;
-
-  constructor() {}
+  set imageSrc(src: Observable<SafeUrl>) {
+    src.subscribe(url => {
+      this.imagePath = url;
+      this.showImage = true;
+    });
+  }
 
-  handleImageIndexChange(index) {
-    this.imagesIndex = index;
+  ngOnInit(): void {
   }
+
 }
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
index 54647dc83..7c49dc0e7 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
@@ -21,10 +21,3 @@
     /*width: 640px;*/
 }
 
-.imagePreview {
-    /*height: 65px*/
-}
-
-.imageFocus {
-    border: 5px solid #39b54a
-}
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.html b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.html
index 911e62a68..271f70a59 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.html
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.html
@@ -22,25 +22,45 @@
     <button mat-icon-button (click)="goToStart()"> <mat-icon>skip_previous</mat-icon></button>
     <button mat-icon-button (click)="nextImage()"> <mat-icon>keyboard_arrow_left</mat-icon></button>
 
-<!--    <div fxLayout="row" fxLayoutAlign="center center"  class="imageBar">-->
-<!--        <div fxFlex="40" fxLayoutAlign="end center">-->
-<!--            <img class="imagePreview" [style.height.px]="imagePreviewHeight" *ngIf="selectedIndex >= 2" src="{{this.restService.getImageUrl(_imageRoutes[selectedIndex - 2])}}" (click)="changeImage(selectedIndex - 2)">-->
-<!--            <img class="imagePreview" [style.height.px]="imagePreviewHeight" *ngIf="selectedIndex >= 1" src="{{this.restService.getImageUrl(_imageRoutes[selectedIndex - 1])}}" (click)="changeImage(selectedIndex - 1)">-->
-<!--        </div>-->
-
-<!--        <div fxFlex="20">-->
-<!--            <div fxLayout="row" fxLayoutAlign="center">-->
-<!--            <img class="imagePreview imageFocus" [style.height.px]="imagePreviewHeight" src="{{this.restService.getImageUrl(_imageRoutes[selectedIndex])}}" (click)="changeImage(selectedIndex)">-->
-<!--            </div>-->
-<!--        </div>-->
-
-<!--        <div fxFlex="40">-->
-<!--            <div fxLayout="row" fxLayoutAlign="start center">-->
-<!--                <img class="imagePreview" [style.height.px]="imagePreviewHeight" *ngIf="selectedIndex < maxImages - 1" src="{{this.restService.getImageUrl(_imageRoutes[selectedIndex + 1])}}" (click)="changeImage(selectedIndex + 1)">-->
-<!--                <img class="imagePreview" [style.height.px]="imagePreviewHeight" *ngIf="selectedIndex < maxImages - 2" src="{{this.restService.getImageUrl(_imageRoutes[selectedIndex + 2])}}" (click)="changeImage(selectedIndex + 2)">-->
-<!--            </div>-->
-<!--        </div>-->
-<!--    </div>-->
+    <div fxLayout="row" fxLayoutAlign="center center"  class="imageBar">
+        <div fxFlex="40" fxLayoutAlign="end center">
+            <sp-image-bar-preview
+                    [imagePreviewHeight]="imagePreviewHeight"
+                    [imageSrc]="_imageRoutes[selectedIndex - 2]"
+                    *ngIf="selectedIndex >= 2"
+                    (click)="changeImage(selectedIndex - 2)"></sp-image-bar-preview>
+            <sp-image-bar-preview
+                    [imagePreviewHeight]="imagePreviewHeight"
+                    [imageSrc]="_imageRoutes[selectedIndex - 1]"
+                    *ngIf="selectedIndex >= 1"
+                    (click)="changeImage(selectedIndex - 1)"></sp-image-bar-preview>
+        </div>
+
+        <div fxFlex="20">
+            <div fxLayout="row" fxLayoutAlign="center">
+                <sp-image-bar-preview
+                        [imagePreviewHeight]="imagePreviewHeight"
+                        [imageSrc]="_imageRoutes[selectedIndex]"
+                        [focus]="true"
+                        (click)="changeImage(selectedIndex)"></sp-image-bar-preview>
+             </div>
+        </div>
+
+        <div fxFlex="40">
+            <div fxLayout="row" fxLayoutAlign="start center">
+                <sp-image-bar-preview
+                        [imagePreviewHeight]="imagePreviewHeight"
+                        [imageSrc]="_imageRoutes[selectedIndex + 1]"
+                        *ngIf="selectedIndex < maxImages - 1"
+                        (click)="changeImage(selectedIndex + 1)"></sp-image-bar-preview>
+                <sp-image-bar-preview
+                        [imagePreviewHeight]="imagePreviewHeight"
+                        [imageSrc]="_imageRoutes[selectedIndex + 2]"
+                        *ngIf="selectedIndex < maxImages - 2"
+                        (click)="changeImage(selectedIndex + 2)"></sp-image-bar-preview>
+              </div>
+        </div>
+    </div>
 
 
     <button mat-icon-button (click)="previousImage()"> <mat-icon>keyboard_arrow_right</mat-icon></button>
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.html b/ui/src/app/core-ui/image/components/image-container/image-container.component.html
index 0d9541524..5c8a1284f 100644
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.html
+++ b/ui/src/app/core-ui/image/components/image-container/image-container.component.html
@@ -17,5 +17,5 @@
   -->
 <div class="canvas-container">
     <mat-spinner *ngIf="isDrawingVar" [diameter]="40" style="position: fixed"></mat-spinner>
-    <div id="canvas-container"  (dblclick)="dblclick($event)"></div>
+    <img [src]="imagePath" #mainImg *ngIf="showImage" [width]="canvasWidth" (load)="onImageLoaded()"/>
 </div>
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts b/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
index 6bbb8d50f..68f2c094b 100644
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
+++ b/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
@@ -15,290 +15,52 @@
  * limitations under the License.
  */
 
-import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
-import Konva from 'konva';
-import { ICoordinates } from '../../model/coordinates';
+import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { SafeUrl } from '@angular/platform-browser';
+import { Observable } from 'rxjs';
 
 @Component({
   selector: 'sp-image-container',
   templateUrl: './image-container.component.html',
   styleUrls: ['./image-container.component.css']
 })
-export class ImageContainerComponent implements OnInit, AfterViewInit {
+export class ImageContainerComponent implements OnInit {
+
+  imagePath: SafeUrl;
+  showImage = false;
 
   @Input()
-  set imageSrc(src) {
-    this.loadImage(src);
+  set imageSrc(src: Observable<Blob>) {
+    src.subscribe(url => {
+      this.imagePath = url;
+      this.showImage = true;
+    });
   }
 
+  @ViewChild('mainImg') imageRef: ElementRef;
+
   @Input()
   public canvasHeight = 500;
 
   @Input()
   public canvasWidth = 800;
 
-  @Output()
-  childRedraw: EventEmitter<[Konva.Layer, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates]>();
-  @Output()
-  mouseDownLeft: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  mouseMove: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  mouseMoveLeft: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  mouseUpLeft: EventEmitter<[Konva.Layer, Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  shortCut: EventEmitter<string> = new EventEmitter<string>();
-  @Output()
-  dbclick: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  mouseDownRight: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
-  @Output()
-  isDrawing: EventEmitter<boolean> = new EventEmitter<boolean>();
-
-
-  private image;
-
-  private mainCanvasStage: Konva.Stage;
-  private imageLayer: Konva.Layer;
-  private annotationLayer: Konva.Layer;
-  private drawLayer: Konva.Layer;
-
   private scale: number;
 
-  private imageShift: ICoordinates;
-  private lastImageTranslation: ICoordinates;
-  private lastImagePointerPosition: ICoordinates;
-
-  private isLeftMouseDown: boolean;
-  private isMiddleMouseDown: boolean;
-  private isRightMouseDown: boolean;
-
-  private isHoverComponent: boolean;
 
   public isDrawingVar: boolean;
 
-  constructor() { }
-
-  ngOnInit(): void {
-    this.scale = 1;
-    this.imageShift = {x: 0, y: 0};
-    this.isLeftMouseDown = false;
-    this.isMiddleMouseDown = false;
-    this.isRightMouseDown = false;
-    this.isHoverComponent = false;
-    this.isDrawingVar = false;
-  }
-
-  ngAfterViewInit(): void {
-    this.reset();
+  constructor() {
   }
 
-  reset() {
+  ngOnInit(): void {
     this.scale = 1;
-    this.imageShift = {x: 0, y: 0};
-    // TODO fit to parent
-    this.mainCanvasStage = new Konva.Stage({
-      container: 'canvas-container',
-      width: this.canvasWidth,
-      height: this.canvasHeight
-    });
-    this.registerEventHandler();
-    window.addEventListener('resize', this.fitStageIntoParentContainer);
-
-  }
-
-  fitStageIntoParentContainer() {
-    this.mainCanvasStage.width(500);
-    this.mainCanvasStage.height(500 * this.scale);
-    this.mainCanvasStage.scale({ x: this.scale, y: this.scale });
-    this.mainCanvasStage.draw();
-  }
-
-  loadImage(src) {
-    this.isDrawing.emit(true);
-    this.isDrawingVar = true;
-    this.reset();
-    this.image = new window.Image();
-
-    this.image.onload = () => {
-      this.scale = Math.min(1, this.mainCanvasStage.width() / this.image.width, this.mainCanvasStage.height() / this.image.height);
-      this.initLayers();
-      this.redrawAll();
-    };
-    // this.image.src = this.restService.getImageUrl(src);
-  }
-
-  getShift() {
-    if (this.imageLayer !== undefined) {
-      const position = this.imageLayer.getChildren().toArray()[0].getPosition();
-      return {x: position.x, y: position.y};
-    }
-  }
-  /* mouse handler */
-
-  imageMouseDown(e) {
-    const button = e.evt.which;
-    if (button === 1) {
-      // left click
-      this.isLeftMouseDown = true;
-      this.mouseDownLeft.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
-      this.drawLayer.batchDraw();
-    } else if (button === 2) {
-      // middle click
-      this.isMiddleMouseDown = true;
-      this.mainCanvasStage.container().style.cursor = 'move';
-      this.lastImagePointerPosition = this.getImagePointerPosition();
-      this.lastImageTranslation = this.imageShift;
-    } else if (button === 3) {
-      // right click
-      this.isRightMouseDown = true;
-      this.mouseDownRight.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
-    }
-  }
-
-  imageMouseMove(e) {
-    if (this.isLeftMouseDown) {
-      this.drawLayer.destroyChildren();
-      this.mouseMoveLeft.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
-      this.drawLayer.batchDraw();
-    } else if (this.isMiddleMouseDown) {
-      const imagePointerPosition = this.getImagePointerPosition();
-      this.imageShift.x = this.lastImageTranslation.x + (imagePointerPosition.x - this.lastImagePointerPosition.x);
-      this.imageShift.y = this.lastImageTranslation.y + (imagePointerPosition.y - this.lastImagePointerPosition.y);
-      this.lastImagePointerPosition = this.getImagePointerPosition();
-      this.lastImageTranslation = this.imageShift;
-      this.shiftViewContent();
-    } else {
-      if (this.drawLayer !== undefined) { this.drawLayer.destroyChildren(); }
-      this.mouseMove.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
-      if (this.drawLayer !== undefined) { this.drawLayer.destroyChildren(); }
-    }
   }
 
-  imageMouseUp(e) {
-    if (this.isLeftMouseDown) {
-      this.isLeftMouseDown = false;
-      this.drawLayer.destroyChildren();
-      this.mouseUpLeft.emit([this.annotationLayer, this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
-      this.drawLayer.batchDraw();
-      this.annotationLayer.batchDraw();
-    }
-    if (this.isMiddleMouseDown) {
-      this.isMiddleMouseDown = false;
-      this.mainCanvasStage.container().style.cursor = 'default';
-    }
+  onImageLoaded() {
+    const naturalImgWidth = (this.imageRef.nativeElement as HTMLImageElement).naturalWidth;
+    const naturalImageHeight = (this.imageRef.nativeElement as HTMLImageElement).naturalHeight;
+    this.scale = Math.min(1, this.canvasWidth / naturalImgWidth, this.canvasHeight / naturalImageHeight);
   }
 
-  dblclick (e) {
-    this.drawLayer.destroyChildren();
-    this.drawLayer.batchDraw();
-    this.dbclick.emit([this.annotationLayer, this.getShift(), this.getImagePointerPosition()]);
-    this.annotationLayer.batchDraw();
-  }
-
-  /* Draw */
-
-  redrawAll() {
-    this.isDrawing.emit(true);
-    this.isDrawingVar = true;
-
-    if (this.drawLayer !== undefined) {
-      this.drawLayer.destroyChildren();
-    }
-    if (this.annotationLayer !== undefined) {
-      this.annotationLayer.destroyChildren();
-    }
-    this.childRedraw.emit([this.annotationLayer, this.getShift()]);
-    this.shiftViewContent();
-    this.isDrawing.emit(false);
-    this.isDrawingVar = false;
-  }
-
-  shiftViewContent() {
-    const newWidth = this.mainCanvasStage.width() * this.scale;
-    const newHeight = this.mainCanvasStage.height() * this.scale;
-
-    this.mainCanvasStage.position({
-      x: -((newWidth - this.mainCanvasStage.width()) / 2) + this.imageShift.x,
-      y: -((newHeight - this.mainCanvasStage.height()) / 2) + this.imageShift.y
-    });
-    this.mainCanvasStage.scale({ x: this.scale, y: this.scale });
-
-    this.mainCanvasStage.batchDraw();
-  }
-
-  initLayers() {
-    this.imageLayer = new Konva.Layer();
-    const konvaImage = new Konva.Image({
-      image: this.image,
-      x: this.mainCanvasStage.width() / 2 - this.image.width / 2,
-      y: this.mainCanvasStage.height() / 2 - this.image.height / 2,
-    });
-    this.imageLayer.add(konvaImage);
-    this.imageLayer.clearBeforeDraw();
-
-    this.annotationLayer = new Konva.Layer();
-    this.drawLayer = new Konva.Layer();
-
-    this.mainCanvasStage.add(this.imageLayer);
-    this.mainCanvasStage.add(this.annotationLayer);
-    this.mainCanvasStage.add(this.drawLayer);
-  }
-
-  @HostListener('document:keydown', ['$event'])
-  handleShortCuts(e) {
-    const key = e.key;
-    this.shortCut.emit(key.toLowerCase());
-    if (this.isHoverComponent) {
-      switch (key.toLowerCase()) {
-          case 'w': this.imageShift.y -= 5; this.redrawAll();
-            break;
-          case 'a': this.imageShift.x -= 5; this.redrawAll();
-            break;
-          case 's': this.imageShift.y += 5; this.redrawAll();
-            break;
-          case 'd': this.imageShift.x += 5; this.redrawAll();
-            break;
-        }
-    }
-  }
-
-  getPointerPosition(): ICoordinates {
-    return this.mainCanvasStage.getPointerPosition();
-  }
-
-  getImagePointerPosition(): ICoordinates {
-    const x = Math.floor((this.getPointerPosition().x / this.scale) -
-      ((this.mainCanvasStage.width() / this.scale - this.image.width) / 2) - (this.imageShift.x / this.scale));
-    const y = Math.floor((this.getPointerPosition().y / this.scale) -
-      ((this.mainCanvasStage.height() / this.scale - this.image.height) / 2) - (this.imageShift.y / this.scale));
-    return {x, y};
-  }
-
-  getImagePositionFromPosition(posistion: ICoordinates): ICoordinates {
-    const x = Math.floor((posistion.x / this.scale) -
-      ((this.mainCanvasStage.width() / this.scale - this.image.width) / 2) - (this.imageShift.x / this.scale));
-    const y = Math.floor((posistion.y / this.scale) -
-      ((this.mainCanvasStage.height() / this.scale - this.image.height) / 2) - (this.imageShift.y / this.scale));
-    return {x, y};
-  }
-
-  registerEventHandler() {
-    this.mainCanvasStage.on('wheel', e => this.scroll(e));
-    this.mainCanvasStage.on('contextmenu', e => e.evt.preventDefault());
-    this.mainCanvasStage.on('mousedown', e => this.imageMouseDown(e));
-    this.mainCanvasStage.on('mousemove', e => this.imageMouseMove(e));
-    this.mainCanvasStage.on('mouseup', e => this.imageMouseUp(e));
-    this.mainCanvasStage.on('mouseover', e => this.isHoverComponent = true);
-    this.mainCanvasStage.on('mouseout', e => this.isHoverComponent = false);
-    this.mainCanvasStage.on('dblclick', e => this.dblclick(e));
-    this.mainCanvasStage.on('dbclick', e => this.dblclick(e));
-  }
-
-  scroll(e) {
-    e.evt.preventDefault();
-    this.scale += e.evt.wheelDeltaY * (1 / 6000);
-    this.redrawAll();
-  }
 }
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
index c0da32014..3b6ca8867 100644
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
+++ b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
@@ -228,7 +228,7 @@ export class ImageLabelingComponent implements OnInit {
       const categoryId = this.cocoFormatService.getLabelId(coco, change[0].category_id, change[1].name, change[1].name);
       change[0].category_id = categoryId;
       change[0].category_name = change[1].categoryId;
-      this.imageView.redrawAll();
+      //this.imageView.redrawAll();
     }
   }
 
@@ -237,7 +237,7 @@ export class ImageLabelingComponent implements OnInit {
       if (annotation !== undefined) {
         const coco = this.cocoFile;
         this.cocoFormatService.removeAnnotation(coco, annotation.id);
-        this.imageView.redrawAll();
+        //this.imageView.redrawAll();
       }
     }
   }
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
index 399b165b9..e9605a78b 100644
--- a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
+++ b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
@@ -16,7 +16,7 @@
   ~
   -->
 
-<div>
+<div style="width: 100%;height:100%;">
     <div fxLayout="column" fxLayoutAlign="space-between " >
         <div  fxLayout="row" fxLayoutAlign="space-around start">
             <div fxLayout="column" fxLayoutAlign="space-between " >
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
index 21bebcac6..1b462de0e 100644
--- a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
+++ b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
@@ -15,17 +15,19 @@
  * limitations under the License.
  */
 
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
+import { SafeUrl } from '@angular/platform-browser';
+import { Observable } from 'rxjs';
 
 @Component({
   selector: 'sp-image-viewer',
   templateUrl: './image-viewer.component.html',
   styleUrls: ['./image-viewer.component.css']
 })
-export class ImageViewerComponent {
+export class ImageViewerComponent implements OnInit {
 
   @Input()
-  public imagesRoutes;
+  public imagesRoutes: Observable<SafeUrl>[];
 
   @Input()
   public canvasHeight = 500;
@@ -44,4 +46,7 @@ export class ImageViewerComponent {
   handleImageIndexChange(index) {
     this.imagesIndex = index;
   }
+
+  ngOnInit(): void {
+  }
 }
diff --git a/ui/src/app/data-explorer/components/designer-panel/visualisation-settings/data-explorer-visualisation-settings.component.html b/ui/src/app/data-explorer/components/designer-panel/visualisation-settings/data-explorer-visualisation-settings.component.html
index b526c1368..f9209ab25 100644
--- a/ui/src/app/data-explorer/components/designer-panel/visualisation-settings/data-explorer-visualisation-settings.component.html
+++ b/ui/src/app/data-explorer/components/designer-panel/visualisation-settings/data-explorer-visualisation-settings.component.html
@@ -68,5 +68,10 @@
                     [currentlyConfiguredWidget]="currentlyConfiguredWidget">
             </sp-data-explorer-distribution-chart-widget-config>
         </div>
+        <div *ngIf="currentlyConfiguredWidget.widgetType === 'image'" class="h-100 p-0">
+            <sp-data-explorer-image-widget-config
+                    [currentlyConfiguredWidget]="currentlyConfiguredWidget">
+            </sp-data-explorer-image-widget-config>
+        </div>
     </div>
 </div>
diff --git a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html b/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html
index fb99b649e..1576c2b55 100644
--- a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html
+++ b/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.html
@@ -15,3 +15,13 @@
   ~ limitations under the License.
   ~
   -->
+
+<div fxFlex="100" fxLayout="column">
+
+    <h5>Image Field</h5>
+    <sp-select-property [availableProperties]="imageFields"
+                        [selectedProperty]="currentlyConfiguredWidget.visualizationConfig.selectedField"
+                        (changeSelectedProperty)="setSelectedImageProperty($event)">
+    </sp-select-property>
+
+</div>
diff --git a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.ts b/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.ts
index 3a5062cb5..19f55efaa 100644
--- a/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.ts
+++ b/ui/src/app/data-explorer/components/widgets/image/config/image-widget-config.component.ts
@@ -20,6 +20,7 @@ import { Component, OnInit } from '@angular/core';
 import { BaseWidgetConfig } from '../../base/base-widget-config';
 import { ImageWidgetModel, ImageWidgetVisConfig } from '../model/image-widget.model';
 import { WidgetType } from '../../../../registry/data-explorer-widgets';
+import { DataExplorerField } from "../../../../../../../dist/streampipes/platform-services";
 
 @Component({
   selector: 'sp-data-explorer-image-widget-config',
@@ -28,6 +29,9 @@ import { WidgetType } from '../../../../registry/data-explorer-widgets';
 })
 export class ImageWidgetConfigComponent extends BaseWidgetConfig<ImageWidgetModel, ImageWidgetVisConfig> implements OnInit {
 
+  imageSemanticType = 'https://image.com';
+  imageFields: DataExplorerField[];
+
   ngOnInit(): void {
   }
 
@@ -36,10 +40,17 @@ export class ImageWidgetConfigComponent extends BaseWidgetConfig<ImageWidgetMode
   }
 
   protected initWidgetConfig(): ImageWidgetVisConfig {
+    this.imageFields = this.fieldProvider.allFields
+      .filter(field => field.fieldCharacteristics.semanticTypes.find(st => st === this.imageSemanticType));
     return {
       forType: this.getWidgetType(),
-      selectedField: this.fieldProvider.allFields[0]
+      selectedField: this.imageFields[0]
     };
   }
 
+  setSelectedImageProperty(field: DataExplorerField) {
+    this.currentlyConfiguredWidget.visualizationConfig.selectedField = field;
+    this.triggerDataRefresh();
+  }
+
 }
diff --git a/ui/src/app/data-explorer/components/widgets/image/image-widget.component.html b/ui/src/app/data-explorer/components/widgets/image/image-widget.component.html
index f168e4d86..15772772b 100644
--- a/ui/src/app/data-explorer/components/widgets/image/image-widget.component.html
+++ b/ui/src/app/data-explorer/components/widgets/image/image-widget.component.html
@@ -17,23 +17,20 @@
   -->
 
 
-<div class="widget-content">
-    <div class="widget-inner-options ml-0 mr-0 mt-0">
-        <div fxFlex="100" fxLayout="row">
-            <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center" class="ml-0 mr-0">
-                <sp-select-properties [availableProperties]="availableColumns" [selectedProperties]="selectedColumn"></sp-select-properties>
-            </div>
-        </div>
-    </div>
+<div fxFlex="100" fxLayoutAlign="center center" fxLayout="column" class="main-panel">
+
+    <sp-load-data-spinner *ngIf="showIsLoadingData" class="h-100"></sp-load-data-spinner>
+
+    <sp-no-data-in-date-range *ngIf="showNoDataInDateRange" [viewDateRange]="timeSettings"></sp-no-data-in-date-range>
+
 
     <div class="widget-inner-content">
-        <sp-load-data-spinner *ngIf="showIsLoadingData"></sp-load-data-spinner>
-        <sp-no-data-in-date-range *ngIf="imagesRoutes.length === 0" [viewDateRange]="viewDateRange"></sp-no-data-in-date-range>
-        <sp-image-viewer *ngIf="imagesRoutes.length > 0"
-                         [imagesRoutes]="imagesRoutes"
+        <sp-image-viewer *ngIf="imagePaths.length > 0"
+                         [imagesRoutes]="imagePaths"
                          [canvasHeight]="canvasHeight"
                          [canvasWidth]="canvasWidth"
                          [imagePreviewHeight]="imagePreviewHeight"
+                         [ngStyle]="{width: canvasWidth + 'px', height: canvasHeight + 'px'}"
         ></sp-image-viewer>
     </div>
 </div>
diff --git a/ui/src/app/data-explorer/components/widgets/image/image-widget.component.ts b/ui/src/app/data-explorer/components/widgets/image/image-widget.component.ts
index eda1600dc..6b3f0ec44 100644
--- a/ui/src/app/data-explorer/components/widgets/image/image-widget.component.ts
+++ b/ui/src/app/data-explorer/components/widgets/image/image-widget.component.ts
@@ -23,11 +23,17 @@ import {
   DataExplorerField,
   DatalakeQueryParameterBuilder,
   DatalakeQueryParameters,
+  DatalakeRestService,
+  DataViewQueryGeneratorService,
   EventPropertyUnion,
-  EventSchema,
   SpQueryResult
 } from '@streampipes/platform-services';
 import { ImageWidgetModel } from './model/image-widget.model';
+import { WidgetConfigurationService } from '../../../services/widget-configuration.service';
+import { ResizeService } from '../../../services/resize.service';
+import { DataExplorerFieldProviderService } from '../../../services/data-explorer-field-provider-service';
+import { TimeSelectionService } from '../../../services/time-selection.service';
+import { SecurePipe } from '../../../../services/secure.pipe';
 
 @Component({
   selector: 'sp-data-explorer-image-widget',
@@ -36,7 +42,10 @@ import { ImageWidgetModel } from './model/image-widget.model';
 })
 export class ImageWidgetComponent extends BaseDataExplorerWidgetDirective<ImageWidgetModel> implements OnInit, OnDestroy {
 
-  @ViewChild(MatSort, { static: true }) sort: MatSort;
+  @ViewChild(MatSort, {static: true}) sort: MatSort;
+
+  imageBaseUrl: string;
+  imagePaths = [];
 
   availableColumns: EventPropertyUnion[];
   selectedColumn: EventPropertyUnion;
@@ -45,46 +54,33 @@ export class ImageWidgetComponent extends BaseDataExplorerWidgetDirective<ImageW
   canvasWidth;
   imagePreviewHeight;
 
-  public imagesRoutes = [];
-
-  ngOnInit(): void {
-    this.canvasHeight = this.gridsterItemComponent.height - 240;
-    this.canvasWidth = this.gridsterItemComponent.width - 20;
-    this.imagePreviewHeight = this.gridsterItemComponent.width / 14;
-
-    this.availableColumns = this.getImageProperties(this.dataExplorerWidget.dataConfig.sourceConfigs[0].measure.eventSchema);
-    this.selectedColumn = this.availableColumns[0];
-    this.updateData();
+  constructor(dataLakeRestService: DatalakeRestService,
+              widgetConfigurationService: WidgetConfigurationService,
+              resizeService: ResizeService,
+              dataViewQueryGeneratorService: DataViewQueryGeneratorService,
+              fieldService: DataExplorerFieldProviderService,
+              timeSelectionService: TimeSelectionService,
+              private securePipe: SecurePipe) {
+    super(
+      dataLakeRestService,
+      widgetConfigurationService,
+      resizeService,
+      dataViewQueryGeneratorService,
+      fieldService,
+      timeSelectionService
+    );
   }
 
-  getImageProperties(eventSchema: EventSchema): EventPropertyUnion[] {
-    return eventSchema.eventProperties.filter(ep => ep.domainProperties.some(dp => dp === 'https://image.com'));
+  ngOnInit(): void {
+    super.ngOnInit();
+    this.onResize(this.gridsterItemComponent.width, this.gridsterItemComponent.height - 40);
+    this.imageBaseUrl = this.dataLakeRestService.dataLakeUrl + '/images/';
   }
 
   ngOnDestroy(): void {
 
   }
 
-  refreshData() {
-    this.setShownComponents(false, false, true);
-
-    this.dataLakeRestService.getData(
-      this.dataExplorerWidget.dataConfig.sourceConfigs[0].measureName, this.buildQuery())
-      .subscribe(
-        (res: SpQueryResult) => {
-          // this.availableImageData = res;
-          this.showIsLoadingData = false;
-          this.imagesRoutes = [];
-          if (res.allDataSeries[0].rows !== null) {
-            const imageField = res.headers.findIndex(name => name === this.selectedColumn.runtimeName);
-            res.allDataSeries[0].rows.forEach(row => {
-              this.imagesRoutes.push(row[imageField]);
-            });
-          }
-        }
-      );
-  }
-
   refreshView() {
   }
 
@@ -93,12 +89,26 @@ export class ImageWidgetComponent extends BaseDataExplorerWidgetDirective<ImageW
   }
 
   onResize(width: number, height: number) {
+    this.canvasHeight = height - 50;
+    this.canvasWidth = width - 20;
+    this.imagePreviewHeight = width / 14;
   }
 
   beforeDataFetched() {
+    this.setShownComponents(false, false, true, false);
   }
 
   onDataReceived(spQueryResult: SpQueryResult[]) {
+    const selectedField = this.dataExplorerWidget.visualizationConfig.selectedField;
+    if (spQueryResult.length > 0) {
+      const qr = spQueryResult[selectedField.sourceIndex];
+      const columnIndex = qr.headers.indexOf(selectedField.runtimeName);
+      this.imagePaths = qr.allDataSeries[0].rows
+        .map(row => row[columnIndex])
+        .map(imageId => this.imageBaseUrl + imageId)
+        .map(imageRoute => this.securePipe.transform(imageRoute));
+    }
+    this.setShownComponents(false, true, false, false);
   }
 
   handleUpdatedFields(addedFields: DataExplorerField[], removedFields: DataExplorerField[]) {