diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 1d8c39d..e8ed7d7 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -3334,21 +3334,6 @@
"dev": true,
"optional": true
},
- "angular-file-uploader": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/angular-file-uploader/-/angular-file-uploader-7.0.3.tgz",
- "integrity": "sha512-0wkr/3Vn3toG/2sTSiD5vJ5EK3HKYXwzyFd3M3hRegcHhQnagN34licn4r+CBWJQQED5aRWUVB9OWOCtuNwimw==",
- "requires": {
- "tslib": "^2.0.0"
- },
- "dependencies": {
- "tslib": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
- "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
- }
- }
- },
"ansi-colors": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 84fee1d..a3bca3c 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -26,7 +26,6 @@
"@angular/platform-browser-dynamic": "~9.1.9",
"@angular/router": "~9.1.9",
"@types/uuid": "8.3.0",
- "angular-file-uploader": "^7.0.3",
"bootstrap": "~4.3.1",
"jquery": "3.5.1",
"ngx-logger": "4.1.9",
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 8b4153a..ab15618 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -21,39 +21,40 @@ import {MatTabsModule} from '@angular/material/tabs';
import {ImagesComponent} from './component/images/images.component';
import {MapComponent} from './component/map/map.component';
import {AgmCoreModule} from '@agm/core';
-import {AngularFileUploaderModule} from 'angular-file-uploader';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
+import { FileUploaderComponent } from './component/file-uploader/file-uploader.component';
+import {MatSnackBarModule} from '@angular/material/snack-bar';
@NgModule({
- declarations: [LandingComponent, ImagesComponent, MapComponent],
- imports: [
- ReactiveFormsModule,
- BrowserModule,
- BrowserAnimationsModule,
- LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
- HttpClientModule,
- MatFormFieldModule,
- FormsModule,
- MatButtonModule,
- MatInputModule,
- MatSlideToggleModule,
- MatListModule,
- MatExpansionModule,
- MatCardModule,
- MatPaginatorModule,
- MatTableModule,
- MatTabsModule,
- // GoogleMapsModule,
- AgmCoreModule.forRoot({
- apiKey: 'AIzaSyBDgLJ1sL48IQg7e5jrwmUyXkqeEfVnf78'
- /* apiKey is required, unless you are a
- premium customer, in which case you can
- use clientId
- */
- }),
- AngularFileUploaderModule,
- MatProgressSpinnerModule,
- ],
+ declarations: [LandingComponent, ImagesComponent, MapComponent, FileUploaderComponent],
+ imports: [
+ ReactiveFormsModule,
+ BrowserModule,
+ BrowserAnimationsModule,
+ LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
+ HttpClientModule,
+ MatFormFieldModule,
+ FormsModule,
+ MatButtonModule,
+ MatInputModule,
+ MatSlideToggleModule,
+ MatListModule,
+ MatExpansionModule,
+ MatCardModule,
+ MatPaginatorModule,
+ MatTableModule,
+ MatTabsModule,
+ MatSnackBarModule,
+ // GoogleMapsModule,
+ AgmCoreModule.forRoot({
+ apiKey: 'AIzaSyBDgLJ1sL48IQg7e5jrwmUyXkqeEfVnf78'
+ /* apiKey is required, unless you are a
+ premium customer, in which case you can
+ use clientId
+ */
+ }),
+ MatProgressSpinnerModule,
+ ],
// enables injecting
providers: [
RestService,
diff --git a/frontend/src/app/component/file-uploader/file-uploader.component.css b/frontend/src/app/component/file-uploader/file-uploader.component.css
new file mode 100644
index 0000000..b600584
--- /dev/null
+++ b/frontend/src/app/component/file-uploader/file-uploader.component.css
@@ -0,0 +1,3 @@
+.margin-right {
+ margin-right: 1em;
+}
diff --git a/frontend/src/app/component/file-uploader/file-uploader.component.html b/frontend/src/app/component/file-uploader/file-uploader.component.html
new file mode 100644
index 0000000..e1318c8
--- /dev/null
+++ b/frontend/src/app/component/file-uploader/file-uploader.component.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+ Tag
+
+
+
+
+ Name
+
+
+
+
+ Longitude
+
+
+
+
+ Longitude
+
+
+
+
+
+
+ {{currentFileName}}
+
diff --git a/frontend/src/app/component/file-uploader/file-uploader.component.ts b/frontend/src/app/component/file-uploader/file-uploader.component.ts
new file mode 100644
index 0000000..44def82
--- /dev/null
+++ b/frontend/src/app/component/file-uploader/file-uploader.component.ts
@@ -0,0 +1,143 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {RestService} from '../../services/rest.service';
+import {NGXLogger} from 'ngx-logger';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {ImageMetadata} from '../../interfaces/interface';
+
+@Component({
+ selector: 'app-file-uploader',
+ templateUrl: './file-uploader.component.html',
+ styleUrls: ['./file-uploader.component.css']
+})
+export class FileUploaderComponent implements OnInit {
+
+ constructor(private restService: RestService, private logger: NGXLogger, private snackBar: MatSnackBar) {
+ }
+
+ @Input()
+ mode: string; // post, put
+ @Input()
+ filename: string; // if mode==put && without file ending
+ @Input()
+ meta: ImageMetadata;
+
+ @Output()
+ reload: EventEmitter = new EventEmitter();
+
+ currentFileName: string;
+
+ id: string;
+
+ file: ArrayBuffer;
+ tag: string;
+ longitude: string;
+ latitude: string;
+ name: string;
+
+ private buildForm(): FormData {
+ const mainForm: FormData = new FormData();
+ const metaDict: {} = {};
+
+ if (this.filename) {
+ this.id = this.filename;
+ } else {
+ this.id = this.currentFileName;
+ }
+
+ metaDict['filename'] = this.currentFileName;
+
+ if (this.mode === 'post') {
+ if (this.longitude && this.latitude) {
+ metaDict['longitude'] = Number.parseFloat(this.longitude);
+ metaDict['latitude'] = Number.parseFloat(this.latitude);
+ } else if (this.meta.longitude && this.meta.latitude) {
+ metaDict['longitude'] = this.meta.longitude;
+ metaDict['latitude'] = this.meta.latitude;
+ } else {
+ throw new Error('Long/Lat necessary if new file!');
+ }
+ }
+
+ if (this.name) {
+ metaDict['name'] = this.name;
+ } else if (this.meta.name) {
+ metaDict['name'] = this.meta.name;
+ } else {
+ if (this.mode === 'post') {
+ throw new Error('Name necessary if new file');
+ }
+ }
+
+ if (this.tag) {
+ metaDict['tag'] = this.tag;
+ } else if (this.meta.tag) {
+ metaDict['tag'] = this.meta.tag;
+ }
+
+ metaDict['datetime'] = new Date().toISOString();
+
+ mainForm.append('id', this.id.split('.')[0]);
+ mainForm.append('metadata', JSON.stringify(metaDict));
+
+ if (this.file) {
+ let binary = '';
+ const bytes = new Uint8Array( this.file );
+ const len = bytes.byteLength;
+ for (let i = 0; i < len; i++) {
+ binary += String.fromCharCode( bytes[ i ] );
+ }
+ mainForm.append('image_data', window.btoa( binary ));
+ } else {
+ throw new Error('No file to upload!');
+ }
+
+ return mainForm;
+ }
+
+ ngOnInit(): void {
+ }
+
+ onFileSelected(fileEvent: Event) {
+ this.logger.debug('fileEvent', fileEvent);
+ const target: HTMLInputElement = fileEvent.target as HTMLInputElement;
+
+ if (typeof (FileReader) !== 'undefined') {
+ const reader = new FileReader();
+ const file: File = target.files[0];
+
+ reader.onload = (e: any) => {
+ this.file = e.target.result;
+ this.currentFileName = file.name;
+ this.logger.debug('Selected file', this.currentFileName, this.file);
+ };
+
+ reader.readAsArrayBuffer(file);
+ }
+ }
+
+ async postFile() {
+ try {
+ const formData = this.buildForm();
+ this.logger.debug('Build form data', formData);
+
+ let response;
+ if (this.mode === 'post') {
+ response = await this.restService.postImage(formData).toPromise();
+ } else if (this.mode === 'put') {
+ response = await this.restService.updateImage(this.id, formData).toPromise();
+ } else {
+ // noinspection ExceptionCaughtLocallyJS
+ throw new Error('Unknown mode: ' + this.mode);
+ }
+
+ this.logger.debug('Response', response);
+
+ this.snackBar.open('Upload successful', 'Dismiss');
+ this.reload.emit(this.currentFileName);
+ } catch (err) {
+ this.logger.error('error', err);
+ this.snackBar.open(err.error.Error, 'Dismiss');
+ }
+ }
+
+}
diff --git a/frontend/src/app/component/images/images.component.html b/frontend/src/app/component/images/images.component.html
index 06b0789..e4f9976 100644
--- a/frontend/src/app/component/images/images.component.html
+++ b/frontend/src/app/component/images/images.component.html
@@ -8,7 +8,7 @@
+ style="width: 100%; margin: auto" [alt]="image.filename"/>
-
-
+
-
+
@@ -48,7 +52,7 @@
showFirstLastButtons
>
+
Upload New Image
-
-
+
+
diff --git a/frontend/src/app/component/images/images.component.ts b/frontend/src/app/component/images/images.component.ts
index 7ae33c2..55cd96d 100644
--- a/frontend/src/app/component/images/images.component.ts
+++ b/frontend/src/app/component/images/images.component.ts
@@ -1,9 +1,9 @@
-import {AfterViewInit, Component, Input, OnInit} from '@angular/core';
+import {AfterViewInit, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {PageEvent} from '@angular/material/paginator';
import {ImageMetadata} from '../../interfaces/interface';
import {RestService} from '../../services/rest.service';
import {NGXLogger} from 'ngx-logger';
-import {AngularFileUploaderConfig} from 'angular-file-uploader';
+import {MatSnackBar} from '@angular/material/snack-bar';
@Component({
selector: 'app-images',
@@ -14,19 +14,16 @@ export class ImagesComponent implements OnInit, AfterViewInit {
@Input()
images: ImageMetadata[];
+ @Output()
+ reload: EventEmitter = new EventEmitter();
pageSizeOptions: number[] = [5, 10, 25, 100];
lastPageEvent: PageEvent;
- imageUploadConfig: AngularFileUploaderConfig = {
- uploadAPI: {
- url: this.restService.getPostImageUrl()
- }
- };
-
constructor(
public restService: RestService,
private logger: NGXLogger,
+ private snackbar: MatSnackBar
) {
}
@@ -46,24 +43,18 @@ export class ImagesComponent implements OnInit, AfterViewInit {
return this.paginationGetStart() + this.lastPageEvent.pageSize;
}
- getImageUpdateConfig(filename: string): AngularFileUploaderConfig {
- return {
- uploadAPI: {
- url: this.restService.getUpdateImageUrl(filename),
- method: 'PUT'
- }
- };
- }
-
deleteImage(image: ImageMetadata) {
- this.restService.deleteImage(image.filename).toPromise().then(() => {
+ this.restService.deleteImage(image.filename.split('.')[0]).toPromise().then(() => {
this.images = this.images.filter(im => im !== image);
+ this.snackbar.open('Successfully deleted ' + image.filename, 'Dismiss');
+ this.reload.emit();
}).catch(err => this.logger.error(err));
}
loadImage(i: number, id: string): void {
this.restService.getImage(id).toPromise().then(image => {
this.images[i].image_b64 = 'data:image/jpg;base64,' + image.image_data;
+ this.logger.debug('image_b64', i, id, this.images[i].image_b64);
}).catch(err => {
this.logger.error('loadImage', i, id, err);
});
diff --git a/frontend/src/app/component/landing/landing.component.html b/frontend/src/app/component/landing/landing.component.html
index f665cae..a1f46d5 100644
--- a/frontend/src/app/component/landing/landing.component.html
+++ b/frontend/src/app/component/landing/landing.component.html
@@ -6,11 +6,12 @@
- 0">
+ 0; else noImages">
-
+
+No images found
diff --git a/frontend/src/app/component/map/map.component.css b/frontend/src/app/component/map/map.component.css
index 55199f0..d71a3d6 100644
--- a/frontend/src/app/component/map/map.component.css
+++ b/frontend/src/app/component/map/map.component.css
@@ -1 +1,3 @@
-agm-map { height: 50vh; /* height is required */ }
+agm-map {
+ height: 50vh; /* height is required */
+}
diff --git a/frontend/src/app/component/map/map.component.html b/frontend/src/app/component/map/map.component.html
index cf00c2e..acce0be 100644
--- a/frontend/src/app/component/map/map.component.html
+++ b/frontend/src/app/component/map/map.component.html
@@ -1,9 +1,9 @@
Map
+ [latitude]="latitude"
+ [longitude]="longitude"
+ [zoom]="2">
{
- return this.http.get(this.getImageUrl + '/' + id).pipe(
+ public getImage(id: string): Observable {
+ return this.http.get(this.getImageUrl + '/' + id).pipe(
tap(next => this.logger.debug('getImage', id, next))
);
}
@@ -61,26 +62,26 @@ export class RestService {
);
}
- // public postImage(id: string, image: any): Observable {
- // return this.http.post(this.postImageUrl + '/' + id, image).pipe(
- // tap(next => this.logger.debug('postImage', id, image, next))
- // );
- // }
-
- public getPostImageUrl(): string {
- return this.postImageUrl;
+ public postImage(imageForm: FormData): Observable {
+ return this.http.post(this.postImageUrl, imageForm).pipe(
+ tap(next => this.logger.debug('postImage', imageForm, next))
+ );
}
- // public updateImage(id: string, image: any): Observable {
- // return this.http.put(this.updateImageUrl + '/' + id, image).pipe(
- // tap(next => this.logger.debug('updateImage', id, image, next))
- // );
+ // public getPostImageUrl(): string {
+ // return this.postImageUrl;
// }
- public getUpdateImageUrl(filenameWithExtension: string): string {
- return this.updateImageUrl + '/' + RestService.stripUploadFileName(filenameWithExtension);
+ public updateImage(id: string, imageForm: FormData): Observable {
+ return this.http.put(this.updateImageUrl + '/' + id, imageForm).pipe(
+ tap(next => this.logger.debug('updateImage', id, imageForm, next))
+ );
}
+ // public getUpdateImageUrl(filenameWithExtension: string): string {
+ // return this.updateImageUrl + '/' + RestService.stripUploadFileName(filenameWithExtension);
+ // }
+
public healthCheck(): Observable {
return this.http.get(this.healthCheckUrl).pipe(
tap(next => this.logger.debug('healthCheck', next))