You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by te...@apache.org on 2020/05/14 14:26:43 UTC

[incubator-streampipes] branch STREAMPIPES-79 updated (dd5fa85 -> 1ba5010)

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

tex pushed a change to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git.


    from dd5fa85  Merge branch 'dev' into STREAMPIPES-79
     new eaf736d  - create image labeling app - improve image labeling tool usability - label can be drawn on top of an existing label
     new 1ba5010  fix bug: data lake paging api without page number returned wrong pae

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../rest/impl/datalake/DataLakeManagementV3.java   |   2 +-
 .../app-image-labeling.component.css}              |   0
 .../app-image-labeling.component.html}             |  70 +++++------
 .../app-image-labeling.component.ts                |  54 ++++++++
 .../app-image-labeling.module.ts}                  |  25 ++--
 .../app/app-overview/app-overview.component.html   |   2 +
 ui/src/app/app-overview/app-overview.component.ts  |  21 ++--
 ui/src/app/app-overview/app-overview.module.ts     |  26 ++--
 .../datalake/datalake-rest.service.ts              |   6 +-
 ui/src/app/core-ui/core-ui.module.ts               |   2 +
 .../image-annotations.component.html               |  12 +-
 .../components/image-bar/image-bar.component.css   |   1 +
 .../image-container/image-container.component.css  |   3 +-
 .../image-container/image-container.component.ts   |  34 ++++--
 .../image-categorize.component.html                |   2 +-
 .../image-labeling/image-labeling.component.html   |  24 ++--
 .../image-labeling/image-labeling.component.ts     | 136 +++++++++++----------
 .../image/image-viewer/image-viewer.component.html |   2 +-
 .../image/services/BrushLabeling.service.ts        |  50 ++++----
 .../core-ui/image/services/LabelingMode.service.ts |  79 ++++++++++++
 .../image/services/PolygonLabeling.service.ts      |  85 +++++++------
 .../image/services/ReactLabeling.service.ts        |  65 ++++++----
 22 files changed, 457 insertions(+), 244 deletions(-)
 copy ui/src/app/{app-asset-monitoring/app-asset-monitoring.component.css => app-image-labeling/app-image-labeling.component.css} (100%)
 copy ui/src/app/{configuration/configuration.component.html => app-image-labeling/app-image-labeling.component.html} (57%)
 create mode 100644 ui/src/app/app-image-labeling/app-image-labeling.component.ts
 copy ui/src/app/{platform-services/platform.module.ts => app-image-labeling/app-image-labeling.module.ts} (59%)
 create mode 100644 ui/src/app/core-ui/image/services/LabelingMode.service.ts


[incubator-streampipes] 02/02: fix bug: data lake paging api without page number returned wrong pae

Posted by te...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1ba5010a3b82fc20612a8136fe1b91ce0db75356
Author: tex <te...@fzi.de>
AuthorDate: Thu May 14 16:25:01 2020 +0200

    fix bug: data lake paging api without page number returned wrong pae
---
 .../org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
index 65786d2..4f8fbff 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
@@ -225,7 +225,7 @@ public class DataLakeManagementV3 {
   }
 
   public PageResult getEvents(String index, int itemsPerPage) throws IOException {
-    int page = getMaxPage(index, itemsPerPage);
+    int page = getMaxPage(index, itemsPerPage) - 1;
     return getEvents(index, itemsPerPage, page);
   }
 


[incubator-streampipes] 01/02: - create image labeling app - improve image labeling tool usability - label can be drawn on top of an existing label

Posted by te...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit eaf736d0966847fd59caa9fe822dbc33285644f9
Author: tex <te...@fzi.de>
AuthorDate: Thu May 14 16:23:45 2020 +0200

    - create image labeling app
    - improve image labeling tool usability
    - label can be drawn on top of an existing label
---
 .../app-image-labeling.component.css}              |   8 +-
 .../app-image-labeling.component.html              |  35 ++++++
 .../app-image-labeling.component.ts                |  54 ++++++++
 .../app-image-labeling.module.ts}                  |  32 ++++-
 .../app/app-overview/app-overview.component.html   |   2 +
 ui/src/app/app-overview/app-overview.component.ts  |  21 ++--
 ui/src/app/app-overview/app-overview.module.ts     |  26 ++--
 .../datalake/datalake-rest.service.ts              |   6 +-
 ui/src/app/core-ui/core-ui.module.ts               |   2 +
 .../image-annotations.component.html               |  12 +-
 .../components/image-bar/image-bar.component.css   |   1 +
 .../image-container/image-container.component.css  |   3 +-
 .../image-container/image-container.component.ts   |  34 ++++--
 .../image-categorize.component.html                |   2 +-
 .../image-labeling/image-labeling.component.html   |  24 ++--
 .../image-labeling/image-labeling.component.ts     | 136 +++++++++++----------
 .../image/image-viewer/image-viewer.component.html |   2 +-
 .../image/services/BrushLabeling.service.ts        |  50 ++++----
 .../core-ui/image/services/LabelingMode.service.ts |  79 ++++++++++++
 .../image/services/PolygonLabeling.service.ts      |  85 +++++++------
 .../image/services/ReactLabeling.service.ts        |  65 ++++++----
 21 files changed, 473 insertions(+), 206 deletions(-)

diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/app-image-labeling/app-image-labeling.component.css
similarity index 89%
copy from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
copy to ui/src/app/app-image-labeling/app-image-labeling.component.css
index a4868af..e9af737 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.css
@@ -16,6 +16,10 @@
  *
  */
 
