@igo2/context
Version:
1,089 lines (1,080 loc) • 260 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Optional, Component, Input, NgModule, EventEmitter, Output, Directive, Self, HostListener, ChangeDetectionStrategy } from '@angular/core';
import { NgIf, NgFor, AsyncPipe, NgClass, KeyValuePipe } from '@angular/common';
import * as i1$3 from '@angular/forms';
import { Validators, FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
import * as i5 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i7 from '@angular/material/button-toggle';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import * as i12 from '@angular/material/core';
import { MatOptionModule } from '@angular/material/core';
import * as i13 from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
import * as i6$1 from '@angular/material/form-field';
import { MatFormFieldModule } from '@angular/material/form-field';
import * as i7$1 from '@angular/material/input';
import { MatInputModule } from '@angular/material/input';
import * as i11 from '@angular/material/select';
import { MatSelectModule } from '@angular/material/select';
import { SpinnerComponent } from '@igo2/common/spinner';
import * as i1$1 from '@igo2/core/config';
import * as i4 from '@igo2/core/language';
import { IgoLanguageModule } from '@igo2/core/language';
import * as i3 from '@igo2/core/message';
import { BehaviorSubject, Subject, of, Observable, ReplaySubject, timer, EMPTY, combineLatest } from 'rxjs';
import { skip, first, catchError, tap, map, mergeMap, debounceTime, take, debounce, filter } from 'rxjs/operators';
import * as i1 from '@angular/common/http';
import * as i2 from '@igo2/auth';
import * as i8 from '@igo2/core/route';
import * as i6 from '@igo2/core/storage';
import * as i1$2 from '@igo2/geo';
import { isLayerGroup, FeatureDataSource, featureRandomStyleFunction, featureRandomStyle, VectorLayer, ClusterDataSource, isLayerGroupOptions, sortLayersByZindex, isLayerItem, moveToOlFeatures, FeatureMotion, FeatureDetailsComponent } from '@igo2/geo';
import { ObjectUtils, uuid, downloadContent, Clipboard } from '@igo2/utils';
import GeoJSON from 'ol/format/GeoJSON';
import olPoint from 'ol/geom/Point';
import Cluster from 'ol/source/Cluster';
import olVectorSource from 'ol/source/Vector';
import * as i7$2 from '@ngx-translate/core';
import * as i6$2 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i5$1 from '@angular/material/tooltip';
import { MatTooltipModule } from '@angular/material/tooltip';
import * as i3$1 from '@angular/material/list';
import { MatListModule } from '@angular/material/list';
import { CollapseDirective, CollapsibleComponent } from '@igo2/common/collapsible';
import { StopPropagationDirective } from '@igo2/common/stop-propagation';
import * as i5$2 from '@igo2/common/confirm-dialog';
import { ConfirmDialogService } from '@igo2/common/confirm-dialog';
import * as i13$1 from '@angular/material/checkbox';
import { MatCheckboxModule } from '@angular/material/checkbox';
import * as i1$4 from '@angular/material/dialog';
import { MatDialogTitle, MatDialogContent, MatDialogActions } from '@angular/material/dialog';
import * as i12$1 from '@angular/material/menu';
import { MatMenuModule } from '@angular/material/menu';
import { ActionStore, ActionbarMode, ActionbarComponent } from '@igo2/common/action';
import { ListComponent, ListItemDirective } from '@igo2/common/list';
import * as i8$1 from '@angular/material/autocomplete';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import * as i5$3 from '@angular/material/radio';
import { MatRadioModule } from '@angular/material/radio';
import * as i3$2 from '@igo2/core/media';
import * as oleasing from 'ol/easing';
import * as olproj from 'ol/proj';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { CustomHtmlComponent } from '@igo2/common/custom-html';
import * as i2$1 from '@angular/material/tabs';
import { MatTabsModule } from '@angular/material/tabs';
import * as i2$2 from '@angular/material/sidenav';
import { MatSidenavModule } from '@angular/material/sidenav';
import * as i1$5 from '@angular/platform-browser';
import { getEntityTitle } from '@igo2/common/entity';
import { FlexibleComponent } from '@igo2/common/flexible';
import { PanelComponent } from '@igo2/common/panel';
var TypePermission;
(function (TypePermission) {
TypePermission[TypePermission["null"] = 0] = "null";
TypePermission[TypePermission["read"] = 1] = "read";
TypePermission[TypePermission["write"] = 2] = "write";
})(TypePermission || (TypePermission = {}));
var Scope;
(function (Scope) {
Scope[Scope["public"] = 0] = "public";
Scope[Scope["protected"] = 1] = "protected";
Scope[Scope["private"] = 2] = "private";
})(Scope || (Scope = {}));
class ContextService {
http;
authService;
languageService;
config;
messageService;
storageService;
exportService;
route;
context$ = new BehaviorSubject(undefined);
contexts$ = new BehaviorSubject({ ours: [] });
defaultContextId$ = new BehaviorSubject(undefined);
editedContext$ = new BehaviorSubject(undefined);
importedContext = [];
toolsChanged$ = new Subject();
mapViewFromRoute = {};
options;
baseUrl;
// Until the ContextService is completely refactored, this is needed
// to track the current tools
tools;
toolbar;
get defaultContextUri() {
return (this.storageService.get('favorite.context.uri') ||
this._defaultContextUri ||
this.options.defaultContextUri);
}
set defaultContextUri(uri) {
this._defaultContextUri = uri;
}
_defaultContextUri;
constructor(http, authService, languageService, config, messageService, storageService, exportService, route) {
this.http = http;
this.authService = authService;
this.languageService = languageService;
this.config = config;
this.messageService = messageService;
this.storageService = storageService;
this.exportService = exportService;
this.route = route;
this.options = Object.assign({
basePath: 'contexts',
contextListFile: '_contexts.json',
defaultContextUri: '_default'
}, this.config.getConfig('context'));
this.baseUrl = this.options.url ?? '';
this.readParamsFromRoute();
if (this.authService.hasAuthService) {
this.authService.logged$.subscribe((logged) => {
if (logged) {
this.contexts$.pipe(skip(1), first()).subscribe(() => {
this.handleContextsChange();
});
this.loadContexts();
}
});
}
else {
this.loadContexts();
this.handleContextsChange(false);
}
}
get(permissions, hidden) {
let url = this.baseUrl + '/contexts';
if (permissions && this.authService.authenticated) {
url += '?permission=' + permissions.join();
if (hidden) {
url += '&hidden=true';
}
}
return this.http.get(url);
}
getById(id) {
const url = this.baseUrl + '/contexts/' + id;
return this.http.get(url);
}
getDetails(id) {
const url = `${this.baseUrl}/contexts/${id}/details`;
return this.http.get(url).pipe(catchError((res) => {
this.handleError(res, id);
throw res;
}));
}
getDefault() {
if (this.authService.authenticated) {
const url = this.baseUrl + '/contexts/default';
return this.http.get(url).pipe(tap((context) => {
this.defaultContextId$.next(context.id);
}));
}
else {
const uri = this.storageService.get('favorite.context.uri');
this.defaultContextId$.next(uri);
return this.getContextByUri(uri);
}
}
getProfilByUser() {
if (this.baseUrl) {
const url = this.baseUrl + '/profils?';
return this.http.get(url);
}
return of([]);
}
setDefault(id) {
if (this.authService.authenticated) {
const url = this.baseUrl + '/contexts/default';
return this.http.post(url, { defaultContextId: id });
}
else {
this.storageService.set('favorite.context.uri', id);
return of(undefined);
}
}
hideContext(id) {
const url = this.baseUrl + '/contexts/' + id + '/hide';
return this.http.post(url, {});
}
showContext(id) {
const url = this.baseUrl + '/contexts/' + id + '/show';
return this.http.post(url, {});
}
delete(id, imported = false) {
const contexts = { ours: [] };
Object.keys(this.contexts$.value).forEach((key) => (contexts[key] = this.contexts$.value[key].filter((c) => c.id !== id)));
if (imported) {
this.importedContext = this.importedContext.filter((c) => c.id !== id);
return of(this.contexts$.next(contexts));
}
const url = this.baseUrl + '/contexts/' + id;
return this.http.delete(url).pipe(tap(() => {
this.contexts$.next(contexts);
}));
}
create(context) {
const url = this.baseUrl + '/contexts';
return this.http.post(url, context).pipe(map((contextCreated) => {
if (this.authService.authenticated) {
contextCreated.permission = TypePermission[TypePermission.write];
}
else {
contextCreated.permission = TypePermission[TypePermission.read];
}
this.contexts$.value.ours.unshift(contextCreated);
this.contexts$.next(this.contexts$.value);
return contextCreated;
}));
}
clone(id, properties = {}) {
const url = this.baseUrl + '/contexts/' + id + '/clone';
return this.http.post(url, properties).pipe(map((contextCloned) => {
contextCloned.permission = TypePermission[TypePermission.write];
this.contexts$.value.ours.unshift(contextCloned);
this.contexts$.next(this.contexts$.value);
return contextCloned;
}));
}
update(id, context) {
const url = this.baseUrl + '/contexts/' + id;
return this.http.patch(url, context);
}
// =================================================================
addToolAssociation(contextId, toolId) {
const url = `${this.baseUrl}/contexts/${contextId}/tools`;
const association = {
toolId
};
return this.http.post(url, association);
}
deleteToolAssociation(contextId, toolId) {
const url = `${this.baseUrl}/contexts/${contextId}/tools/${toolId}`;
return this.http.delete(url);
}
getPermissions(id) {
const url = this.baseUrl + '/contexts/' + id + '/permissions';
return this.http.get(url);
}
addPermissionAssociation(contextId, profil, type) {
const url = `${this.baseUrl}/contexts/${contextId}/permissions`;
const association = {
profil,
typePermission: type
};
return this.http.post(url, association).pipe(catchError((res) => {
this.handleError(res, undefined, true);
throw [res]; // TODO Not sure about this.
}));
}
deletePermissionAssociation(contextId, permissionId) {
const url = `${this.baseUrl}/contexts/${contextId}/permissions/${permissionId}`;
return this.http.delete(url);
}
// ======================================================================
getLocalContexts() {
const url = this.getPath(this.options.contextListFile);
return this.http.get(url).pipe(map((res) => {
return { ours: res };
}));
}
getLocalContext(uri) {
const url = this.getPath(`${uri}.json`);
return this.http.get(url).pipe(mergeMap((res) => {
if (!res.base) {
return of(res);
}
const urlBase = this.getPath(`${res.base}.json`);
return this.http.get(urlBase).pipe(map((resBase) => {
const resMerge = res;
resMerge.map = ObjectUtils.mergeDeep(resBase.map, res.map);
resMerge.layers = (resBase.layers || [])
.concat(res.layers || [])
.reverse()
.filter((l, index, self) => !l.id || self.findIndex((l2) => l2.id === l.id) === index)
.reverse();
resMerge.toolbar = res.toolbar || resBase.toolbar;
resMerge.message = res.message || resBase.message;
resMerge.messages = res.messages || resBase.messages;
resMerge.tools = (res.tools || [])
.concat(resBase.tools || [])
.filter((t, index, self) => self.findIndex((t2) => t2.name === t.name) === index);
return resMerge;
}), catchError((err) => {
this.handleError(err, uri);
throw err;
}));
}), catchError((err2) => {
this.handleError(err2, uri);
throw err2;
}));
}
loadContexts(permissions, hidden) {
let request;
if (this.baseUrl) {
request = this.get(permissions, hidden);
}
else {
request = this.getLocalContexts();
}
request.subscribe((contexts) => {
contexts.ours = this.importedContext.concat(contexts.ours);
this.contexts$.next(contexts);
});
}
loadDefaultContext() {
const loadFct = (direct = false) => {
if (!direct && this.baseUrl && this.authService.authenticated) {
this.getDefault().subscribe((_context) => {
this.defaultContextUri = _context.uri;
this.addContextToList(_context);
this.setContext(_context);
}, () => {
this.defaultContextId$.next(undefined);
this.loadContext(this.defaultContextUri);
});
}
else {
this.loadContext(this.defaultContextUri);
}
};
if (this.route && this.route.options.contextKey) {
this.route.queryParams.pipe(debounceTime(100)).subscribe((params) => {
const contextParam = params[this.route.options.contextKey];
let direct = false;
if (contextParam) {
this.defaultContextUri = contextParam;
direct = true;
}
loadFct(direct);
});
}
else {
loadFct();
}
}
loadContext(uri) {
const context = this.context$.value;
if (context && context.uri === uri) {
return;
}
this.getContextByUri(uri)
.pipe(first())
.subscribe((_context) => {
this.addContextToList(_context);
this.setContext(_context);
}, () => {
if (uri !== this.options.defaultContextUri) {
this.loadContext(this.options.defaultContextUri);
}
});
}
setContext(context) {
this.handleContextMessage(context);
const currentContext = this.context$.value;
if (currentContext && context && context.id === currentContext.id) {
if (context.map.view.keepCurrentView === undefined) {
context.map.view.keepCurrentView = true;
}
this.context$.next(context);
return;
}
if (!context.map) {
context.map = { view: {} };
}
Object.assign(context.map.view, this.mapViewFromRoute);
this.context$.next(context);
}
loadEditedContext(uri) {
this.getContextByUri(uri).subscribe((_context) => {
this.setEditedContext(_context);
});
}
setEditedContext(context) {
this.editedContext$.next(context);
}
getContextFromMap(igoMap, empty) {
const view = igoMap.ol.getView();
const proj = view.getProjection().getCode();
const center = new olPoint(view.getCenter()).transform(proj, 'EPSG:4326');
const context = {
uri: uuid(),
title: '',
scope: 'private',
map: {
view: {
center: center.getCoordinates(),
zoom: view.getZoom(),
projection: proj,
maxZoomOnExtent: igoMap.viewController.maxZoomOnExtent
}
},
layers: [],
tools: []
};
const layers = empty
? []
: [...igoMap.layerController.treeLayers];
const baseLayer = igoMap.layerController.baseLayer;
if (baseLayer) {
layers.unshift(baseLayer);
}
// Remove null for the first level, the LayerGroup is already doing that for it's children
context.layers = layers
.map((layer) => layer.saveableOptions)
.filter(Boolean);
context.tools = this.tools.map((tool) => {
return {
id: String(tool.id),
global: tool.global
};
});
return context;
}
getContextFromLayers(igoMap, layers, name, keepCurrentView) {
const view = igoMap.ol.getView();
const proj = view.getProjection().getCode();
const center = new olPoint(view.getCenter()).transform(proj, 'EPSG:4326');
const context = {
uri: name,
title: name,
map: {
view: {
center: center.getCoordinates(),
zoom: view.getZoom(),
projection: proj,
keepCurrentView
}
},
layers: [],
toolbar: [],
tools: [],
extraFeatures: []
};
context.layers = igoMap.layerController.baseLayers.map((l) => {
return {
baseLayer: true,
sourceOptions: l.options.sourceOptions,
title: l.options.title,
visible: l.visible
};
});
layers.forEach((layer) => {
if (isLayerGroup(layer)) {
return context.layers.push(layer.options);
}
if (!(layer.ol.getSource() instanceof olVectorSource)) {
const layerOptions = layer.options;
layerOptions.zIndex = layer.zIndex;
layerOptions.visible = layer.visible;
layerOptions.opacity = layer.opacity;
delete layerOptions.source;
context.layers.push(layerOptions);
}
else {
if (!(layer.ol.getSource() instanceof olVectorSource)) {
const catalogLayer = layer.options;
catalogLayer.zIndex = layer.zIndex;
catalogLayer.visible = layer.visible;
catalogLayer.opacity = layer.opacity;
delete catalogLayer.source;
context.layers.push(catalogLayer);
}
else {
const extraFeatures = this.getExtraFeatures(layer);
extraFeatures.name = layer.options.title;
extraFeatures.opacity = layer.opacity;
extraFeatures.visible = layer.visible;
context.extraFeatures.push(extraFeatures);
}
}
});
context.toolbar = this.toolbar;
context.tools = this.tools;
return context;
}
getExtraFeatures(layer) {
const writer = new GeoJSON();
let olFeatures;
if (layer.ol.getSource() instanceof Cluster) {
const clusterSource = layer.ol.getSource();
olFeatures = clusterSource.getFeatures();
olFeatures = olFeatures.flatMap((cluster) => cluster.get('features'));
}
else {
const source = layer.ol.getSource();
olFeatures = source.getFeatures();
}
const cleanedOlFeatures = this.exportService.cleanFeatures(olFeatures, 'GeoJSON', '_featureStore');
const features = writer.writeFeatures(cleanedOlFeatures, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
return JSON.parse(features);
}
setTools(tools) {
this.tools = tools;
}
setToolbar(toolbar) {
this.toolbar = toolbar;
}
handleContextMessage(context) {
if (this.context$.value &&
context.uri &&
this.context$.value.uri !== context.uri) {
this.messageService.removeAllAreNotError();
}
context.messages = context.messages ? context.messages : [];
context.messages.push(context.message);
context.messages.map((message) => {
if (message) {
this.messageService.message(message);
}
});
}
getContextByUri(uri) {
if (this.baseUrl) {
let contextToLoad;
for (const key of Object.keys(this.contexts$.value)) {
contextToLoad = this.contexts$.value[key].find((c) => {
return c.uri === uri;
});
if (contextToLoad) {
break;
}
}
if (contextToLoad && contextToLoad.imported) {
return of(contextToLoad);
}
// TODO : use always id or uri
const id = contextToLoad ? contextToLoad.id : uri;
return this.getDetails(id);
}
const importedContext = this.contexts$.value.ours.find((currentContext) => {
return currentContext.uri === uri && currentContext.imported === true;
});
if (importedContext) {
return of(importedContext);
}
else {
return this.getLocalContext(uri);
}
}
getContextLayers(map) {
return map.layerController.treeLayers;
}
readParamsFromRoute() {
if (!this.route) {
return;
}
this.route.queryParams.subscribe((params) => {
const centerKey = this.route.options.centerKey;
if (centerKey && params[centerKey]) {
const centerParams = params[centerKey];
this.mapViewFromRoute.center = centerParams.split(',').map(Number);
}
const projectionKey = this.route.options.projectionKey;
if (projectionKey && params[projectionKey]) {
const projectionParam = params[projectionKey];
this.mapViewFromRoute.projection = projectionParam;
}
const zoomKey = this.route.options.zoomKey;
if (zoomKey && params[zoomKey]) {
const zoomParam = params[zoomKey];
this.mapViewFromRoute.zoom = Number(zoomParam);
}
});
}
getPath(file) {
const basePath = this.options.basePath.replace(/\/$/, '');
return `${basePath}/${file}`;
}
handleError(error, uri, permissionError) {
const context = this.contexts$.value.ours.find((obj) => obj.uri === uri);
const titleContext = context ? context.title : uri;
error.error.title = this.languageService.translate.instant('igo.context.contextManager.invalid.title');
error.error.message = this.languageService.translate.instant('igo.context.contextManager.invalid.text', { value: titleContext });
error.error.toDisplay = true;
if (permissionError) {
error.error.title = this.languageService.translate.instant('igo.context.contextManager.errors.addPermissionTitle');
error.error.message = this.languageService.translate.instant('igo.context.contextManager.errors.addPermission');
}
this.messageService.error('igo.context.contextManager.errors.addPermission', 'igo.context.contextManager.errors.addPermissionTitle');
}
handleContextsChange(keepCurrentContext = true) {
const context = this.context$.value;
const editedContext = this.editedContext$.value;
if (!context || context.uri === this.options.defaultContextUri) {
keepCurrentContext = false;
}
if (!keepCurrentContext || !this.findContext(context)) {
this.defaultContextUri = undefined;
this.loadDefaultContext();
}
else {
this.getContextByUri(context.uri)
.pipe(first())
.subscribe((newContext) => {
this.toolsChanged$.next(newContext);
});
if (this.baseUrl && this.authService.authenticated) {
this.getDefault().subscribe();
}
}
const editedFound = this.findContext(editedContext);
if (!editedFound || editedFound.permission !== 'write') {
this.setEditedContext(undefined);
}
}
addContextToList(context) {
const contextFound = this.findContext(context);
if (!contextFound) {
const contextSimplifie = {
id: context.id,
uri: context.uri,
title: context.title,
scope: context.scope,
permission: TypePermission[TypePermission.read]
};
if (this.contexts$.value && this.contexts$.value.public) {
this.contexts$.value.public.push(contextSimplifie);
this.contexts$.next(this.contexts$.value);
}
}
}
findContext(context) {
if (!context) {
return false;
}
const contexts = this.contexts$.value;
let found;
for (const key of Object.keys(contexts)) {
const value = contexts[key];
found = value.find((c) => (context.id && c.id === context.id) ||
(context.uri && c.uri === context.uri));
if (found) {
break;
}
}
return found;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextService, deps: [{ token: i1.HttpClient }, { token: i2.AuthService }, { token: i4.LanguageService }, { token: i1$1.ConfigService }, { token: i3.MessageService }, { token: i6.StorageService }, { token: i1$2.ExportService }, { token: i8.RouteService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.AuthService }, { type: i4.LanguageService }, { type: i1$1.ConfigService }, { type: i3.MessageService }, { type: i6.StorageService }, { type: i1$2.ExportService }, { type: i8.RouteService, decorators: [{
type: Optional
}] }] });
class ExportError extends Error {
}
class ExportInvalidFileError extends ExportError {
constructor() {
super('Invalid context');
Object.setPrototypeOf(this, ExportInvalidFileError.prototype);
}
}
class ExportNothingToExportError extends ExportError {
constructor() {
super('Nothing to export');
Object.setPrototypeOf(this, ExportNothingToExportError.prototype);
}
}
class ContextExportService {
export(res) {
return this.exportAsync(res);
}
exportAsync(res) {
const doExport = (observer) => {
const nothingToExport = this.nothingToExport(res);
if (nothingToExport === true) {
observer.error(new ExportNothingToExportError());
return;
}
const contextJSON = JSON.stringify(res);
downloadContent(contextJSON, 'text/json;charset=utf-8', `${res.uri}.json`);
observer.complete();
};
return new Observable(doExport);
}
nothingToExport(res) {
if (res.map === undefined) {
return true;
}
return false;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextExportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextExportService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextExportService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
function handleFileExportError(error, messageService) {
if (error instanceof ExportNothingToExportError) {
this.handleNothingToExportError(messageService);
return;
}
messageService.error('igo.context.contextImportExport.export.failed.text', 'igo.context.contextImportExport.export.failed.title');
}
function handleFileExportSuccess(messageService) {
messageService.success('igo.context.contextImportExport.export.success.text', 'igo.context.contextImportExport.export.success.title');
}
function handleNothingToExportError(messageService) {
messageService.error('igo.context.contextImportExport.export.nothing.text', 'igo.context.contextImportExport.export.nothing.title');
}
class ImportError extends Error {
}
class ImportInvalidFileError extends ImportError {
constructor() {
super('Invalid file');
Object.setPrototypeOf(this, ImportInvalidFileError.prototype);
}
}
class ImportUnreadableFileError extends ImportError {
constructor() {
super('Failed to read file');
Object.setPrototypeOf(this, ImportUnreadableFileError.prototype);
}
}
class ImportNothingToImportError extends ImportError {
constructor() {
super('Nothing to import');
Object.setPrototypeOf(this, ImportNothingToImportError.prototype);
}
}
class ImportSizeError extends ImportError {
constructor() {
super('File is too large');
Object.setPrototypeOf(this, ImportNothingToImportError.prototype);
}
}
class ImportSRSError extends ImportError {
constructor() {
super('Invalid SRS definition');
Object.setPrototypeOf(this, ImportNothingToImportError.prototype);
}
}
function handleFileImportSuccess(file, context, messageService, contextService) {
if (Object.keys(context).length <= 0) {
handleNothingToImportError(file, messageService);
return;
}
const contextTitle = computeLayerTitleFromFile(file);
addContextToContextList(context, contextTitle, contextService);
messageService.success('igo.context.contextImportExport.import.success.text', 'igo.context.contextImportExport.import.success.title', undefined, {
value: contextTitle
});
}
function handleFileImportError(file, error, messageService, sizeMb) {
sizeMb = sizeMb ? sizeMb : 30;
const errMapping = {
'Invalid file': handleInvalidFileImportError,
'File is too large': handleSizeFileImportError,
'Failed to read file': handleUnreadbleFileImportError
};
errMapping[error.message](file, error, messageService, sizeMb);
}
function handleInvalidFileImportError(file, error, messageService) {
messageService.error('igo.context.contextImportExport.import.invalid.text', 'igo.context.contextImportExport.import.invalid.title', undefined, {
value: file.name,
mimeType: file.type
});
}
function handleSizeFileImportError(file, error, messageService, sizeMb) {
messageService.error('igo.context.contextImportExport.import.tooLarge.text', 'igo.context.contextImportExport.import.tooLarge.title', undefined, {
value: file.name,
size: sizeMb
});
}
function handleUnreadbleFileImportError(file, error, messageService) {
messageService.error('igo.context.contextImportExport.import.unreadable.text', 'igo.context.contextImportExport.import.unreadable.title', undefined, {
value: file.name
});
}
function handleNothingToImportError(file, messageService) {
messageService.error('igo.context.contextImportExport.import.empty.text', 'igo.context.contextImportExport.import.empty.title', undefined, { value: file.name });
}
function addContextToContextList(context, contextTitle, contextService) {
context.title = contextTitle;
context.imported = true;
contextService.contexts$.value.ours.unshift(context);
contextService.contexts$.next(contextService.contexts$.value);
contextService.importedContext.unshift(context);
contextService.loadContext(context.uri);
}
function getFileExtension(file) {
return file.name.split('.').pop().toLowerCase();
}
function computeLayerTitleFromFile(file) {
return file.name.substr(0, file.name.lastIndexOf('.'));
}
function addImportedFeaturesToMap(extraFeatures, map) {
const sourceOptions = {
type: 'vector',
queryable: true
};
const olFeatures = collectFeaturesFromExtraFeatures(extraFeatures);
const source = new FeatureDataSource(sourceOptions);
source.ol.addFeatures(olFeatures);
let randomStyle;
let editable = false;
const featureKeys = olFeatures[0]?.getKeys() ?? [];
if (featureKeys.includes('_style') || featureKeys.includes('_mapTitle')) {
randomStyle = featureRandomStyleFunction();
}
else {
randomStyle = featureRandomStyle();
editable = true;
}
const layer = new VectorLayer({
title: extraFeatures.name,
isIgoInternalLayer: true,
source,
igoStyle: { editable },
style: randomStyle,
visible: extraFeatures.visible,
opacity: extraFeatures.opacity
});
map.layerController.add(layer);
return layer;
}
function addImportedFeaturesStyledToMap(extraFeatures, map, styleListService, styleService) {
let style;
let distance;
if (styleListService.getStyleList(extraFeatures.name + '.styleByAttribute')) {
const styleByAttribute = styleListService.getStyleList(extraFeatures.name + '.styleByAttribute');
style = (feature, resolution) => {
return styleService.createStyleByAttribute(feature, styleByAttribute, resolution);
};
}
else if (styleListService.getStyleList(extraFeatures.name + '.clusterStyle')) {
const clusterParam = styleListService.getStyleList(extraFeatures.name + '.clusterParam');
distance = styleListService.getStyleList(extraFeatures.name + '.distance');
style = (feature, resolution) => {
const baseStyle = styleService.createStyle(styleListService.getStyleList(extraFeatures.name + '.clusterStyle'), feature, resolution);
return styleService.createClusterStyle(feature, resolution, clusterParam, baseStyle);
};
}
else if (styleListService.getStyleList(extraFeatures.name + '.style')) {
style = (feature, resolution) => styleService.createStyle(styleListService.getStyleList(extraFeatures.name + '.style'), feature, resolution);
}
else {
style = (feature, resolution) => styleService.createStyle(styleListService.getStyleList('default.style'), feature, resolution);
}
let source;
const olFeatures = collectFeaturesFromExtraFeatures(extraFeatures);
if (styleListService.getStyleList(extraFeatures.name + '.clusterStyle')) {
const sourceOptions = {
distance,
type: 'cluster',
queryable: true
};
source = new ClusterDataSource(sourceOptions);
source.ol.source.addFeatures(olFeatures);
}
else {
const sourceOptions = {
type: 'vector',
queryable: true
};
source = new FeatureDataSource(sourceOptions);
source.ol.addFeatures(olFeatures);
}
const layer = new VectorLayer({
title: extraFeatures.name,
isIgoInternalLayer: true,
source,
style,
opacity: extraFeatures.opacity,
visible: extraFeatures.visible
});
map.layerController.add(layer);
return layer;
}
function collectFeaturesFromExtraFeatures(featureCollection) {
const format = new GeoJSON();
const features = format.readFeatures(featureCollection, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
return features;
}
class ContextImportService {
config;
static allowedMimeTypes = ['application/json'];
static allowedExtensions = 'json';
clientSideFileSizeMax;
constructor(config) {
this.config = config;
const configFileSizeMb = this.config.getConfig('importExport.clientSideFileSizeMaxMb');
this.clientSideFileSizeMax =
(configFileSizeMb ? configFileSizeMb : 30) * Math.pow(1024, 2);
}
import(file) {
return this.importAsync(file);
}
getFileImporter(file) {
const extension = getFileExtension(file);
const mimeType = file.type;
const allowedMimeTypes = [...ContextImportService.allowedMimeTypes];
const allowedExtensions = ContextImportService.allowedExtensions;
if (allowedMimeTypes.indexOf(mimeType) < 0 &&
allowedExtensions.indexOf(extension) < 0) {
return undefined;
}
else if (mimeType === 'application/json' ||
extension === ContextImportService.allowedExtensions) {
return this.importFile;
}
return undefined;
}
importAsync(file) {
const doImport = (observer) => {
if (file.size >= this.clientSideFileSizeMax) {
observer.error(new ImportSizeError());
return;
}
const importer = this.getFileImporter(file);
if (importer === undefined) {
observer.error(new ImportInvalidFileError());
return;
}
importer.call(this, file, observer);
};
return new Observable(doImport);
}
importFile(file, observer) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const context = this.parseContextFromFile(file, event.target.result);
observer.next(context);
}
catch {
observer.error(new ImportUnreadableFileError());
}
observer.complete();
};
reader.onerror = () => {
observer.error(new ImportUnreadableFileError());
};
reader.readAsText(file, 'UTF-8');
}
parseContextFromFile(file, data) {
const context = JSON.parse(data);
return context;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextImportService, deps: [{ token: i1$1.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextImportService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextImportService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1$1.ConfigService }] });
class ContextImportExportComponent {
contextImportService;
contextExportService;
messageService;
formBuilder;
config;
contextService;
form;
layers;
inputProj = 'EPSG:4326';
loading$ = new BehaviorSubject(false);
forceNaming = false;
layerList;
userControlledLayerList;
res;
clientSideFileSizeMax;
fileSizeMb;
activeImportExport = 'import';
map;
constructor(contextImportService, contextExportService, messageService, formBuilder, config, contextService) {
this.contextImportService = contextImportService;
this.contextExportService = contextExportService;
this.messageService = messageService;
this.formBuilder = formBuilder;
this.config = config;
this.contextService = contextService;
this.buildForm();
}
ngOnInit() {
const configFileSizeMb = this.config.getConfig('importExport.clientSideFileSizeMaxMb');
this.clientSideFileSizeMax =
(configFileSizeMb ? configFileSizeMb : 30) * Math.pow(1024, 2);
this.fileSizeMb = this.clientSideFileSizeMax / Math.pow(1024, 2);
this.layerList = this.map.layerController.all;
this.userControlledLayerList = this.map.layerController.treeLayers;
}
importFiles(files) {
this.loading$.next(true);
for (const file of files) {
this.contextImportService
.import(file)
.pipe(take(1))
.subscribe((context) => this.onFileImportSuccess(file, context), (error) => this.onFileImportError(file, error), () => {
this.loading$.next(false);
});
}
}
handleExportFormSubmit(contextOptions) {
this.loading$.next(true);
this.res = this.contextService.getContextFromLayers(this.map, contextOptions.layers, contextOptions.name, false);
this.res.imported = true;
this.contextExportService
.export(this.res)
.pipe(take(1))
.subscribe({
error: (error) => this.onFileExportError(error),
complete: () => {
this.onFileExportSuccess();
this.loading$.next(false);
}
});
}
buildForm() {
this.form = this.formBuilder.group({
layers: ['', [Validators.required]],
name: ['', [Validators.required]]
});
}
onFileImportSuccess(file, context) {
handleFileImportSuccess(file, context, this.messageService, this.contextService);
}
onFileImportError(file, error) {
this.loading$.next(false);
handleFileImportError(file, error, this.messageService, this.fileSizeMb);
}
onFileExportError(error) {
this.loading$.next(false);
handleFileExportError(error, this.messageService);
}
onFileExportSuccess() {
handleFileExportSuccess(this.messageService);
}
selectAll(e) {
if (e._selected) {
this.form.controls.layers.setValue(this.userControlledLayerList);
e._selected = true;
}
if (e._selected === false) {
this.form.controls.layers.setValue([]);
}
}
onImportExportChange(event) {
this.activeImportExport = event.value;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContextImportExportComponent, deps: [{ token: ContextImportService }, { token: ContextExportService }, { token: i3.MessageService }, { token: i1$3.UntypedFormBuilder }, { token: i1$1.ConfigService }, { token: ContextService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ContextImportExportComponent, isStandalone: true, selector: "igo-context-import-export", inputs: { map: "map" }, ngImport: i0, template: "<div class=\"import-export-toggle mat-typography\">\n <mat-button-toggle-group\n [value]=\"activeImportExport\"\n (change)=\"onImportExportChange($event)\"\n >\n <mat-button-toggle [value]=\"'import'\">\n {{ 'igo.geo.importExportForm.importTabTitle' | translate }}\n </mat-button-toggle>\n <mat-button-toggle [value]=\"'export'\">\n {{ 'igo.geo.importExportForm.exportTabTitle' | translate }}\n </mat-button-toggle>\n </mat-button-toggle-group>\n</div>\n\n<div *ngIf=\"activeImportExport === 'import'\">\n <form class=\"igo-form\">\n <div class=\"igo-form-button-group\">\n <button\n mat-raised-button\n type=\"button\"\n (click)=\"fileInput.click()\"\n [disabled]=\"loading$ | async\"\n >\n {{ 'igo.geo.importExportForm.importButton' | translate }}\n </button>\n <igo-spinner [shown]=\"loading$ | async\"></igo-spinner>\n <input\n #fileInput\n type=\"file\"\n [style.display]=\"'none'\"\n (click)=\"fileInput.value = null\"\n (change)=\"importFiles($any($event.target).files)\"\n />\n </div>\n </form>\n <section class=\"mat-typography\">\n <h4>{{ 'igo.geo.importExportForm.importClarifications' | translate }}</h4>\n <ul>\n <li>\n {{\n 'igo.geo.importExportForm.importSizeMax'\n | translate: { size: fileSizeMb }\n }}\n </li>\n </ul>\n </section>\n</div>\n\n<form\n class=\"igo-form\"\n *ngIf=\"activeImportExport === 'export'\"\n [formGroup]=\"form\"\n>\n <div class=\"igo-input-container\">\n <mat-form-field class=\"example-full-width\">\n <mat-label>{{\n 'igo.context.contextImportExport.export.exportContextName' | translate\n }}</mat-label>\n <input formControlName=\"name\" matInput [value]=\"\" />\n </mat-form-field>\n </div>\n <div class=\"igo-input-container\">\n <mat-form-field>\n <mat-label>{{\n 'igo.context.contextImportExport.export.exportPlaceHolder' | translate\n }}</mat-label>\n <mat-select formControlName=\"layers\" multiple>\n <mat-option [value]=\"1\" (click)=\"selectAll(e)\" #e>\n {{\n 'igo.context.contextImportExport.export.exportSelectAll' | translate\n }}\n </mat-option>\n <mat-divider></mat-divider>\n <mat-option\n *ngFor=\"let layer of userControlledLayerList\"\n [value]=\"layer\"\n >{{ layer.title }}</mat-option\n >\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"igo-form-button-group\">\n <button\n mat-raised-button\n type=\"button\"\n [disabled]=\"!form.valid || (loading$ | async)\"\n (click)=\"handleExportFormSubmit(form.value)\"\n >\n {{ 'igo.geo.importExportForm.exportButton' | translate }}\n </button>\n <igo-spinner [shown]=\"loading$ | async\"></igo-spinner>\n </div>\n</form>\n", styles: [".import-export-toggle{padding:10px;text-align:center}.import-export-toggle mat-button-toggle-group{width:100%}.import-export-toggle mat-button-toggle-group mat-button-toggle{width:50%}.igo-input-container{padding:10px}.igo-input-container mat-form-field{width:100%}h4{padding:0 5px}.igo-form{padding:15px 5px}.igo-form-button-group{text-align:center;padding-top:10px}igo-spinner{position:absolute;padding-left:10px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i7.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i7.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: SpinnerComponent, selector: "igo-spinner", inputs: ["shown"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i6$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLab