-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #848 from geonetwork/mv-add-from-ogc-api
[Map-Viewer] Add layer from OGC API service
- Loading branch information
Showing
33 changed files
with
445 additions
and
428 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
36 changes: 36 additions & 0 deletions
36
libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<div class="flex items-center mb-5"> | ||
<gn-ui-text-input | ||
[(value)]="ogcUrl" | ||
(valueChange)="urlChange.next($event)" | ||
[hint]="'map.ogc.urlInput.hint' | translate" | ||
class="w-96" | ||
> | ||
</gn-ui-text-input> | ||
</div> | ||
|
||
<div *ngIf="errorMessage" class="text-red-500 mt-2"> | ||
{{ errorMessage }} | ||
</div> | ||
|
||
<div *ngIf="loading"> | ||
<p class="loading-message" translate>map.loading.service</p> | ||
</div> | ||
|
||
<div *ngIf="!loading && layers.length > 0"> | ||
<h2 class="font-bold" translate>map.layers.available</h2> | ||
<ng-container *ngFor="let layer of layers"> | ||
<div class="flex items-center justify-between my-2 layer-item-tree"> | ||
<p class="max-w-xs overflow-hidden overflow-ellipsis whitespace-nowrap"> | ||
{{ layer }} | ||
</p> | ||
<gn-ui-button | ||
class="layer-add-btn" | ||
type="primary" | ||
(buttonClick)="addLayer(layer)" | ||
extraClass="text-sm !px-2 !py-1" | ||
translate | ||
><span translate> map.layer.add </span></gn-ui-button | ||
> | ||
</div> | ||
</ng-container> | ||
</div> |
82 changes: 82 additions & 0 deletions
82
libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing' | ||
import { AddLayerFromOgcApiComponent } from './add-layer-from-ogc-api.component' | ||
import { MapFacade } from '../+state/map.facade' | ||
import { TranslateModule } from '@ngx-translate/core' | ||
import { NO_ERRORS_SCHEMA } from '@angular/core' | ||
import { MapContextLayerTypeEnum } from '../map-context/map-context.model' | ||
|
||
jest.mock('@camptocamp/ogc-client', () => ({ | ||
OgcApiEndpoint: class { | ||
constructor(private url) {} | ||
isReady() { | ||
if (this.url.indexOf('error') > -1) { | ||
return Promise.reject(new Error('Something went wrong')) | ||
} | ||
if (this.url.indexOf('wait') > -1) { | ||
return new Promise(() => { | ||
// do nothing | ||
}) | ||
} | ||
return Promise.resolve(this) | ||
} | ||
get featureCollections() { | ||
if (this.url.includes('error')) { | ||
return Promise.reject(new Error('Simulated loading error')) | ||
} | ||
return Promise.resolve(['layer1', 'layer2', 'layer3']) | ||
} | ||
getCollectionItemsUrl(collectionId) { | ||
return Promise.resolve( | ||
`http://example.com/collections/${collectionId}/items` | ||
) | ||
} | ||
}, | ||
})) | ||
|
||
describe('AddLayerFromOgcApiComponent', () => { | ||
let component: AddLayerFromOgcApiComponent | ||
let fixture: ComponentFixture<AddLayerFromOgcApiComponent> | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [TranslateModule.forRoot(), AddLayerFromOgcApiComponent], | ||
declarations: [], | ||
schemas: [NO_ERRORS_SCHEMA], | ||
}).compileComponents() | ||
|
||
fixture = TestBed.createComponent(AddLayerFromOgcApiComponent) | ||
component = fixture.componentInstance | ||
fixture.detectChanges() | ||
}) | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy() | ||
expect(component.errorMessage).toBeFalsy() | ||
expect(component.loading).toBe(false) | ||
expect(component.layers.length).toBe(0) | ||
}) | ||
|
||
describe('loadLayers', () => { | ||
it('should clear layers if OGC URL is empty', async () => { | ||
component.ogcUrl = '' | ||
await component.loadLayers() | ||
expect(component.layers.length).toBe(0) | ||
}) | ||
|
||
it('should load layers on valid OGC API service', async () => { | ||
component.ogcUrl = 'http://example.com/ogc' | ||
await component.loadLayers() | ||
expect(component.errorMessage).toBeFalsy() | ||
expect(component.loading).toBe(false) | ||
expect(component.layers).toEqual(['layer1', 'layer2', 'layer3']) | ||
}) | ||
|
||
it('should handle errors while loading layers', async () => { | ||
component.ogcUrl = 'http://example.com/error' | ||
await component.loadLayers() | ||
expect(component.errorMessage).toContain('Error loading layers:') | ||
expect(component.loading).toBe(false) | ||
expect(component.layers.length).toBe(0) | ||
}) | ||
}) | ||
}) |
80 changes: 80 additions & 0 deletions
80
libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { | ||
Component, | ||
OnInit, | ||
Output, | ||
EventEmitter, | ||
ChangeDetectionStrategy, | ||
Input, | ||
ChangeDetectorRef, | ||
} from '@angular/core' | ||
import { OgcApiEndpoint } from '@camptocamp/ogc-client' | ||
import { Subject, debounceTime } from 'rxjs' | ||
import { | ||
MapContextLayerModel, | ||
MapContextLayerTypeEnum, | ||
} from '../map-context/map-context.model' | ||
import { TranslateModule } from '@ngx-translate/core' | ||
import { UiInputsModule } from '@geonetwork-ui/ui/inputs' | ||
import { CommonModule } from '@angular/common' | ||
import { MapLayer } from '../+state/map.models' | ||
|
||
@Component({ | ||
selector: 'gn-ui-add-layer-from-ogc-api', | ||
templateUrl: './add-layer-from-ogc-api.component.html', | ||
styleUrls: ['./add-layer-from-ogc-api.component.css'], | ||
standalone: true, | ||
imports: [CommonModule, TranslateModule, UiInputsModule], | ||
}) | ||
export class AddLayerFromOgcApiComponent implements OnInit { | ||
@Input() ogcUrl: string | ||
@Output() layerAdded = new EventEmitter<MapLayer>() | ||
|
||
urlChange = new Subject<string>() | ||
layerUrl = '' | ||
loading = false | ||
layers: string[] = [] | ||
ogcEndpoint: OgcApiEndpoint = null | ||
errorMessage: string | null = null | ||
|
||
constructor(private changeDetectorRef: ChangeDetectorRef) {} | ||
|
||
ngOnInit() { | ||
this.urlChange.pipe(debounceTime(700)).subscribe(() => { | ||
this.loadLayers() | ||
this.changeDetectorRef.detectChanges() // manually trigger change detection | ||
}) | ||
} | ||
|
||
async loadLayers() { | ||
this.errorMessage = null | ||
try { | ||
this.loading = true | ||
if (this.ogcUrl.trim() === '') { | ||
this.layers = [] | ||
return | ||
} | ||
this.ogcEndpoint = await new OgcApiEndpoint(this.ogcUrl) | ||
|
||
// Currently only supports feature collections | ||
this.layers = await this.ogcEndpoint.featureCollections | ||
} catch (error) { | ||
const err = error as Error | ||
this.layers = [] | ||
this.errorMessage = 'Error loading layers: ' + err.message | ||
} finally { | ||
this.loading = false | ||
this.changeDetectorRef.markForCheck() | ||
} | ||
} | ||
|
||
async addLayer(layer: string) { | ||
this.layerUrl = await this.ogcEndpoint.getCollectionItemsUrl(layer) | ||
|
||
const layerToAdd: MapContextLayerModel = { | ||
name: layer, | ||
url: this.layerUrl, | ||
type: MapContextLayerTypeEnum.OGCAPI, | ||
} | ||
this.layerAdded.emit({ ...layerToAdd, title: layer }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.