Skip to content

Commit

Permalink
feat: Create a wfs-filter component
Browse files Browse the repository at this point in the history
dev version, not usable as a main panel yet
  • Loading branch information
FilipLeitner authored and jmacura committed Oct 3, 2024
1 parent 952875a commit b5aa8b4
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,61 @@
<hs-filters [rule]="rule"></hs-filters>
<div class="container-fluid">
<!-- Check if there are available WFS layers -->
@if (availableLayers.length > 0) {
<!-- Layer selection dropdown -->
<div class="row mb-3">
<div class="col-12">
<label for="layerSelect" class="form-label">Select WFS Layer:</label>
<select id="layerSelect" class="form-select" [(ngModel)]="selectedLayer"
(ngModelChange)="selectLayer($event)">
<option [ngValue]="null">-- Select a layer --</option>
<!-- Loop through available layers -->
@for (layer of availableLayers; track layer) {
<option [ngValue]="layer">
{{ layer.title }}
</option>
}
</select>
</div>
</div>

<!-- Display filter options if a layer is selected -->
@if (selectedLayer) {
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Filter for: {{ selectedLayer.title }}</h5>
<!-- Filter component -->
<hs-filters [rule]="rule" [selectedLayer]="selectedLayer" (change)="onChange()"></hs-filters>
</div>
</div>
</div>
</div>
} @else {
<!-- Prompt to select a layer if none is selected -->
<div class="row">
<div class="col-12">
<div class="alert alert-info" role="alert">
Please select a WFS layer to apply filters.
</div>
</div>
</div>
}
} @else {
<!-- Display when no WFS layers are available -->
<div class="row">
<div class="col-12">
<div class="text-center py-4">
<i class="icon-layers mb-3 text-muted" style="font-size: 48px;"></i>
<h4>No WFS Layers Available</h4>
<p class="mb-0">There are currently no WFS layers available for filtering.</p>
<p>To use this feature, please add a WFS layer to the map first.</p>
<!-- Button to open the Add Data panel -->
<button class="btn btn-primary mt-3" (click)="openAddDataPanel()">
Add WFS Layer
</button>
</div>
</div>
</div>
}
</div>
145 changes: 108 additions & 37 deletions projects/hslayers/components/wfs-filter/wfs-filter.component.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,120 @@
import * as olFormatFilter from 'ol/format/filter';
import {Component, Input, inject} from '@angular/core';
import {
HsFiltersComponent,
HsFiltersService,
} from 'hslayers-ng/components/styler';
import {Component, Input, OnInit, inject} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HsFiltersComponent, HsFiltersService} from 'hslayers-ng/common/filters';
import {HsLayerDescriptor} from 'hslayers-ng/types';
import {HsLayerManagerService} from 'hslayers-ng/services/layer-manager';
import {HsLayoutService} from 'hslayers-ng/services/layout';
import {HsUtilsService} from 'hslayers-ng/services/utils';
import {Vector as VectorLayer} from 'ol/layer';
import {Vector as VectorSource} from 'ol/source';
import {getWfsUrl} from 'hslayers-ng/common/extensions';

