diff --git a/packages/geo/src/lib/import-export/import-export/import-export.component.html b/packages/geo/src/lib/import-export/import-export/import-export.component.html index 8657764df4..68a2186ae6 100644 --- a/packages/geo/src/lib/import-export/import-export/import-export.component.html +++ b/packages/geo/src/lib/import-export/import-export/import-export.component.html @@ -13,10 +13,10 @@
- - + {{'igo.geo.importExportForm.importClarifications' | translate}} - +
diff --git a/packages/geo/src/lib/import-export/import-export/import-export.component.scss b/packages/geo/src/lib/import-export/import-export/import-export.component.scss index 8fcdaee323..bcbc7ba3cf 100644 --- a/packages/geo/src/lib/import-export/import-export/import-export.component.scss +++ b/packages/geo/src/lib/import-export/import-export/import-export.component.scss @@ -2,8 +2,12 @@ mat-form-field { width: 100%; } +h4 { + padding: 0 5px; +} + .igo-form { - padding: 5px; + padding: 15px 5px; } .igo-form-button-group { diff --git a/packages/geo/src/lib/import-export/import-export/import-export.component.ts b/packages/geo/src/lib/import-export/import-export/import-export.component.ts index 57c8868a8a..099b5a9c2c 100644 --- a/packages/geo/src/lib/import-export/import-export/import-export.component.ts +++ b/packages/geo/src/lib/import-export/import-export/import-export.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { Subscription, throwError } from 'rxjs'; +import { Subscription, BehaviorSubject } from 'rxjs'; import { MessageService, LanguageService } from '@igo2/core'; @@ -29,7 +29,7 @@ export class ImportExportComponent implements OnDestroy, OnInit { public formats = ExportFormat; public layers: VectorLayer[]; public inputProj: string = 'EPSG:4326'; - public loading = false; + public loading$ = new BehaviorSubject(false); private layers$$: Subscription; @@ -58,26 +58,30 @@ export class ImportExportComponent implements OnDestroy, OnInit { } importFiles(files: File[]) { - this.loading = true; + this.loading$.next(true); for (const file of files) { - this.importService - .import(file, this.inputProj) - .subscribe( - (features: Feature[]) => this.onFileImportSuccess(file, features), - (error: Error) => this.onFileImportError(file, error) - ); + this.importService.import(file, this.inputProj).subscribe( + (features: Feature[]) => this.onFileImportSuccess(file, features), + (error: Error) => this.onFileImportError(file, error), + () => { + this.loading$.next(false); + } + ); } } handleExportFormSubmit(data: ExportOptions) { - this.loading = true; + this.loading$.next(true); const layer = this.map.getLayerById(data.layer); const olFeatures = layer.dataSource.ol.getFeatures(); this.exportService .export(olFeatures, data.format, layer.title, this.map.projection) .subscribe( - () => { this.loading = false;}, - (error: Error) => this.onFileExportError(error) + () => {}, + (error: Error) => this.onFileExportError(error), + () => { + this.loading$.next(false); + } ); } @@ -89,7 +93,6 @@ export class ImportExportComponent implements OnDestroy, OnInit { } private onFileImportSuccess(file: File, features: Feature[]) { - this.loading = false; handleFileImportSuccess( file, features, @@ -100,7 +103,7 @@ export class ImportExportComponent implements OnDestroy, OnInit { } private onFileImportError(file: File, error: Error) { - this.loading = false; + this.loading$.next(false); handleFileImportError( file, error, @@ -110,7 +113,7 @@ export class ImportExportComponent implements OnDestroy, OnInit { } private onFileExportError(error: Error) { - this.loading = false; + this.loading$.next(false); handleFileExportError(error, this.messageService, this.languageService); } } diff --git a/packages/geo/src/lib/import-export/shared/export.service.ts b/packages/geo/src/lib/import-export/shared/export.service.ts index bcba80ad8c..0d704220b1 100644 --- a/packages/geo/src/lib/import-export/shared/export.service.ts +++ b/packages/geo/src/lib/import-export/shared/export.service.ts @@ -9,13 +9,15 @@ import OlFeature from 'ol/Feature'; import { downloadContent } from './export.utils'; import { ExportFormat } from './export.type'; -import { ExportInvalidFileError, ExportNothingToExportError } from './export.errors'; +import { + ExportInvalidFileError, + ExportNothingToExportError +} from './export.errors'; @Injectable({ providedIn: 'root' }) export class ExportService { - static ogreFormats = { GML: 'gml', GPX: 'gpx', @@ -39,15 +41,26 @@ export class ExportService { projectionOut = 'EPSG:4326' ): Observable { const exportOlFeatures = olFeatures.map((olFeature: OlFeature) => { - const keys = olFeature.getKeys().filter((key: string) => !key.startsWith('_')); - const properties = keys.reduce((acc: object, key: string) => { - acc[key] = olFeature.get(key); - return acc; - }, {geometry: olFeature.getGeometry()}); + const keys = olFeature + .getKeys() + .filter((key: string) => !key.startsWith('_')); + const properties = keys.reduce( + (acc: object, key: string) => { + acc[key] = olFeature.get(key); + return acc; + }, + { geometry: olFeature.getGeometry() } + ); return new OlFeature(properties); }); - return this.exportAsync(exportOlFeatures, format, title, projectionIn, projectionOut); + return this.exportAsync( + exportOlFeatures, + format, + title, + projectionIn, + projectionOut + ); } private exportAsync( @@ -68,15 +81,36 @@ export class ExportService { if (ogreFormats.indexOf(format) >= 0) { if (this.ogreUrl === undefined) { if (ExportService.noOgreFallbacks.indexOf(format) >= 0) { - this.exportToFile(olFeatures, observer, format, title, projectionIn, projectionOut); + this.exportToFile( + olFeatures, + observer, + format, + title, + projectionIn, + projectionOut + ); } else { observer.error(new ExportInvalidFileError()); } return; } - this.exportWithOgre(olFeatures, observer, format, title, projectionIn, projectionOut); + this.exportWithOgre( + olFeatures, + observer, + format, + title, + projectionIn, + projectionOut + ); } else { - this.exportToFile(olFeatures, observer, format, title, projectionIn, projectionOut); + this.exportToFile( + olFeatures, + observer, + format, + title, + projectionIn, + projectionOut + ); } }; @@ -120,9 +154,10 @@ export class ExportService { featureNS: 'http://example.com/feature' }); - const url = `${this.ogreUrl}/convert`; + const url = `${this.ogreUrl}/convertJson`; const form = document.createElement('form'); form.setAttribute('method', 'post'); + form.setAttribute('target', '_blank'); form.setAttribute('action', url); const geojsonField = document.createElement('input'); @@ -132,7 +167,10 @@ export class ExportService { form.appendChild(geojsonField); const outputNameField = document.createElement('input'); - const outputName = format === 'Shapefile' ? `${title}.zip` : title; + const outputName = + format === 'Shapefile' + ? `${title}.zip` + : `${title}.${format.toLowerCase()}`; outputNameField.setAttribute('type', 'hidden'); outputNameField.setAttribute('name', 'outputName'); outputNameField.setAttribute('value', outputName); @@ -141,7 +179,7 @@ export class ExportService { const ogreFormat = ExportService.ogreFormats[format]; const outputFormatField = document.createElement('input'); outputFormatField.setAttribute('type', 'hidden'); - outputFormatField.setAttribute('name', 'formatOutput'); + outputFormatField.setAttribute('name', 'format'); outputFormatField.setAttribute('value', ogreFormat); form.appendChild(outputFormatField); @@ -153,10 +191,15 @@ export class ExportService { } private nothingToExport(olFeatures: OlFeature[], format: string): boolean { - if (olFeatures.length === 0) { return true; } + if (olFeatures.length === 0) { + return true; + } if (format === 'GPX') { - const pointOrLine = olFeatures.find((olFeature) => { - return ['Point', 'LineString'].indexOf(olFeature.getGeometry().getType()) >= 0; + const pointOrLine = olFeatures.find(olFeature => { + return ( + ['Point', 'LineString'].indexOf(olFeature.getGeometry().getType()) >= + 0 + ); }); return pointOrLine === undefined; } diff --git a/packages/geo/src/lib/import-export/shared/import.service.ts b/packages/geo/src/lib/import-export/shared/import.service.ts index 0467d8c4b5..d094088f35 100644 --- a/packages/geo/src/lib/import-export/shared/import.service.ts +++ b/packages/geo/src/lib/import-export/shared/import.service.ts @@ -10,14 +10,18 @@ import * as olformat from 'ol/format'; import OlFeature from 'ol/Feature'; import { Feature } from '../../feature/shared/feature.interfaces'; -import { ImportInvalidFileError, ImportUnreadableFileError, ImportSizeError, ImportSRSError } from './import.errors'; +import { + ImportInvalidFileError, + ImportUnreadableFileError, + ImportSizeError, + ImportSRSError +} from './import.errors'; import { computeLayerTitleFromFile, getFileExtension } from './import.utils'; @Injectable({ providedIn: 'root' }) export class ImportService { - static allowedMimeTypes = [ 'application/gml+xml', 'application/vnd.google-earth.kml+xml', @@ -31,36 +35,47 @@ export class ImportService { 'application/x-zip' ]; - static allowedExtensions = [ - 'geojson', - 'kml', - 'gpx', - 'json', - 'gml' - ]; + static allowedExtensions = ['geojson', 'kml', 'gpx', 'json', 'gml']; private ogreUrl: string; - constructor( - private http: HttpClient, - private config: ConfigService - ) { + constructor(private http: HttpClient, private config: ConfigService) { this.ogreUrl = this.config.getConfig('importExport.url'); } - import(file: File, projectionIn = 'EPSG:4326', projectionOut = 'EPSG:4326'): Observable { + import( + file: File, + projectionIn = 'EPSG:4326', + projectionOut = 'EPSG:4326' + ): Observable { return this.importAsync(file, projectionIn, projectionOut); } - private getFileImporter(file: File): (file: File, observer: Observer, projectionIn: string, projectionOut: string) => void { + private getFileImporter( + file: File + ): ( + file: File, + observer: Observer, + projectionIn: string, + projectionOut: string + ) => void { const extension = getFileExtension(file); const mimeType = file.type; - const allowedMimeTypes = [...ImportService.allowedMimeTypes, ...ImportService.allowedZipMimeTypes]; + const allowedMimeTypes = [ + ...ImportService.allowedMimeTypes, + ...ImportService.allowedZipMimeTypes + ]; const allowedExtensions = ImportService.allowedExtensions; - if (allowedMimeTypes.indexOf(mimeType) < 0 && allowedExtensions.indexOf(extension) < 0) { + if ( + allowedMimeTypes.indexOf(mimeType) < 0 && + allowedExtensions.indexOf(extension) < 0 + ) { return undefined; - } else if (mimeType === 'application/json' || ['json', 'geojson', 'kml'].indexOf(extension) >= 0) { + } else if ( + mimeType === 'application/json' || + ['json', 'geojson', 'kml'].indexOf(extension) >= 0 + ) { return this.importFile; } else if (this.ogreUrl !== undefined) { return this.importFileWithOgre; @@ -69,7 +84,11 @@ export class ImportService { return undefined; } - private importAsync(file: File, projectionIn: string, projectionOut: string): Observable { + private importAsync( + file: File, + projectionIn: string, + projectionOut: string + ): Observable { const doImport = (observer: Observer) => { if (file.size >= 30000000) { observer.error(new ImportSizeError()); @@ -87,7 +106,12 @@ export class ImportService { return new Observable(doImport); } - private importFile(file: File, observer: Observer, projectionIn: string, projectionOut: string) { + private importFile( + file: File, + observer: Observer, + projectionIn: string, + projectionOut: string + ) { const reader = new FileReader(); reader.onload = (event: any) => { @@ -113,7 +137,12 @@ export class ImportService { reader.readAsText(file, 'UTF-8'); } - private importFileWithOgre(file: File, observer: Observer, projectionIn: string, projectionOut: string) { + private importFileWithOgre( + file: File, + observer: Observer, + projectionIn: string, + projectionOut: string + ) { const url = `${this.ogreUrl}/convert`; const formData = new FormData(); formData.append('upload', file); @@ -122,39 +151,48 @@ export class ImportService { formData.append('formatOutput', 'GEOJSON'); formData.append('skipFailures', ''); - this.http - .post(url, formData, {headers: new HttpHeaders()}) - .subscribe( - (response: {errors?: string[]} | object | null) => { - if (response === null) { - observer.error(new ImportUnreadableFileError()); - return; - } - - const errors = (response as any).errors || []; - if (errors.length > 0) { - observer.error(new ImportUnreadableFileError()); - } else { - const features = this.parseFeaturesFromGeoJSON(file, response, projectionOut); - observer.next(features); - observer.complete(); - } - }, - (error: any) => { - error.error.caught = true; - const errMsg = error.error.msg; - if (errMsg === 'No valid files found') { - observer.error(new ImportInvalidFileError()); - } else if (errMsg.startWith("ERROR 1: Failed to process SRS definition")) { - observer.error(new ImportSRSError()); - } else { - observer.error(new ImportUnreadableFileError()); - } + this.http.post(url, formData, { headers: new HttpHeaders() }).subscribe( + (response: { errors?: string[] } | object | null) => { + if (response === null) { + observer.error(new ImportUnreadableFileError()); + return; } - ); + + const errors = (response as any).errors || []; + if (errors.length > 0) { + observer.error(new ImportUnreadableFileError()); + } else { + const features = this.parseFeaturesFromGeoJSON( + file, + response, + projectionOut + ); + observer.next(features); + observer.complete(); + } + }, + (error: any) => { + error.error.caught = true; + const errMsg = error.error.msg || ''; + if (errMsg === 'No valid files found') { + observer.error(new ImportInvalidFileError()); + } else if ( + errMsg.startWith('ERROR 1: Failed to process SRS definition') + ) { + observer.error(new ImportSRSError()); + } else { + observer.error(new ImportUnreadableFileError()); + } + } + ); } - private parseFeaturesFromFile(file: File, data: string, projectionIn: string, projectionOut: string): Feature[] { + private parseFeaturesFromFile( + file: File, + data: string, + projectionIn: string, + projectionOut: string + ): Feature[] { const extension = getFileExtension(file); const mimeType = file.type; @@ -172,7 +210,7 @@ export class ImportService { case 'kml': format = new olformat.KML(); break; - case 'gpx': + case 'gpx': format = new olformat.GPX(); break; case 'gml': @@ -201,7 +239,11 @@ export class ImportService { return features; } - private parseFeaturesFromGeoJSON(file: File, data: object, projectionOut: string): Feature[] { + private parseFeaturesFromGeoJSON( + file: File, + data: object, + projectionOut: string + ): Feature[] { const olFormat = new olformat.GeoJSON(); const olFeatures = olFormat.readFeatures(data); const features = olFeatures.map((olFeature: OlFeature) => {