-.imageBar {
-    height: 70px;
+.page-container-padding {
+    width:100%;
+}
+
+.page-container-padding-inner {
+    padding: 10px;
 }
\ No newline at end of file
diff --git a/ui/src/app/app-image-labeling/app-image-labeling.component.html b/ui/src/app/app-image-labeling/app-image-labeling.component.html
new file mode 100644
index 0000000..818003e
--- /dev/null
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.html
@@ -0,0 +1,35 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (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
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<div fxLayout="column" fxFlex="100" class="page-container-connect">
+    <div fxLayout="row" style="padding:0px;background-color:#f6f6f6;">
+        <div fxLayout="fill" style="line-height:24px;border-bottom:1px solid #ccc">
+            <mat-form-field style="margin-top: 5px; margin-left: 5px;">
+                <mat-label>Select data lake measurement</mat-label>
+                <mat-select [(value)]="selectedMeasure">
+                    <mat-option *ngFor="let measure of dataLakeMeasures" [value]="measure">
+                        {{measure.measureName}}
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>
+    </div>
+
+    <div class="fixed-height page-container-padding-inner" fxLayout="column" fxFlex="100">
+        <sp-image-labeling [measureName]="selectedMeasure?.measureName" [eventSchema]="selectedMeasure?.eventSchema"></sp-image-labeling>
+    </div>
+</div>
\ No newline at end of file
diff --git a/ui/src/app/app-image-labeling/app-image-labeling.component.ts b/ui/src/app/app-image-labeling/app-image-labeling.component.ts
new file mode 100644
index 0000000..f6c923c
--- /dev/null
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.ts
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, EventEmitter, OnInit, Output } from '@angular/core';
+import { DatalakeRestService } from "../core-services/datalake/datalake-rest.service";
+import { TsonLdSerializerService } from "../platform-services/tsonld-serializer.service";
+import { DataLakeMeasure } from "../core-model/datalake/DataLakeMeasure";
+
+@Component({
+    selector: 'app-image-labeling',
+    templateUrl: './app-image-labeling.component.html',
+    styleUrls: ['./app-image-labeling.component.css']
+})
+export class AppImageLabelingComponent implements  OnInit {
+
+
+  dataLakeMeasures: DataLakeMeasure[] = [];
+  selectedMeasure: DataLakeMeasure;
+
+  @Output() appOpened = new EventEmitter<boolean>();
+
+  constructor(private restService: DatalakeRestService,
+              private tsonLdSerializerService: TsonLdSerializerService) {
+
+  }
+
+  ngOnInit() {
+      this.appOpened.emit(true);
+        this.restService.getAllInfos().map(data => {
+          return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
+        }).subscribe(
+          res => {
+              this.dataLakeMeasures = res;
+              this.selectedMeasure = res[0];
+          }
+        );
+  }
+
+
+}
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/app-image-labeling/app-image-labeling.module.ts
similarity index 51%
copy from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
copy to ui/src/app/app-image-labeling/app-image-labeling.module.ts
index a4868af..9895c72 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/app-image-labeling/app-image-labeling.module.ts
@@ -6,16 +6,38 @@
  * (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,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-.imageBar {
-    height: 70px;
-}
\ No newline at end of file
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { CoreUiModule } from '../core-ui/core-ui.module';
+import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
+import { AppImageLabelingComponent } from './app-image-labeling.component';
+
+@NgModule({
+    imports: [
+        CommonModule,
+        CustomMaterialModule,
+        CoreUiModule,
+    ],
+    declarations: [
+        AppImageLabelingComponent
+    ],
+    providers: [
+    ],
+    entryComponents: [
+        AppImageLabelingComponent,
+    ],
+    exports: [
+        AppImageLabelingComponent
+    ]
+})
+export class AppImageLabelingModule {
+}
diff --git a/ui/src/app/app-overview/app-overview.component.html b/ui/src/app/app-overview/app-overview.component.html
index f358675..9824a55 100644
--- a/ui/src/app/app-overview/app-overview.component.html
+++ b/ui/src/app/app-overview/app-overview.component.html
@@ -49,4 +49,6 @@
     </div>
     <app-asset-monitoring (appOpened)="appOpened($event)"
                           *ngIf="currentlySelectedApp === apps[0].appId"></app-asset-monitoring>
+    <app-image-labeling (appOpened)="appOpened($event)"
+                          *ngIf="currentlySelectedApp === apps[1].appId"></app-image-labeling>
 </div>
diff --git a/ui/src/app/app-overview/app-overview.component.ts b/ui/src/app/app-overview/app-overview.component.ts
index 465d38b..5c7fe70 100644
--- a/ui/src/app/app-overview/app-overview.component.ts
+++ b/ui/src/app/app-overview/app-overview.component.ts
@@ -16,23 +16,28 @@
  *
  */
 
-import {Component} from '@angular/core';
+import { Component, OnInit } from "@angular/core";
 
 @Component({
     templateUrl: './app-overview.component.html',
     styleUrls: ['./app-overview.component.css']
 })
-export class AppOverviewComponent {
+export class AppOverviewComponent implements OnInit {
 
-    selectedIndex: number = 0;
+    selectedIndex = 0;
     appOpen = false;
-    currentlySelectedApp: string = "";
+    currentlySelectedApp = '';
 
     apps: any[] = [
         {
-            appName: "Asset Dashboards",
-            appDescription: "Monitor measurements of your assets by placing visualizations on an image of your asset.",
-            appId: "asset-monitoring",
+            appName: 'Asset Dashboards',
+            appDescription: 'Monitor measurements of your assets by placing visualizations on an image of your asset.',
+            appId: 'asset-monitoring',
+        },
+        {
+            appName: 'Image Labeling',
+            appDescription: 'Label in data lake stored images.',
+            appId: 'image-labeling',
         },
     ];
 
@@ -54,7 +59,7 @@ export class AppOverviewComponent {
 
     appClosed() {
         this.appOpen = false;
-        this.currentlySelectedApp = "";
+        this.currentlySelectedApp = '';
     }
 
     selectApp(appId: string) {
diff --git a/ui/src/app/app-overview/app-overview.module.ts b/ui/src/app/app-overview/app-overview.module.ts
index 10f9ead..03dc7f7 100644
--- a/ui/src/app/app-overview/app-overview.module.ts
+++ b/ui/src/app/app-overview/app-overview.module.ts
@@ -16,18 +16,19 @@
  *
  */
 
-import {NgModule} from '@angular/core';
-import {FlexLayoutModule} from '@angular/flex-layout';
-import {CommonModule} from '@angular/common';
-import {CustomMaterialModule} from '../CustomMaterial/custom-material.module';
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
 
-import {MatFormFieldModule} from "@angular/material/form-field";
-import {MatGridListModule} from "@angular/material/grid-list";
-import {MatInputModule} from "@angular/material/input";
-import {FormsModule} from "@angular/forms";
-import {AppOverviewComponent} from "./app-overview.component";
-import {AppAssetMonitoringModule} from "../app-asset-monitoring/app-asset-monitoring.module";
-import {AppTransportMonitoringModule} from "../app-transport-monitoring/app-transport-monitoring.module";
+import { FormsModule } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatGridListModule } from '@angular/material/grid-list';
+import { MatInputModule } from '@angular/material/input';
+import { AppAssetMonitoringModule } from '../app-asset-monitoring/app-asset-monitoring.module';
+import { AppImageLabelingModule } from '../app-image-labeling/app-image-labeling.module';
+import { AppTransportMonitoringModule } from '../app-transport-monitoring/app-transport-monitoring.module';
+import { AppOverviewComponent } from './app-overview.component';
 
 @NgModule({
     imports: [
@@ -39,7 +40,8 @@ import {AppTransportMonitoringModule} from "../app-transport-monitoring/app-tran
         MatFormFieldModule,
         FormsModule,
         AppAssetMonitoringModule,
-        AppTransportMonitoringModule
+        AppTransportMonitoringModule,
+        AppImageLabelingModule,
     ],
     declarations: [
         AppOverviewComponent,
diff --git a/ui/src/app/core-services/datalake/datalake-rest.service.ts b/ui/src/app/core-services/datalake/datalake-rest.service.ts
index 465cc0b..91e1184 100644
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@ -18,12 +18,12 @@
 
 import { HttpClient, HttpRequest } from '@angular/common/http';
 import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { DataLakeMeasure } from '../../core-model/datalake/DataLakeMeasure';
 import { DataResult } from '../../core-model/datalake/DataResult';
 import { GroupedDataResult } from '../../core-model/datalake/GroupedDataResult';
-import { DataLakeMeasure } from '../../core-model/datalake/DataLakeMeasure';
 import { PageResult } from '../../core-model/datalake/PageResult';
 import { AuthStatusService } from '../../services/auth-status.service';
-import { Observable } from "rxjs/Observable";
 
 @Injectable()
 export class DatalakeRestService {
@@ -146,4 +146,4 @@ export class DatalakeRestService {
     saveCocoFileForImage(imageRoute, data) {
       return this.http.post(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco', data);
     }
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts
index a1483ab..da4936e 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -45,6 +45,7 @@ import { ColorService } from './image/services/color.service';
 import { PolygonLabelingService } from './image/services/PolygonLabeling.service';
 import { ReactLabelingService } from './image/services/ReactLabeling.service';
 import { CocoFormatService } from "./image/services/CocoFormat.service";
+import { LabelingModeService } from "./image/services/LabelingMode.service";
 // PlotlyViaCDNModule.plotlyjs = PlotlyJS;
 
 @NgModule({
@@ -80,6 +81,7 @@ import { CocoFormatService } from "./image/services/CocoFormat.service";
         PolygonLabelingService,
         BrushLabelingService,
         CocoFormatService,
+        LabelingModeService,
     ],
     entryComponents: [
     ],
diff --git a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
index feedf72..abfaf1d 100644
--- a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
+++ b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
@@ -20,19 +20,25 @@
     <h2>Annotations</h2>
     <mat-list>
         <mat-list-item *ngFor="let annotation of annotations" (mouseover)="enterAnnotation(annotation)" (mouseout)="leaveAnnotation(annotation)"
-                       style="height: 30px; padding-top: 5px; padding-buttom: 5px; border-radius: 50px; margin-bottom: 5px"
+                       style="height: 40px; padding-top: 5px; padding-buttom: 5px; border-radius: 50px; margin-bottom: 5px"
                        [style.background-color]="annotation.isHovered || annotation.isSelected ? 'lightgrey' : 'white'">
-            <mat-icon matListIcon [style.color]="colorService.getColor(annotation.category_name)" style="margin-top: -8px;">color_lens</mat-icon>
             <mat-form-field>
                 <mat-select [value]="annotation.category_name">
+
+                    <mat-select-trigger>
+                        <mat-icon [style.color]="colorService.getColor(annotation.category_name)">color_lens</mat-icon>
+                        {{annotation.category_name}}
+                    </mat-select-trigger>
+
                     <mat-optgroup *ngFor="let category of categories" [label]="category">
                         <mat-option *ngFor="let label of _labels[category]" [value]="label" (click)="changeLabel(annotation, label, category)">
+                            <mat-icon [style.color]="colorService.getColor(label)">color_lens</mat-icon>
                             {{label}}
                         </mat-option>
                     </mat-optgroup>
                 </mat-select>
             </mat-form-field>
-            <button mat-icon-button (click)="delete(annotation)" style="margin-top: -10px; margin-left: 10px"> <mat-icon>delete_forever</mat-icon></button>
+            <button mat-icon-button (click)="delete(annotation)" style="margin-top: -10px; margin-left: -5px"> <mat-icon>delete_forever</mat-icon></button>
         </mat-list-item>
     </mat-list>
 </div>
\ No newline at end of file
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 a4868af..b0f2e09 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
@@ -18,4 +18,5 @@
 
 .imageBar {
     height: 70px;
+    width: 1000px;
 }
\ No newline at end of file
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.css b/ui/src/app/core-ui/image/components/image-container/image-container.component.css
index 612fce8..dfaa975 100644
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.css
+++ b/ui/src/app/core-ui/image/components/image-container/image-container.component.css
@@ -20,6 +20,5 @@
     background-color: #fafafa;
     border-color: lightgrey;
     border-style: solid;
-    height: 500px;
-    width: 800px;
+
 }
\ No newline at end of file
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 7667a8f..b1c47c0 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
@@ -17,7 +17,6 @@
 
 import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
 import Konva from 'konva';
-import { Context } from 'konva/types/Context';
 import { ICoordinates } from '../../model/coordinates';
 
 @Component({
@@ -46,6 +45,8 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
   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]>();
 
 
   private image;
@@ -62,6 +63,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
   private lastImagePointerPosition: ICoordinates;
 
   private isLeftMouseDown: boolean;
+  private isMiddleMouseDown: boolean;
   private isRightMouseDown: boolean;
 
   private isHoverComponent: boolean;
@@ -72,6 +74,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
     this.scale = 1;
     this.imageShift = {x: 0, y: 0};
     this.isLeftMouseDown = false;
+    this.isMiddleMouseDown = false;
     this.isRightMouseDown = false;
     this.isHoverComponent = false;
   }
@@ -106,8 +109,10 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
   }
 
   getShift() {
-    const position = this.imageLayer.getChildren().toArray()[0].getPosition();
-    return {x: position.x, y: position.y};
+    if (this.imageLayer !== undefined) {
+      const position = this.imageLayer.getChildren().toArray()[0].getPosition();
+      return {x: position.x, y: position.y};
+    }
   }
   /* mouse handler */
 
@@ -120,13 +125,14 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
       this.drawLayer.batchDraw();
     } else if (button === 2) {
       // middle click
-
-    } else if (button === 3) {
-      // right click
-      this.isRightMouseDown = true;
+      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()]);
     }
   }
 
@@ -135,7 +141,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
       this.drawLayer.destroyChildren();
       this.mouseMoveLeft.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
       this.drawLayer.batchDraw();
-    } else if (this.isRightMouseDown) {
+    } 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);
@@ -157,8 +163,8 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
       this.drawLayer.batchDraw();
       this.annotationLayer.batchDraw();
     }
-    if (this.isRightMouseDown) {
-      this.isRightMouseDown = false;
+    if (this.isMiddleMouseDown) {
+      this.isMiddleMouseDown = false;
       this.mainCanvasStage.container().style.cursor = 'default';
     }
   }
@@ -173,8 +179,12 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
   /* Draw */
 
   redrawAll() {
-    this.drawLayer.destroyChildren();
-    this.annotationLayer.destroyChildren();
+    if (this.drawLayer !== undefined) {
+      this.drawLayer.destroyChildren();
+    }
+    if (this.annotationLayer !== undefined) {
+      this.annotationLayer.destroyChildren();
+    }
     this.childRedraw.emit([this.annotationLayer, this.getShift()]);
     this.shiftViewContent();
   }
diff --git a/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html b/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
index a75852d..a5d6a0c 100644
--- a/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
+++ b/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
@@ -46,7 +46,7 @@
             </div>
         </div>
         <br />
-        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum }}</div></div>
         <sp-image-bar style="width: 100%"
                       [imagesSrcs]="imagesSrcs"
                       [selectedIndex]="imagesIndex"
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
index 0669e12..a6390f8 100644
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
+++ b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
@@ -21,19 +21,24 @@
     <div fxLayout="column" fxLayoutAlign="space-between " >
         <div  fxLayout="row" fxLayoutAlign="space-around start">
 
-            <sp-image-labels
+            <sp-image-labels style="width: 270px"
                     [labels]="labels"
                     [enableShortCuts]="isHoverComponent"
                     (labelChange)="handleLabelChange($event)">
             </sp-image-labels>
 
             <div fxLayout="column" fxLayoutAlign="space-between " >
-                <div>
-                    <button mat-button (click)="setReactMode()" [style.background-color]="isReactMode() ? 'lightgrey' : 'white'"> <mat-icon>crop_3_2</mat-icon></button>
-                    <button mat-button (click)="setPolygonMode()" [style.background-color]="isPolygonMode() ? 'lightgrey' : 'white'"> <mat-icon>details</mat-icon></button>
-                    <button mat-button (click)="setBrushMode()" [style.background-color]="isBrushMode() ? 'lightgrey' : 'white'"> <mat-icon>blur_circular</mat-icon></button>
+                <div style="display: flex; position: relative">
+                    <button mat-button matTooltip="Edit Annotations"
+                            (click)="labelingMode.setNoneMode()" [style.background-color]="labelingMode.isNoneMode() ? 'lightgrey' : 'white'"> <mat-icon>create</mat-icon></button>
+                    <button mat-button matTooltip="Create React Annotation"
+                            (click)="labelingMode.setReactMode()" [style.background-color]="labelingMode.isReactMode() ? 'lightgrey' : 'white'"> <mat-icon>crop_3_2</mat-icon></button>
+                    <button mat-button matTooltip="Create Polygon Annotation"
+                            (click)="labelingMode.setPolygonMode()" [style.background-color]="labelingMode.isPolygonMode() ? 'lightgrey' : 'white'"> <mat-icon>details</mat-icon></button>
+                    <button mat-button matTooltip="Create Brush Annotation"
+                            (click)="labelingMode.setBrushMode()" [style.background-color]="labelingMode.isBrushMode() ? 'lightgrey' : 'white'"> <mat-icon>blur_circular</mat-icon></button>
                     <mat-slider [min]="1"  [max]="50" [step]="1" [thumbLabel]="true" [(ngModel)]="brushSize"></mat-slider>
-                    <button mat-button (click)="save()"> <mat-icon>save</mat-icon></button>
+                    <button mat-button (click)="save()" style="position: absolute; right: 0px;"> <mat-icon>save</mat-icon></button>
                 </div>
 
                 <sp-image-container
@@ -44,11 +49,12 @@
                         (mouseMoveLeft)="handleMouseMoveLeft($event[0], $event[1], $event[2])"
                         (mouseUpLeft)="handleMouseUpLeft($event[0], $event[1], $event[2], $event[3])"
                         (shortCut)="handleImageViewShortCuts($event)"
-                        (dbclick)="handleImageViewDBClick($event[0], $event[1], $event[2])">
+                        (dbclick)="handleImageViewDBClick($event[0], $event[1], $event[2])"
+                        (mouseDownRight)="handleMouseDownRight($event[0], $event[1], $event[2])">
                 </sp-image-container>
             </div>
 
-            <sp-image-annotations
+            <sp-image-annotations style="width: 270px"
                     [annotations]="this.cocoFiles[this.imagesIndex]?.annotations"
                     [labels]="labels"
                     (changeAnnotationLabel)="handleChangeAnnotationLabel($event)"
@@ -56,7 +62,7 @@
             </sp-image-annotations>
         </div>
         <br />
-        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum}}</div></div>
         <sp-image-bar style="width: 100%"
                 [imagesSrcs]="imagesSrcs"
                 [selectedIndex]="imagesIndex"
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 2cd0b24..c2c68f7 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
@@ -17,27 +17,28 @@
  */
 
 
