hslayers-ng
Version:
HSLayers-NG mapping library
605 lines (596 loc) • 214 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, DestroyRef, Component, Pipe, ViewChild, Input, Injectable, input, computed, ViewChildren, ChangeDetectionStrategy, signal, model, viewChild, ViewContainerRef, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BehaviorSubject, filter, merge, Subject, map, fromEvent, debounceTime, tap, startWith, timer, throwError, of } from 'rxjs';
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
import { HsDimensionDescriptor } from 'hslayers-ng/common/dimensions';
import { HsDimensionService, HsDimensionTimeService, HsWmsGetCapabilitiesService } from 'hslayers-ng/services/get-capabilities';
import { HsEventBusService } from 'hslayers-ng/services/event-bus';
import { HsLayerSelectorService, HsLayerManagerVisibilityService, HsLayerEditorVectorLayerService, HsLayerManagerMetadataService, HsLayerManagerService, HsLayerManagerFolderService, HsLayerManagerLoadingProgressService, HsLayerManagerUtilsService, HsLayerManagerCopyLayerService } from 'hslayers-ng/services/layer-manager';
import { HsMapService } from 'hslayers-ng/services/map';
import { isFunction, getLayerParams, isLayerArcgis, updateLayerParams, isLayerWMS, getURL, isLayerVectorLayer, instOf, listNumericAttributes, isLayerIDW, calculateResolutionFromScale, layerIsZoomable, layerIsStyleable, isLayerQueryable, layerInvalid } from 'hslayers-ng/services/utils';
import { getDimensions, getCachedCapabilities, getQueryCapabilities, getWmsOriginalExtent, setCluster, getCluster, getInlineLegend, setWmsOriginalExtent, getPath, getBase, setPath, setBase, getAttribution, setAbstract, getAbstract, getGreyscale, getRemovable, getWfsUrl, getWorkspace, getTitle, setTitle, getExclusive, getHsLaymanSynchronizing, getShowInLayerManager, getActive, getThumbnail } from 'hslayers-ng/common/extensions';
import * as i1 from '@angular/forms';
import { FormsModule, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
import * as i2 from '@ng-bootstrap/ng-bootstrap';
import { NgbTooltipModule, NgbDropdownModule, NgbProgressbarModule, NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
import * as i2$1 from '@ngx-translate/core';
import { TranslatePipe } from '@ngx-translate/core';
import * as i1$1 from '@angular/common';
import { CommonModule, NgClass, AsyncPipe } from '@angular/common';
import { HsConfig } from 'hslayers-ng/config';
import { HsLayoutService } from 'hslayers-ng/services/layout';
export * from 'hslayers-ng/types';
import { HsConfirmDialogComponent } from 'hslayers-ng/common/confirm';
import { HsDialogContainerService } from 'hslayers-ng/common/dialogs';
import * as i2$3 from 'hslayers-ng/common/panels';
import { HsPanelHelpersModule, HsGuiOverlayBaseComponent, HsPanelBaseComponent, HsPanelHeaderComponent } from 'hslayers-ng/common/panels';
import { HsStylerService } from 'hslayers-ng/services/styler';
import { WMSCapabilities } from 'ol/format';
import { transformExtent, METERS_PER_UNIT } from 'ol/proj';
import { HsShareUrlService, HS_PRMS } from 'hslayers-ng/services/share';
import * as i1$2 from 'hslayers-ng/components/legend';
import { HsLegendService, HsLegendModule } from 'hslayers-ng/components/legend';
import { HsPanelContainerService } from 'hslayers-ng/services/panels';
import { HsDrawService } from 'hslayers-ng/services/draw';
import colorScales from 'colormap/colorScale';
import { HsLanguageService } from 'hslayers-ng/services/language';
import { InterpolatedSource } from 'hslayers-ng/common/layers';
import * as i2$2 from 'hslayers-ng/common/color-map-picker';
import { HsColormapPickerModule } from 'hslayers-ng/common/color-map-picker';
import { OSM, ImageWMS, TileWMS } from 'ol/source';
import * as i1$3 from 'hslayers-ng/common/clipboard-text';
import { HsClipboardTextComponent } from 'hslayers-ng/common/clipboard-text';
import { Image, Tile } from 'ol/layer';
import { HsLayerShiftingService } from 'hslayers-ng/services/layer-shifting';
import { startWith as startWith$1, combineLatestWith, filter as filter$1, map as map$1, debounceTime as debounceTime$1, take, switchMap, debounce, distinctUntilChanged, share, retry, catchError } from 'rxjs/operators';
import * as i2$4 from '@angular/cdk/drag-drop';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { HsCommonLaymanService } from 'hslayers-ng/common/layman';
import { HsCompositionsParserService } from 'hslayers-ng/services/compositions';
import { HsRemoveLayerDialogService } from 'hslayers-ng/common/remove-multiple';
import { HslayersService } from 'hslayers-ng/core';
import { FilterPipe } from 'hslayers-ng/common/pipes';
class HsLayerEditorWidgetBaseComponent {
constructor() {
this.hsLayerSelectorService = inject(HsLayerSelectorService);
this.layerDescriptor = new BehaviorSubject(null);
this.isVisible$ = new BehaviorSubject(true);
this.baseComponentInitRun = false;
this.destroyRef = inject(DestroyRef);
this.layerDescriptor.subscribe((descriptor) => {
this.olLayer = descriptor?.layer;
});
setTimeout(() => {
if (!this.baseComponentInitRun) {
console.warn(`${this.name || this.constructor.name} implements ngOnInit lifecycle hook without calling HsLayerEditorWidgetBaseComponent ngOnInit.
Make sure it is executed by calling super.ngOnInit() from component's ngOnInit manually`);
}
}, 3000);
}
ngOnInit() {
this.baseComponentInitRun = true;
this.layerDescriptor.next(this.hsLayerSelectorService.currentLayer);
this.hsLayerSelectorService.layerSelected
.pipe(filter((layer) => !!layer), takeUntilDestroyed(this.destroyRef))
.subscribe((layer) => {
this.layerDescriptor.next(layer);
});
}
isVisible() {
return true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorWidgetBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.3", type: HsLayerEditorWidgetBaseComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: '<div></div>', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorWidgetBaseComponent, decorators: [{
type: Component,
args: [{
template: '<div></div>',
standalone: false,
}]
}], ctorParameters: () => [] });
class HsLayerEditorDimensionsComponent extends HsLayerEditorWidgetBaseComponent {
constructor() {
super();
this.hsDimensionService = inject(HsDimensionService);
this.hsDimensionTimeService = inject(HsDimensionTimeService);
this.hsMapService = inject(HsMapService);
this.hsEventBusService = inject(HsEventBusService);
this.name = 'dimensions';
this.dimensions = [];
merge(this.hsEventBusService.layerDimensionDefinitionChanges.pipe(filter((layer) => layer === this.olLayer)), this.layerDescriptor)
.pipe(filter((data) => !!data), takeUntilDestroyed())
.subscribe(() => {
this.updateDimensions();
});
}
updateDimensions() {
const layer = this.olLayer;
if (!layer) {
return;
}
this.dimensions = [];
const dimensions = getDimensions(layer);
if (dimensions && Object.entries(dimensions)) {
for (const [key, dimension] of Object.entries(dimensions)) {
let available = true;
if (isFunction(dimension.availability)) {
available = dimension.availability(layer);
}
if (available) {
if (typeof dimension.values === 'string') {
dimension.values = this.hsDimensionTimeService.parseTimePoints(dimension.values);
}
this.dimensions.push(new HsDimensionDescriptor(key, dimension));
}
}
}
}
dimensionIsTime(dimension) {
const dimensions = getDimensions(this.olLayer);
const type = Object.keys(dimensions).find((key) => dimensions[key] === dimension);
// value of time.onlyInEditor used inversely here intentionally
// ( => replacement for inline time-editor)
return type === 'time' && dimensions.time?.onlyInEditor;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorDimensionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.3", type: HsLayerEditorDimensionsComponent, isStandalone: false, selector: "hs-layer-editor-dimensions", usesInheritance: true, ngImport: i0, template: "@if (hsDimensionService.isLayerWithDimensions(olLayer)) {\n <div>\n <!-- TODO: Remove function call from template -->\n <p style=\"text-align: center; font-weight: bold\">\n {{'LAYERMANAGER.dimensions' | translate }}\n </p>\n @for (dimension of dimensions; track $index) {\n <div class=\"form-inline\">\n <label class=\"control-label\" style=\"width: 100%; justify-content: left\">{{dimension.label ||\n dimension.name}}:</label>\n @if (!dimension.type) {\n <select class=\"form-control form-select\" style=\"width: 100%\" [(ngModel)]=\"dimension.modelValue\"\n [ngModelOptions]=\"{standalone: true}\"\n (change)=\"hsDimensionService.dimensionChanged(dimension)\">\n @for (dimension_value of dimension.values; track dimension_value) {\n <option [ngValue]=\"dimension_value\">\n {{dimension_value}}\n </option>\n }\n </select>\n }\n <!-- Time slider replaced by layermanger-time-editor component -->\n <div class=\"form-group\" [hidden]=\"!dimensionIsTime(dimension)\">\n <label>{{'COMMON.date' | translate }}</label>\n </div>\n @if (dimension.type === 'datetime' || dimension.type === 'date') {\n <div class=\"input-group flex-fill\">\n <input type=\"text\" style=\"width: 6.1em\" ngbDatepicker placeholder=\"YYYY-MM-DD\" #d=\"ngbDatepicker\"\n [(ngModel)]=\"dimension.modelValue\" (dateSelect)=\"hsDimensionService.dimensionChanged(dimension)\"\n [ngModelOptions]=\"{standalone: true}\" />\n <button class=\"btn btn-outline-secondary\" (click)=\"d.toggle()\" type=\"button\">\n <i class=\"fa-solid fa-calendar\"></i>\n </button>\n </div>\n }\n @if (dimension.type === 'datetime') {\n <ngb-timepicker size=\"small\" [(ngModel)]=\"dimension.modelTimeValue\"\n (ngModelChange)=\"hsDimensionService.dimensionChanged(dimension)\" [ngModelOptions]=\"{standalone: true}\">\n </ngb-timepicker>\n }\n </div>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "contentTemplate", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "popperOptions", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { kind: "pipe", type: i2$1.TranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorDimensionsComponent, decorators: [{
type: Component,
args: [{ selector: 'hs-layer-editor-dimensions', standalone: false, template: "@if (hsDimensionService.isLayerWithDimensions(olLayer)) {\n <div>\n <!-- TODO: Remove function call from template -->\n <p style=\"text-align: center; font-weight: bold\">\n {{'LAYERMANAGER.dimensions' | translate }}\n </p>\n @for (dimension of dimensions; track $index) {\n <div class=\"form-inline\">\n <label class=\"control-label\" style=\"width: 100%; justify-content: left\">{{dimension.label ||\n dimension.name}}:</label>\n @if (!dimension.type) {\n <select class=\"form-control form-select\" style=\"width: 100%\" [(ngModel)]=\"dimension.modelValue\"\n [ngModelOptions]=\"{standalone: true}\"\n (change)=\"hsDimensionService.dimensionChanged(dimension)\">\n @for (dimension_value of dimension.values; track dimension_value) {\n <option [ngValue]=\"dimension_value\">\n {{dimension_value}}\n </option>\n }\n </select>\n }\n <!-- Time slider replaced by layermanger-time-editor component -->\n <div class=\"form-group\" [hidden]=\"!dimensionIsTime(dimension)\">\n <label>{{'COMMON.date' | translate }}</label>\n </div>\n @if (dimension.type === 'datetime' || dimension.type === 'date') {\n <div class=\"input-group flex-fill\">\n <input type=\"text\" style=\"width: 6.1em\" ngbDatepicker placeholder=\"YYYY-MM-DD\" #d=\"ngbDatepicker\"\n [(ngModel)]=\"dimension.modelValue\" (dateSelect)=\"hsDimensionService.dimensionChanged(dimension)\"\n [ngModelOptions]=\"{standalone: true}\" />\n <button class=\"btn btn-outline-secondary\" (click)=\"d.toggle()\" type=\"button\">\n <i class=\"fa-solid fa-calendar\"></i>\n </button>\n </div>\n }\n @if (dimension.type === 'datetime') {\n <ngb-timepicker size=\"small\" [(ngModel)]=\"dimension.modelTimeValue\"\n (ngModelChange)=\"hsDimensionService.dimensionChanged(dimension)\" [ngModelOptions]=\"{standalone: true}\">\n </ngb-timepicker>\n }\n </div>\n }\n </div>\n}\n" }]
}], ctorParameters: () => [] });
/**
* Formats a shorthand date in a form YYYYMMDD into a full ISO form YYYY-MM-DD
* If not used, the shorthand date is parsed as milliseconds in the DatePipe
*/
class DatePreformatPipe {
transform(value) {
if (!value.includes('-') && value.length === 8) {
return `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`;
}
return value;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: DatePreformatPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: DatePreformatPipe, isStandalone: true, name: "datePreformat" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: DatePreformatPipe, decorators: [{
type: Pipe,
args: [{
name: 'datePreformat',
standalone: true,
}]
}] });
class HsLayerManagerTimeEditorComponent {
constructor() {
this.hsEventBusService = inject(HsEventBusService);
this.hsDimensionTimeService = inject(HsDimensionTimeService);
this.hsLayoutService = inject(HsLayoutService);
this.hsConfig = inject(HsConfig);
this.availableTimesFetched = false;
this.hasPreviousTime = false;
this.hasFollowingTime = false;
this.timeDisplayFormat = 'yyyy-MM-dd HH:mm:ss z';
this.hsDimensionTimeService.layerTimeChanges
.pipe(takeUntilDestroyed())
.subscribe(({ layer: layerDescriptor, time }) => {
if (this.layer.uid !== layerDescriptor.uid) {
return;
}
if (!this.availableTimes) {
this.fillAvailableTimes(layerDescriptor, time);
}
this.setCurrentTimeIfAvailable(time);
});
this.hsEventBusService.layerTimeSynchronizations
.pipe(takeUntilDestroyed())
.subscribe(({ sync, time }) => {
this.timesInSync = sync;
if (sync) {
this.hideTimeSelect();
this.setCurrentTimeIfAvailable(time);
if (this.currentTime) {
this.setLayerTime();
}
}
});
}
ngOnInit() {
this.selectVisible = false;
this.timesInSync = false;
if (this.layer.time) {
this.fillAvailableTimes(this.layer, undefined);
}
}
/**
* Sets hasPreviousTime and hasFollowingTime properties
*/
checkPrevFollowTimesAvailability() {
if (!this.availableTimes) {
return;
}
if (this.currentTimeIdx > 0) {
this.hasPreviousTime = true;
}
else {
this.hasPreviousTime = false;
}
if (this.currentTimeIdx < this.availableTimes.length - 1) {
this.hasFollowingTime = true;
}
else {
this.hasFollowingTime = false;
}
}
/**
* Handler for the "prev" button click
*/
previousTime() {
if (!this.hasPreviousTime) {
return;
}
this.currentTime = this.availableTimes[--this.currentTimeIdx];
if (this.timesInSync) {
this.hsEventBusService.layerTimeSynchronizations.next({
sync: this.timesInSync,
time: this.currentTime,
});
}
this.setLayerTime();
this.checkPrevFollowTimesAvailability();
}
/**
* Handler for the "next" button click
*/
followingTime() {
if (!this.hasFollowingTime) {
return;
}
this.currentTime = this.availableTimes[++this.currentTimeIdx];
if (this.timesInSync) {
this.hsEventBusService.layerTimeSynchronizations.next({
sync: this.timesInSync,
time: this.currentTime,
});
}
this.setLayerTime();
this.checkPrevFollowTimesAvailability();
}
/**
* Handler for a time selection from the SELECT element
*/
selectTime() {
this.currentTimeIdx = this.availableTimes.indexOf(this.currentTime);
if (this.timesInSync) {
this.hsEventBusService.layerTimeSynchronizations.next({
sync: this.timesInSync,
time: this.currentTime,
});
}
this.setLayerTime();
this.checkPrevFollowTimesAvailability();
}
setCurrentTimeIfAvailable(time) {
if (this.availableTimes.includes(time)) {
this.currentTime = time;
this.currentTimeIdx = this.availableTimes.indexOf(time);
}
else {
this.currentTime = null;
this.currentTimeIdx = -1;
}
this.checkPrevFollowTimesAvailability();
}
/**
* Set selected 'time' on the layer object
*/
setLayerTime() {
setTimeout(() => {
this.hsDimensionTimeService.setLayerTime(this.layer, this.currentTime);
}, 100);
}
showTimeSelect() {
this.selectVisible = true;
this.selectElement.nativeElement.focus(); //FIXME: this just refuse to work...
}
hideTimeSelect() {
this.selectVisible = false;
}
synchronizeTimes() {
this.timesInSync = !this.timesInSync;
this.hsEventBusService.layerTimeSynchronizations.next({
sync: this.timesInSync,
time: this.currentTime,
});
}
/**
* This gets called from subscriber and also OnInit because
* subscriber could have been set up after the event was broadcasted
*/
fillAvailableTimes(layer, defaultTime) {
this.availableTimes = layer.time.timePoints;
this.setDateTimeFormatting();
this.setCurrentTimeIfAvailable(defaultTime ?? this.layer.time.default);
if (!this.currentTime && this.availableTimes.length > 0) {
this.currentTime = this.availableTimes[0];
this.currentTimeIdx = this.availableTimes.indexOf(this.currentTime);
}
this.availableTimesFetched = true;
this.checkPrevFollowTimesAvailability();
}
setDateTimeFormatting() {
if (this.hsConfig.timeDisplayFormat) {
this.timeDisplayFormat = this.hsConfig.timeDisplayFormat;
}
else if (this.availableTimes.every((time) => time.endsWith('00-00T00:00:00.000Z'))) {
this.timeDisplayFormat = 'yyyy';
}
else if (this.availableTimes.every((time) => time.endsWith('00T00:00:00.000Z'))) {
this.timeDisplayFormat = 'yyyy-MM';
}
else if (this.availableTimes.every((time) => time.endsWith('00:00:00.000Z'))) {
this.timeDisplayFormat = 'yyyy-MM-dd';
}
else if (this.availableTimes.every((time) => time.endsWith('00.000Z'))) {
this.timeDisplayFormat = 'y-MM-dd HH:mm';
}
else if (this.availableTimes.every((time) => time.endsWith('000Z'))) {
this.timeDisplayFormat = 'y-MM-dd HH:mm:ss';
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerManagerTimeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.3", type: HsLayerManagerTimeEditorComponent, isStandalone: true, selector: "hs-layer-manager-time-editor", inputs: { layer: "layer" }, viewQueries: [{ propertyName: "selectElement", first: true, predicate: ["hstimeselector"], descendants: true }], ngImport: i0, template: "<div class=\"p-0 flex-grow-1\">\n <button type=\"button\" class=\"btn btn-sm text-info border-0\" (click)=\"previousTime()\" [disabled]=\"!hasPreviousTime\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @if (availableTimesFetched) {\n <button class=\"hs-lm-time-editor-currentTime btn btn-sm text-info px-0\"\n (click)=\"$event.preventDefault();showTimeSelect()\" [hidden]=\"selectVisible\"\n >\n @if (currentTime) {\n <span>\n {{currentTime | datePreformat | date:timeDisplayFormat:timeDisplayFormat}}\n </span>\n } @else {\n <span class=\"bg-warning text-dark\" data-toggle=\"tooltip\" data-container=\"body\" data-placement=\"auto\"\n [ngbTooltip]=\"'LAYERMANAGER.time.outOfRangeDescription' | translate \"\n [closeDelay]=\"hsConfig.layerTooltipDelay || 0\">\n {{'LAYERMANAGER.time.outOfRange' | translate }}\n </span>\n }\n </button>\n } @else {\n <div class=\"spinner-border spinner-border-sm text-info\" role=\"status\">\n <span class=\"visually-hidden\">{{'COMMON.loading' | translate }}</span>\n </div> <span class=\"text-muted\">{{'LAYERMANAGER.time.loading'| translate }}</span>\n }\n <select #hstimeselector [hidden]=\"!selectVisible\" [(ngModel)]=\"currentTime\" (change)=\"selectTime();hideTimeSelect();\"\n (blur)=\"hideTimeSelect()\">\n @for (time of availableTimes; track time) {\n <option [ngValue]=\"time\">\n {{time | datePreformat | date:timeDisplayFormat:timeDisplayFormat}}\n </option>\n }\n </select>\n <button type=\"button\" class=\"btn btn-sm text-info border-0\" (click)=\"followingTime()\" [disabled]=\"!hasFollowingTime\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n</div>\n<div class=\"p-0\">\n <button type=\"button\" class=\"hs-timeSync-toggle btn px-0 py-md-1 py-2 me-1\"\n [ngClass]=\"{'btn-info text-white': timesInSync, 'text-secondary': !timesInSync}\" (click)=\"synchronizeTimes()\"\n [title]=\"'LAYERMANAGER.time.syncTimesTooltip' | translate \">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n <span class=\"visually-hidden\">{{'LAYERMANAGER.time.syncTimes' | translate }}</span>\n </button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: NgbTooltipModule }, { kind: "directive", type: i2.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: DatePreformatPipe, name: "datePreformat" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerManagerTimeEditorComponent, decorators: [{
type: Component,
args: [{ selector: 'hs-layer-manager-time-editor', imports: [
CommonModule,
TranslatePipe,
NgClass,
DatePreformatPipe,
NgbTooltipModule,
FormsModule,
], template: "<div class=\"p-0 flex-grow-1\">\n <button type=\"button\" class=\"btn btn-sm text-info border-0\" (click)=\"previousTime()\" [disabled]=\"!hasPreviousTime\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @if (availableTimesFetched) {\n <button class=\"hs-lm-time-editor-currentTime btn btn-sm text-info px-0\"\n (click)=\"$event.preventDefault();showTimeSelect()\" [hidden]=\"selectVisible\"\n >\n @if (currentTime) {\n <span>\n {{currentTime | datePreformat | date:timeDisplayFormat:timeDisplayFormat}}\n </span>\n } @else {\n <span class=\"bg-warning text-dark\" data-toggle=\"tooltip\" data-container=\"body\" data-placement=\"auto\"\n [ngbTooltip]=\"'LAYERMANAGER.time.outOfRangeDescription' | translate \"\n [closeDelay]=\"hsConfig.layerTooltipDelay || 0\">\n {{'LAYERMANAGER.time.outOfRange' | translate }}\n </span>\n }\n </button>\n } @else {\n <div class=\"spinner-border spinner-border-sm text-info\" role=\"status\">\n <span class=\"visually-hidden\">{{'COMMON.loading' | translate }}</span>\n </div> <span class=\"text-muted\">{{'LAYERMANAGER.time.loading'| translate }}</span>\n }\n <select #hstimeselector [hidden]=\"!selectVisible\" [(ngModel)]=\"currentTime\" (change)=\"selectTime();hideTimeSelect();\"\n (blur)=\"hideTimeSelect()\">\n @for (time of availableTimes; track time) {\n <option [ngValue]=\"time\">\n {{time | datePreformat | date:timeDisplayFormat:timeDisplayFormat}}\n </option>\n }\n </select>\n <button type=\"button\" class=\"btn btn-sm text-info border-0\" (click)=\"followingTime()\" [disabled]=\"!hasFollowingTime\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n</div>\n<div class=\"p-0\">\n <button type=\"button\" class=\"hs-timeSync-toggle btn px-0 py-md-1 py-2 me-1\"\n [ngClass]=\"{'btn-info text-white': timesInSync, 'text-secondary': !timesInSync}\" (click)=\"synchronizeTimes()\"\n [title]=\"'LAYERMANAGER.time.syncTimesTooltip' | translate \">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n <span class=\"visually-hidden\">{{'LAYERMANAGER.time.syncTimes' | translate }}</span>\n </button>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { layer: [{
type: Input
}], selectElement: [{
type: ViewChild,
args: ['hstimeselector']
}] } });
class HsCopyLayerDialogComponent {
constructor() {
this.hsDialogContainerService = inject(HsDialogContainerService);
}
yes() {
this.hsDialogContainerService.destroy(this);
this.dialogItem.resolve({
confirmed: 'yes',
layerTitle: this.data.layerTitle,
});
}
no() {
this.hsDialogContainerService.destroy(this);
this.dialogItem.resolve('no');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsCopyLayerDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.3", type: HsCopyLayerDialogComponent, isStandalone: false, selector: "hs-copy-layer-dialog", ngImport: i0, template: "<div class=\"modal in\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">\n {{data.title | translate }} ?\n </h4>\n <button type=\"button\" (click)=\"no()\" class=\"btn-close\" data-dismiss=\"modal\"\n [attr.aria-label]=\"'COMMON.close' | translate\"></button>\n </div>\n <div class=\"modal-body\" style=\"overflow-y:auto\">\n <p class=\"fw-bold h6\">{{data.message | translate }}</p>\n <div class=\"form-floating m-2\">\n <input class=\"form-control\" name=\"title\" [(ngModel)]=\"data.layerTitle\"\n [placeholder]=\"'COMMON.title' | translate\" />\n <label for=\"title\" class=\"capabilities_label control-label\">{{'COMMON.title' | translate\n }}</label>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"yes()\" [title]=\"'COMMON.yes' | translate\"\n data-dismiss=\"modal\">{{'COMMON.yes' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-secondary compositions-btn-cancel\"\n [title]=\"'COMMON.no' | translate\" (click)=\"no()\" data-dismiss=\"modal\">{{'COMMON.no' |\n translate }}</button>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i2$1.TranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsCopyLayerDialogComponent, decorators: [{
type: Component,
args: [{ selector: 'hs-copy-layer-dialog', standalone: false, template: "<div class=\"modal in\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">\n {{data.title | translate }} ?\n </h4>\n <button type=\"button\" (click)=\"no()\" class=\"btn-close\" data-dismiss=\"modal\"\n [attr.aria-label]=\"'COMMON.close' | translate\"></button>\n </div>\n <div class=\"modal-body\" style=\"overflow-y:auto\">\n <p class=\"fw-bold h6\">{{data.message | translate }}</p>\n <div class=\"form-floating m-2\">\n <input class=\"form-control\" name=\"title\" [(ngModel)]=\"data.layerTitle\"\n [placeholder]=\"'COMMON.title' | translate\" />\n <label for=\"title\" class=\"capabilities_label control-label\">{{'COMMON.title' | translate\n }}</label>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"yes()\" [title]=\"'COMMON.yes' | translate\"\n data-dismiss=\"modal\">{{'COMMON.yes' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-secondary compositions-btn-cancel\"\n [title]=\"'COMMON.no' | translate\" (click)=\"no()\" data-dismiss=\"modal\">{{'COMMON.no' |\n translate }}</button>\n </div>\n </div>\n </div>\n</div>\n" }]
}] });
class HsLayerEditorSublayerService {
constructor() {
this.hsLayerManagerVisibilityService = inject(HsLayerManagerVisibilityService);
this.populatedLayers = [];
this.sublayers = [];
}
getSubLayers(layer) {
return layer ? this.populateSubLayers(layer) : [];
}
/**
* Converts HsWmsLayer to HsSublayer
*/
mapWMSToLayerVisibility(layers, visible) {
return layers.map((layer) => {
const mappedLayer = {
name: layer.Name,
title: layer.Title,
visible: visible,
previousVisible: undefined,
maxResolution: layer.maxResolution,
// Recursively map sublayers if they exist
sublayers: layer.Layer
? this.mapWMSToLayerVisibility(layer.Layer, visible)
: undefined,
};
return mappedLayer;
});
}
/**
* Populates the sublayers of a layer with the given layer descriptor.
*/
populateSubLayers(lyr) {
if (this.populatedLayers.includes(lyr.uid)) {
return lyr._sublayers;
}
const layer = lyr.layer;
const subLayers = getCachedCapabilities(layer)?.Layer;
if (subLayers?.length > 0) {
const parsed = this.mapWMSToLayerVisibility(subLayers, lyr.visible);
this.populatedLayers.push(lyr.uid);
lyr._sublayers = parsed;
return parsed;
}
}
/**
* Constructs LAYERS param for layer visibility
*/
constructLayersParam(layers) {
const getVisibleLayers = (layer) => {
if (!layer.visible) {
return [];
}
if (layer.sublayers?.length) {
const visibleSublayers = layer.sublayers.flatMap(getVisibleLayers);
return visibleSublayers.length === layer.sublayers.length
? [layer.name]
: visibleSublayers;
}
return [layer.name];
};
return layers.flatMap(getVisibleLayers).join(',');
}
/**
* Handles the selection of a sublayer, updating the layer's visibility and parameters.
*/
subLayerSelected() {
const layer = this.layer;
if (!layer) {
console.error('Trying to update sublayer on undefined layer');
return;
}
const params = getLayerParams(layer.layer);
params.LAYERS = this.constructLayersParam(layer._sublayers);
// Special handling for ArcGIS layers
if (isLayerArcgis(layer.layer)) {
params.LAYERS = `show:${params.LAYERS}`;
}
// If no sublayers are visible, update the overall layer visibility
if (params.LAYERS == '' || params.LAYERS == 'show:') {
this.hsLayerManagerVisibilityService.changeLayerVisibility(!layer.visible, layer);
return;
}
// If the layer was previously invisible, make it visible
if (layer.visible == false) {
this.hsLayerManagerVisibilityService.changeLayerVisibility(!layer.visible, layer);
}
// Update the layer parameters
updateLayerParams(layer.layer, params);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSublayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSublayerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSublayerService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class HsLayerEditorSubLayerCheckboxesComponent {
constructor() {
this.hsLayerEditorSublayerService = inject(HsLayerEditorSublayerService);
this.hsLayerManagerVisibilityService = inject(HsLayerManagerVisibilityService);
this.hsConfig = inject(HsConfig);
this.parent = input(...(ngDevMode ? [undefined, { debugName: "parent" }] : []));
this.subLayer = input.required(...(ngDevMode ? [{ debugName: "subLayer" }] : []));
this.getNestedLayers = computed(() => {
const wmsLayer = this.subLayer();
if (!wmsLayer || !wmsLayer.sublayers) {
return [];
}
return Array.isArray(wmsLayer.sublayers)
? wmsLayer.sublayers
: [wmsLayer.sublayers];
}, ...(ngDevMode ? [{ debugName: "getNestedLayers" }] : []));
this.expanded = false;
}
ngOnInit() {
this.app = this.hsConfig.id;
}
subLayerIsString(subLayer) {
return typeof subLayer == 'string';
}
toggleExpanded() {
this.expanded = !this.expanded;
}
/**
* Controls state of layer´s sublayers manipulated by input checkboxes
* @param sublayer - Selected sublayer
* @param state - New state of sublayer
*/
subLayerSelected(sublayer, parent) {
if (parent) {
parent.visible =
sublayer.visible || parent.sublayers.some((sl) => sl.visible);
}
if (sublayer.sublayers) {
sublayer.sublayers.forEach((sl) => {
sl.visible = sublayer.visible;
});
}
return this.hsLayerEditorSublayerService.subLayerSelected();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSubLayerCheckboxesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.3", type: HsLayerEditorSubLayerCheckboxesComponent, isStandalone: true, selector: "hs-layer-editor-sub-layer-checkbox", inputs: { parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: false, transformFunction: null }, subLayer: { classPropertyName: "subLayer", publicName: "subLayer", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let sl = subLayer();\n<div class=\"d-flex m-auto sublayerContainer\">\n <div class=\"w-100\">\n <div class=\"d-flex\">\n <div class=\"p-0 form-check form-check-inline\">\n @if (!sl.sublayers) {\n <input class=\"form-check-input\" type=\"checkbox\" [(ngModel)]=\"sl.visible\" name=\"sublayerWithoutNestedSublayers\"\n (change)=\"subLayerSelected(sl, parent())\" [attr.id]=\"'hs-sublayers-' + sl.name + '-' + app \">\n <div>\n <label class=\"form-check-label m-0\" [ngClass]=\"{\n 'hs-checkmark':sl.visible,\n 'hs-uncheckmark':!sl.visible,\n 'grayed': (hsLayerManagerVisibilityService.currentResolution > sl.maxResolution) }\"\n [attr.for]=\"'hs-sublayers-' + sl.name + '-' + app \">{{sl.title ||sl.name}}</label>\n </div>\n }\n @else {\n <input class=\"form-check-input\" type=\"checkbox\" [(ngModel)]=\"sl.visible\" name=\"sublayerWithNestedSublayers\"\n (change)=\"subLayerSelected(sl, parent())\" [attr.id]=\"'hs-sublayers-' + sl.name + '-' + app \">\n <div class=\"p-0 d-inline-flex flex-grow\">\n <label class=\"form-check-label m-0\" [ngClass]=\"{'hs-checkmark':sl.visible,'hs-uncheckmark':!sl.visible}\"\n [attr.for]=\"'hs-sublayers-' + sl.name + '-' + app \"></label>\n <div (click)=\"toggleExpanded()\" style=\"cursor:pointer\"\n [ngClass]=\"{'grayed': hsLayerManagerVisibilityService.currentResolution >= sl.maxResolution}\">\n {{sl.title || sl.name}}\n <button type=\"button\" class=\"btn btn-sm p-0\" style=\"font-size: 0.6rem;\">\n <i class=\"fa-solid\" [ngClass]=\"expanded ? 'fa-chevron-down' : 'fa-chevron-right'\"></i>\n </button>\n </div>\n </div>\n <div class=\"mt-2 ms-3\" [hidden]=\"!expanded\">\n @for (nestedLayer of getNestedLayers(); track nestedLayer) {\n <hs-layer-editor-sub-layer-checkbox [subLayer]=\"nestedLayer\" [parent]=\"sl\">\n </hs-layer-editor-sub-layer-checkbox>\n }\n </div>\n }\n </div>\n </div>\n </div>\n</div>", dependencies: [{ kind: "component", type: HsLayerEditorSubLayerCheckboxesComponent, selector: "hs-layer-editor-sub-layer-checkbox", inputs: ["parent", "subLayer"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSubLayerCheckboxesComponent, decorators: [{
type: Component,
args: [{ selector: 'hs-layer-editor-sub-layer-checkbox', imports: [FormsModule, NgClass], template: "@let sl = subLayer();\n<div class=\"d-flex m-auto sublayerContainer\">\n <div class=\"w-100\">\n <div class=\"d-flex\">\n <div class=\"p-0 form-check form-check-inline\">\n @if (!sl.sublayers) {\n <input class=\"form-check-input\" type=\"checkbox\" [(ngModel)]=\"sl.visible\" name=\"sublayerWithoutNestedSublayers\"\n (change)=\"subLayerSelected(sl, parent())\" [attr.id]=\"'hs-sublayers-' + sl.name + '-' + app \">\n <div>\n <label class=\"form-check-label m-0\" [ngClass]=\"{\n 'hs-checkmark':sl.visible,\n 'hs-uncheckmark':!sl.visible,\n 'grayed': (hsLayerManagerVisibilityService.currentResolution > sl.maxResolution) }\"\n [attr.for]=\"'hs-sublayers-' + sl.name + '-' + app \">{{sl.title ||sl.name}}</label>\n </div>\n }\n @else {\n <input class=\"form-check-input\" type=\"checkbox\" [(ngModel)]=\"sl.visible\" name=\"sublayerWithNestedSublayers\"\n (change)=\"subLayerSelected(sl, parent())\" [attr.id]=\"'hs-sublayers-' + sl.name + '-' + app \">\n <div class=\"p-0 d-inline-flex flex-grow\">\n <label class=\"form-check-label m-0\" [ngClass]=\"{'hs-checkmark':sl.visible,'hs-uncheckmark':!sl.visible}\"\n [attr.for]=\"'hs-sublayers-' + sl.name + '-' + app \"></label>\n <div (click)=\"toggleExpanded()\" style=\"cursor:pointer\"\n [ngClass]=\"{'grayed': hsLayerManagerVisibilityService.currentResolution >= sl.maxResolution}\">\n {{sl.title || sl.name}}\n <button type=\"button\" class=\"btn btn-sm p-0\" style=\"font-size: 0.6rem;\">\n <i class=\"fa-solid\" [ngClass]=\"expanded ? 'fa-chevron-down' : 'fa-chevron-right'\"></i>\n </button>\n </div>\n </div>\n <div class=\"mt-2 ms-3\" [hidden]=\"!expanded\">\n @for (nestedLayer of getNestedLayers(); track nestedLayer) {\n <hs-layer-editor-sub-layer-checkbox [subLayer]=\"nestedLayer\" [parent]=\"sl\">\n </hs-layer-editor-sub-layer-checkbox>\n }\n </div>\n }\n </div>\n </div>\n </div>\n</div>" }]
}] });
class HsLayerEditorSublayersComponent {
constructor() {
this.hsLayerEditorSublayerService = inject(HsLayerEditorSublayerService);
this.layer = input.required(...(ngDevMode ? [{ debugName: "layer" }] : []));
this.subLayers = computed(() => this.hsLayerEditorSublayerService.getSubLayers(this.layer()), ...(ngDevMode ? [{ debugName: "subLayers" }] : []));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSublayersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.3", type: HsLayerEditorSublayersComponent, isStandalone: true, selector: "hs-layer-editor-sublayers", inputs: { layer: { classPropertyName: "layer", publicName: "layer", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
<div class="card-body py-2">
@for (subLayer of subLayers(); track subLayer) {
<hs-layer-editor-sub-layer-checkbox [subLayer]="subLayer">
</hs-layer-editor-sub-layer-checkbox>
}
</div>
`, isInline: true, dependencies: [{ kind: "component", type: HsLayerEditorSubLayerCheckboxesComponent, selector: "hs-layer-editor-sub-layer-checkbox", inputs: ["parent", "subLayer"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HsLayerEditorSublayersComponent, decorators: [{
type: Component,
args: [{
selector: 'hs-layer-editor-sublayers',
template: `
<div class="card-body py-2">
@for (subLayer of subLayers(); track subLayer) {
<hs-layer-editor-sub-layer-checkbox [subLayer]="subLayer">
</hs-layer-editor-sub-layer-checkbox>
}
</div>
`,
imports: [HsLayerEditorSubLayerCheckboxesComponent],
}]
}] });
class HsLayerEditorService {
constructor() {
this.hsMapService = inject(HsMapService);
this.hsWmsGetCapabilitiesService = inject(HsWmsGetCapabilitiesService);
this.hsLayerEditorVectorLayerService = inject(HsLayerEditorVectorLayerService);
this.hsEventBusService = inject(HsEventBusService);
this.hsLayoutService = inject(HsLayoutService);
this.hsLegendService = inject(HsLegendService);
this.hsLayerSelectorService = inject(HsLayerSelectorService);
this.hsLayerManagerMetadataService = inject(HsLayerManagerMetadataService);
this.hsLayerEditorSublayerService = inject(HsLayerEditorSublayerService);
this.hsShareUrlService = inject(HsShareUrlService);
this.layerTitleChange = new Subject();
this.hsLayerSelectorService.layerSelected
.pipe(filter((layer) => !!layer))
.subscribe(async (layer) => {
this.legendDescriptor =
await this.hsLegendService.getLayerLegendDescriptor(layer.layer);
});
}
/**
* Create layer editor component for settings
* @param vcr - View container reference
* @param layer - Layer
*/
createLayerEditor(vcr, layer) {
return this.createEditor(vcr, layer, 'settings', HsLayerEditorComponent);
}
/**
* Create layer editor component for sublayers
* @param vcr - View container reference
* @param layer - Layer
*/
createSublayerEditor(vcr, layer) {
return th