Skip to content

Commit

Permalink
perf(wfs): cancel outgoing wfs xhr on pan/zoom (#1421)
Browse files Browse the repository at this point in the history
* perf(wfs): cancel outgoing wfs xhr on pan/zoom

* chore(Geo): adding typing to vector-layer

* perf(vector-layer): optimize xhr abort looping execution
  • Loading branch information
pelord authored Sep 29, 2023
1 parent b27a1dd commit b796cec
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { AuthInterceptor } from '@igo2/auth';

export class WFSDataSource extends DataSource {
public declare ol: olSourceVector<OlGeometry>;
public mostRecentIdCallOGCFilter: number = 0;

set ogcFilters(value: OgcFiltersOptions) {
(this.options as OgcFilterableDataSourceOptions).ogcFilters = value;
Expand Down Expand Up @@ -100,7 +99,6 @@ export class WFSDataSource extends DataSource {

setOgcFilters(ogcFilters: OgcFiltersOptions, triggerEvent: boolean = false) {
this.ogcFilters = ogcFilters;
this.mostRecentIdCallOGCFilter += 1;
if (triggerEvent) {
this.ol.notify('ogcFilters', this.ogcFilters);
}
Expand Down
115 changes: 66 additions & 49 deletions packages/geo/src/lib/layer/shared/layers/vector-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import olFeature from 'ol/Feature';
import olProjection from 'ol/proj/Projection';
import * as olproj from 'ol/proj';
import * as olformat from 'ol/format';
import OlFeature from 'ol/Feature';

import { FeatureDataSource } from '../../../datasource/shared/datasources/feature-datasource';
import { WFSDataSource } from '../../../datasource/shared/datasources/wfs-datasource';
Expand All @@ -27,7 +28,10 @@ import {
buildUrl,
defaultMaxFeatures
} from '../../../datasource/shared/datasources/wms-wfs.utils';
import { OgcFilterableDataSourceOptions } from '../../../filter/shared/ogc-filter.interface';
import {
OgcFilterableDataSourceOptions,
OgcFiltersOptions
} from '../../../filter/shared/ogc-filter.interface';
import {
GeoNetworkService,
SimpleGetOptions
Expand All @@ -48,8 +52,14 @@ import BaseEvent from 'ol/events/Event';
import { olStyleToBasicIgoStyle } from '../../../style/shared/vector/conversion.utils';
import { FeatureDataSourceOptions } from '../../../datasource/shared/datasources/feature-datasource.interface';
import { ObjectUtils } from '@igo2/utils';
import { Extent } from 'ol/extent';
import { FeatureLoader } from 'ol/featureloader';

export class VectorLayer extends Layer {
private previousLoadExtent: Extent;
private previousLoadResolution: number;
private previousOgcFilters: OgcFiltersOptions;
private xhrAccumulator: XMLHttpRequest[] = [];
public declare dataSource:
| FeatureDataSource
| WFSDataSource
Expand Down Expand Up @@ -167,8 +177,11 @@ export class VectorLayer extends Layer {
const vector = new olLayerVector(olOptions);
const vectorSource = vector.getSource() as olSourceVector<OlGeometry>;
const url = vectorSource.getUrl();
if (typeof url === 'function') {
return vector;
}
if (url) {
let loader;
let loader: FeatureLoader;
const wfsOptions = olOptions.sourceOptions as WFSDataSourceOptions;
if (
wfsOptions?.type === 'wfs' &&
Expand Down Expand Up @@ -436,14 +449,14 @@ export class VectorLayer extends Layer {
* @param randomParam random parameter to ensure cache is not causing problems in retrieving new data
*/
public customWFSLoader(
vectorSource,
options,
interceptor,
extent,
resolution,
proj,
success,
failure,
vectorSource: olSourceVector<OlGeometry>,
options: WFSDataSourceOptions,
interceptor: AuthInterceptor,
extent: Extent,
resolution: number,
proj: olProjection,
success: (features: olFeature<OlGeometry>[]) => void,
failure: () => void,
randomParam?: boolean
) {
{
Expand All @@ -452,12 +465,31 @@ export class VectorLayer extends Layer {
? new olProjection({ code: paramsWFS.srsName })
: proj;
const currentExtent = olproj.transformExtent(extent, proj, wfsProj);
const ogcFilters = (options as OgcFilterableDataSourceOptions).ogcFilters;

if (
(this.previousLoadExtent &&
this.previousLoadExtent !== currentExtent) ||
(this.previousLoadResolution &&
this.previousLoadResolution !== resolution) ||
(this.previousOgcFilters && this.previousOgcFilters !== ogcFilters)
) {
vectorSource.removeLoadedExtent(this.previousLoadExtent);
for (let xhr of this.xhrAccumulator) {
xhr.abort();
}
}

this.previousLoadExtent = currentExtent;
this.previousLoadResolution = resolution;
this.previousOgcFilters = ogcFilters;

paramsWFS.srsName = paramsWFS.srsName || proj.getCode();
const url = buildUrl(
options,
currentExtent,
wfsProj,
(options as OgcFilterableDataSourceOptions).ogcFilters,
ogcFilters,
randomParam
);
let startIndex = 0;
Expand All @@ -481,7 +513,6 @@ export class VectorLayer extends Layer {
wfsProj,
proj,
alteredUrl,
nbOfFeature,
success,
failure
);
Expand All @@ -495,7 +526,6 @@ export class VectorLayer extends Layer {
wfsProj,
proj,
url,
paramsWFS.maxFeatures,
success,
failure
);
Expand All @@ -512,23 +542,19 @@ export class VectorLayer extends Layer {
* @param dataProjection the projection of the retrieved data
* @param featureProjection the projection of the created features
* @param url the url string to retrieve the data
* @param threshold the threshold to manage "more features" (TODO)
* @param success success callback
* @param failure failure callback
*/
private getFeatures(
vectorSource: olSourceVector<OlGeometry>,
interceptor,
extent,
dataProjection,
featureProjection,
interceptor: AuthInterceptor,
extent: Extent,
dataProjection: olProjection,
featureProjection: olProjection,
url: string,
threshold: number,
success,
failure
success: (features: olFeature<OlGeometry>[]) => void,
failure: () => void
) {
const idAssociatedCall = (this.dataSource as WFSDataSource)
.mostRecentIdCallOGCFilter;
const xhr = new XMLHttpRequest();
const alteredUrlWithKeyAuth = interceptor.alterUrlWithKeyAuth(url);
let modifiedUrl = url;
Expand All @@ -552,15 +578,7 @@ export class VectorLayer extends Layer {
dataProjection,
featureProjection
}) as olFeature<OlGeometry>[];
// TODO Manage "More feature"
/*if (features.length === 0 || features.length < threshold ) {
console.log('No more data to download at this resolution');
}*/
// Avoids retrieving an older call that took longer to be process
if (
idAssociatedCall ===
(this.dataSource as WFSDataSource).mostRecentIdCallOGCFilter
) {
if (features) {
vectorSource.addFeatures(features);
success(features);
} else {
Expand All @@ -570,6 +588,7 @@ export class VectorLayer extends Layer {
onError();
}
};
this.xhrAccumulator.push(xhr);
xhr.send();
}

Expand All @@ -584,14 +603,14 @@ export class VectorLayer extends Layer {
* @param projection the projection to retrieve the data
*/
private customLoader(
vectorSource,
url,
interceptor,
extent,
resolution,
projection,
success,
failure
vectorSource: olSourceVector<OlGeometry>,
url: string,
interceptor: AuthInterceptor,
extent: Extent,
resolution: number,
projection: olProjection,
success: (features: olFeature<OlGeometry>[]) => void,
failure: () => void
) {
const xhr = new XMLHttpRequest();
let modifiedUrl = url;
Expand All @@ -600,8 +619,6 @@ export class VectorLayer extends Layer {
if (alteredUrlWithKeyAuth) {
modifiedUrl = alteredUrlWithKeyAuth;
}
} else {
modifiedUrl = url(extent, resolution, projection);
}

if (this.geoNetworkService && typeof url !== 'function') {
Expand All @@ -622,7 +639,7 @@ export class VectorLayer extends Layer {
concatMap((r) =>
r
? of(r)
: this.geoNetworkService.get(modifiedUrl, options).pipe(
: this.geoNetworkService.get(modifiedUrl as string, options).pipe(
first(),
catchError((res) => {
onError();
Expand Down Expand Up @@ -653,22 +670,22 @@ export class VectorLayer extends Layer {
const features = format.readFeatures(source, {
extent,
featureProjection: projection
});
vectorSource.addFeatures(features, format.readProjection(source));
}) as OlFeature<OlGeometry>[];
vectorSource.addFeatures(features);
success(features);
} else {
onError();
}
}
});
} else {
xhr.open('GET', modifiedUrl);
xhr.open('GET', modifiedUrl as string);
const format = vectorSource.getFormat();
if (format.getType() === 'arraybuffer') {
xhr.responseType = 'arraybuffer';
}
if (interceptor) {
interceptor.interceptXhr(xhr, modifiedUrl);
interceptor.interceptXhr(xhr, modifiedUrl as string);
}

const onError = () => {
Expand Down Expand Up @@ -698,8 +715,8 @@ export class VectorLayer extends Layer {
const features = format.readFeatures(source, {
extent,
featureProjection: projection
});
vectorSource.addFeatures(features, format.readProjection(source));
}) as OlFeature<OlGeometry>[];
vectorSource.addFeatures(features);
success(features);
} else {
onError();
Expand Down
9 changes: 2 additions & 7 deletions packages/geo/src/lib/offline/shared/geo-network.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { ConnectionState, NetworkService } from '@igo2/core';
import { Observable } from 'rxjs';
import { GeoDBService } from '../geoDB/geoDB.service';
import { Type } from 'ol/format/Feature';

export enum ResponseType {
Arraybuffer = 'arraybuffer',
Expand All @@ -11,7 +12,7 @@ export enum ResponseType {
Json = 'json'
}
export interface SimpleGetOptions {
responseType: ResponseType;
responseType: Type;
withCredentials?: boolean;
}
@Injectable({
Expand Down Expand Up @@ -49,12 +50,6 @@ export class GeoNetworkService {
withCredentials: simpleGetOptions.withCredentials
});
break;
case 'blob':
request = this.http.get(url, {
responseType: 'blob',
withCredentials: simpleGetOptions.withCredentials
});
break;
case 'text':
request = this.http.get(url, {
responseType: 'text',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import olSourceImageWMS from 'ol/source/ImageWMS';
import type { default as OlGeometry } from 'ol/geom/Geometry';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { createFilterInMapExtentOrResolutionStrategy } from './workspace.utils';
import { FeatureLoader } from 'ol/featureloader';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -738,10 +739,16 @@ export class EditionWorkspaceService {
*/
refreshMap(layer: VectorLayer, map: MapBase) {
const wfsOlLayer = layer.dataSource.ol;
const loader = (extent, resolution, proj, success, failure) => {
const loader: FeatureLoader = (
extent,
resolution,
proj,
success,
failure
) => {
layer.customWFSLoader(
layer.ol.getSource(),
layer.options.sourceOptions,
layer.options.sourceOptions as WFSDataSourceOptions,
this.authInterceptor,
extent,
resolution,
Expand Down

0 comments on commit b796cec

Please sign in to comment.