From 17fc2f3c45f8fd035494da246542bca89adda252 Mon Sep 17 00:00:00 2001 From: cbourget Date: Mon, 1 Apr 2019 09:54:44 -0400 Subject: [PATCH 1/8] wip(wfs): wfs editor with table and widgets --- demo/src/app/app.component.html | 1 + demo/src/app/app.module.ts | 2 + .../wfs-browser/wfs-browser-routing.module.ts | 15 ++ .../wfs-browser/wfs-browser.component.html | 46 +++++ .../wfs-browser/wfs-browser.component.scss | 17 ++ .../geo/wfs-browser/wfs-browser.component.ts | 106 +++++++++++ .../app/geo/wfs-browser/wfs-browser.module.ts | 40 +++++ .../common/src/lib/edition/edition.module.ts | 17 ++ .../editor-outlet.component.html | 8 + .../editor-outlet.component.scss | 3 + .../editor-outlet/editor-outlet.component.ts | 76 ++++++++ .../editor-outlet/editor-outlet.module.ts | 23 +++ .../editor-selector.component.html | 11 ++ .../editor-selector.component.scss | 11 ++ .../editor-selector.component.ts | 121 +++++++++++++ .../editor-selector/editor-selector.module.ts | 24 +++ packages/common/src/lib/edition/index.ts | 1 + .../lib/edition/shared/edition.interfaces.ts | 10 ++ .../common/src/lib/edition/shared/editor.ts | 169 ++++++++++++++++++ .../common/src/lib/edition/shared/index.ts | 2 + .../entity-table/entity-table.component.html | 1 + .../entity-table/entity-table.component.ts | 9 +- packages/common/src/public_api.ts | 4 + .../shared/datasources/wfs-datasource.ts | 10 +- .../src/lib/feature/shared/feature.utils.ts | 30 ++++ packages/geo/src/lib/feature/shared/store.ts | 31 +++- .../lib/feature/shared/strategies/index.ts | 1 + .../shared/strategies/loading-layer.ts | 112 ++++++++++++ .../feature/shared/strategies/selection.ts | 1 + packages/geo/src/lib/geo.module.ts | 2 + packages/geo/src/lib/wfs/index.ts | 1 + packages/geo/src/lib/wfs/shared/index.ts | 1 + .../src/lib/wfs/shared/wfs-editor.service.ts | 98 ++++++++++ .../wfs-editor-selector.directive.ts | 63 +++++++ .../wfs-editor-selector.module.ts | 22 +++ .../wfs-ogc-filter.component.html | 4 + .../wfs-ogc-filter.component.scss | 0 .../wfs-ogc-filter.component.ts | 48 +++++ .../wfs-ogc-filter/wfs-ogc-filter.module.ts | 24 +++ .../wfs-ogc-filter/wfs-ogc-filter.widget.ts | 21 +++ packages/geo/src/lib/wfs/wfs.module.ts | 21 +++ packages/geo/src/locale/en.geo.json | 4 + packages/geo/src/locale/fr.geo.json | 4 + packages/geo/src/public_api.ts | 3 + 44 files changed, 1206 insertions(+), 12 deletions(-) create mode 100644 demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts create mode 100644 demo/src/app/geo/wfs-browser/wfs-browser.component.html create mode 100644 demo/src/app/geo/wfs-browser/wfs-browser.component.scss create mode 100644 demo/src/app/geo/wfs-browser/wfs-browser.component.ts create mode 100644 demo/src/app/geo/wfs-browser/wfs-browser.module.ts create mode 100644 packages/common/src/lib/edition/edition.module.ts create mode 100644 packages/common/src/lib/edition/editor-outlet/editor-outlet.component.html create mode 100644 packages/common/src/lib/edition/editor-outlet/editor-outlet.component.scss create mode 100644 packages/common/src/lib/edition/editor-outlet/editor-outlet.component.ts create mode 100644 packages/common/src/lib/edition/editor-outlet/editor-outlet.module.ts create mode 100644 packages/common/src/lib/edition/editor-selector/editor-selector.component.html create mode 100644 packages/common/src/lib/edition/editor-selector/editor-selector.component.scss create mode 100644 packages/common/src/lib/edition/editor-selector/editor-selector.component.ts create mode 100644 packages/common/src/lib/edition/editor-selector/editor-selector.module.ts create mode 100644 packages/common/src/lib/edition/index.ts create mode 100644 packages/common/src/lib/edition/shared/edition.interfaces.ts create mode 100644 packages/common/src/lib/edition/shared/editor.ts create mode 100644 packages/common/src/lib/edition/shared/index.ts create mode 100644 packages/geo/src/lib/feature/shared/strategies/loading-layer.ts create mode 100644 packages/geo/src/lib/wfs/index.ts create mode 100644 packages/geo/src/lib/wfs/shared/index.ts create mode 100644 packages/geo/src/lib/wfs/shared/wfs-editor.service.ts create mode 100644 packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts create mode 100644 packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts create mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html create mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.scss create mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts create mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts create mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts create mode 100644 packages/geo/src/lib/wfs/wfs.module.ts diff --git a/demo/src/app/app.component.html b/demo/src/app/app.component.html index 592a1c1646..1c8cf46d95 100644 --- a/demo/src/app/app.component.html +++ b/demo/src/app/app.component.html @@ -48,6 +48,7 @@

{{title}}