-import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
+import { AfterViewInit, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
 import { MatSnackBar } from '@angular/material/snack-bar';
 import Konva from 'konva';
 import { Annotation } from '../../../core-model/coco/Annotation';
 import { CocoFormat } from '../../../core-model/coco/Coco.format';
 import { DatalakeRestService } from '../../../core-services/datalake/datalake-rest.service';
+import { TsonLdSerializerService } from '../../../platform-services/tsonld-serializer.service';
 import { ImageContainerComponent } from '../components/image-container/image-container.component';
 import { ICoordinates } from '../model/coordinates';
 import { LabelingMode } from '../model/labeling-mode';
 import { BrushLabelingService } from '../services/BrushLabeling.service';
 import { CocoFormatService } from '../services/CocoFormat.service';
+import { LabelingModeService } from '../services/LabelingMode.service';
 import { PolygonLabelingService } from '../services/PolygonLabeling.service';
 import { ReactLabelingService } from '../services/ReactLabeling.service';
-import { TsonLdSerializerService } from '../../../platform-services/tsonld-serializer.service';
 
 @Component({
   selector: 'sp-image-labeling',
   templateUrl: './image-labeling.component.html',
   styleUrls: ['./image-labeling.component.css']
 })
-export class ImageLabelingComponent implements OnInit, AfterViewInit {
+export class ImageLabelingComponent implements OnInit, AfterViewInit, OnChanges {
 
   // label
   public labels;
@@ -54,8 +55,8 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
 
   @ViewChild(ImageContainerComponent) imageView: ImageContainerComponent;
 
-  measureName = 'image'; // TODO: Remove hard coded Index, should be injected
-  eventSchema = undefined; // TODO: event schema should be also injected
+  @Input() measureName = 'image'; // TODO: Remove default value for production
+  @Input() eventSchema = undefined; // TODO: event schema should be always injected by production
   imageField = undefined;
   pageIndex = undefined;
   pageSum = undefined;
@@ -65,38 +66,57 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   private setImagesIndexToLast = false;
 
 
-  public labelingMode: LabelingMode = LabelingMode.ReactLabeling;
 
   constructor(private restService: DatalakeRestService, private reactLabelingService: ReactLabelingService,
               private polygonLabelingService: PolygonLabelingService, private brushLabelingService: BrushLabelingService,
               private snackBar: MatSnackBar, private cocoFormatService: CocoFormatService,
-              private tsonLdSerializerService: TsonLdSerializerService) { }
+              private tsonLdSerializerService: TsonLdSerializerService,
+              public labelingMode: LabelingModeService) { }
 
   ngOnInit(): void {
     this.isHoverComponent = false;
     this.brushSize = 5;
+    this.imagesIndex = 0;
+
 
-    // TODO Get Labels
     this.labels = this.restService.getLabels();
 
-    this.restService.getAllInfos().map(data => {
-      return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
-    }).subscribe(
-      res => {
-        this.eventSchema = res.find(elem => elem.measureName === this.measureName).eventSchema;
-        const properties = this.eventSchema.eventProperties;
-        for (const prop of properties) {
-          // if (prop.domainProperties.find(type => type === 'https://image.com')) {
+    // TODO remove for production, if default dev values are not necessary
+    if (this.eventSchema === undefined) {
+      this.restService.getAllInfos().map(data => {
+        return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
+      }).subscribe(
+        res => {
+          this.eventSchema = res.find(elem => elem.measureName === this.measureName).eventSchema;
+          const properties = this.eventSchema.eventProperties;
+          for (const prop of properties) {
+            // if (prop.domainProperties.find(type => type === 'https://image.com')) {
             if (prop.domainProperty === 'https://image.com') {
-            this.imageField = prop;
-            break;
+              this.imageField = prop;
+              break;
+            }
           }
+          this.loadData();
+        }
+      );
+    }
+  }
+
+  ngOnChanges() {
+    if (this.eventSchema !== null) {
+      const properties = this.eventSchema.eventProperties;
+      for (const prop of properties) {
+        if (prop.domainProperty === 'https://image.com') {
+          this.imageField = prop;
+          break;
         }
-        this.loadData();
       }
-    );
+      this.pageIndex = undefined;
+      this.pageSum = undefined;
+      this.imagesIndex = 0;
 
-    this.imagesIndex = 0;
+      this.loadData();
+    }
   }
 
   ngAfterViewInit(): void {
@@ -159,7 +179,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   /* sp-image-view handler */
   handleMouseDownLeft(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
     if (this.labelingEnabled()) {
-      switch (this.labelingMode) {
+      switch (this.labelingMode.getMode()) {
         case LabelingMode.ReactLabeling: this.reactLabelingService.startLabeling(position);
           break;
         case LabelingMode.PolygonLabeling: this.polygonLabelingService.startLabeling(position);
@@ -170,7 +190,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   }
 
   handleMouseMove(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
-    switch (this.labelingMode) {
+    switch (this.labelingMode.getMode()) {
       case LabelingMode.PolygonLabeling: {
         this.polygonLabelingService.executeLabeling(position);
         this.polygonLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
@@ -180,7 +200,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
 
   handleMouseMoveLeft(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
     if (this.labelingEnabled()) {
-      switch (this.labelingMode) {
+      switch (this.labelingMode.getMode()) {
         case LabelingMode.ReactLabeling: {
           this.reactLabelingService.executeLabeling(position);
           this.reactLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
@@ -191,12 +211,12 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
           this.brushLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
         }
       }
-    }
+   }
   }
 
   handleMouseUpLeft(annotationLayer: Konva.Layer, drawLayer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
     if (this.labelingEnabled()) {
-      switch (this.labelingMode) {
+      switch (this.labelingMode.getMode()) {
         case LabelingMode.ReactLabeling: {
           const result = this.reactLabelingService.endLabeling(position);
           const coco = this.cocoFiles[this.imagesIndex];
@@ -220,10 +240,14 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
     }
   }
 
+  handleMouseDownRight(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
+    this.labelingMode.toggleNoneMode();
+  }
+
 
   handleImageViewDBClick(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
     if (this.labelingEnabled()) {
-      switch (this.labelingMode) {
+      switch (this.labelingMode.getMode()) {
         case LabelingMode.PolygonLabeling:
           const points = this.polygonLabelingService.endLabeling(position);
           const coco = this.cocoFiles[this.imagesIndex];
@@ -236,15 +260,17 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
 
   handleChildRedraw(layer: Konva.Layer, shift: ICoordinates) {
     const coco = this.cocoFiles[this.imagesIndex];
-    for (const annotation of coco.annotations) {
-      annotation.isHovered = false;
-      annotation.isSelected = false;
-      if (this.cocoFormatService.isBoxAnnonation(annotation)) {
-        this.reactLabelingService.draw(layer, shift, annotation, this.imageView);
-      } else if (this.cocoFormatService.isPolygonAnnonation(annotation) && !this.cocoFormatService.isBrushAnnonation(annotation)) {
-        this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
-      } else if (this.cocoFormatService.isBrushAnnonation(annotation)) {
-        this.brushLabelingService.draw(layer, shift, annotation, this.imageView);
+    if (coco  !== undefined) {
+      for (const annotation of coco.annotations) {
+        annotation.isHovered = false;
+        annotation.isSelected = false;
+        if (this.cocoFormatService.isBoxAnnonation(annotation)) {
+          this.reactLabelingService.draw(layer, shift, annotation, this.imageView);
+        } else if (this.cocoFormatService.isPolygonAnnonation(annotation) && !this.cocoFormatService.isBrushAnnonation(annotation)) {
+          this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
+        } else if (this.cocoFormatService.isBrushAnnonation(annotation)) {
+          this.brushLabelingService.draw(layer, shift, annotation, this.imageView);
+        }
       }
     }
   }
@@ -306,7 +332,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
 
   private labelingEnabled() {
     const coco = this.cocoFiles[this.imagesIndex];
-    const annotation = coco.annotations.find(anno => anno.isHovered);
+    const annotation = coco.annotations.find(anno => anno.isHovered && anno.isSelected);
     if (annotation !== undefined) {
       return false;
     } else {
@@ -317,11 +343,13 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   save() {
     // TODO
     const coco = this.cocoFiles[this.imagesIndex];
-    const imageSrcSplitted = this.imagesSrcs[this.imagesIndex].split('/');
-    const imageRoute = imageSrcSplitted[imageSrcSplitted.length - 2];
-    this.restService.saveCocoFileForImage(imageRoute, JSON.stringify(coco)).subscribe(
-      res =>    this.openSnackBar('Saved')
-    );
+    if (coco !== undefined) {
+      const imageSrcSplitted = this.imagesSrcs[this.imagesIndex].split('/');
+      const imageRoute = imageSrcSplitted[imageSrcSplitted.length - 2];
+      this.restService.saveCocoFileForImage(imageRoute, JSON.stringify(coco)).subscribe(
+        res =>    this.openSnackBar('Saved')
+      );
+    }
   }
 
   private openSnackBar(message: string) {
@@ -332,28 +360,4 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
     });
   }
 
-  /* UI */
-  isReactMode() {
-    return this.labelingMode === LabelingMode.ReactLabeling;
-  }
-
-  setReactMode() {
-    this.labelingMode = LabelingMode.ReactLabeling;
-  }
-
-  isPolygonMode() {
-    return this.labelingMode === LabelingMode.PolygonLabeling;
-  }
-
-  setPolygonMode() {
-    this.labelingMode = LabelingMode.PolygonLabeling;
-  }
-
-  isBrushMode() {
-    return this.labelingMode === LabelingMode.BrushLabeling;
-  }
-
-  setBrushMode() {
-    this.labelingMode = LabelingMode.BrushLabeling;
-  }
 }
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 d373a2b..60ca4a6 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
@@ -26,7 +26,7 @@
             </div>
         </div>
         <br />
-        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+        <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum }}</div></div>
         <sp-image-bar style="width: 100%"
                       [imagesSrcs]="imagesSrcs"
                       [selectedIndex]="imagesIndex"
diff --git a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts b/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
index d97b0dc..07ffd3b 100644
--- a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
 import { Annotation } from '../../../core-model/coco/Annotation';
 import { ICoordinates } from '../model/coordinates';
 import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
 
 @Injectable()
 export class BrushLabelingService {
@@ -29,7 +30,8 @@ export class BrushLabelingService {
 
   private isLabeling: boolean;
 
-  constructor(private colorService: ColorService) {
+  constructor(private colorService: ColorService,
+              private labelingMode: LabelingModeService) {
 
   }
 
@@ -100,43 +102,49 @@ export class BrushLabelingService {
       transformer.attachTo(line);
     }
 
-    this.addMouseHandler(line, annotation, layer, transformer);
-    this.addClickHandler(line, annotation, layer, transformer);
+    this.addMouseHandler(line, annotation, layer, transformer, this.labelingMode);
+    this.addClickHandler(line, annotation, layer, transformer, this.labelingMode);
 
     layer.add(line);
     layer.add(transformer);
   }
 
-  private addClickHandler(rect, annotation, layer, transformer) {
-    rect.on('click', function() {
-      annotation.isSelected = true;
-      transformer.attachTo(this);
-      layer.batchDraw();
-    });
+  private addClickHandler(brush, annotation, layer, transformer, labelingMode) {
+    brush.on('click', function() {
+      if (labelingMode.isNoneMode()) {
+        annotation.isSelected = !annotation.isSelected;
 
-    rect.on('dblclick', function() {
-      annotation.isSelected = false;
-      transformer.detach();
-      layer.batchDraw();
+        if (annotation.isSelected) {
+          transformer.attachTo(this);
+        } else {
+          transformer.detach();
+        }
+
+        layer.batchDraw();
+      }
     });
 
   }
 
-  private addMouseHandler(rect, annotation, layer, transformer) {
-    rect.on('mouseover', function() {
-      annotation.isHovered = true;
-      rect.opacity(0.8);
-      layer.batchDraw();
+  private addMouseHandler(brush, annotation, layer, transformer, labelingMode) {
+    brush.on('mouseover', function() {
+      if (labelingMode.isNoneMode()) {
+        annotation.isHovered = true;
+        brush.opacity(0.8);
+        layer.batchDraw();
+      }
     });
 
-    rect.on('mouseout', function() {
+    brush.on('mouseout', function() {
       annotation.isHovered = false;
-      rect.opacity(0.5);
+      brush.opacity(0.5);
       layer.batchDraw();
     });
 
     transformer.on('mouseover', function() {
-      annotation.isHovered = true;
+      if (labelingMode.isNoneMode()) {
+        annotation.isHovered = true;
+      }
     });
 
     transformer.on('mouseout', function() {
diff --git a/ui/src/app/core-ui/image/services/LabelingMode.service.ts b/ui/src/app/core-ui/image/services/LabelingMode.service.ts
new file mode 100644
index 0000000..fe5307e
--- /dev/null
+++ b/ui/src/app/core-ui/image/services/LabelingMode.service.ts
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { LabelingMode } from '../model/labeling-mode';
+
+@Injectable()
+export class LabelingModeService {
+
+  private labelingMode: LabelingMode;
+  private lastLabelingMode: LabelingMode;
+
+  constructor() {
+    this.labelingMode = LabelingMode.ReactLabeling;
+    this.lastLabelingMode = LabelingMode.ReactLabeling;
+  }
+
+  isReactMode() {
+    return this.labelingMode === LabelingMode.ReactLabeling;
+  }
+
+  setReactMode() {
+    this.labelingMode = LabelingMode.ReactLabeling;
+    this.lastLabelingMode = LabelingMode.ReactLabeling;
+  }
+
+  isPolygonMode() {
+    return this.labelingMode === LabelingMode.PolygonLabeling;
+  }
+
+  setPolygonMode() {
+    this.labelingMode = LabelingMode.PolygonLabeling;
+    this.lastLabelingMode = LabelingMode.PolygonLabeling;
+  }
+
+  isBrushMode() {
+    return this.labelingMode === LabelingMode.BrushLabeling;
+  }
+
+  setBrushMode() {
+    this.labelingMode = LabelingMode.BrushLabeling;
+    this.lastLabelingMode = LabelingMode.BrushLabeling;
+  }
+
+  isNoneMode() {
+    return this.labelingMode === LabelingMode.NoneLabeling;
+  }
+
+  setNoneMode() {
+    this.labelingMode = LabelingMode.NoneLabeling;
+  }
+
+  getMode() {
+    return this.labelingMode;
+  }
+
+  toggleNoneMode() {
+    if (this.labelingMode === LabelingMode.NoneLabeling) {
+      this.labelingMode = this.lastLabelingMode;
+    } else {
+      this.labelingMode = LabelingMode.NoneLabeling;
+    }
+  }
+
+}
diff --git a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts b/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
index 153e8fd..2c65fee 100644
--- a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
 import { Annotation } from '../../../core-model/coco/Annotation';
 import { ICoordinates } from '../model/coordinates';
 import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
 
 @Injectable()
 export class PolygonLabelingService {
@@ -29,7 +30,8 @@ export class PolygonLabelingService {
 
   private isLabeling: boolean;
 
-  constructor(private colorService: ColorService) {
+  constructor(private colorService: ColorService,
+              private labelingMode: LabelingModeService) {
     this.isLabeling = false;
   }
 
@@ -182,36 +184,41 @@ export class PolygonLabelingService {
       PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
     }
 
-    this.addDragHandler(poly, annotation, layer, imageView);
-    this.addMouseHandler(poly, annotation, layer, transformer);
-    this.addClickHandler(poly, annotation, layer, transformer, imageView);
+    this.addDragHandler(poly, annotation, layer, imageView, this.labelingMode);
+    this.addMouseHandler(poly, annotation, layer, transformer, this.labelingMode);
+    this.addClickHandler(poly, annotation, layer, transformer, imageView, this.labelingMode);
 
     layer.add(poly);
     layer.add(transformer);
   }
 
-  private addClickHandler(poly, annotation, layer, transformer, imageView) {
+  private addClickHandler(poly, annotation, layer, transformer, imageView, labelingMode) {
     poly.on('click', function() {
-      annotation.isSelected = true;
-      transformer.attachTo(this);
-      PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
-      layer.batchDraw();
-    });
-
-    poly.on('dblclick', function() {
-      annotation.isSelected = false;
-      PolygonLabelingService.removeAnchors(layer, annotation.id);
-      transformer.detach();
-      layer.batchDraw();
+      if (labelingMode.isNoneMode()) {
+
+        annotation.isSelected = !annotation.isSelected;
+
+        if (annotation.isSelected) {
+          annotation.isSelected = true;
+          transformer.attachTo(this);
+          PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+        } else {
+          PolygonLabelingService.removeAnchors(layer, annotation.id);
+          transformer.detach();
+        }
+        layer.batchDraw();
+      }
     });
 
   }
 
-  private addMouseHandler(poly, annotation, layer, transformer) {
+  private addMouseHandler(poly, annotation, layer, transformer, labelingMode) {
     poly.on('mouseover', function() {
-      annotation.isHovered = true;
-      this.opacity(0.8);
-      layer.batchDraw();
+      if (labelingMode.isNoneMode()) {
+        annotation.isHovered = true;
+        poly.opacity(0.8);
+        layer.batchDraw();
+      }
     });
 
     poly.on('mouseout', function() {
@@ -222,28 +229,36 @@ export class PolygonLabelingService {
   }
 
 
-  private addDragHandler(poly, annotation, layer, imageView) {
+  private addDragHandler(poly, annotation, layer, imageView, labelingMode) {
     let offset: number[];
 
     poly.on('dragstart', function() {
-      const position = imageView.getImagePointerPosition();
-      offset = [];
-      for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-        offset.push(annotation.segmentation[0][i] - position.x);
-        offset.push(annotation.segmentation[0][i + 1] - position.y);
+      if (!labelingMode.isNoneMode()) {
+        poly.stopDrag();
+      } else {
+        const position = imageView.getImagePointerPosition();
+        offset = [];
+        for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
+          offset.push(annotation.segmentation[0][i] - position.x);
+          offset.push(annotation.segmentation[0][i + 1] - position.y);
+        }
       }
     });
 
     poly.on('dragmove', function() {
-      const position = imageView.getImagePointerPosition();
-      const tmp = [];
-      for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-        tmp.push(position.x + offset[i]);
-        tmp.push(position.y + offset[i + 1]);
-      }
-      annotation.segmentation[0] = tmp;
-      if (annotation.isSelected) {
-        PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+      if (!labelingMode.isNoneMode()) {
+        poly.stopDrag();
+      } else {
+        const position = imageView.getImagePointerPosition();
+        const tmp = [];
+        for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
+          tmp.push(position.x + offset[i]);
+          tmp.push(position.y + offset[i + 1]);
+        }
+        annotation.segmentation[0] = tmp;
+        if (annotation.isSelected) {
+          PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+        }
       }
     });
 
diff --git a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts b/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
index 1059dba..1c795b2 100644
--- a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
 import { Annotation } from '../../../core-model/coco/Annotation';
 import { ICoordinates } from '../model/coordinates';
 import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
 
 @Injectable()
 export class ReactLabelingService {
@@ -29,8 +30,8 @@ export class ReactLabelingService {
 
   private isLabeling: boolean;
 
-  constructor(private colorService: ColorService) {
-
+  constructor(private colorService: ColorService,
+              private labelingMode: LabelingModeService) {
   }
 
 
@@ -94,35 +95,38 @@ export class ReactLabelingService {
       transformer.attachTo(rect);
     }
 
-    this.addDragHandler(rect, annotation, imageView);
+    this.addDragHandler(rect, annotation, imageView, this.labelingMode);
     this.addTransformHandler(rect, annotation, imageView);
-    this.addMouseHandler(rect, annotation, layer, transformer);
-    this.addClickHandler(rect, annotation, layer, transformer);
+    this.addMouseHandler(rect, annotation, layer, transformer, this.labelingMode);
+    this.addClickHandler(rect, annotation, layer, transformer, this.labelingMode);
 
     layer.add(rect);
     layer.add(transformer);
   }
 
-  private addClickHandler(rect, annotation, layer, transformer) {
+  private addClickHandler(rect, annotation, layer, transformer, labelingMode) {
     rect.on('click', function() {
-      annotation.isSelected = true;
-      transformer.attachTo(this);
-      layer.batchDraw();
-    });
+      if (labelingMode.isNoneMode()) {
+        annotation.isSelected = !annotation.isSelected;
 
-    rect.on('dblclick', function() {
-      annotation.isSelected = false;
-      transformer.detach();
-      layer.batchDraw();
-    });
+        if (annotation.isSelected) {
+          transformer.attachTo(this);
+        } else {
+          transformer.detach();
+        }
 
+        layer.batchDraw();
+      }
+    });
   }
 
-  private addMouseHandler(rect, annotation, layer, transformer) {
+  private addMouseHandler(rect, annotation, layer, transformer, labelingMode) {
     rect.on('mouseover', function() {
-      annotation.isHovered = true;
-      rect.opacity(0.8);
-      layer.batchDraw();
+      if (labelingMode.isNoneMode()) {
+        annotation.isHovered = true;
+        rect.opacity(0.8);
+        layer.batchDraw();
+      }
     });
 
     rect.on('mouseout', function() {
@@ -132,7 +136,9 @@ export class ReactLabelingService {
     });
 
     transformer.on('mouseover', function() {
-      annotation.isHovered = true;
+      if (labelingMode.isNoneMode()) {
+        annotation.isHovered = true;
+      }
     });
 
     transformer.on('mouseout', function() {
@@ -179,18 +185,25 @@ export class ReactLabelingService {
     });
   }
 
-  private addDragHandler(rect, annotation, imageView) {
+  private addDragHandler(rect, annotation, imageView, labelingMode) {
     let offset: ICoordinates;
 
     rect.on('dragstart', function() {
-      const position = imageView.getImagePointerPosition();
-      offset = {x: annotation.bbox[0] - position.x, y: annotation.bbox[1] - position.y};
+      if (!labelingMode.isNoneMode()) {
+        rect.stopDrag();
+      } else {
+        const position = imageView.getImagePointerPosition();
+        offset = {x: annotation.bbox[0] - position.x, y: annotation.bbox[1] - position.y};
+      }
     });
 
     rect.on('dragmove', function() {
-      const position = imageView.getImagePointerPosition();
-      annotation.bbox[0] = imageView.getImagePointerPosition().x + offset.x;
-      annotation.bbox[1] = imageView.getImagePointerPosition().y + offset.y;
+      if (!labelingMode.isNoneMode()) {
+        rect.stopDrag();
+      } else {
+        const position = imageView.getImagePointerPosition();
+        annotation.bbox[0] = imageView.getImagePointerPosition().x + offset.x;
+        annotation.bbox[1] = imageView.getImagePointerPosition().y + offset.y;     }
     });
 
   }