diff --git a/packages/geo/src/lib/search/search-results/save-feature-dialog.component.html b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.html
new file mode 100644
index 0000000000..dade16b92c
--- /dev/null
+++ b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.html
@@ -0,0 +1,48 @@
+
{{'igo.geo.layer.saveFeatureInLayer' | translate}}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/geo/src/lib/search/search-results/save-feature-dialog.component.scss b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.scss
new file mode 100644
index 0000000000..e2feb76b12
--- /dev/null
+++ b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.scss
@@ -0,0 +1,30 @@
+mat-form-field {
+ width: 100%;
+ }
+
+ .create-layer-button-top-padding {
+ padding-top: 25px;
+ }
+
+ .igo-form {
+ padding: 10px 5px 5px;
+ }
+
+ .igo-form-button-group {
+ text-align: center;
+ }
+
+ button {
+ cursor: pointer;
+ }
+
+ button#createLayerBtnDialog[disabled=true] {
+ cursor: default;
+ background-color: rgba(0,0,0,.12);
+ color: rgba(0,0,0,.26);
+ }
+
+ .error {
+ color: red;
+ }
+
\ No newline at end of file
diff --git a/packages/geo/src/lib/search/search-results/save-feature-dialog.component.ts b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.ts
new file mode 100644
index 0000000000..f46462ebf7
--- /dev/null
+++ b/packages/geo/src/lib/search/search-results/save-feature-dialog.component.ts
@@ -0,0 +1,64 @@
+import { LanguageService } from '@igo2/core';
+import { Component, OnInit, Optional, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
+import { Layer } from '../../layer';
+import { SearchResult } from '../shared';
+import { Observable } from 'rxjs';
+import { map, startWith } from 'rxjs/operators';
+
+@Component({
+ selector: 'igo-save-feature-dialog',
+ templateUrl: './save-feature-dialog.component.html',
+ styleUrls: ['./save-feature-dialog.component.scss']
+})
+export class SaveFeatureDialogComponent implements OnInit {
+
+ public form: UntypedFormGroup;
+ feature: SearchResult;
+ layers: Layer[] = [];
+ filteredLayers: Observable;
+
+ constructor(
+ private formBuilder: UntypedFormBuilder,
+ public languageService: LanguageService,
+ public dialogRef: MatDialogRef,
+ @Optional()
+ @Inject(MAT_DIALOG_DATA)
+ public data: { feature: SearchResult; layers: Layer[]}
+ ) {
+ this.form = this.formBuilder.group({
+ layerName: ['', [Validators.required]],
+ });
+ }
+
+ ngOnInit() {
+ this.feature = this.data.feature;
+ this.layers = this.data.layers;
+ this.filteredLayers = this.form.controls['layerName'].valueChanges.pipe(
+ startWith(''),
+ map(val => this.filter(val))
+ );
+ }
+
+ private filter(val): Layer[] {
+ if(typeof val !== 'string'){
+ return;
+ }
+ return this.layers.map(l => l).filter(layer =>
+ layer?.title?.toLowerCase().includes(val.toLowerCase()));
+ }
+
+ displayFn(layer: Layer): string {
+ return layer && layer.title ? layer.title: '';
+ }
+
+ save() {
+ const data: {layer: string | Layer, feature: SearchResult} = {layer: this.form.value.layerName, feature: this.feature};
+ this.dialogRef.close(data);
+ }
+
+ cancel() {
+ this.dialogRef.close();
+ }
+}
diff --git a/packages/geo/src/lib/search/search-results/search-results-add-button.component.html b/packages/geo/src/lib/search/search-results/search-results-add-button.component.html
index 429a0b7c2f..b7a0ed2c98 100644
--- a/packages/geo/src/lib/search/search-results/search-results-add-button.component.html
+++ b/packages/geo/src/lib/search/search-results/search-results-add-button.component.html
@@ -21,3 +21,23 @@
[svgIcon]="(isPreview$ | async) ? 'plus' : added ? 'delete' : 'plus'">
+
+
diff --git a/packages/geo/src/lib/search/search-results/search-results-add-button.component.ts b/packages/geo/src/lib/search/search-results/search-results-add-button.component.ts
index 975f3d065a..3c0c403acb 100644
--- a/packages/geo/src/lib/search/search-results/search-results-add-button.component.ts
+++ b/packages/geo/src/lib/search/search-results/search-results-add-button.component.ts
@@ -3,7 +3,7 @@ import {
Input,
ChangeDetectionStrategy,
OnInit,
- OnDestroy
+ OnDestroy,
} from '@angular/core';
import { SearchResult } from '../shared/search.interfaces';
@@ -11,7 +11,29 @@ import { IgoMap } from '../../map/shared/map';
import { LayerOptions } from '../../layer/shared/layers/layer.interface';
import { LayerService } from '../../layer/shared/layer.service';
import { LAYER } from '../../layer/shared/layer.enums';
-import { Subscription, BehaviorSubject } from 'rxjs';
+import { Subscription, BehaviorSubject, take } from 'rxjs';
+import { SaveFeatureDialogComponent } from './save-feature-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
+import { VectorLayer } from '../../layer/shared/layers/vector-layer';
+import { DataSourceService, FeatureDataSource } from '../../datasource';
+import {
+ Feature,
+ FeatureMotion,
+ FeatureStore,
+ FeatureStoreLoadingStrategy,
+ FeatureStoreSelectionStrategy,
+ tryAddLoadingStrategy,
+ tryAddSelectionStrategy,
+ tryBindStoreLayer
+} from '../../feature';
+import { EntityStore } from '@igo2/common';
+import { getTooltipsOfOlGeometry } from '../../measure';
+import OlOverlay from 'ol/Overlay';
+import { VectorSourceEvent as OlVectorSourceEvent } from 'ol/source/Vector';
+import { default as OlGeometry } from 'ol/geom/Geometry';
+import { QueryableDataSourceOptions } from '../../query';
+import { createOverlayDefaultStyle } from '../../overlay';
+
@Component({
selector: 'igo-search-add-button',
@@ -19,7 +41,14 @@ import { Subscription, BehaviorSubject } from 'rxjs';
styleUrls: ['./search-results-add-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class SearchResultAddButtonComponent implements OnInit, OnDestroy {
+export class SearchResultAddButtonComponent implements OnInit, OnDestroy{
+ public tooltip$: BehaviorSubject = new BehaviorSubject(
+ 'igo.geo.catalog.layer.addToMap'
+ );
+
+ public addFeatureToLayerTooltip$: BehaviorSubject = new BehaviorSubject(
+ 'igo.geo.search.addToLayer'
+ );
private resolution$$: Subscription;
private layers$$: Subscription;
@@ -38,6 +67,8 @@ export class SearchResultAddButtonComponent implements OnInit, OnDestroy {
@Input() layer: SearchResult;
+ @Input() store: EntityStore;
+
/**
* Whether the layer is already added to the map
*/
@@ -57,7 +88,18 @@ export class SearchResultAddButtonComponent implements OnInit, OnDestroy {
}
private _color = 'primary';
- constructor(private layerService: LayerService) {}
+ @Input() stores: FeatureStore[] = [];
+
+ get allLayers() {
+ return this.map.layers.filter((layer) =>
+ layer.id.includes('igo-search-layer')
+ );
+ }
+
+ constructor(
+ private layerService: LayerService,
+ private dialog: MatDialog,
+ private dataSourceService: DataSourceService) {}
/**
* @internal
@@ -230,4 +272,138 @@ export class SearchResultAddButtonComponent implements OnInit, OnDestroy {
: 'igo.geo.catalog.layer.addToMapOutRange';
}
}
+
+ addFeatureToLayer() {
+ if (this.layer.meta.dataType !== 'Feature') {
+ return;
+ }
+
+ const selectedFeature = this.layer;
+ const dialogRef = this.dialog.open(SaveFeatureDialogComponent, {
+ width: '700px',
+ data: {
+ feature: selectedFeature,
+ layers: this.allLayers
+ }
+ });
+
+ dialogRef.afterClosed().subscribe((data: {layer: string | any, feature: SearchResult}) => {
+ if (data) {
+ if(this.stores.length > 0) {
+ this.stores.map((store) => {
+ store.state.updateAll({selected: false});
+ (store?.layer).visible = false;
+ return store;
+ });
+ }
+ // check if is new layer
+ if (typeof data.layer === 'string') {
+ this.createLayer(data.layer, data.feature);
+ } else {
+ const activeStore = this.stores.find(store => store.layer.id === data.layer.id);
+ activeStore.layer.visible = true;
+ activeStore.layer.opacity = 1;
+ this.addFeature(data.feature, activeStore);
+ }
+ }
+ });
+ }
+
+ createLayer(layerTitle: string, selectedFeature: SearchResult) {
+
+ const activeStore: FeatureStore = new FeatureStore([], {
+ map: this.map
+ });
+
+ // set layer id
+ let layerCounterID: number = 0;
+ for (const layer of this.allLayers) {
+ let numberId = Number(layer.id.replace('igo-search-layer',''));
+ layerCounterID = Math.max(numberId,layerCounterID);
+ }
+
+ this.dataSourceService
+ .createAsyncDataSource({
+ type: 'vector',
+ queryable: true
+ } as QueryableDataSourceOptions)
+ .pipe(take(1))
+ .subscribe((dataSource: FeatureDataSource) => {
+ let searchLayer: VectorLayer = new VectorLayer({
+ isIgoInternalLayer: true,
+ id: 'igo-search-layer' + ++layerCounterID,
+ title: layerTitle,
+ source: dataSource,
+ style: createOverlayDefaultStyle({
+ text: '',
+ strokeWidth: 1,
+ fillColor: 'rgba(255,255,255,0.4)',
+ strokeColor: 'rgba(143,7,7,1)'
+ }),
+ showInLayerList: true,
+ exportable: true,
+ workspace: {
+ enabled: true
+ }
+ });
+
+ tryBindStoreLayer(activeStore, searchLayer);
+ tryAddLoadingStrategy(
+ activeStore,
+ new FeatureStoreLoadingStrategy({
+ motion: FeatureMotion.None
+ })
+ );
+
+ tryAddSelectionStrategy(
+ activeStore,
+ new FeatureStoreSelectionStrategy({
+ map: this.map,
+ motion: FeatureMotion.None,
+ many: true
+ })
+ );
+
+ activeStore.layer.visible = true;
+ activeStore.source.ol.on(
+ 'removefeature',
+ (event: OlVectorSourceEvent) => {
+ const olGeometry = event.feature.getGeometry();
+ this.clearLabelsOfOlGeometry(olGeometry);
+ }
+ );
+
+ this.addFeature(selectedFeature, activeStore);
+ this.stores.push(activeStore);
+ });
+ }
+
+ addFeature(feature: SearchResult, activeStore: FeatureStore) {
+ const newFeature = {
+ type: feature.data.type,
+ geometry: {
+ coordinates: feature.data.geometry.coordinates,
+ type: feature.data.geometry.type
+ },
+ projection: feature.data.projection,
+ properties: feature.data.properties,
+ meta: {
+ id: feature.meta.id
+ }
+ };
+ delete newFeature.properties.Route;
+ activeStore.update(newFeature);
+ activeStore.setLayerExtent();
+ activeStore.layer.ol.getSource().refresh();
+ }
+
+ private clearLabelsOfOlGeometry(olGeometry) {
+ getTooltipsOfOlGeometry(olGeometry).forEach(
+ (olTooltip: OlOverlay | undefined) => {
+ if (olTooltip && olTooltip.getMap()) {
+ this.map.ol.removeOverlay(olTooltip);
+ }
+ }
+ );
+ }
}
diff --git a/packages/geo/src/lib/search/search-results/search-results.component.ts b/packages/geo/src/lib/search/search-results/search-results.component.ts
index d6b44349e1..ec30ed98cd 100644
--- a/packages/geo/src/lib/search/search-results/search-results.component.ts
+++ b/packages/geo/src/lib/search/search-results/search-results.component.ts
@@ -142,7 +142,7 @@ export class SearchResultsComponent implements OnInit, OnDestroy {
constructor(private cdRef: ChangeDetectorRef,
private searchService: SearchService,
- private configService: ConfigService
+ private configService: ConfigService,
) {}
/**
diff --git a/packages/geo/src/lib/search/search-results/search-results.module.ts b/packages/geo/src/lib/search/search-results/search-results.module.ts
index 56669d379d..529232716a 100644
--- a/packages/geo/src/lib/search/search-results/search-results.module.ts
+++ b/packages/geo/src/lib/search/search-results/search-results.module.ts
@@ -6,6 +6,12 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatTooltipModule } from '@angular/material/tooltip';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
import {
IgoCollapsibleModule,
@@ -19,6 +25,7 @@ import { IgoMetadataModule } from './../../metadata/metadata.module';
import { SearchResultsComponent } from './search-results.component';
import { SearchResultsItemComponent } from './search-results-item.component';
import { SearchResultAddButtonComponent } from './search-results-add-button.component';
+import { SaveFeatureDialogComponent } from './save-feature-dialog.component';
/**
* @ignore
@@ -31,12 +38,18 @@ import { SearchResultAddButtonComponent } from './search-results-add-button.comp
MatIconModule,
MatListModule,
MatButtonModule,
+ MatDialogModule,
IgoCollapsibleModule,
IgoListModule,
IgoStopPropagationModule,
IgoLanguageModule,
IgoMatBadgeIconModule,
IgoMetadataModule,
+ MatFormFieldModule,
+ ReactiveFormsModule,
+ MatInputModule,
+ MatSelectModule,
+ MatAutocompleteModule,
],
exports: [
SearchResultsComponent,
@@ -45,7 +58,8 @@ import { SearchResultAddButtonComponent } from './search-results-add-button.comp
declarations: [
SearchResultsComponent,
SearchResultsItemComponent,
- SearchResultAddButtonComponent
+ SearchResultAddButtonComponent,
+ SaveFeatureDialogComponent
]
})
export class IgoSearchResultsModule {}
diff --git a/packages/geo/src/locale/en.geo.json b/packages/geo/src/locale/en.geo.json
index cead2cbf90..c8ea144be7 100644
--- a/packages/geo/src/locale/en.geo.json
+++ b/packages/geo/src/locale/en.geo.json
@@ -263,7 +263,11 @@
"style": {
"styleModal": "Edit style",
"styleModalTooltip": "Edit the style of the selected entities "
- }
+ },
+ "saveBtn": "Save",
+ "cancelBtn": "Cancel",
+ "saveFeatureInLayer": "Save selection in layer",
+ "chooseOrSet": "Choose a layer or set a new layer name"
},
"download": {
"action": "Download data",
@@ -426,6 +430,7 @@
"layer.placeholder": "Search for a layer",
"ichercheReverse.name": "Search by coordinates",
"clearSearch": "Clear search",
+ "addToLayer": "Add to layer",
"ilayer": {
"name": "Layers",
"properties": {
diff --git a/packages/geo/src/locale/fr.geo.json b/packages/geo/src/locale/fr.geo.json
index 6690654cd9..a29fb65115 100644
--- a/packages/geo/src/locale/fr.geo.json
+++ b/packages/geo/src/locale/fr.geo.json
@@ -262,7 +262,11 @@
"style": {
"styleModal": "Modifier le style",
"styleModalTooltip": "Éditer le style des entités sélectionnées"
- }
+ },
+ "saveBtn": "Sauvegarder",
+ "cancelBtn": "Cancel",
+ "saveFeatureInLayer": "Ajouter la sélection dans une couche",
+ "chooseOrSet": "Choisir une couche ou définir un nouveau nom"
},
"download": {
"action": "Télécharger les données associées",
@@ -425,6 +429,7 @@
"layer.placeholder": "Rechercher une couche de données.",
"ichercheReverse.name": "Recherche par coordonnées",
"clearSearch": "Effacer la recherche",
+ "addToLayer": "Ajouter à une couche",
"ilayer": {
"name": "Couches",
"properties": {
diff --git a/packages/integration/src/lib/draw/draw.state.ts b/packages/integration/src/lib/draw/draw.state.ts
index 1707a50d4b..2e54bf6fc6 100644
--- a/packages/integration/src/lib/draw/draw.state.ts
+++ b/packages/integration/src/lib/draw/draw.state.ts
@@ -11,6 +11,7 @@ import { MapState } from '../map/map.state';
})
export class DrawState {
+ public searchLayerStores: FeatureStore[] = [];
public stores: FeatureStore[] = [];
public layersID: string[] = [];
public drawControls: [string, DrawControl][] = [];
diff --git a/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.html b/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.html
index 552195e186..3b9e7837d7 100644
--- a/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.html
+++ b/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.html
@@ -30,10 +30,13 @@ {{ 'igo.integration.searchResultsTool.noResults' | translate }}
+ (moreResults)="onSearch($event)"
+ [map]="map">
diff --git a/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.ts b/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.ts
index 0aea1f580b..a073108dd6 100644
--- a/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.ts
+++ b/packages/integration/src/lib/search/search-results-tool/search-results-tool.component.ts
@@ -41,7 +41,9 @@ import {
getCommonVectorSelectedStyle,
computeOlFeaturesExtent,
featuresAreOutOfView,
- roundCoordTo
+ roundCoordTo,
+ FeatureWithDraw,
+ FeatureStore
} from '@igo2/geo';
import { MapState } from '../../map/map.state';
@@ -49,6 +51,7 @@ import { MapState } from '../../map/map.state';
import { SearchState } from '../search.state';
import { ToolState } from '../../tool/tool.state';
import { DirectionState } from '../../directions/directions.state';
+import { DrawState } from '../../draw';
/**
* Tool to browse the search results
@@ -148,13 +151,18 @@ export class SearchResultsToolComponent implements OnInit, OnDestroy {
private format = new olFormatGeoJSON();
+ get stores(): FeatureStore[] {
+ return this.drawState.searchLayerStores;
+ }
+
constructor(
private mapState: MapState,
private searchState: SearchState,
private elRef: ElementRef,
public toolState: ToolState,
private directionState: DirectionState,
- configService: ConfigService
+ configService: ConfigService,
+ private drawState: DrawState
) {
this.hasFeatureEmphasisOnSelection = configService.getConfig(
'hasFeatureEmphasisOnSelection'