Directions Time filter OGC filter + WFS browser
diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index c85e1d0611..469d20ae77 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -41,6 +41,7 @@ import { AppPrintModule } from './geo/print/print.module'; import { AppDirectionsModule } from './geo/directions/directions.module'; import { AppTimeFilterModule } from './geo/time-filter/time-filter.module'; import { AppOgcFilterModule } from './geo/ogc-filter/ogc-filter.module'; +import { AppWfsBrowserModule } from './geo/wfs-browser/wfs-browser.module'; import { AppContextModule } from './context/context/context.module'; @@ -90,6 +91,7 @@ import { AppComponent } from './app.component'; AppDirectionsModule, AppTimeFilterModule, AppOgcFilterModule, + AppWfsBrowserModule, AppContextModule, diff --git a/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts b/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts new file mode 100644 index 0000000000..e79774ba57 --- /dev/null +++ b/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts @@ -0,0 +1,15 @@ +import { Routes, RouterModule } from '@angular/router'; +import { ModuleWithProviders } from '@angular/core'; + +import { AppWfsBrowserComponent } from './wfs-browser.component'; + +const routes: Routes = [ + { + path: 'wfs-browser', + component: AppWfsBrowserComponent + } +]; + +export const AppWfsBrowserRoutingModule: ModuleWithProviders = RouterModule.forChild( + routes +); diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.html b/demo/src/app/geo/wfs-browser/wfs-browser.component.html new file mode 100644 index 0000000000..477aa1512a --- /dev/null +++ b/demo/src/app/geo/wfs-browser/wfs-browser.component.html @@ -0,0 +1,46 @@ + + Geo + Simple Map + +
  • Dependencies: LanguageService
  • + +
    + See the code of this example +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.scss b/demo/src/app/geo/wfs-browser/wfs-browser.component.scss new file mode 100644 index 0000000000..0a808cfb8a --- /dev/null +++ b/demo/src/app/geo/wfs-browser/wfs-browser.component.scss @@ -0,0 +1,17 @@ +igo-map-browser { + width: 100%; + height: 300px; +} + +igo-panel { + width: 100%; + padding-top: 10px; +} + +igo-entity-table { + height: 400px; +} + +igo-actionbar { + height: 48px; +} diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.ts b/demo/src/app/geo/wfs-browser/wfs-browser.component.ts new file mode 100644 index 0000000000..d2b8e4a5ca --- /dev/null +++ b/demo/src/app/geo/wfs-browser/wfs-browser.component.ts @@ -0,0 +1,106 @@ +import { Component, OnInit } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { LanguageService } from '@igo2/core'; +import { + ActionbarMode, + EntityRecord, + EntityStore, + EntityTableScrollBehavior, + Editor +} from '@igo2/common'; +import { + IgoMap, + DataSourceService, + LayerService, + LayerOptions, + WFSDataSourceOptions +} from '@igo2/geo'; + +@Component({ + selector: 'app-wfs-browser', + templateUrl: './wfs-browser.component.html', + styleUrls: ['./wfs-browser.component.scss'] +}) +export class AppWfsBrowserComponent implements OnInit { + + public map = new IgoMap({ + controls: { + attribution: { + collapsed: true + } + } + }); + + public view = { + center: [-0, 47.2], + zoom: 5 + }; + + public editorStore = new EntityStore([]); + + public activeEditor$: Observable; + + public actionbarMode = ActionbarMode.Dock; + + public scrollBehavior = EntityTableScrollBehavior.Instant; + + constructor( + private languageService: LanguageService, + private dataSourceService: DataSourceService, + private layerService: LayerService + ) {} + + ngOnInit() { + this.activeEditor$ = this.editorStore.stateView + .firstBy$((record: EntityRecord) => record.state.selected === true) + .pipe( + map((record: EntityRecord) => { + console.log(record); + return record ? record.entity : undefined; + }) + ); + + this.dataSourceService + .createAsyncDataSource({ + type: 'osm' + }) + .subscribe(dataSource => { + this.map.addLayer( + this.layerService.createLayer({ + title: 'OSM', + source: dataSource + }) + ); + }); + + const wfsDatasource: WFSDataSourceOptions = { + type: 'wfs', + url: 'https://ahocevar.com/geoserver/wfs', + params: { + featureTypes: 'ne:ne_10m_admin_0_countries', + fieldNameGeometry: 'the_geom', + version: '1.1.0', + outputFormat: 'application/json' + }, + sourceFields: [ + {name: 'name', alias: 'Name'}, + {name: 'type', alias: 'Type'} + ] + }; + + this.dataSourceService + .createAsyncDataSource(wfsDatasource) + .subscribe(dataSource => { + const layer: LayerOptions = { + title: 'WFS ', + visible: true, + source: dataSource + }; + this.map.addLayer(this.layerService.createLayer(layer)); + }); + } + +} diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.module.ts b/demo/src/app/geo/wfs-browser/wfs-browser.module.ts new file mode 100644 index 0000000000..86c8a57f6b --- /dev/null +++ b/demo/src/app/geo/wfs-browser/wfs-browser.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { + MatCardModule, + MatButtonModule, + MatIconModule +} from '@angular/material'; + +import { + IgoActionModule, + IgoEntityModule, + IgoEditionModule, + IgoPanelModule +} from '@igo2/common'; +import { + IgoMapModule, + IgoWfsModule +} from '@igo2/geo'; + +import { AppWfsBrowserComponent } from './wfs-browser.component'; +import { AppWfsBrowserRoutingModule } from './wfs-browser-routing.module'; + +@NgModule({ + declarations: [AppWfsBrowserComponent], + imports: [ + CommonModule, + AppWfsBrowserRoutingModule, + MatCardModule, + MatButtonModule, + MatIconModule, + IgoActionModule, + IgoEntityModule, + IgoEditionModule, + IgoPanelModule, + IgoMapModule, + IgoWfsModule + ], + exports: [AppWfsBrowserComponent] +}) +export class AppWfsBrowserModule {} diff --git a/packages/common/src/lib/edition/edition.module.ts b/packages/common/src/lib/edition/edition.module.ts new file mode 100644 index 0000000000..d259a46fda --- /dev/null +++ b/packages/common/src/lib/edition/edition.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { IgoEditorOutletModule } from './editor-outlet/editor-outlet.module'; +import { IgoEditorSelectorModule } from './editor-selector/editor-selector.module'; + +@NgModule({ + imports: [ + CommonModule + ], + exports: [ + IgoEditorOutletModule, + IgoEditorSelectorModule + ], + declarations: [] +}) +export class IgoEditionModule {} diff --git a/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.html b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.html new file mode 100644 index 0000000000..b5a3be5209 --- /dev/null +++ b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.html @@ -0,0 +1,8 @@ + + + + diff --git a/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.scss b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.scss new file mode 100644 index 0000000000..31c5e40a86 --- /dev/null +++ b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.scss @@ -0,0 +1,3 @@ +igo-widget-outlet { + height: 100%; +} diff --git a/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.ts b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.ts new file mode 100644 index 0000000000..87e189989d --- /dev/null +++ b/packages/common/src/lib/edition/editor-outlet/editor-outlet.component.ts @@ -0,0 +1,76 @@ +import { + Component, + Input, + Output, + EventEmitter, + ChangeDetectionStrategy +} from '@angular/core'; + +import { BehaviorSubject } from 'rxjs'; + +import { Widget } from '../../widget'; +import { Editor } from '../shared/editor'; + +/** + * This component dynamically render an Editor's active widget. + * It also deactivate that widget whenever the widget's component + * emit the 'cancel' or 'complete' event. + */ +@Component({ + selector: 'igo-editor-outlet', + templateUrl: './editor-outlet.component.html', + styleUrls: ['./editor-outlet.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EditorOutletComponent { + + /** + * Editor + */ + @Input() editor: Editor; + + /** + * Event emitted when a widget is deactivate which happens + * when the widget's component emits the 'cancel' or 'complete' event. + */ + @Output() deactivateWidget = new EventEmitter(); + + /** + * Observable of the editor's active widget + * @internal + */ + get widget$(): BehaviorSubject { return this.editor.widget$; } + + /** + * Observable of the editor's widget inputs + * @internal + */ + get widgetInputs$(): BehaviorSubject<{ [key: string]: any }> { + return this.editor.widgetInputs$; + } + + constructor() {} + + /** + * When a widget's component emit the 'cancel' event, + * deactivate that widget and emit the 'deactivateWidget' event. + * @param widget Widget + * @internal + */ + onWidgetCancel(widget: Widget) { + this.editor.deactivateWidget(); + this.deactivateWidget.emit(widget); + } + + /** + * When a widget's component emit the 'cancel' event, + * deactivate that widget and emit the 'deactivateWidget' event. + * @param widget Widget + * @internal + */ + onWidgetComplete(widget: Widget) { + this.editor.deactivateWidget(); + this.deactivateWidget.emit(widget); + } + +} diff --git a/packages/common/src/lib/edition/editor-outlet/editor-outlet.module.ts b/packages/common/src/lib/edition/editor-outlet/editor-outlet.module.ts new file mode 100644 index 0000000000..dc4430c87f --- /dev/null +++ b/packages/common/src/lib/edition/editor-outlet/editor-outlet.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { IgoWidgetOutletModule } from '../../widget/widget-outlet/widget-outlet.module'; + +import { EditorOutletComponent } from './editor-outlet.component'; + +/** + * @ignore + */ +@NgModule({ + imports: [ + CommonModule, + IgoWidgetOutletModule + ], + exports: [ + EditorOutletComponent + ], + declarations: [ + EditorOutletComponent + ] +}) +export class IgoEditorOutletModule {} diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.html b/packages/common/src/lib/edition/editor-selector/editor-selector.component.html new file mode 100644 index 0000000000..5f9e9bcb8a --- /dev/null +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.html @@ -0,0 +1,11 @@ + + + + + {{getEditorTitle(editor)}} + + + + \ No newline at end of file diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss b/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss new file mode 100644 index 0000000000..8ef7b9144d --- /dev/null +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss @@ -0,0 +1,11 @@ +mat-form-field { + width: 100%; +} + +mat-form-field ::ng-deep .mat-form-field-infix { + padding: 0; +} + +mat-form-field ::ng-deep .mat-form-field-wrapper { + padding-bottom: 1.75em; +} diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts new file mode 100644 index 0000000000..d8de99d024 --- /dev/null +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts @@ -0,0 +1,121 @@ +import { + Component, + Input, + Output, + EventEmitter, + ChangeDetectionStrategy, + ChangeDetectorRef, + OnInit, + OnDestroy +} from '@angular/core'; + +import { BehaviorSubject, Subscription } from 'rxjs'; + +import { + EntityRecord, + EntityStore, + EntityStoreController, + getEntityTitle +} from '../../entity'; +import { Editor } from '../shared/editor'; + +/** + * Drop list that activates the selected editor emit an event. + */ +@Component({ + selector: 'igo-editor-selector', + templateUrl: './editor-selector.component.html', + styleUrls: ['./editor-selector.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EditorSelectorComponent implements OnInit, OnDestroy { + + /** + * The current editor + * @internal + */ + public current$ = new BehaviorSubject(undefined); + + /** + * Subscription to the store's selected editor + */ + private selected$$: Subscription; + + /** + * Store controller + */ + private controller: EntityStoreController; + + /** + * Store that holds the available editors. + */ + @Input() store: EntityStore; + + /** + * Event emitted when an editor is selected or unselected + */ + @Output() selectedChange = new EventEmitter<{ + selected: boolean; + editor: Editor; + }>(); + + constructor(private cdRef: ChangeDetectorRef) {} + + /** + * Observe the store's selected editor and activate it + * @internal + */ + ngOnInit() { + this.controller = new EntityStoreController(this.store, this.cdRef); + this.selected$$ = this.store.stateView + .firstBy$((record: EntityRecord) => record.state.selected === true) + .subscribe((record: EntityRecord) => { + const editor = record ? record.entity : undefined; + this.activateEditor(editor); + }); + } + + /** + * Unsubscribe to the store selected editor + * @internal + */ + ngOnDestroy() { + this.controller.destroy(); + this.selected$$.unsubscribe(); + } + + /** + * @internal + */ + getEditorTitle(editor: Editor): string { + return getEntityTitle(editor); + } + + /** + * When an editor is manually selected, select it into the + * store and emit an event. + * @internal + * @param event The selection change event + */ + onSelectionChange(event: {value: Editor}) { + const editor = event.value; + this.store.state.update(editor, {selected: true}, true); + this.selectedChange.emit({selected: true, editor}); + } + + /** + * Activate the newly selected editor and deactivate the one previously selected + * @internal + * @param editor Editor + */ + private activateEditor(editor: Editor) { + const current = this.current$.value; + if (current !== undefined) { + current.deactivate(); + } + if (editor !== undefined) { + editor.activate(); + } + this.current$.next(editor); + } +} diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts b/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts new file mode 100644 index 0000000000..2298d3de35 --- /dev/null +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MatSelectModule } from '@angular/material'; + +import { EditorSelectorComponent } from './editor-selector.component'; + +/** + * @ignore + */ +@NgModule({ + imports: [ + CommonModule, + FormsModule, + MatSelectModule + ], + exports: [ + EditorSelectorComponent + ], + declarations: [ + EditorSelectorComponent + ] +}) +export class IgoEditorSelectorModule {} diff --git a/packages/common/src/lib/edition/index.ts b/packages/common/src/lib/edition/index.ts new file mode 100644 index 0000000000..c3da79f741 --- /dev/null +++ b/packages/common/src/lib/edition/index.ts @@ -0,0 +1 @@ +export * from './shared'; diff --git a/packages/common/src/lib/edition/shared/edition.interfaces.ts b/packages/common/src/lib/edition/shared/edition.interfaces.ts new file mode 100644 index 0000000000..fdb10546a0 --- /dev/null +++ b/packages/common/src/lib/edition/shared/edition.interfaces.ts @@ -0,0 +1,10 @@ +import { ActionStore } from '../../action'; +import { EntityStore, EntityTableTemplate } from '../../entity'; + +export interface EditorConfig { + id: string; + title: string; + tableTemplate?: EntityTableTemplate; + entityStore?: EntityStore; + actionStore?: ActionStore; +} diff --git a/packages/common/src/lib/edition/shared/editor.ts b/packages/common/src/lib/edition/shared/editor.ts new file mode 100644 index 0000000000..8ba60284d1 --- /dev/null +++ b/packages/common/src/lib/edition/shared/editor.ts @@ -0,0 +1,169 @@ +import { Subscription, BehaviorSubject, Subject } from 'rxjs'; +import { distinctUntilChanged, debounceTime } from 'rxjs/operators'; + +import { ActionStore } from '../../action'; +import { EntityRecord, EntityStore, EntityTableTemplate } from '../../entity'; +import { Widget } from '../../widget'; + +import { EditorConfig } from './edition.interfaces'; + +/** + * This class is responsible of managing the relations between + * entities and the actions that consume them. It also defines an + * entity table template that may be used by an entity table component. + */ +export class Editor { + + /** + * Observable of the selected entity + */ + public entity$ = new BehaviorSubject(undefined); + + /** + * Observable of the selected widget + */ + public widget$ = new BehaviorSubject(undefined); + + /** + * Observable of the selected widget's inputs + */ + public widgetInputs$ = new BehaviorSubject<{ [key: string]: any }>({}); + + /** + * Subscription to the selected entity + */ + private entity$$: Subscription; + + /** + * Whether this editor is active + */ + private active: boolean = false; + + /** + * State change that trigger an update of the actions availability + */ + private changes$: Subject = new Subject(); + + /** + * Subscription to state changes + */ + private changes$$: Subscription; + + /** + * Editor id + */ + get id(): string { return this.config.id; } + + /** + * Editor title + */ + get title(): string { return this.config.title; } + + /** + * Entity table template + */ + get tableTemplate(): EntityTableTemplate { return this.config.tableTemplate; } + + /** + * Entities store + */ + get entityStore(): EntityStore { return this.config.entityStore; } + + /** + * Actions store (some actions activate a widget) + */ + get actionStore(): ActionStore { return this.config.actionStore; } + + /** + * Selected entity + */ + get entity(): object { return this.entity$.value; } + + /** + * Selected widget + */ + get widget(): Widget { return this.widget$.value; } + + /** + * Whether a widget is selected + */ + get hasWidget(): boolean { return this.widget !== undefined; } + + constructor(private config: EditorConfig) {} + + /** + * Whether this editor is active + */ + isActive(): boolean { return this.active; } + + /** + * Activate the editor. By doing that, the editor will observe + * the selected entity (from the store) and update the actions availability. + * For example, some actions require an entity to be selected. + */ + activate() { + if (this.active === true) { + this.deactivate(); + } + this.active = true; + + this.entity$$ = this.entityStore.stateView + .firstBy$((record: EntityRecord) => record.state.selected === true) + .pipe(distinctUntilChanged()) + .subscribe((record: EntityRecord) => { + const editor = record ? record.entity : undefined; + this.onSelectEntity(editor); + }); + + this.changes$$ = this.changes$ + .pipe(debounceTime(50)) + .subscribe(() => this.actionStore.updateActionsAvailability()); + this.changes$.next(); + } + + /** + * Deactivate the editor. Unsubcribe to the selected entity. + */ + deactivate() { + this.active = false; + this.deactivateWidget(); + + if (this.entity$$ !== undefined) { + this.entity$$.unsubscribe(); + } + if (this.changes$$ !== undefined) { + this.changes$$.unsubscribe(); + } + } + + /** + * Activate a widget. In itself, activating a widget doesn't render it but, + * if an EditorOutlet component is bound to this editor, the widget will + * show up. + * @param widget Widget + * @param inputs Inputs the widget will receive + */ + activateWidget(widget: Widget, inputs: {[key: string]: any} = {}) { + this.widget$.next(widget); + this.widgetInputs$.next(inputs); + } + + /** + * Deactivate a widget. + */ + deactivateWidget() { + this.widget$.next(undefined); + this.changes$.next(); + } + + /** + * When an entity is selected, keep a reference to that + * entity and update the actions availability. + * @param entity Entity + */ + private onSelectEntity(entity: object) { + this.entity$.next(entity); + this.changes$.next(); + } + +} diff --git a/packages/common/src/lib/edition/shared/index.ts b/packages/common/src/lib/edition/shared/index.ts new file mode 100644 index 0000000000..6fccffbbf0 --- /dev/null +++ b/packages/common/src/lib/edition/shared/index.ts @@ -0,0 +1,2 @@ +export * from './editor'; +export * from './edition.interfaces'; diff --git a/packages/common/src/lib/entity/entity-table/entity-table.component.html b/packages/common/src/lib/entity/entity-table/entity-table.component.html index 7287e5e347..21745ceb78 100644 --- a/packages/common/src/lib/entity/entity-table/entity-table.component.html +++ b/packages/common/src/lib/entity/entity-table/entity-table.component.html @@ -65,6 +65,7 @@ mat-row igoEntityTableRow *matRowDef="let entity; columns: headers;" + [scrollBehavior]="scrollBehavior" [ngClass]="getRowClass(entity)" [selection]="selection" [selected]="rowIsSelected(entity)" diff --git a/packages/common/src/lib/entity/entity-table/entity-table.component.ts b/packages/common/src/lib/entity/entity-table/entity-table.component.ts index 9287d5822f..afd6596301 100644 --- a/packages/common/src/lib/entity/entity-table/entity-table.component.ts +++ b/packages/common/src/lib/entity/entity-table/entity-table.component.ts @@ -20,7 +20,8 @@ import { EntityTableTemplate, EntityTableColumn, EntityTableColumnRenderer, - EntityTableSelectionState + EntityTableSelectionState, + EntityTableScrollBehavior } from '../shared'; @Component({ @@ -69,6 +70,12 @@ export class EntityTableComponent implements OnInit, OnDestroy, OnChanges { */ @Input() template: EntityTableTemplate; + /** + * Scroll behavior on selection + */ + @Input() + scrollBehavior: EntityTableScrollBehavior = EntityTableScrollBehavior.Smooth; + /** * Event emitted when an entity (row) is clicked */ diff --git a/packages/common/src/public_api.ts b/packages/common/src/public_api.ts index b04ab144fd..f7cc7c1a4f 100644 --- a/packages/common/src/public_api.ts +++ b/packages/common/src/public_api.ts @@ -18,6 +18,8 @@ export * from './lib/form/form.module'; export * from './lib/form/form/form.module'; export * from './lib/form/form-field/form-field.module'; export * from './lib/form/form-group/form-group.module'; +export * from './lib/edition/edition.module'; +export * from './lib/edition/editor-selector/editor-selector.module'; export * from './lib/entity/entity.module'; export * from './lib/entity/entity-selector/entity-selector.module'; export * from './lib/entity/entity-table/entity-table.module'; @@ -45,6 +47,8 @@ export * from './lib/custom-html'; export * from './lib/drag-drop'; export * from './lib/dynamic-component'; export * from './lib/form'; +export * from './lib/edition'; +export * from './lib/edition/editor-selector/editor-selector.component'; export * from './lib/entity'; export * from './lib/flexible'; export * from './lib/image'; diff --git a/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.ts b/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.ts index 47a97ed73e..860ab5f8d2 100644 --- a/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.ts +++ b/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.ts @@ -1,6 +1,6 @@ import olSourceVector from 'ol/source/Vector'; -import * as olloadingstrategy from 'ol/loadingstrategy'; -import * as olformat from 'ol/format'; +import * as OlLoadingStrategy from 'ol/loadingstrategy'; +import * as OlFormat from 'ol/format'; import { uuid } from '@igo2/utils'; @@ -124,7 +124,7 @@ export class WFSDataSource extends DataSource { return baseUrl; }, - strategy: olloadingstrategy.bbox + strategy: OlLoadingStrategy.bbox }); } @@ -136,10 +136,10 @@ export class WFSDataSource extends DataSource { const patternGeojson = new RegExp('.*?json.*?'); if (patternGeojson.test(outputFormat)) { - olFormatCls = olformat.GeoJSON; + olFormatCls = OlFormat.GeoJSON; } if (patternGml3.test(outputFormat)) { - olFormatCls = olformat.WFS; + olFormatCls = OlFormat.WFS; } return new olFormatCls(); diff --git a/packages/geo/src/lib/feature/shared/feature.utils.ts b/packages/geo/src/lib/feature/shared/feature.utils.ts index 5a31cc03c8..e626fcd0d0 100644 --- a/packages/geo/src/lib/feature/shared/feature.utils.ts +++ b/packages/geo/src/lib/feature/shared/feature.utils.ts @@ -51,6 +51,36 @@ export function featureToOl( return olFeature; } +/** + * Create a feature object out of an OL feature + * The output object has a reference to the feature id. + * @param olFeature OL Feature + * @param projectionIn OL feature projection + * @param projectionOut Feature projection + * @returns Feature + */ +export function featureFromOl( + olFeature: OlFeature, + projectionIn: string, + projectionOut = 'EPSG:4326' +): OlFeature { + const olFormat = new OlFormatGeoJSON(); + const feature = olFormat.writeFeatureObject(olFeature, { + dataProjection: projectionOut, + featureProjection: projectionIn + }); + + return Object.assign({}, feature, { + projection: projectionOut, + extent: olFeature.get('_extent'), + meta: { + id: olFeature.getId(), + revision: olFeature.getRevision(), + mapTitle: olFeature.get('_mapTitle') + } + }); +} + /** * Compute an OL feature extent in it's map projection * @param map Map diff --git a/packages/geo/src/lib/feature/shared/store.ts b/packages/geo/src/lib/feature/shared/store.ts index 21f1c0c2b4..cd44bbe2f9 100644 --- a/packages/geo/src/lib/feature/shared/store.ts +++ b/packages/geo/src/lib/feature/shared/store.ts @@ -8,7 +8,7 @@ import { IgoMap } from '../../map'; import { FeatureMotion } from './feature.enums'; import { Feature, FeatureStoreOptions } from './feature.interfaces'; -import { featureToOl, moveToFeatures } from './feature.utils'; +import { featureFromOl, featureToOl, moveToFeatures } from './feature.utils'; import { FeatureStoreStrategy } from './strategies/strategy'; /** @@ -128,23 +128,42 @@ export class FeatureStore extends EntityStore { * @param motion Optional: The type of motion to perform */ setLayerFeatures(features: Feature[], motion: FeatureMotion = FeatureMotion.Default) { - if (this.layer === undefined) { - throw new Error('This FeatureStore is not bound to a layer.'); - } + this.checkLayer(); const olFeatures = features .map((feature: Feature) => featureToOl(feature, this.map.projection)); this.setLayerOlFeatures(olFeatures, motion); } + /** + * Set the store's features from an array of OL features. + * @param olFeatures Ol features + */ + setOlFeatures(olFeatures: OlFeature[]) { + this.checkLayer(); + + const features = olFeatures.map((olFeature: OlFeature) => { + olFeature.set('_featureStore', this, true); + return featureFromOl(olFeature, this.layer.map.projection); + }); + this.load(features); + } + /** * Remove all features from the layer */ clearLayer() { + this.checkLayer(); + this.source.ol.clear(); + } + + /** + * Check wether a layer is bound or not and throw an error if not. + */ + private checkLayer() { if (this.layer === undefined) { throw new Error('This FeatureStore is not bound to a layer.'); - } - this.source.ol.clear(); + } } /** diff --git a/packages/geo/src/lib/feature/shared/strategies/index.ts b/packages/geo/src/lib/feature/shared/strategies/index.ts index f4de8aca6d..871c770d17 100644 --- a/packages/geo/src/lib/feature/shared/strategies/index.ts +++ b/packages/geo/src/lib/feature/shared/strategies/index.ts @@ -1,3 +1,4 @@ export * from './loading'; +export * from './loading-layer'; export * from './selection'; export * from './strategy'; diff --git a/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts new file mode 100644 index 0000000000..c96e056490 --- /dev/null +++ b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts @@ -0,0 +1,112 @@ +import { unByKey } from 'ol/Observable'; +import { OlEvent } from 'ol/events/Event'; +import { OlFeature } from 'ol/Feature'; + +import { FeatureStore } from '../store'; +import { FeatureStoreStrategy } from './strategy'; + +/** + * This strategy loads a layer's features into it's store counterpart. + * The layer -> store binding is a one-way binding. That means any OL feature + * added to the layer will be added to the store but the opposite is false. + * + * Important: In it's current state, this strategy is to meant to be combined + * with a standard Loading strategy and it would probably cause recursion issues. + */ +export class FeatureStoreLoadingLayerStrategy extends FeatureStoreStrategy { + + /** + * Subscription to the store's OL source changes + */ + private stores$$ = new Map(); + + /** + * Bind this strategy to a store and start watching for Ol source changes + * @param store Feature store + */ + bindStore(store: FeatureStore) { + super.bindStore(store); + if (this.isActive() === true) { + this.watchStore(store); + } + } + + /** + * Unbind this strategy from a store and stop watching for Ol source changes + * @param store Feature store + */ + unbindStore(store: FeatureStore) { + super.unbindStore(store); + if (this.isActive() === true) { + this.unwatchStore(store); + } + } + + /** + * Start watching all stores already bound to that strategy at once. + * @internal + */ + protected doActivate() { + this.stores.forEach((store: FeatureStore) => this.watchStore(store)); + } + + /** + * Stop watching all stores bound to that strategy + * @internal + */ + protected doDeactivate() { + this.unwatchAll(); + } + + /** + * Watch for a store's OL source changes + * @param store Feature store + */ + private watchStore(store: FeatureStore) { + if (this.stores$$.has(store)) { + return; + } + + this.onSourceChanges(store); + const olSource = store.layer.ol.getSource(); + olSource.on('change', (event: OlEvent) => { + this.onSourceChanges(store); + }); + } + + /** + * Stop watching for a store's OL source changes + * @param store Feature store + */ + private unwatchStore(store: FeatureStore) { + const key = this.stores$$.get(store); + if (key !== undefined) { + unByKey(key) + this.stores$$.delete(store); + } + } + + /** + * Stop watching for OL source changes in all stores. + */ + private unwatchAll() { + Array.from(this.stores$$.entries()).forEach((entries: [FeatureStore, string]) => { + unByKey(entries[1]); + }); + this.stores$$.clear(); + } + + /** + * Load features from an OL source into a store or clear the store if the source is empty + * @param features Store filtered features + * @param store Feature store + */ + private onSourceChanges(store: FeatureStore) { + const olFeatures = store.layer.ol.getSource().getFeatures(); + if (olFeatures.length === 0) { + store.clear(); + } else { + store.setOlFeatures(olFeatures); + } + } +} diff --git a/packages/geo/src/lib/feature/shared/strategies/selection.ts b/packages/geo/src/lib/feature/shared/strategies/selection.ts index c9a905b27f..0f9ddf40a6 100644 --- a/packages/geo/src/lib/feature/shared/strategies/selection.ts +++ b/packages/geo/src/lib/feature/shared/strategies/selection.ts @@ -237,6 +237,7 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { olFeatures.forEach((olFeature: OlFeature) => { const store = olFeature.get('_featureStore'); + if (store === undefined) { return; } let features = groupedFeatures.get(store); if (features === undefined) { diff --git a/packages/geo/src/lib/geo.module.ts b/packages/geo/src/lib/geo.module.ts index a5f92070d3..1d1aa5f5d0 100644 --- a/packages/geo/src/lib/geo.module.ts +++ b/packages/geo/src/lib/geo.module.ts @@ -17,6 +17,7 @@ import { IgoQueryModule } from './query/query.module'; import { IgoRoutingModule } from './routing/routing.module'; import { IgoSearchModule } from './search/search.module'; import { IgoToastModule } from './toast/toast.module'; +import { IgoWfsModule } from './wfs/wfs.module'; import { IgoWktModule } from './wkt/wkt.module'; @NgModule({ @@ -40,6 +41,7 @@ import { IgoWktModule } from './wkt/wkt.module'; IgoRoutingModule, IgoSearchModule, IgoToastModule, + IgoWfsModule, IgoWktModule ] }) diff --git a/packages/geo/src/lib/wfs/index.ts b/packages/geo/src/lib/wfs/index.ts new file mode 100644 index 0000000000..c3da79f741 --- /dev/null +++ b/packages/geo/src/lib/wfs/index.ts @@ -0,0 +1 @@ +export * from './shared'; diff --git a/packages/geo/src/lib/wfs/shared/index.ts b/packages/geo/src/lib/wfs/shared/index.ts new file mode 100644 index 0000000000..7d32d07846 --- /dev/null +++ b/packages/geo/src/lib/wfs/shared/index.ts @@ -0,0 +1 @@ +export * from './wfs-editor.service'; \ No newline at end of file diff --git a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts new file mode 100644 index 0000000000..ab2d3ac5e3 --- /dev/null +++ b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts @@ -0,0 +1,98 @@ +import { Inject, Injectable } from '@angular/core'; + +import { + Action, + ActionStore, + Editor, + EntityTableTemplate, + Widget +} from '@igo2/common'; + +import { + FeatureStore, + FeatureStoreLoadingLayerStrategy, + FeatureStoreSelectionStrategy +} from '../../feature'; +import { VectorLayer } from '../../layer'; +import { IgoMap } from '../../map'; +import { SourceFieldsOptionsParams } from '../../datasource'; + +import { WfsOgcFilterWidget } from '../wfs-ogc-filter/wfs-ogc-filter.widget' + +@Injectable({ + providedIn: 'root' +}) +export class WfsEditorService { + + + constructor( + @Inject(WfsOgcFilterWidget) private wfsOgcFilterWidget: Widget + ) {} + + createEditor(layer: VectorLayer, map: IgoMap): Editor { + const actionStore = new ActionStore([]); + const editor = new Editor({ + id: layer.id, + title: layer.title, + tableTemplate: this.createTableTemplate(layer), + entityStore: this.createFeatureStore(layer, map), + actionStore: actionStore + }); + actionStore.load(this.buildActions(editor)); + + return editor; + } + + private createFeatureStore(layer: VectorLayer, map: IgoMap): FeatureStore { + const store = new FeatureStore([], { + map: map + }); + store.bindLayer(layer); + + const loadingStrategy = new FeatureStoreLoadingLayerStrategy(); + const selectionStrategy = new FeatureStoreSelectionStrategy({ + map: map + }); + store.addStrategy(loadingStrategy); + store.addStrategy(selectionStrategy); + + loadingStrategy.activate(); + selectionStrategy.activate(); + + return store; + } + + private createTableTemplate(layer: VectorLayer): EntityTableTemplate { + const fields = layer.dataSource.options.sourceFields || []; + const columns = fields.map((field: SourceFieldsOptionsParams) => { + return { + name: `properties.${field.name}`, + title: field.alias ? field.alias : field.name + } + }); + + return { + selection: true, + sort: true, + columns + }; + } + + private buildActions(editor: Editor): Action[] { + const layer = (editor.entityStore as FeatureStore).layer; + return [ + { + id: 'wfsOgcFilter', + icon: 'filter_list', + title: 'menu', + tooltip: 'menu', + handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { + layer: layer, + map: layer.map, + }), + conditions: [] + } + ]; + } + +} diff --git a/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts b/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts new file mode 100644 index 0000000000..6184680f9c --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts @@ -0,0 +1,63 @@ +import { Directive, Input, OnInit, OnDestroy } from '@angular/core'; + +import { Subscription } from 'rxjs'; + +import { + Editor, + EditorSelectorComponent, + EntityStore +} from '@igo2/common'; + +import { Layer, VectorLayer } from '../../layer'; +import { IgoMap } from '../../map'; +import { WFSDataSource } from '../../datasource'; + +import { WfsEditorService } from '../shared/wfs-editor.service'; + +@Directive({ + selector: '[igoWfsEditorSelector]' +}) +export class WfsEditorSelectorDirective implements OnInit, OnDestroy { + + private layers$$: Subscription; + + @Input() map: IgoMap; + + get editorStore(): EntityStore { + return this.component.store; + } + + constructor( + private component: EditorSelectorComponent, + private wfsEditorService: WfsEditorService + ) {} + + ngOnInit() { + this.layers$$ = this.map.layers$ + .subscribe((layers: Layer[]) => this.onLayersChange(layers)); + } + + ngOnDestroy() { + this.layers$$.unsubscribe(); + } + + private onLayersChange(layers: Layer[]) { + const wfsLayers = layers.filter((layer: Layer) => { + return layer.dataSource instanceof WFSDataSource; + }); + + const editors = wfsLayers.map((layer: VectorLayer) => { + return this.getOrCreateEditor(layer); + }); + this.editorStore.updateMany(editors); + } + + private getOrCreateEditor(layer: VectorLayer): Editor { + let editor = this.editorStore.get(layer.id); + if (editor !== undefined) { + return editor; + } + return this.wfsEditorService.createEditor(layer, this.map); + } + +} diff --git a/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts b/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts new file mode 100644 index 0000000000..aaf6c55079 --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { IgoWfsOgcFilterModule } from '../wfs-ogc-filter/wfs-ogc-filter.module'; +import { WfsEditorSelectorDirective } from './wfs-editor-selector.directive'; + +/** + * @ignore + */ +@NgModule({ + imports: [ + CommonModule, + IgoWfsOgcFilterModule + ], + exports: [ + WfsEditorSelectorDirective + ], + declarations: [ + WfsEditorSelectorDirective + ] +}) +export class IgoWfsEditorSelectorModule {} diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html new file mode 100644 index 0000000000..7a76b0e0b7 --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.scss b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts new file mode 100644 index 0000000000..4f3097934d --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts @@ -0,0 +1,48 @@ +import { + Component, + Input, + Output, + EventEmitter, + ChangeDetectionStrategy, + ChangeDetectorRef +} from '@angular/core'; + +import { OnUpdateInputs, WidgetComponent } from '@igo2/common'; + +import { Layer } from '../../layer/shared/layers/layer'; +import { IgoMap } from '../../map/shared/map'; + +@Component({ + selector: 'igo-wfs-ogc-filter', + templateUrl: './wfs-ogc-filter.component.html', + styleUrls: ['./wfs-ogc-filter.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class WfsOgcFilterComponent implements OnUpdateInputs, WidgetComponent { + + @Input() layer: Layer; + + @Input() map: IgoMap; + + @Input() showFeatureOnMap: boolean = true; + + /** + * Event emitted on complete + */ + @Output() complete = new EventEmitter(); + + /** + * Event emitted on cancel + */ + @Output() cancel = new EventEmitter(); + + constructor(private cdRef: ChangeDetectorRef) {} + + /** + * Implemented as part of OnUpdateInputs + */ + onUpdateInputs() { + this.cdRef.detectChanges(); + } + +} diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts new file mode 100644 index 0000000000..9c8952e08a --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { IgoFilterModule } from '../../filter/filter.module'; + +import { provideWfsOgcFilterWidget } from './wfs-ogc-filter.widget'; +import { WfsOgcFilterComponent } from './wfs-ogc-filter.component'; + +/** + * @ignore + */ +@NgModule({ + imports: [ + CommonModule, + IgoFilterModule + ], + exports: [WfsOgcFilterComponent], + declarations: [WfsOgcFilterComponent], + entryComponents: [WfsOgcFilterComponent], + providers: [ + provideWfsOgcFilterWidget() + ] +}) +export class IgoWfsOgcFilterModule {} diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts new file mode 100644 index 0000000000..c535839c89 --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts @@ -0,0 +1,21 @@ + + +import { InjectionToken } from '@angular/core'; + +import { Widget, WidgetService } from '@igo2/common'; + +import { WfsOgcFilterComponent } from './wfs-ogc-filter.component'; + +export const WfsOgcFilterWidget = new InjectionToken('WfsOgcFilterWidget'); + +export function wfsOgcFilterWidgetFactory(widgetService: WidgetService) { + return widgetService.create(WfsOgcFilterComponent); +} + +export function provideWfsOgcFilterWidget() { + return { + provide: WfsOgcFilterWidget, + useFactory: wfsOgcFilterWidgetFactory, + deps: [WidgetService] + }; +} diff --git a/packages/geo/src/lib/wfs/wfs.module.ts b/packages/geo/src/lib/wfs/wfs.module.ts new file mode 100644 index 0000000000..d8e1684a68 --- /dev/null +++ b/packages/geo/src/lib/wfs/wfs.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IgoWidgetModule } from '@igo2/common'; + +import { IgoWfsEditorSelectorModule } from './wfs-editor-selector/wfs-editor-selector.module'; +import { IgoWfsOgcFilterModule } from './wfs-ogc-filter/wfs-ogc-filter.module'; + +@NgModule({ + imports: [ + CommonModule, + IgoWidgetModule, + IgoWfsEditorSelectorModule, + IgoWfsOgcFilterModule + ], + exports: [ + IgoWfsEditorSelectorModule, + IgoWfsOgcFilterModule + ], + declarations: [] +}) +export class IgoWfsModule {} diff --git a/packages/geo/src/locale/en.geo.json b/packages/geo/src/locale/en.geo.json index 55575e3416..618c1471d0 100644 --- a/packages/geo/src/locale/en.geo.json +++ b/packages/geo/src/locale/en.geo.json @@ -226,6 +226,10 @@ "calculate.tooltip": "Calculate", "delete.tooltip": "Delete" } + }, + "wfs": { + "ogcFilter.title": "Filters", + "ogcFilter.tooltip": "Apply filters" } } }, diff --git a/packages/geo/src/locale/fr.geo.json b/packages/geo/src/locale/fr.geo.json index 70551838e8..d295327681 100644 --- a/packages/geo/src/locale/fr.geo.json +++ b/packages/geo/src/locale/fr.geo.json @@ -229,6 +229,10 @@ "delete.title": "Supprimer", "delete.tooltip": "Supprimer" } + }, + "wfs": { + "ogcFilter.title": "Filtres", + "ogcFilter.tooltip": "Appliquer des filtres sur la couche" } } }, diff --git a/packages/geo/src/public_api.ts b/packages/geo/src/public_api.ts index a6b2cc3933..591555bd74 100644 --- a/packages/geo/src/public_api.ts +++ b/packages/geo/src/public_api.ts @@ -28,6 +28,9 @@ export * from './lib/search/search.module'; export * from './lib/search/search-bar/search-bar.module'; export * from './lib/search/search-results/search-results.module'; export * from './lib/toast/toast.module'; +export * from './lib/wfs/wfs.module'; +export * from './lib/wfs/wfs-editor-selector/wfs-editor-selector.module'; +export * from './lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module'; export * from './lib/wkt/wkt.module'; export * from './lib/query/shared/query-search-source.providers'; From 6002236b2151405610ac4f6bd275ce8110ffe48e Mon Sep 17 00:00:00 2001 From: cbourget Date: Mon, 1 Apr 2019 09:56:16 -0400 Subject: [PATCH 2/8] wip(wfs): ogc filter widget title and tooltip --- packages/geo/src/lib/wfs/shared/wfs-editor.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts index ab2d3ac5e3..660ca4963f 100644 --- a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts @@ -84,8 +84,8 @@ export class WfsEditorService { { id: 'wfsOgcFilter', icon: 'filter_list', - title: 'menu', - tooltip: 'menu', + title: 'igo.geo.wfs.ogcFilter.title', + tooltip: 'igo.geo.wfs.ogcFilter.tooltip', handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { layer: layer, map: layer.map, From 83336d4a8fc6dff6058067a8d7f29622d7ab952c Mon Sep 17 00:00:00 2001 From: cbourget Date: Mon, 1 Apr 2019 09:54:44 -0400 Subject: [PATCH 3/8] wip(wfs): wfs editor with table and widgets --- packages/geo/src/lib/wfs/shared/wfs-editor.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts index 660ca4963f..d50f1a9821 100644 --- a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts @@ -84,8 +84,13 @@ export class WfsEditorService { { id: 'wfsOgcFilter', icon: 'filter_list', +<<<<<<< HEAD title: 'igo.geo.wfs.ogcFilter.title', tooltip: 'igo.geo.wfs.ogcFilter.tooltip', +======= + title: 'menu', + tooltip: 'menu', +>>>>>>> wip(wfs): wfs editor with table and widgets handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { layer: layer, map: layer.map, From 035f25488a51e14bffd0014a7253d44d10c02fd8 Mon Sep 17 00:00:00 2001 From: cbourget Date: Wed, 3 Apr 2019 12:08:28 -0400 Subject: [PATCH 4/8] feat(wfs-editor): fix a few issues with the wfs editor and rename the wfs module --- demo/src/app/app.component.html | 2 +- demo/src/app/app.module.ts | 4 +- .../entity-selector.component.html | 3 +- demo/src/app/common/form/form.component.ts | 6 +- .../app/geo/edition/edition-routing.module.ts | 15 ++++ .../app/geo/edition/edition.component.html | 36 ++++++++ .../edition.component.scss} | 0 .../edition.component.ts} | 40 ++++----- .../edition.module.ts} | 16 ++-- .../wfs-browser/wfs-browser-routing.module.ts | 15 ---- .../wfs-browser/wfs-browser.component.html | 46 ---------- .../editor-selector.component.html | 16 ++-- .../editor-selector.component.scss | 16 ++-- .../editor-selector.component.ts | 83 ++----------------- .../editor-selector/editor-selector.module.ts | 7 +- .../common/src/lib/edition/shared/index.ts | 1 + .../common/src/lib/edition/shared/store.ts | 26 ++++++ .../entity-selector.component.ts | 2 +- .../entity-table-row.directive.ts | 4 +- .../entity-table/entity-table.component.ts | 2 +- .../form-field-select.component.html | 1 + .../form-field/form-field-select.component.ts | 14 +++- .../form-field/form-field-text.component.html | 1 + .../form-field/form-field-text.component.ts | 14 +++- .../form/form-field/form-field.component.ts | 11 ++- .../lib/form/form-field/form-field.module.ts | 2 + .../src/lib/form/shared/form.interfaces.ts | 1 + .../common/src/lib/form/shared/form.utils.ts | 15 ++++ .../common/src/lib/widget/shared/index.ts | 1 + .../lib/widget/shared/widget.interfaces.ts | 4 - .../src/lib/widget/shared/widget.service.ts | 8 +- .../common/src/lib/widget/shared/widget.ts | 4 + packages/common/src/locale/en.common.json | 5 ++ packages/common/src/locale/fr.common.json | 5 ++ .../lib/share-map/shared/share-map.service.ts | 6 +- .../datasources/datasource.interface.ts | 1 + .../datasources/wfs-datasource.interface.ts | 1 + .../edition.module.ts} | 8 +- .../geo/src/lib/{wfs => edition}/index.ts | 0 packages/geo/src/lib/edition/shared/index.ts | 2 + .../shared/wfs-editor.service.ts | 37 +++++---- .../shared/wfs.widgets.ts} | 6 +- .../wfs-editor-selector.directive.ts | 10 +-- .../wfs-editor-selector.module.ts | 0 .../wfs-ogc-filter.component.html | 13 +++ .../wfs-ogc-filter.component.scss | 0 .../wfs-ogc-filter.component.ts | 11 ++- .../wfs-ogc-filter/wfs-ogc-filter.module.ts | 11 ++- .../lib/feature/shared/feature.interfaces.ts | 8 ++ .../src/lib/feature/shared/feature.utils.ts | 16 ++-- packages/geo/src/lib/feature/shared/store.ts | 21 +++-- .../shared/strategies/loading-layer.ts | 10 ++- .../lib/feature/shared/strategies/loading.ts | 8 +- .../feature/shared/strategies/selection.ts | 29 +++++-- packages/geo/src/lib/geo.module.ts | 4 +- .../shared/import-export.service.ts | 8 +- packages/geo/src/lib/wfs/shared/index.ts | 1 - .../wfs-ogc-filter.component.html | 4 - packages/geo/src/locale/en.geo.json | 3 +- packages/geo/src/locale/fr.geo.json | 7 +- packages/geo/src/public_api.ts | 6 +- 61 files changed, 365 insertions(+), 292 deletions(-) create mode 100644 demo/src/app/geo/edition/edition-routing.module.ts create mode 100644 demo/src/app/geo/edition/edition.component.html rename demo/src/app/geo/{wfs-browser/wfs-browser.component.scss => edition/edition.component.scss} (100%) rename demo/src/app/geo/{wfs-browser/wfs-browser.component.ts => edition/edition.component.ts} (68%) rename demo/src/app/geo/{wfs-browser/wfs-browser.module.ts => edition/edition.module.ts} (63%) delete mode 100644 demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts delete mode 100644 demo/src/app/geo/wfs-browser/wfs-browser.component.html create mode 100644 packages/common/src/lib/edition/shared/store.ts create mode 100644 packages/common/src/lib/widget/shared/widget.ts rename packages/geo/src/lib/{wfs/wfs.module.ts => edition/edition.module.ts} (75%) rename packages/geo/src/lib/{wfs => edition}/index.ts (100%) create mode 100644 packages/geo/src/lib/edition/shared/index.ts rename packages/geo/src/lib/{wfs => edition}/shared/wfs-editor.service.ts (80%) rename packages/geo/src/lib/{wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts => edition/shared/wfs.widgets.ts} (82%) rename packages/geo/src/lib/{wfs => edition}/wfs-editor-selector/wfs-editor-selector.directive.ts (88%) rename packages/geo/src/lib/{wfs => edition}/wfs-editor-selector/wfs-editor-selector.module.ts (100%) create mode 100644 packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html rename packages/geo/src/lib/{wfs => edition}/wfs-ogc-filter/wfs-ogc-filter.component.scss (100%) rename packages/geo/src/lib/{wfs => edition}/wfs-ogc-filter/wfs-ogc-filter.component.ts (91%) rename packages/geo/src/lib/{wfs => edition}/wfs-ogc-filter/wfs-ogc-filter.module.ts (69%) delete mode 100644 packages/geo/src/lib/wfs/shared/index.ts delete mode 100644 packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html diff --git a/demo/src/app/app.component.html b/demo/src/app/app.component.html index 1c8cf46d95..61b7dcad22 100644 --- a/demo/src/app/app.component.html +++ b/demo/src/app/app.component.html @@ -48,7 +48,7 @@

    {{title}}

    Directions Time filter OGC filter - WFS browser + Edition
    diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index 469d20ae77..504c66b4e8 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -41,7 +41,7 @@ import { AppPrintModule } from './geo/print/print.module'; import { AppDirectionsModule } from './geo/directions/directions.module'; import { AppTimeFilterModule } from './geo/time-filter/time-filter.module'; import { AppOgcFilterModule } from './geo/ogc-filter/ogc-filter.module'; -import { AppWfsBrowserModule } from './geo/wfs-browser/wfs-browser.module'; +import { AppEditionModule } from './geo/edition/edition.module'; import { AppContextModule } from './context/context/context.module'; @@ -91,7 +91,7 @@ import { AppComponent } from './app.component'; AppDirectionsModule, AppTimeFilterModule, AppOgcFilterModule, - AppWfsBrowserModule, + AppEditionModule, AppContextModule, diff --git a/demo/src/app/common/entity-selector/entity-selector.component.html b/demo/src/app/common/entity-selector/entity-selector.component.html index 471d4076d3..27b4cde822 100644 --- a/demo/src/app/common/entity-selector/entity-selector.component.html +++ b/demo/src/app/common/entity-selector/entity-selector.component.html @@ -2,8 +2,9 @@ Common Entity Selector - See the code of this example + See the code of this example + + Geo + Edition + + See the code of this example + + + + + + + + + + + + + + + + + + + diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.scss b/demo/src/app/geo/edition/edition.component.scss similarity index 100% rename from demo/src/app/geo/wfs-browser/wfs-browser.component.scss rename to demo/src/app/geo/edition/edition.component.scss diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.ts b/demo/src/app/geo/edition/edition.component.ts similarity index 68% rename from demo/src/app/geo/wfs-browser/wfs-browser.component.ts rename to demo/src/app/geo/edition/edition.component.ts index d2b8e4a5ca..264ef485fb 100644 --- a/demo/src/app/geo/wfs-browser/wfs-browser.component.ts +++ b/demo/src/app/geo/edition/edition.component.ts @@ -7,9 +7,9 @@ import { LanguageService } from '@igo2/core'; import { ActionbarMode, EntityRecord, - EntityStore, EntityTableScrollBehavior, - Editor + Editor, + EditorStore } from '@igo2/common'; import { IgoMap, @@ -20,11 +20,11 @@ import { } from '@igo2/geo'; @Component({ - selector: 'app-wfs-browser', - templateUrl: './wfs-browser.component.html', - styleUrls: ['./wfs-browser.component.scss'] + selector: 'app-edition', + templateUrl: './edition.component.html', + styleUrls: ['./edition.component.scss'] }) -export class AppWfsBrowserComponent implements OnInit { +export class AppEditionComponent implements OnInit { public map = new IgoMap({ controls: { @@ -35,13 +35,13 @@ export class AppWfsBrowserComponent implements OnInit { }); public view = { - center: [-0, 47.2], + center: [-72, 47.2], zoom: 5 }; - public editorStore = new EntityStore([]); + public editorStore = new EditorStore([]); - public activeEditor$: Observable; + public selectedEditor$: Observable; public actionbarMode = ActionbarMode.Dock; @@ -54,12 +54,11 @@ export class AppWfsBrowserComponent implements OnInit { ) {} ngOnInit() { - this.activeEditor$ = this.editorStore.stateView + this.selectedEditor$ = this.editorStore.stateView .firstBy$((record: EntityRecord) => record.state.selected === true) .pipe( map((record: EntityRecord) => { - console.log(record); - return record ? record.entity : undefined; + return record === undefined ? undefined : record.entity; }) ); @@ -78,16 +77,17 @@ export class AppWfsBrowserComponent implements OnInit { const wfsDatasource: WFSDataSourceOptions = { type: 'wfs', - url: 'https://ahocevar.com/geoserver/wfs', + url: 'https://geoegl.msp.gouv.qc.ca/apis/ws/swtq', params: { - featureTypes: 'ne:ne_10m_admin_0_countries', - fieldNameGeometry: 'the_geom', - version: '1.1.0', - outputFormat: 'application/json' + featureTypes: 'etablissement_mtq', + fieldNameGeometry: 'geometry', + version: '2.0.0', + outputFormat: 'geojson' }, sourceFields: [ - {name: 'name', alias: 'Name'}, - {name: 'type', alias: 'Type'} + {name: 'idetablis', alias: 'ID'}, + {name: 'nometablis', alias: 'Name'}, + {name: 'typetablis', alias: 'Type'} ] }; @@ -95,7 +95,7 @@ export class AppWfsBrowserComponent implements OnInit { .createAsyncDataSource(wfsDatasource) .subscribe(dataSource => { const layer: LayerOptions = { - title: 'WFS ', + title: 'Simple WFS ', visible: true, source: dataSource }; diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.module.ts b/demo/src/app/geo/edition/edition.module.ts similarity index 63% rename from demo/src/app/geo/wfs-browser/wfs-browser.module.ts rename to demo/src/app/geo/edition/edition.module.ts index 86c8a57f6b..7d1a85c6e0 100644 --- a/demo/src/app/geo/wfs-browser/wfs-browser.module.ts +++ b/demo/src/app/geo/edition/edition.module.ts @@ -14,17 +14,17 @@ import { } from '@igo2/common'; import { IgoMapModule, - IgoWfsModule + IgoGeoEditionModule } from '@igo2/geo'; -import { AppWfsBrowserComponent } from './wfs-browser.component'; -import { AppWfsBrowserRoutingModule } from './wfs-browser-routing.module'; +import { AppEditionComponent } from './edition.component'; +import { AppEditionRoutingModule } from './edition-routing.module'; @NgModule({ - declarations: [AppWfsBrowserComponent], + declarations: [AppEditionComponent], imports: [ CommonModule, - AppWfsBrowserRoutingModule, + AppEditionRoutingModule, MatCardModule, MatButtonModule, MatIconModule, @@ -33,8 +33,8 @@ import { AppWfsBrowserRoutingModule } from './wfs-browser-routing.module'; IgoEditionModule, IgoPanelModule, IgoMapModule, - IgoWfsModule + IgoGeoEditionModule ], - exports: [AppWfsBrowserComponent] + exports: [AppEditionComponent] }) -export class AppWfsBrowserModule {} +export class AppEditionModule {} diff --git a/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts b/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts deleted file mode 100644 index e79774ba57..0000000000 --- a/demo/src/app/geo/wfs-browser/wfs-browser-routing.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Routes, RouterModule } from '@angular/router'; -import { ModuleWithProviders } from '@angular/core'; - -import { AppWfsBrowserComponent } from './wfs-browser.component'; - -const routes: Routes = [ - { - path: 'wfs-browser', - component: AppWfsBrowserComponent - } -]; - -export const AppWfsBrowserRoutingModule: ModuleWithProviders = RouterModule.forChild( - routes -); diff --git a/demo/src/app/geo/wfs-browser/wfs-browser.component.html b/demo/src/app/geo/wfs-browser/wfs-browser.component.html deleted file mode 100644 index 477aa1512a..0000000000 --- a/demo/src/app/geo/wfs-browser/wfs-browser.component.html +++ /dev/null @@ -1,46 +0,0 @@ - - Geo - Simple Map - -
  • Dependencies: LanguageService
  • - -
    - See the code of this example -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.html b/packages/common/src/lib/edition/editor-selector/editor-selector.component.html index 5f9e9bcb8a..abce91769b 100644 --- a/packages/common/src/lib/edition/editor-selector/editor-selector.component.html +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.html @@ -1,11 +1,5 @@ - - - - - {{getEditorTitle(editor)}} - - - - \ No newline at end of file + + diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss b/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss index 8ef7b9144d..c520d95b59 100644 --- a/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.scss @@ -1,11 +1,9 @@ -mat-form-field { - width: 100%; -} - -mat-form-field ::ng-deep .mat-form-field-infix { - padding: 0; -} +igo-entity-selector ::ng-deep mat-form-field { + .mat-form-field-infix { + padding: 0; + } -mat-form-field ::ng-deep .mat-form-field-wrapper { - padding-bottom: 1.75em; + .mat-form-field-wrapper { + padding-bottom: 1.75em; + } } diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts index d8de99d024..2fe6cd72b6 100644 --- a/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts @@ -3,21 +3,12 @@ import { Input, Output, EventEmitter, - ChangeDetectionStrategy, - ChangeDetectorRef, - OnInit, - OnDestroy + ChangeDetectionStrategy } from '@angular/core'; -import { BehaviorSubject, Subscription } from 'rxjs'; - -import { - EntityRecord, - EntityStore, - EntityStoreController, - getEntityTitle -} from '../../entity'; +import { getEntityTitle } from '../../entity'; import { Editor } from '../shared/editor'; +import { EditorStore } from '../shared/store'; /** * Drop list that activates the selected editor emit an event. @@ -28,62 +19,21 @@ import { Editor } from '../shared/editor'; styleUrls: ['./editor-selector.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class EditorSelectorComponent implements OnInit, OnDestroy { - - /** - * The current editor - * @internal - */ - public current$ = new BehaviorSubject(undefined); - - /** - * Subscription to the store's selected editor - */ - private selected$$: Subscription; - - /** - * Store controller - */ - private controller: EntityStoreController; +export class EditorSelectorComponent { /** * Store that holds the available editors. */ - @Input() store: EntityStore; + @Input() store: EditorStore; /** * Event emitted when an editor is selected or unselected */ @Output() selectedChange = new EventEmitter<{ selected: boolean; - editor: Editor; + entity: Editor; }>(); - constructor(private cdRef: ChangeDetectorRef) {} - - /** - * Observe the store's selected editor and activate it - * @internal - */ - ngOnInit() { - this.controller = new EntityStoreController(this.store, this.cdRef); - this.selected$$ = this.store.stateView - .firstBy$((record: EntityRecord) => record.state.selected === true) - .subscribe((record: EntityRecord) => { - const editor = record ? record.entity : undefined; - this.activateEditor(editor); - }); - } - - /** - * Unsubscribe to the store selected editor - * @internal - */ - ngOnDestroy() { - this.controller.destroy(); - this.selected$$.unsubscribe(); - } - /** * @internal */ @@ -97,25 +47,10 @@ export class EditorSelectorComponent implements OnInit, OnDestroy { * @internal * @param event The selection change event */ - onSelectionChange(event: {value: Editor}) { + onSelectedChange(event: {value: Editor}) { const editor = event.value; - this.store.state.update(editor, {selected: true}, true); - this.selectedChange.emit({selected: true, editor}); + this.store.activateEditor(editor); + this.selectedChange.emit({selected: true, entity: editor}); } - /** - * Activate the newly selected editor and deactivate the one previously selected - * @internal - * @param editor Editor - */ - private activateEditor(editor: Editor) { - const current = this.current$.value; - if (current !== undefined) { - current.deactivate(); - } - if (editor !== undefined) { - editor.activate(); - } - this.current$.next(editor); - } } diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts b/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts index 2298d3de35..5d6a4fc98f 100644 --- a/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { MatSelectModule } from '@angular/material'; + +import { IgoEntitySelectorModule } from '../../entity/entity-selector/entity-selector.module'; import { EditorSelectorComponent } from './editor-selector.component'; @@ -11,8 +11,7 @@ import { EditorSelectorComponent } from './editor-selector.component'; @NgModule({ imports: [ CommonModule, - FormsModule, - MatSelectModule + IgoEntitySelectorModule ], exports: [ EditorSelectorComponent diff --git a/packages/common/src/lib/edition/shared/index.ts b/packages/common/src/lib/edition/shared/index.ts index 6fccffbbf0..618a16cf8a 100644 --- a/packages/common/src/lib/edition/shared/index.ts +++ b/packages/common/src/lib/edition/shared/index.ts @@ -1,2 +1,3 @@ export * from './editor'; export * from './edition.interfaces'; +export * from './store'; diff --git a/packages/common/src/lib/edition/shared/store.ts b/packages/common/src/lib/edition/shared/store.ts new file mode 100644 index 0000000000..579a787462 --- /dev/null +++ b/packages/common/src/lib/edition/shared/store.ts @@ -0,0 +1,26 @@ +import { EntityStore } from '../../entity'; +import { Editor } from './editor'; + +/** + * The class is a specialized version of an EntityStore that stores + * editors. + */ +export class EditorStore extends EntityStore { + + /** + * Activate the an editor editor and deactivate the one currently active + * @param editor Editor + */ + activateEditor(editor: Editor) { + const active = this.view.firstBy((_editor: Editor) => _editor.isActive() === true); + + if (active !== undefined) { + active.deactivate(); + } + if (editor !== undefined) { + editor.activate(); + this.state.update(editor, {active: true, selected: true}, true); + } + } + +} diff --git a/packages/common/src/lib/entity/entity-selector/entity-selector.component.ts b/packages/common/src/lib/entity/entity-selector/entity-selector.component.ts index 76966a82b9..11b40cd725 100644 --- a/packages/common/src/lib/entity/entity-selector/entity-selector.component.ts +++ b/packages/common/src/lib/entity/entity-selector/entity-selector.component.ts @@ -58,7 +58,7 @@ export class EntitySelectorComponent implements OnInit, OnDestroy { /** * Field placeholder */ - @Input() placeholder: string = ''; + @Input() placeholder: string; /** * Event emitted when the selection changes diff --git a/packages/common/src/lib/entity/entity-table/entity-table-row.directive.ts b/packages/common/src/lib/entity/entity-table/entity-table-row.directive.ts index f8325c9c6c..73ff9c46c5 100644 --- a/packages/common/src/lib/entity/entity-table/entity-table-row.directive.ts +++ b/packages/common/src/lib/entity/entity-table/entity-table-row.directive.ts @@ -64,7 +64,7 @@ export class EntityTableRowDirective { * Scroll behavior on selection */ @Input() - scrollBehavior: EntityTableScrollBehavior = EntityTableScrollBehavior.Smooth; + scrollBehavior: EntityTableScrollBehavior = EntityTableScrollBehavior.Auto; /** * Event emitted when a row is selected @@ -109,7 +109,7 @@ export class EntityTableRowDirective { */ private scroll() { if (this._selected === true) { - this.el.nativeElement.scrollIntoView({behavior: this.scrollBehavior}); + this.el.nativeElement.scrollIntoView({behavior: this.scrollBehavior, block: 'nearest'}); } } diff --git a/packages/common/src/lib/entity/entity-table/entity-table.component.ts b/packages/common/src/lib/entity/entity-table/entity-table.component.ts index afd6596301..6172ed3d26 100644 --- a/packages/common/src/lib/entity/entity-table/entity-table.component.ts +++ b/packages/common/src/lib/entity/entity-table/entity-table.component.ts @@ -74,7 +74,7 @@ export class EntityTableComponent implements OnInit, OnDestroy, OnChanges { * Scroll behavior on selection */ @Input() - scrollBehavior: EntityTableScrollBehavior = EntityTableScrollBehavior.Smooth; + scrollBehavior: EntityTableScrollBehavior = EntityTableScrollBehavior.Auto; /** * Event emitted when an entity (row) is clicked diff --git a/packages/common/src/lib/form/form-field/form-field-select.component.html b/packages/common/src/lib/form/form-field/form-field-select.component.html index 4a676c3014..ad848e4493 100644 --- a/packages/common/src/lib/form/form-field/form-field-select.component.html +++ b/packages/common/src/lib/form/form-field/form-field-select.component.html @@ -7,4 +7,5 @@ {{choice.title}} + {{getErrorMessage() | translate}} diff --git a/packages/common/src/lib/form/form-field/form-field-select.component.ts b/packages/common/src/lib/form/form-field/form-field-select.component.ts index f43a25f398..6fa4d8c822 100644 --- a/packages/common/src/lib/form/form-field/form-field-select.component.ts +++ b/packages/common/src/lib/form/form-field/form-field-select.component.ts @@ -7,7 +7,7 @@ import { FormControl } from '@angular/forms'; import { Observable, of } from 'rxjs'; -import { formControlIsRequired } from '../shared/form.utils'; +import { formControlIsRequired, getControlErrorMessage } from '../shared/form.utils'; import { FormFieldSelectChoice } from '../shared/form.interfaces'; import { FormFieldComponent } from '../shared/form-field-component'; @@ -46,6 +46,11 @@ export class FormFieldSelectComponent { } } + /** + * Field placeholder + */ + @Input() errors: {[key: string]: string}; + /** * Whether the field is required */ @@ -53,4 +58,11 @@ export class FormFieldSelectComponent { return formControlIsRequired(this.formControl); } + /** + * Get error message + */ + getErrorMessage(): string { + return getControlErrorMessage(this.formControl, this.errors); + } + } diff --git a/packages/common/src/lib/form/form-field/form-field-text.component.html b/packages/common/src/lib/form/form-field/form-field-text.component.html index 6a0746c2d5..026f0c7e26 100644 --- a/packages/common/src/lib/form/form-field/form-field-text.component.html +++ b/packages/common/src/lib/form/form-field/form-field-text.component.html @@ -4,4 +4,5 @@ [required]="required" [placeholder]="placeholder" [formControl]="formControl"> + {{getErrorMessage() | translate}} diff --git a/packages/common/src/lib/form/form-field/form-field-text.component.ts b/packages/common/src/lib/form/form-field/form-field-text.component.ts index 2129ac2c05..39a026ccaf 100644 --- a/packages/common/src/lib/form/form-field/form-field-text.component.ts +++ b/packages/common/src/lib/form/form-field/form-field-text.component.ts @@ -5,7 +5,7 @@ import { } from '@angular/core'; import { FormControl } from '@angular/forms'; -import { formControlIsRequired } from '../shared/form.utils'; +import { formControlIsRequired, getControlErrorMessage } from '../shared/form.utils'; import { FormFieldComponent } from '../shared/form-field-component'; /** @@ -29,6 +29,11 @@ export class FormFieldTextComponent { */ @Input() placeholder: string; + /** + * Field placeholder + */ + @Input() errors: {[key: string]: string}; + /** * Whether the field is required */ @@ -36,4 +41,11 @@ export class FormFieldTextComponent { return formControlIsRequired(this.formControl); } + /** + * Get error message + */ + getErrorMessage(): string { + return getControlErrorMessage(this.formControl, this.errors); + } + } diff --git a/packages/common/src/lib/form/form-field/form-field.component.ts b/packages/common/src/lib/form/form-field/form-field.component.ts index ad228f9778..d83416da38 100644 --- a/packages/common/src/lib/form/form-field/form-field.component.ts +++ b/packages/common/src/lib/form/form-field/form-field.component.ts @@ -6,6 +6,7 @@ import { import { FormField, FormFieldInputs } from '../shared/form.interfaces'; import { FormFieldService } from '../shared/form-field.service'; +import { getDefaultErrorMessages } from '../shared'; /** * This component renders the proper form input based on @@ -31,10 +32,16 @@ export class FormFieldComponent { } getFieldInputs(): FormFieldInputs { + const errors = this.field.options.errors || {}; return Object.assign( - {placeholder: this.field.title}, + { + placeholder: this.field.title + }, Object.assign({}, this.field.inputs || {}), - {formControl: this.field.control} + { + formControl: this.field.control, + errors: Object.assign({}, getDefaultErrorMessages(), errors) + } ); } diff --git a/packages/common/src/lib/form/form-field/form-field.module.ts b/packages/common/src/lib/form/form-field/form-field.module.ts index 8a7ff102e0..68890a9295 100644 --- a/packages/common/src/lib/form/form-field/form-field.module.ts +++ b/packages/common/src/lib/form/form-field/form-field.module.ts @@ -8,6 +8,7 @@ import { MatSelectModule } from '@angular/material'; +import { IgoLanguageModule } from '@igo2/core'; import { IgoDynamicOutletModule } from '../../dynamic-component/dynamic-outlet/dynamic-outlet.module'; import { FormFieldComponent } from './form-field.component'; @@ -26,6 +27,7 @@ import { FormFieldTextComponent } from './form-field-text.component'; MatFormFieldModule, MatInputModule, MatSelectModule, + IgoLanguageModule, IgoDynamicOutletModule ], exports: [ diff --git a/packages/common/src/lib/form/shared/form.interfaces.ts b/packages/common/src/lib/form/shared/form.interfaces.ts index bcd5f69c68..e54c627f33 100644 --- a/packages/common/src/lib/form/shared/form.interfaces.ts +++ b/packages/common/src/lib/form/shared/form.interfaces.ts @@ -35,6 +35,7 @@ export interface FormFieldOptions { disabled?: boolean; visible?: boolean; cols?: number; + errors?: {[key: string]: string}; } export interface FormFieldInputs {} diff --git a/packages/common/src/lib/form/shared/form.utils.ts b/packages/common/src/lib/form/shared/form.utils.ts index 3dce198559..066126d8a3 100644 --- a/packages/common/src/lib/form/shared/form.utils.ts +++ b/packages/common/src/lib/form/shared/form.utils.ts @@ -17,3 +17,18 @@ export function formControlIsRequired(control: AbstractControl): boolean { return false; } + +export function getDefaultErrorMessages(): {[key: string]: string} { + return { + required: 'igo.common.form.errors.required' + }; +} + +export function getControlErrorMessage(control: AbstractControl, messages: {[key: string]: string}): string { + const errors = control.errors || {}; + const errorKeys = Object.keys(errors); + const errorMessages = errorKeys + .map((key: string) => messages[key]) + .filter((message: string) => message !== undefined); + return errorMessages.length > 0 ? errorMessages[0] : ''; +} diff --git a/packages/common/src/lib/widget/shared/index.ts b/packages/common/src/lib/widget/shared/index.ts index 5c8f777cad..01a38bdfe0 100644 --- a/packages/common/src/lib/widget/shared/index.ts +++ b/packages/common/src/lib/widget/shared/index.ts @@ -1,2 +1,3 @@ +export * from './widget'; export * from './widget.interfaces'; export * from './widget.service'; diff --git a/packages/common/src/lib/widget/shared/widget.interfaces.ts b/packages/common/src/lib/widget/shared/widget.interfaces.ts index 8ec2d2237e..3fa14e789a 100644 --- a/packages/common/src/lib/widget/shared/widget.interfaces.ts +++ b/packages/common/src/lib/widget/shared/widget.interfaces.ts @@ -1,7 +1,5 @@ import { EventEmitter } from '@angular/core'; -import { DynamicComponent } from '../../dynamic-component/shared/dynamic-component'; - /** * This is the interface a widget component needs to implement. A widget * component is component that can be created dynamically. It needs @@ -13,5 +11,3 @@ export interface WidgetComponent { complete: EventEmitter; cancel: EventEmitter; } - -export type Widget = DynamicComponent; diff --git a/packages/common/src/lib/widget/shared/widget.service.ts b/packages/common/src/lib/widget/shared/widget.service.ts index f6b7b413a7..53ca6fc311 100644 --- a/packages/common/src/lib/widget/shared/widget.service.ts +++ b/packages/common/src/lib/widget/shared/widget.service.ts @@ -1,10 +1,8 @@ -import { - Injectable -} from '@angular/core'; +import { Injectable } from '@angular/core'; -import { DynamicComponent } from '../../dynamic-component/shared/dynamic-component'; import { DynamicComponentService } from '../../dynamic-component/shared/dynamic-component.service'; +import { Widget } from './widget'; import { WidgetComponent } from './widget.interfaces'; @Injectable({ @@ -14,7 +12,7 @@ export class WidgetService { constructor(private dynamicComponentService: DynamicComponentService) {} - create(widgetCls: any): DynamicComponent { + create(widgetCls: any): Widget { return this.dynamicComponentService.create(widgetCls as WidgetComponent); } } diff --git a/packages/common/src/lib/widget/shared/widget.ts b/packages/common/src/lib/widget/shared/widget.ts new file mode 100644 index 0000000000..d2376f64c9 --- /dev/null +++ b/packages/common/src/lib/widget/shared/widget.ts @@ -0,0 +1,4 @@ +import { DynamicComponent } from '../../dynamic-component/shared/dynamic-component'; +import { WidgetComponent } from './widget.interfaces'; + +export class Widget extends DynamicComponent {} diff --git a/packages/common/src/locale/en.common.json b/packages/common/src/locale/en.common.json index 71eaa0cca0..9cb5ab7a1f 100644 --- a/packages/common/src/locale/en.common.json +++ b/packages/common/src/locale/en.common.json @@ -8,6 +8,11 @@ }, "table": { "filter": "Filter" + }, + "form": { + "errors": { + "required": "This field is required" + } } } } diff --git a/packages/common/src/locale/fr.common.json b/packages/common/src/locale/fr.common.json index c175c91e14..e862709a11 100644 --- a/packages/common/src/locale/fr.common.json +++ b/packages/common/src/locale/fr.common.json @@ -8,6 +8,11 @@ }, "table": { "filter": "Filtre" + }, + "form": { + "errors": { + "required": "Ce champ est requis" + } } } } diff --git a/packages/context/src/lib/share-map/shared/share-map.service.ts b/packages/context/src/lib/share-map/shared/share-map.service.ts index 4b366e85bc..1da97706de 100644 --- a/packages/context/src/lib/share-map/shared/share-map.service.ts +++ b/packages/context/src/lib/share-map/shared/share-map.service.ts @@ -9,7 +9,7 @@ import { ContextService } from '../../context-manager/shared/context.service'; providedIn: 'root' }) export class ShareMapService { - private urlApi: string; + private apiUrl: string; constructor( private config: ConfigService, @@ -18,11 +18,11 @@ export class ShareMapService { @Optional() private routingFormService: RoutingFormService, @Optional() private route: RouteService ) { - this.urlApi = this.config.getConfig('context.url'); + this.apiUrl = this.config.getConfig('context.url'); } getUrl(map: IgoMap, formValues, publicShareOption) { - if (this.urlApi) { + if (this.apiUrl) { return this.getUrlWithApi(map, formValues); } return this.getUrlWithoutApi(map, publicShareOption); diff --git a/packages/geo/src/lib/datasource/shared/datasources/datasource.interface.ts b/packages/geo/src/lib/datasource/shared/datasources/datasource.interface.ts index d00ccebaa5..64a90f5052 100644 --- a/packages/geo/src/lib/datasource/shared/datasources/datasource.interface.ts +++ b/packages/geo/src/lib/datasource/shared/datasources/datasource.interface.ts @@ -21,6 +21,7 @@ export interface DataSourceOptions { // view?: ol.olx.layer.ImageOptions; ol?: olSource; + // TODO: Should those options really belong here? sourceFields?: SourceFieldsOptionsParams[]; download?: DownloadOptions; } diff --git a/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.interface.ts b/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.interface.ts index 54dd9d1b5b..177f2692a0 100644 --- a/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.interface.ts +++ b/packages/geo/src/lib/datasource/shared/datasources/wfs-datasource.interface.ts @@ -10,6 +10,7 @@ export interface WFSDataSourceOptions urlWfs?: string; // Used by code } +// TODO: Are those WFS protocol params or something else? This is not clear export interface WFSDataSourceOptionsParams { version?: string; featureTypes: string; diff --git a/packages/geo/src/lib/wfs/wfs.module.ts b/packages/geo/src/lib/edition/edition.module.ts similarity index 75% rename from packages/geo/src/lib/wfs/wfs.module.ts rename to packages/geo/src/lib/edition/edition.module.ts index d8e1684a68..1617898396 100644 --- a/packages/geo/src/lib/wfs/wfs.module.ts +++ b/packages/geo/src/lib/edition/edition.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IgoWidgetModule } from '@igo2/common'; +import { provideWfsOgcFilterWidget } from './shared/wfs.widgets'; import { IgoWfsEditorSelectorModule } from './wfs-editor-selector/wfs-editor-selector.module'; import { IgoWfsOgcFilterModule } from './wfs-ogc-filter/wfs-ogc-filter.module'; @@ -16,6 +17,9 @@ import { IgoWfsOgcFilterModule } from './wfs-ogc-filter/wfs-ogc-filter.module'; IgoWfsEditorSelectorModule, IgoWfsOgcFilterModule ], - declarations: [] + declarations: [], + providers: [ + provideWfsOgcFilterWidget() + ] }) -export class IgoWfsModule {} +export class IgoGeoEditionModule {} diff --git a/packages/geo/src/lib/wfs/index.ts b/packages/geo/src/lib/edition/index.ts similarity index 100% rename from packages/geo/src/lib/wfs/index.ts rename to packages/geo/src/lib/edition/index.ts diff --git a/packages/geo/src/lib/edition/shared/index.ts b/packages/geo/src/lib/edition/shared/index.ts new file mode 100644 index 0000000000..54f7c2cbbc --- /dev/null +++ b/packages/geo/src/lib/edition/shared/index.ts @@ -0,0 +1,2 @@ +export * from './wfs-editor.service'; +export * from './wfs.widgets'; diff --git a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts similarity index 80% rename from packages/geo/src/lib/wfs/shared/wfs-editor.service.ts rename to packages/geo/src/lib/edition/shared/wfs-editor.service.ts index d50f1a9821..167ee7c915 100644 --- a/packages/geo/src/lib/wfs/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts @@ -1,5 +1,7 @@ import { Inject, Injectable } from '@angular/core'; +import { OlFeature } from 'ol/Feature'; + import { Action, ActionStore, @@ -17,14 +19,13 @@ import { VectorLayer } from '../../layer'; import { IgoMap } from '../../map'; import { SourceFieldsOptionsParams } from '../../datasource'; -import { WfsOgcFilterWidget } from '../wfs-ogc-filter/wfs-ogc-filter.widget' +import { WfsOgcFilterWidget } from './wfs.widgets'; @Injectable({ providedIn: 'root' }) export class WfsEditorService { - constructor( @Inject(WfsOgcFilterWidget) private wfsOgcFilterWidget: Widget ) {} @@ -36,41 +37,40 @@ export class WfsEditorService { title: layer.title, tableTemplate: this.createTableTemplate(layer), entityStore: this.createFeatureStore(layer, map), - actionStore: actionStore + actionStore }); actionStore.load(this.buildActions(editor)); - + return editor; } - + private createFeatureStore(layer: VectorLayer, map: IgoMap): FeatureStore { - const store = new FeatureStore([], { - map: map - }); + const store = new FeatureStore([], {map}); store.bindLayer(layer); - + const loadingStrategy = new FeatureStoreLoadingLayerStrategy(); const selectionStrategy = new FeatureStoreSelectionStrategy({ - map: map + map, + hitTolerance: 5 }); store.addStrategy(loadingStrategy); store.addStrategy(selectionStrategy); - + loadingStrategy.activate(); selectionStrategy.activate(); - + return store; } - + private createTableTemplate(layer: VectorLayer): EntityTableTemplate { const fields = layer.dataSource.options.sourceFields || []; const columns = fields.map((field: SourceFieldsOptionsParams) => { return { name: `properties.${field.name}`, title: field.alias ? field.alias : field.name - } + }; }); - + return { selection: true, sort: true, @@ -84,6 +84,7 @@ export class WfsEditorService { { id: 'wfsOgcFilter', icon: 'filter_list', +<<<<<<< HEAD:packages/geo/src/lib/wfs/shared/wfs-editor.service.ts <<<<<<< HEAD title: 'igo.geo.wfs.ogcFilter.title', tooltip: 'igo.geo.wfs.ogcFilter.tooltip', @@ -91,8 +92,12 @@ export class WfsEditorService { title: 'menu', tooltip: 'menu', >>>>>>> wip(wfs): wfs editor with table and widgets +======= + title: 'igo.geo.edition.wfsOgcFilter.title', + tooltip: 'igo.geo.edition.wfsOgcFilter.tooltip', +>>>>>>> feat(wfs-editor): fix a few issues with the wfs editor and rename the wfs module:packages/geo/src/lib/edition/shared/wfs-editor.service.ts handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { - layer: layer, + layer, map: layer.map, }), conditions: [] diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts b/packages/geo/src/lib/edition/shared/wfs.widgets.ts similarity index 82% rename from packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts rename to packages/geo/src/lib/edition/shared/wfs.widgets.ts index c535839c89..871512fd49 100644 --- a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.widget.ts +++ b/packages/geo/src/lib/edition/shared/wfs.widgets.ts @@ -1,14 +1,12 @@ - - import { InjectionToken } from '@angular/core'; import { Widget, WidgetService } from '@igo2/common'; -import { WfsOgcFilterComponent } from './wfs-ogc-filter.component'; +import { WfsOgcFilterComponent } from '../wfs-ogc-filter/wfs-ogc-filter.component'; export const WfsOgcFilterWidget = new InjectionToken('WfsOgcFilterWidget'); -export function wfsOgcFilterWidgetFactory(widgetService: WidgetService) { +export function wfsOgcFilterWidgetFactory(widgetService: WidgetService): Widget { return widgetService.create(WfsOgcFilterComponent); } diff --git a/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts b/packages/geo/src/lib/edition/wfs-editor-selector/wfs-editor-selector.directive.ts similarity index 88% rename from packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts rename to packages/geo/src/lib/edition/wfs-editor-selector/wfs-editor-selector.directive.ts index 6184680f9c..56d7579227 100644 --- a/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.directive.ts +++ b/packages/geo/src/lib/edition/wfs-editor-selector/wfs-editor-selector.directive.ts @@ -4,8 +4,8 @@ import { Subscription } from 'rxjs'; import { Editor, - EditorSelectorComponent, - EntityStore + EditorStore, + EditorSelectorComponent } from '@igo2/common'; import { Layer, VectorLayer } from '../../layer'; @@ -23,9 +23,7 @@ export class WfsEditorSelectorDirective implements OnInit, OnDestroy { @Input() map: IgoMap; - get editorStore(): EntityStore { - return this.component.store; - } + get editorStore(): EditorStore { return this.component.store; } constructor( private component: EditorSelectorComponent, @@ -53,7 +51,7 @@ export class WfsEditorSelectorDirective implements OnInit, OnDestroy { } private getOrCreateEditor(layer: VectorLayer): Editor { - let editor = this.editorStore.get(layer.id); + const editor = this.editorStore.get(layer.id); if (editor !== undefined) { return editor; } diff --git a/packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts b/packages/geo/src/lib/edition/wfs-editor-selector/wfs-editor-selector.module.ts similarity index 100% rename from packages/geo/src/lib/wfs/wfs-editor-selector/wfs-editor-selector.module.ts rename to packages/geo/src/lib/edition/wfs-editor-selector/wfs-editor-selector.module.ts diff --git a/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html new file mode 100644 index 0000000000..d1ac49e8a1 --- /dev/null +++ b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html @@ -0,0 +1,13 @@ + + + +
    + +
    diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.scss b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.scss similarity index 100% rename from packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.scss rename to packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.scss diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.ts similarity index 91% rename from packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts rename to packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.ts index 4f3097934d..f0ce91485c 100644 --- a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.ts +++ b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.ts @@ -38,11 +38,18 @@ export class WfsOgcFilterComponent implements OnUpdateInputs, WidgetComponent { constructor(private cdRef: ChangeDetectorRef) {} - /** + /** * Implemented as part of OnUpdateInputs */ onUpdateInputs() { this.cdRef.detectChanges(); } - + + /** + * On close, emit the cancel event + */ + onClose() { + this.cancel.emit(); + } + } diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.module.ts similarity index 69% rename from packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts rename to packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.module.ts index 9c8952e08a..1cafb0cf71 100644 --- a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module.ts +++ b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material'; +import { IgoLanguageModule } from '@igo2/core'; import { IgoFilterModule } from '../../filter/filter.module'; - -import { provideWfsOgcFilterWidget } from './wfs-ogc-filter.widget'; import { WfsOgcFilterComponent } from './wfs-ogc-filter.component'; /** @@ -12,13 +12,12 @@ import { WfsOgcFilterComponent } from './wfs-ogc-filter.component'; @NgModule({ imports: [ CommonModule, + MatButtonModule, + IgoLanguageModule, IgoFilterModule ], exports: [WfsOgcFilterComponent], declarations: [WfsOgcFilterComponent], - entryComponents: [WfsOgcFilterComponent], - providers: [ - provideWfsOgcFilterWidget() - ] + entryComponents: [WfsOgcFilterComponent] }) export class IgoWfsOgcFilterModule {} diff --git a/packages/geo/src/lib/feature/shared/feature.interfaces.ts b/packages/geo/src/lib/feature/shared/feature.interfaces.ts index a12efa20d5..b5a3f3b8ed 100644 --- a/packages/geo/src/lib/feature/shared/feature.interfaces.ts +++ b/packages/geo/src/lib/feature/shared/feature.interfaces.ts @@ -37,11 +37,19 @@ export interface FeatureStoreOptions extends EntityStoreOptions { export interface FeatureStoreStrategyOptions {} +export interface FeatureStoreLoadingStrategyOptions extends FeatureStoreStrategyOptions { + getFeatureId?: (Feature) => EntityKey; +} + +export interface FeatureStoreLoadingLayerStrategyOptions extends FeatureStoreStrategyOptions {} + export interface FeatureStoreSelectionStrategyOptions extends FeatureStoreStrategyOptions { map: IgoMap; + getFeatureId?: (Feature) => EntityKey; motion?: FeatureMotion; style?: olstyle.Style; many?: boolean; + hitTolerance?: number; } export interface FeatureFormSubmitEvent { diff --git a/packages/geo/src/lib/feature/shared/feature.utils.ts b/packages/geo/src/lib/feature/shared/feature.utils.ts index e626fcd0d0..b30fb969bf 100644 --- a/packages/geo/src/lib/feature/shared/feature.utils.ts +++ b/packages/geo/src/lib/feature/shared/feature.utils.ts @@ -5,6 +5,7 @@ import OlFeature from 'ol/Feature'; import OlFormatGeoJSON from 'ol/format/GeoJSON'; import { + EntityKey, getEntityId, getEntityRevision, getEntityProperty @@ -23,30 +24,33 @@ import { Feature } from './feature.interfaces'; */ export function featureToOl( feature: Feature, - projectionOut: string + projectionOut: string, + getId?: (Feature) => EntityKey ): OlFeature { + getId = getId ? getId : getEntityId; + const olFormat = new OlFormatGeoJSON(); const olFeature = olFormat.readFeature(feature, { dataProjection: feature.projection, featureProjection: projectionOut }); - olFeature.setId(getEntityId(feature)); + olFeature.setId(getId(feature)); if (feature.projection !== undefined) { - olFeature.set('_projection', feature.projection); + olFeature.set('_projection', feature.projection, true); } if (feature.extent !== undefined) { - olFeature.set('_extent', feature.extent); + olFeature.set('_extent', feature.extent, true); } const mapTitle = getEntityProperty(feature, 'meta.mapTitle'); if (mapTitle !== undefined) { - olFeature.set('_mapTitle', mapTitle); + olFeature.set('_mapTitle', mapTitle, true); } - olFeature.set('_entityRevision', getEntityRevision(feature)); + olFeature.set('_entityRevision', getEntityRevision(feature), true); return olFeature; } diff --git a/packages/geo/src/lib/feature/shared/store.ts b/packages/geo/src/lib/feature/shared/store.ts index cd44bbe2f9..fb1e4fa4d0 100644 --- a/packages/geo/src/lib/feature/shared/store.ts +++ b/packages/geo/src/lib/feature/shared/store.ts @@ -1,6 +1,10 @@ import OlFeature from 'ol/Feature'; -import { EntityStore } from '@igo2/common'; +import { + getEntityId, + EntityKey, + EntityStore +} from '@igo2/common'; import { FeatureDataSource } from '../../datasource'; import { VectorLayer } from '../../layer'; @@ -127,11 +131,16 @@ export class FeatureStore extends EntityStore { * @param features Features * @param motion Optional: The type of motion to perform */ - setLayerFeatures(features: Feature[], motion: FeatureMotion = FeatureMotion.Default) { + setLayerFeatures( + features: Feature[], + motion: FeatureMotion = FeatureMotion.Default, + getId?: (Feature) => EntityKey + ) { + getId = getId ? getId : getEntityId; this.checkLayer(); const olFeatures = features - .map((feature: Feature) => featureToOl(feature, this.map.projection)); + .map((feature: Feature) => featureToOl(feature, this.map.projection, getId)); this.setLayerOlFeatures(olFeatures, motion); } @@ -139,7 +148,7 @@ export class FeatureStore extends EntityStore { * Set the store's features from an array of OL features. * @param olFeatures Ol features */ - setOlFeatures(olFeatures: OlFeature[]) { + setStoreOlFeatures(olFeatures: OlFeature[]) { this.checkLayer(); const features = olFeatures.map((olFeature: OlFeature) => { @@ -163,7 +172,7 @@ export class FeatureStore extends EntityStore { private checkLayer() { if (this.layer === undefined) { throw new Error('This FeatureStore is not bound to a layer.'); - } + } } /** @@ -216,7 +225,7 @@ export class FeatureStore extends EntityStore { */ private addOlFeaturesToLayer(olFeatures: OlFeature[]) { olFeatures.forEach((olFeature: OlFeature) => { - olFeature.set('_featureStore', this); + olFeature.set('_featureStore', this, true); }); this.source.ol.addFeatures(olFeatures); } diff --git a/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts index c96e056490..dfb94c821c 100644 --- a/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts +++ b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts @@ -1,8 +1,8 @@ import { unByKey } from 'ol/Observable'; import { OlEvent } from 'ol/events/Event'; -import { OlFeature } from 'ol/Feature'; import { FeatureStore } from '../store'; +import { FeatureStoreLoadingLayerStrategyOptions } from '../feature.interfaces'; import { FeatureStoreStrategy } from './strategy'; /** @@ -20,6 +20,10 @@ export class FeatureStoreLoadingLayerStrategy extends FeatureStoreStrategy { */ private stores$$ = new Map(); + constructor(private options?: FeatureStoreLoadingLayerStrategyOptions) { + super(); + } + /** * Bind this strategy to a store and start watching for Ol source changes * @param store Feature store @@ -81,7 +85,7 @@ export class FeatureStoreLoadingLayerStrategy extends FeatureStoreStrategy { private unwatchStore(store: FeatureStore) { const key = this.stores$$.get(store); if (key !== undefined) { - unByKey(key) + unByKey(key); this.stores$$.delete(store); } } @@ -106,7 +110,7 @@ export class FeatureStoreLoadingLayerStrategy extends FeatureStoreStrategy { if (olFeatures.length === 0) { store.clear(); } else { - store.setOlFeatures(olFeatures); + store.setStoreOlFeatures(olFeatures); } } } diff --git a/packages/geo/src/lib/feature/shared/strategies/loading.ts b/packages/geo/src/lib/feature/shared/strategies/loading.ts index fcc4c59d90..286c7a43be 100644 --- a/packages/geo/src/lib/feature/shared/strategies/loading.ts +++ b/packages/geo/src/lib/feature/shared/strategies/loading.ts @@ -1,7 +1,7 @@ import { Subscription } from 'rxjs'; import { FeatureMotion } from '../feature.enums'; -import { Feature } from '../feature.interfaces'; +import { Feature, FeatureStoreLoadingStrategyOptions } from '../feature.interfaces'; import { FeatureStore } from '../store'; import { FeatureStoreStrategy } from './strategy'; @@ -20,6 +20,10 @@ export class FeatureStoreLoadingStrategy extends FeatureStoreStrategy { */ private stores$$ = new Map(); + constructor(private options?: FeatureStoreLoadingStrategyOptions) { + super(); + } + /** * Bind this strategy to a store and start watching for entities changes * @param store Feature store @@ -117,7 +121,7 @@ export class FeatureStoreLoadingStrategy extends FeatureStoreStrategy { // On insert, update or delete, do nothing motion = FeatureMotion.None; } - store.setLayerFeatures(features, motion); + store.setLayerFeatures(features, motion, this.options.getFeatureId); } } } diff --git a/packages/geo/src/lib/feature/shared/strategies/selection.ts b/packages/geo/src/lib/feature/shared/strategies/selection.ts index 0f9ddf40a6..b6a5515540 100644 --- a/packages/geo/src/lib/feature/shared/strategies/selection.ts +++ b/packages/geo/src/lib/feature/shared/strategies/selection.ts @@ -5,7 +5,7 @@ import { ListenerFunction } from 'ol/events'; import { Subscription, combineLatest } from 'rxjs'; import { map, debounceTime, skip } from 'rxjs/operators'; -import { EntityRecord } from '@igo2/common'; +import { EntityKey, EntityRecord } from '@igo2/common'; import { FeatureDataSource } from '../../../datasource'; import { VectorLayer } from '../../../layer'; @@ -14,6 +14,7 @@ import { IgoMap } from '../../../map'; import { Feature, FeatureStoreSelectionStrategyOptions } from '../feature.interfaces'; import { FeatureStore } from '../store'; import { FeatureStoreStrategy } from './strategy'; +import { FeatureMotion } from '../feature.enums'; /** * This strategy synchronizes a store and a layer selected entities. @@ -27,11 +28,6 @@ import { FeatureStoreStrategy } from './strategy'; */ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { - /** - * The map the layers belong to - */ - private map: IgoMap; - /** * Listener to the map click event that allows selecting a feature * by clicking on the map @@ -49,9 +45,13 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { */ private stores$$: Subscription; + /** + * The map the layers belong to + */ + get map(): IgoMap { return this.options.map; } + constructor(private options: FeatureStoreSelectionStrategyOptions) { super(); - this.map = options.map; this.overlayStore = this.createOverlayStore(); } @@ -131,7 +131,7 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { this.stores$$ = combineLatest(...stores$) .pipe( debounceTime(50), - skip(1), // Skip intial selection. TODO: make sure this is what we want and/or make it configurable + skip(1), // Skip intial selection map((features: Array) => features.reduce((a, b) => a.concat(b))) ).subscribe((features: Feature[]) => this.onSelectFromStore(features)); } @@ -153,6 +153,7 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { private listenToMapClick() { this.mapClickListener = this.map.ol.on('singleclick', (event) => { const olFeatures = event.map.getFeaturesAtPixel(event.pixel, { + hitTolerance: this.options.hitTolerance || 0, layerFilter: (olLayer) => { const storeOlLayer = this.stores.find((store: FeatureStore) => { return store.layer.ol === olLayer; @@ -183,7 +184,17 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { */ private onSelectFromStore(features: Feature[]) { const motion = this.options ? this.options.motion : undefined; - this.overlayStore.setLayerFeatures(features, motion); + const olOverlayFeatures = this.overlayStore.layer.ol.getSource().getFeatures(); + const overlayFeaturesKeys = olOverlayFeatures.map((olFeature: OlFeature) => olFeature.getId()); + const featuresKeys = features.map(this.overlayStore.getKey); + const doMotion = overlayFeaturesKeys.length !== featuresKeys.length || + !overlayFeaturesKeys.every((key: EntityKey) => featuresKeys.indexOf(key) >= 0); + + this.overlayStore.setLayerFeatures( + features, + doMotion ? motion : FeatureMotion.None, + this.options.getFeatureId + ); } /** diff --git a/packages/geo/src/lib/geo.module.ts b/packages/geo/src/lib/geo.module.ts index 1d1aa5f5d0..b09bb16329 100644 --- a/packages/geo/src/lib/geo.module.ts +++ b/packages/geo/src/lib/geo.module.ts @@ -17,7 +17,7 @@ import { IgoQueryModule } from './query/query.module'; import { IgoRoutingModule } from './routing/routing.module'; import { IgoSearchModule } from './search/search.module'; import { IgoToastModule } from './toast/toast.module'; -import { IgoWfsModule } from './wfs/wfs.module'; +import { IgoGeoEditionModule } from './edition/edition.module'; import { IgoWktModule } from './wkt/wkt.module'; @NgModule({ @@ -41,7 +41,7 @@ import { IgoWktModule } from './wkt/wkt.module'; IgoRoutingModule, IgoSearchModule, IgoToastModule, - IgoWfsModule, + IgoGeoEditionModule, IgoWktModule ] }) diff --git a/packages/geo/src/lib/import-export/shared/import-export.service.ts b/packages/geo/src/lib/import-export/shared/import-export.service.ts index 5c161a7d37..ef28c39c62 100644 --- a/packages/geo/src/lib/import-export/shared/import-export.service.ts +++ b/packages/geo/src/lib/import-export/shared/import-export.service.ts @@ -16,7 +16,7 @@ import { ExportOptions } from './import-export.interface'; providedIn: 'root' }) export class ImportExportService { - private urlApi: string; + private exportUrl: string; constructor( private http: HttpClient, @@ -25,7 +25,7 @@ export class ImportExportService { private messageService: MessageService, private languageService: LanguageService ) { - this.urlApi = this.config.getConfig('importExport.url'); + this.exportUrl = this.config.getConfig('importExport.url'); } public import(fileList: Array, sourceSrs = 'EPSG:4326') { @@ -227,7 +227,7 @@ export class ImportExportService { const translate = this.languageService.translate; const layerTitle = file.name.substr(0, file.name.lastIndexOf('.')); const map = this.mapService.getMap(); - const url = this.urlApi + '/convert'; + const url = this.exportUrl + '/convert'; const formData = new FormData(); formData.append('upload', file); @@ -263,7 +263,7 @@ export class ImportExportService { } private callExportService(geojson, title) { - const url = this.urlApi + '/convertJson'; + const url = this.exportUrl + '/convertJson'; const form = document.createElement('form'); form.setAttribute('method', 'post'); diff --git a/packages/geo/src/lib/wfs/shared/index.ts b/packages/geo/src/lib/wfs/shared/index.ts deleted file mode 100644 index 7d32d07846..0000000000 --- a/packages/geo/src/lib/wfs/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './wfs-editor.service'; \ No newline at end of file diff --git a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html b/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html deleted file mode 100644 index 7a76b0e0b7..0000000000 --- a/packages/geo/src/lib/wfs/wfs-ogc-filter/wfs-ogc-filter.component.html +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/packages/geo/src/locale/en.geo.json b/packages/geo/src/locale/en.geo.json index 618c1471d0..eb2bf5c373 100644 --- a/packages/geo/src/locale/en.geo.json +++ b/packages/geo/src/locale/en.geo.json @@ -227,7 +227,8 @@ "delete.tooltip": "Delete" } }, - "wfs": { + "edition": { + "ogcFilter.close": "Close", "ogcFilter.title": "Filters", "ogcFilter.tooltip": "Apply filters" } diff --git a/packages/geo/src/locale/fr.geo.json b/packages/geo/src/locale/fr.geo.json index d295327681..067f84660a 100644 --- a/packages/geo/src/locale/fr.geo.json +++ b/packages/geo/src/locale/fr.geo.json @@ -230,9 +230,10 @@ "delete.tooltip": "Supprimer" } }, - "wfs": { - "ogcFilter.title": "Filtres", - "ogcFilter.tooltip": "Appliquer des filtres sur la couche" + "edition": { + "wfsOgcFilter.close": "Fermer", + "wfsOgcFilter.title": "Filtres", + "wfsOgcFilter.tooltip": "Appliquer des filtres sur la couche" } } }, diff --git a/packages/geo/src/public_api.ts b/packages/geo/src/public_api.ts index 591555bd74..6e4182c344 100644 --- a/packages/geo/src/public_api.ts +++ b/packages/geo/src/public_api.ts @@ -8,6 +8,9 @@ export * from './lib/catalog/catalog-browser/catalog-browser.module'; export * from './lib/catalog/catalog-library/catalog-library.module'; export * from './lib/datasource/datasource.module'; export * from './lib/download/download.module'; +export * from './lib/edition/edition.module'; +export * from './lib/edition/wfs-editor-selector/wfs-editor-selector.module'; +export * from './lib/edition/wfs-ogc-filter/wfs-ogc-filter.module'; export * from './lib/feature/feature.module'; export * from './lib/feature/feature-form/feature-form.module'; export * from './lib/feature/feature-details/feature-details.module'; @@ -28,9 +31,6 @@ export * from './lib/search/search.module'; export * from './lib/search/search-bar/search-bar.module'; export * from './lib/search/search-results/search-results.module'; export * from './lib/toast/toast.module'; -export * from './lib/wfs/wfs.module'; -export * from './lib/wfs/wfs-editor-selector/wfs-editor-selector.module'; -export * from './lib/wfs/wfs-ogc-filter/wfs-ogc-filter.module'; export * from './lib/wkt/wkt.module'; export * from './lib/query/shared/query-search-source.providers'; From 586695f2507958fd7cf09c87e9d8fc3c0c6f6203 Mon Sep 17 00:00:00 2001 From: cbourget Date: Wed, 3 Apr 2019 12:15:34 -0400 Subject: [PATCH 5/8] fix conflict --- .../geo/src/lib/edition/shared/wfs-editor.service.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts index 167ee7c915..716edfccd9 100644 --- a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts @@ -84,18 +84,8 @@ export class WfsEditorService { { id: 'wfsOgcFilter', icon: 'filter_list', -<<<<<<< HEAD:packages/geo/src/lib/wfs/shared/wfs-editor.service.ts -<<<<<<< HEAD - title: 'igo.geo.wfs.ogcFilter.title', - tooltip: 'igo.geo.wfs.ogcFilter.tooltip', -======= - title: 'menu', - tooltip: 'menu', ->>>>>>> wip(wfs): wfs editor with table and widgets -======= title: 'igo.geo.edition.wfsOgcFilter.title', tooltip: 'igo.geo.edition.wfsOgcFilter.tooltip', ->>>>>>> feat(wfs-editor): fix a few issues with the wfs editor and rename the wfs module:packages/geo/src/lib/edition/shared/wfs-editor.service.ts handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { layer, map: layer.map, From df04856b1e3198592f33223d4a0d564af1e01957 Mon Sep 17 00:00:00 2001 From: cbourget Date: Wed, 3 Apr 2019 13:35:54 -0400 Subject: [PATCH 6/8] fix(feature): make startegy options mandatory --- demo/src/app/geo/feature/feature.component.ts | 2 +- packages/geo/src/lib/edition/shared/wfs-editor.service.ts | 2 +- .../geo/src/lib/feature/shared/strategies/loading-layer.ts | 4 ++-- packages/geo/src/lib/feature/shared/strategies/loading.ts | 4 ++-- packages/geo/src/lib/feature/shared/strategies/selection.ts | 4 ++-- packages/geo/src/lib/feature/shared/strategies/strategy.ts | 4 +++- packages/geo/src/lib/measure/measurer/measurer.component.ts | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/demo/src/app/geo/feature/feature.component.ts b/demo/src/app/geo/feature/feature.component.ts index 506d75f82d..ef5a8707b3 100644 --- a/demo/src/app/geo/feature/feature.component.ts +++ b/demo/src/app/geo/feature/feature.component.ts @@ -61,7 +61,7 @@ export class AppFeatureComponent implements OnInit, OnDestroy { ) {} ngOnInit() { - const loadingStrategy = new FeatureStoreLoadingStrategy(); + const loadingStrategy = new FeatureStoreLoadingStrategy({}); this.store.addStrategy(loadingStrategy); const selectionStrategy = new FeatureStoreSelectionStrategy({ diff --git a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts index 716edfccd9..7c77246fa1 100644 --- a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts @@ -48,7 +48,7 @@ export class WfsEditorService { const store = new FeatureStore([], {map}); store.bindLayer(layer); - const loadingStrategy = new FeatureStoreLoadingLayerStrategy(); + const loadingStrategy = new FeatureStoreLoadingLayerStrategy({}); const selectionStrategy = new FeatureStoreSelectionStrategy({ map, hitTolerance: 5 diff --git a/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts index dfb94c821c..6e7aab87d7 100644 --- a/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts +++ b/packages/geo/src/lib/feature/shared/strategies/loading-layer.ts @@ -20,8 +20,8 @@ export class FeatureStoreLoadingLayerStrategy extends FeatureStoreStrategy { */ private stores$$ = new Map(); - constructor(private options?: FeatureStoreLoadingLayerStrategyOptions) { - super(); + constructor(protected options: FeatureStoreLoadingLayerStrategyOptions) { + super(options); } /** diff --git a/packages/geo/src/lib/feature/shared/strategies/loading.ts b/packages/geo/src/lib/feature/shared/strategies/loading.ts index 286c7a43be..7eee112326 100644 --- a/packages/geo/src/lib/feature/shared/strategies/loading.ts +++ b/packages/geo/src/lib/feature/shared/strategies/loading.ts @@ -20,8 +20,8 @@ export class FeatureStoreLoadingStrategy extends FeatureStoreStrategy { */ private stores$$ = new Map(); - constructor(private options?: FeatureStoreLoadingStrategyOptions) { - super(); + constructor(protected options: FeatureStoreLoadingStrategyOptions) { + super(options); } /** diff --git a/packages/geo/src/lib/feature/shared/strategies/selection.ts b/packages/geo/src/lib/feature/shared/strategies/selection.ts index b6a5515540..87531de31c 100644 --- a/packages/geo/src/lib/feature/shared/strategies/selection.ts +++ b/packages/geo/src/lib/feature/shared/strategies/selection.ts @@ -50,8 +50,8 @@ export class FeatureStoreSelectionStrategy extends FeatureStoreStrategy { */ get map(): IgoMap { return this.options.map; } - constructor(private options: FeatureStoreSelectionStrategyOptions) { - super(); + constructor(protected options: FeatureStoreSelectionStrategyOptions) { + super(options); this.overlayStore = this.createOverlayStore(); } diff --git a/packages/geo/src/lib/feature/shared/strategies/strategy.ts b/packages/geo/src/lib/feature/shared/strategies/strategy.ts index 282738d71b..c957ec7b34 100644 --- a/packages/geo/src/lib/feature/shared/strategies/strategy.ts +++ b/packages/geo/src/lib/feature/shared/strategies/strategy.ts @@ -23,7 +23,9 @@ export class FeatureStoreStrategy { */ protected active = false; - constructor(options: FeatureStoreStrategyOptions = {}) {} + constructor(protected options: FeatureStoreStrategyOptions = {}) { + this.options = options; + } /** * Whether this strategy is active diff --git a/packages/geo/src/lib/measure/measurer/measurer.component.ts b/packages/geo/src/lib/measure/measurer/measurer.component.ts index 0afb9c6de8..898a239aa9 100644 --- a/packages/geo/src/lib/measure/measurer/measurer.component.ts +++ b/packages/geo/src/lib/measure/measurer/measurer.component.ts @@ -396,7 +396,7 @@ export class MeasurerComponent implements OnInit, OnDestroy { } if (store.getStrategyOfType(FeatureStoreLoadingStrategy) === undefined) { - store.addStrategy(new FeatureStoreLoadingStrategy()); + store.addStrategy(new FeatureStoreLoadingStrategy({})); } store.activateStrategyOfType(FeatureStoreLoadingStrategy); From 113b7da05c1174205b604be50f0529637b914571 Mon Sep 17 00:00:00 2001 From: cbourget Date: Wed, 3 Apr 2019 13:37:30 -0400 Subject: [PATCH 7/8] fix(edition): fix editor selector issue --- .../lib/edition/editor-selector/editor-selector.component.ts | 4 ++-- packages/common/src/lib/edition/shared/editor.ts | 4 ++-- packages/common/src/lib/edition/shared/store.ts | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts index 2fe6cd72b6..0111df452a 100644 --- a/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts +++ b/packages/common/src/lib/edition/editor-selector/editor-selector.component.ts @@ -47,8 +47,8 @@ export class EditorSelectorComponent { * @internal * @param event The selection change event */ - onSelectedChange(event: {value: Editor}) { - const editor = event.value; + onSelectedChange(event: {entity: Editor}) { + const editor = event.entity; this.store.activateEditor(editor); this.selectedChange.emit({selected: true, entity: editor}); } diff --git a/packages/common/src/lib/edition/shared/editor.ts b/packages/common/src/lib/edition/shared/editor.ts index 8ba60284d1..d3664ef014 100644 --- a/packages/common/src/lib/edition/shared/editor.ts +++ b/packages/common/src/lib/edition/shared/editor.ts @@ -111,8 +111,8 @@ export class Editor { .firstBy$((record: EntityRecord) => record.state.selected === true) .pipe(distinctUntilChanged()) .subscribe((record: EntityRecord) => { - const editor = record ? record.entity : undefined; - this.onSelectEntity(editor); + const entity = record ? record.entity : undefined; + this.onSelectEntity(entity); }); this.changes$$ = this.changes$ diff --git a/packages/common/src/lib/edition/shared/store.ts b/packages/common/src/lib/edition/shared/store.ts index 579a787462..304a3a508f 100644 --- a/packages/common/src/lib/edition/shared/store.ts +++ b/packages/common/src/lib/edition/shared/store.ts @@ -13,13 +13,12 @@ export class EditorStore extends EntityStore { */ activateEditor(editor: Editor) { const active = this.view.firstBy((_editor: Editor) => _editor.isActive() === true); - if (active !== undefined) { active.deactivate(); } if (editor !== undefined) { - editor.activate(); this.state.update(editor, {active: true, selected: true}, true); + editor.activate(); } } From 2c8c02c0ef27c67dc5f8c0858c2fed3486138644 Mon Sep 17 00:00:00 2001 From: cbourget Date: Thu, 4 Apr 2019 09:53:50 -0400 Subject: [PATCH 8/8] add missing i18n --- packages/geo/src/lib/edition/shared/wfs-editor.service.ts | 6 +++--- .../edition/wfs-ogc-filter/wfs-ogc-filter.component.html | 2 +- packages/geo/src/lib/feature/shared/feature.utils.ts | 2 +- packages/geo/src/lib/feature/shared/store.ts | 2 +- packages/geo/src/locale/fr.geo.json | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts index 7c77246fa1..bf3b4240a9 100644 --- a/packages/geo/src/lib/edition/shared/wfs-editor.service.ts +++ b/packages/geo/src/lib/edition/shared/wfs-editor.service.ts @@ -82,10 +82,10 @@ export class WfsEditorService { const layer = (editor.entityStore as FeatureStore).layer; return [ { - id: 'wfsOgcFilter', + id: 'ogcFilter', icon: 'filter_list', - title: 'igo.geo.edition.wfsOgcFilter.title', - tooltip: 'igo.geo.edition.wfsOgcFilter.tooltip', + title: 'igo.geo.edition.ogcFilter.title', + tooltip: 'igo.geo.edition.ogcFilter.tooltip', handler: () => editor.activateWidget(this.wfsOgcFilterWidget, { layer, map: layer.map, diff --git a/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html index d1ac49e8a1..8376e49ad5 100644 --- a/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html +++ b/packages/geo/src/lib/edition/wfs-ogc-filter/wfs-ogc-filter.component.html @@ -8,6 +8,6 @@ mat-button type="button" (click)="onClose()"> - {{ 'igo.geo.edition.wfsOgcFilter.close' | translate }} + {{ 'igo.geo.edition.ogcFilter.close' | translate }} diff --git a/packages/geo/src/lib/feature/shared/feature.utils.ts b/packages/geo/src/lib/feature/shared/feature.utils.ts index b30fb969bf..e5778fbcec 100644 --- a/packages/geo/src/lib/feature/shared/feature.utils.ts +++ b/packages/geo/src/lib/feature/shared/feature.utils.ts @@ -67,7 +67,7 @@ export function featureFromOl( olFeature: OlFeature, projectionIn: string, projectionOut = 'EPSG:4326' -): OlFeature { +): Feature { const olFormat = new OlFormatGeoJSON(); const feature = olFormat.writeFeatureObject(olFeature, { dataProjection: projectionOut, diff --git a/packages/geo/src/lib/feature/shared/store.ts b/packages/geo/src/lib/feature/shared/store.ts index fb1e4fa4d0..4125e9c362 100644 --- a/packages/geo/src/lib/feature/shared/store.ts +++ b/packages/geo/src/lib/feature/shared/store.ts @@ -155,7 +155,7 @@ export class FeatureStore extends EntityStore { olFeature.set('_featureStore', this, true); return featureFromOl(olFeature, this.layer.map.projection); }); - this.load(features); + this.load(features as T[]); } /** diff --git a/packages/geo/src/locale/fr.geo.json b/packages/geo/src/locale/fr.geo.json index 067f84660a..df3218feea 100644 --- a/packages/geo/src/locale/fr.geo.json +++ b/packages/geo/src/locale/fr.geo.json @@ -231,9 +231,9 @@ } }, "edition": { - "wfsOgcFilter.close": "Fermer", - "wfsOgcFilter.title": "Filtres", - "wfsOgcFilter.tooltip": "Appliquer des filtres sur la couche" + "ogcFilter.close": "Fermer", + "ogcFilter.title": "Filtres", + "ogcFilter.tooltip": "Appliquer des filtres sur la couche" } } },