This is an automated email from the ASF dual-hosted git repository. riemer pushed a commit to branch fix-image-display in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 1167b1e3f634166e201c0e85b240d5698faadc37 Author: Dominik Riemer <[email protected]> AuthorDate: Thu Jun 6 15:59:31 2024 +0200 fix: Image rendering in data explorer --- .../streampipes/ps/DataLakeImageResource.java | 10 +- .../image-bar-preview.component.html | 2 +- .../image-bar-preview.component.scss | 1 + .../image/image-bar/image-bar.component.html | 114 ++++++++++----------- .../image/image-bar/image-bar.component.scss | 4 - .../image-container.component.html} | 18 ++-- .../image-container.component.scss} | 7 +- .../image-container/image-container.component.ts | 81 +++++++++++++++ .../image/image-viewer/image-viewer.component.html | 30 +++--- .../widgets/image/image-widget.component.html | 31 +++--- .../widgets/image/image-widget.component.ts | 21 +--- ui/src/app/data-explorer/data-explorer.module.ts | 2 + 12 files changed, 191 insertions(+), 130 deletions(-) diff --git a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeImageResource.java b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeImageResource.java index 6e71ec1a36..4dd67466c6 100644 --- a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeImageResource.java +++ b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeImageResource.java @@ -21,21 +21,23 @@ package org.apache.streampipes.ps; import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource; import org.apache.streampipes.storage.management.StorageDispatcher; +import org.apache.commons.io.IOUtils; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; - -import java.io.InputStream; +import java.io.IOException; @RestController @RequestMapping("/api/v4/datalake/images") public class DataLakeImageResource extends AbstractAuthGuardedRestResource { @GetMapping(path = "{imageId}", produces = "image/jpeg") - public ResponseEntity<InputStream> getImage(@PathVariable("imageId") String imageId) { - return ok(StorageDispatcher.INSTANCE.getNoSqlStore().getImageStorage().getImageBytes(imageId)); + public ResponseEntity<byte[]> getImage(@PathVariable("imageId") String imageId) throws IOException { + var image = StorageDispatcher.INSTANCE.getNoSqlStore().getImageStorage().getImageBytes(imageId); + return ok(IOUtils.toByteArray(image)); } } diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html index 6bb8c48acc..f2c949996e 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html +++ b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html @@ -18,7 +18,7 @@ <img [ngClass]="focus ? 'imagePreview imageFocus' : 'imagePreview'" - [style.height.px]="imagePreviewHeight" + [style.height.px]="imagePreviewHeight - 40" *ngIf="showImage" [src]="imagePath" /> diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss index 499314a337..adc14cfa96 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss +++ b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss @@ -20,6 +20,7 @@ height: 65px; margin-left: 10px; margin-right: 10px; + cursor: pointer; } .imageFocus { diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html index 6606552180..e1ba9ec18f 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html +++ b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html @@ -16,67 +16,59 @@ ~ --> -<div fxLayout="row" fxLayoutAlign="center center"> - <div>{{ selectedIndex + 1 }} / {{ maxImages }}</div> -</div> - -<div fxLayout="row" fxLayoutAlign="space-around center"> - <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"> - <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 fxFlex="100" fxLayout="column"> + <div fxLayout="row" fxLayoutAlign="center center" class="mb-10"> + <div>{{ selectedIndex + 1 }} / {{ maxImages }}</div> </div> - <button mat-icon-button (click)="previousImage()"> - <mat-icon>keyboard_arrow_right</mat-icon> - </button> - <button mat-icon-button (click)="goToEnd()"> - <mat-icon>skip_next</mat-icon> - </button> + <div + fxFlex + fxLayout="row" + fxLayoutAlign="center center" + class="imageBar" + fxLayoutGap="10px" + > + <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> + <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> + <sp-image-bar-preview + [imagePreviewHeight]="imagePreviewHeight" + [imageSrc]="_imageRoutes[selectedIndex]" + [focus]="true" + (click)="changeImage(selectedIndex)" + ></sp-image-bar-preview> + <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> + <button mat-icon-button (click)="previousImage()"> + <mat-icon>keyboard_arrow_right</mat-icon> + </button> + <button mat-icon-button (click)="goToEnd()"> + <mat-icon>skip_next</mat-icon> + </button> + </div> </div> diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss index 0c64d80038..13cbc4aacb 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss +++ b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss @@ -15,7 +15,3 @@ * limitations under the License. * */ - -.imageBar { - height: 70px; -} diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.html similarity index 73% copy from ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html copy to ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.html index 6bb8c48acc..5536e7c2bb 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html +++ b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.html @@ -15,10 +15,14 @@ ~ limitations under the License. ~ --> - -<img - [ngClass]="focus ? 'imagePreview imageFocus' : 'imagePreview'" - [style.height.px]="imagePreviewHeight" - *ngIf="showImage" - [src]="imagePath" -/> +<div class="main-image" fxLayoutAlign="center center" fxFlex="100"> + <img + class="main-image" + [src]="imagePath" + [style.width.px]="imageWidth" + [style.height.px]="imageHeight" + #mainImg + *ngIf="showImage" + (load)="onImageLoaded()" + /> +</div> diff --git a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.scss similarity index 88% copy from ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss copy to ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.scss index 0c64d80038..754495c653 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.scss +++ b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.scss @@ -16,6 +16,9 @@ * */ -.imageBar { - height: 70px; +.main-image { + margin-left: auto; + margin-right: auto; + max-height: 100%; + max-width: 100%; } diff --git a/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.ts b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.ts new file mode 100644 index 0000000000..ac5123b1c6 --- /dev/null +++ b/ui/src/app/data-explorer/components/widgets/image/image-container/image-container.component.ts @@ -0,0 +1,81 @@ +/* + * 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, ElementRef, Input, 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.scss'], +}) +export class SpImageContainerComponent { + imagePath: SafeUrl; + showImage = false; + + @ViewChild('mainImg') imageRef: ElementRef; + + @Input() + public canvasHeight = 500; + + @Input() + public canvasWidth = 800; + + imageWidth = 0; + imageHeight = 0; + + constructor() {} + + onImageLoaded() { + const naturalImageWidth = ( + this.imageRef.nativeElement as HTMLImageElement + ).naturalWidth; + const naturalImageHeight = ( + this.imageRef.nativeElement as HTMLImageElement + ).naturalHeight; + this.calculateImageDimensions(naturalImageWidth, naturalImageHeight); + } + + calculateImageDimensions(imageWidth: number, imageHeight: number): void { + const canvasAspectRatio = this.canvasWidth / this.canvasHeight; + const imageAspectRatio = imageWidth / imageHeight; + + let finalWidth: number; + let finalHeight: number; + + if (imageAspectRatio > canvasAspectRatio) { + finalWidth = this.canvasWidth; + finalHeight = this.canvasWidth / imageAspectRatio; + } else { + finalHeight = this.canvasHeight; + finalWidth = this.canvasHeight * imageAspectRatio; + } + + this.imageWidth = finalWidth; + this.imageHeight = finalHeight; + } + + @Input() + set imageSrc(src: Observable<Blob>) { + src.subscribe(url => { + this.imagePath = url; + this.showImage = true; + }); + } +} diff --git a/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html b/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html index c6bfa13867..59d4bd48ad 100644 --- a/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html +++ b/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html @@ -17,22 +17,22 @@ --> <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 "> - <sp-image-container - *ngIf="imagesRoutes.length > 0" - [imageSrc]="imagesRoutes[imagesIndex]" - [canvasWidth]="canvasWidth" - [canvasHeight]="canvasHeight" - > - </sp-image-container> - </div> - </div> - <br /> - + <div [ngStyle]="{ width: canvasWidth, height: canvasHeight + 'px' }"> + <sp-image-container + fxFlex="100" + *ngIf="imagesRoutes.length > 0" + [imageSrc]="imagesRoutes[imagesIndex]" + [canvasWidth]="canvasWidth" + [canvasHeight]="canvasHeight" + > + </sp-image-container> + </div> + <div + [ngStyle]="{ width: '100%', height: imagePreviewHeight + 'px' }" + fxLayout="row" + > <sp-image-bar - style="width: 100%" + fxFlex="100" [imagesRoutes]="imagesRoutes" [selectedIndex]="imagesIndex" [enableShortCuts]="true" 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 519fd70ebd..b492ab54e0 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 @@ -16,28 +16,21 @@ ~ --> -<div - fxFlex="100" - fxLayoutAlign="center center" - fxLayout="column" - class="main-panel" -> +<div fxFlex="100" fxLayoutAlign="center center" fxLayout="column"> <sp-no-data-in-date-range *ngIf="showNoDataInDateRange" [viewDateRange]="timeSettings" ></sp-no-data-in-date-range> - <div class="widget-inner-content"> - <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> + <sp-image-viewer + *ngIf="imagePaths.length > 0" + [imagesRoutes]="imagePaths" + [canvasHeight]="canvasHeight" + [canvasWidth]="canvasWidth" + [imagePreviewHeight]="imagePreviewHeight" + [ngStyle]="{ + width: '100%', + height: '100%' + }" + ></sp-image-viewer> </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 4d8d7dc83c..c14cb303de 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 @@ -21,9 +21,6 @@ import { MatSort } from '@angular/material/sort'; import { BaseDataExplorerWidgetDirective } from '../base/base-data-explorer-widget.directive'; import { DataExplorerField, - DatalakeQueryParameterBuilder, - DatalakeQueryParameters, - EventPropertyUnion, SpQueryResult, } from '@streampipes/platform-services'; import { ImageWidgetModel } from './model/image-widget.model'; @@ -43,9 +40,6 @@ export class ImageWidgetComponent imageBaseUrl: string; imagePaths = []; - availableColumns: EventPropertyUnion[]; - selectedColumn: EventPropertyUnion; - canvasHeight; canvasWidth; imagePreviewHeight; @@ -58,24 +52,17 @@ export class ImageWidgetComponent super.ngOnInit(); this.onResize( this.gridsterItemComponent.width, - this.gridsterItemComponent.height - 40, + this.gridsterItemComponent.height - 53, ); this.imageBaseUrl = this.dataLakeRestService.dataLakeUrl + '/images/'; } refreshView() {} - buildQuery(): DatalakeQueryParameters { - return DatalakeQueryParameterBuilder.create( - this.timeSettings.startTime, - this.timeSettings.endTime, - ).build(); - } - onResize(width: number, height: number) { - this.canvasHeight = height - 50; - this.canvasWidth = width - 20; - this.imagePreviewHeight = width / 14; + this.canvasHeight = height * 0.8; + this.canvasWidth = width; + this.imagePreviewHeight = height * 0.2; } beforeDataFetched() { diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts index aa0b3823b9..6d31754b25 100644 --- a/ui/src/app/data-explorer/data-explorer.module.ts +++ b/ui/src/app/data-explorer/data-explorer.module.ts @@ -112,6 +112,7 @@ import { MatDividerModule } from '@angular/material/divider'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { SpImageContainerComponent } from './components/widgets/image/image-container/image-container.component'; @NgModule({ imports: [ @@ -225,6 +226,7 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle'; SpValueHeatmapWidgetConfigComponent, SpHistogramChartWidgetConfigComponent, SpPieChartWidgetConfigComponent, + SpImageContainerComponent, SpInvalidConfigurationComponent, SpConfigurationBoxComponent, SpVisualizationConfigOuterComponent,