@Component({
selector: 'hs-wfs-filter',
templateUrl: './wfs-filter.component.html',
styles: ``,
standalone: true,
imports: [HsFiltersComponent],
imports: [HsFiltersComponent, FormsModule],
})
export class HsWfsFilterComponent {
@Input() rule: any;
export class HsWfsFilterComponent implements OnInit {
@Input() rule: any = {};
@Input() preselectedLayer: HsLayerDescriptor;

hsFiltersService = inject(HsFiltersService);
constructor() {}

parseFilters(filters: any[]): any[] {
return filters.map((filter) => {
switch (filter[0]) {
case '==':
return olFormatFilter.equalTo(filter[1], filter[2]);
case '*=':
return olFormatFilter.like(filter[1], filter[2]);
case '!=':
return olFormatFilter.notEqualTo(filter[1], filter[2]);
case '<':
return olFormatFilter.lessThan(filter[1], filter[2]);
case '<=':
return olFormatFilter.lessThanOrEqualTo(filter[1], filter[2]);
case '>':
return olFormatFilter.greaterThan(filter[1], filter[2]);
case '>=':
return olFormatFilter.greaterThanOrEqualTo(filter[1], filter[2]);
case '&&':
return olFormatFilter.and(...this.parseFilters(filter.slice(1)));
case '||':
return olFormatFilter.or(...this.parseFilters(filter.slice(1)));
case '!':
return olFormatFilter.not(this.parseFilters([filter[1]])[0]);
default:
throw new Error('Invalid filter type');
}
});
hsLayerManagerService = inject(HsLayerManagerService);
hsUtilsService = inject(HsUtilsService);
hsLayoutService = inject(HsLayoutService);

availableLayers: HsLayerDescriptor[] = [];
selectedLayer: HsLayerDescriptor | null = null;

ngOnInit() {
this.updateAvailableLayers();
if (this.preselectedLayer) {
this.selectLayer(this.preselectedLayer);
}
}

/**
* Updates the list of available WFS layers
*/
updateAvailableLayers() {
this.availableLayers = this.hsLayerManagerService.data.layers.filter(
(l: HsLayerDescriptor) =>
this.hsUtilsService.instOf(l.layer, VectorLayer) &&
this.hsUtilsService.instOf(l.layer.getSource(), VectorSource) &&
getWfsUrl(l.layer),
);
}

/**
* Selects a layer and updates the filter service
* @param layer The layer to select
*/
selectLayer(layer: HsLayerDescriptor | null) {
this.selectedLayer = layer;
this.hsFiltersService.setSelectedLayer(layer);
// Reset the rule when changing layers
this.rule = {};
}

/**
* Handles changes in the filter
*/
onChange() {
if (this.rule.filter) {
const parsedFilter = this.parseFilter(this.rule.filter);
console.log('Parsed OpenLayers filter for WFS:', parsedFilter);
// You can now use this parsedFilter with OpenLayers for WFS requests
}
}

/**
* Parses the filter into OpenLayers format
* @param filter The filter to parse
* @returns Parsed OpenLayers filter
*/
parseFilter(filter: any[]): any {
if (!Array.isArray(filter)) {
return null;
}

const [operator, ...operands] = filter;

switch (operator) {
case '||':
return olFormatFilter.or(...operands.map((op) => this.parseFilter(op)));
case '&&':
return olFormatFilter.and(
...operands.map((op) => this.parseFilter(op)),
);
case '!':
return olFormatFilter.not(this.parseFilter(operands[0]));
case '==':
return olFormatFilter.equalTo(operands[0], operands[1]);
case '*=':
return olFormatFilter.like(operands[0], operands[1]);
case '!=':
return olFormatFilter.notEqualTo(operands[0], operands[1]);
case '<':
return olFormatFilter.lessThan(operands[0], operands[1]);
case '<=':
return olFormatFilter.lessThanOrEqualTo(operands[0], operands[1]);
case '>':
return olFormatFilter.greaterThan(operands[0], operands[1]);
case '>=':
return olFormatFilter.greaterThanOrEqualTo(operands[0], operands[1]);
default:
console.warn(`Unsupported operator: ${operator}`);
return null;
}
}

/**
* Opens the Add Data panel
*/
openAddDataPanel() {
this.hsLayoutService.setMainPanel('addData');
}
}
1 change: 1 addition & 0 deletions projects/hslayers/services/styler/public-api.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './styler.service';
export * from './default-style';
export * from './style-part-base.component';
20 changes: 20 additions & 0 deletions projects/hslayers/services/styler/style-part-base.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';

@Component({
template: '<div></div>',
standalone: true,
})
export class HsStylerPartBaseComponent {
@Output() changes = new EventEmitter<void>();
@Output() deleteFilter = new EventEmitter<void>();

@Input() warning: string;

emitChange(): void {
this.changes.emit();
}

deleteRuleFilter(): void {
this.deleteFilter.emit();
}
}

0 comments on commit b5aa8b4

Please sign in to comment.