Skip to content

WfsV2CompositeAsyncFeatureSource

Namespace: ThinkGeo.Core

Reads a composite WFS 2.0 feature type: records that keep their id and attributes on themselves and their geometry on objects of another type, related by a reference.

public class WfsV2CompositeAsyncFeatureSource : WebBasedAsyncFeatureSource, IHttpRequestService

Inheritance object → AsyncFeatureSource → WebBasedAsyncFeatureSource → WfsV2CompositeAsyncFeatureSource
Implements IHttpRequestService

Remarks:

Each feature this source returns is one record of WfsV2CompositeAsyncFeatureSource.TypeName, with the record's own id and attributes and the geometry of all its components, collected into a multipart shape - or a geometry collection where the parts differ in kind - and never merged into a different shape. The geometry is always complete: a record found through a bounding box carries its components outside that box too, so a feature read here can be selected, zoomed to or exported as it is.

A service generally cannot filter a composite type by a bounding box, having no geometry on it to compare (the Bayern ATKIS service rejects a BBOX on adv:AX_Strasse). The box is sent to each component type instead, and the records are found through the components inside it. A record whose components fall on several pages of that search is returned once, so WfsV2CompositeAsyncFeatureSource.GetFeaturesPageAsync(RectangleShape, IEnumerable<string>, string, int, CancellationToken) counts records, never components, and its continuation token is opaque.

Where a component cannot be found, the record is returned with the geometry of the ones that were, and with no geometry at all when none of them were: its id and its attributes are half of what a composite type is read for, and a record of it legitimately has no geometry of its own. WfsV2CompositeAsyncFeatureSource.ComponentsUnresolved is raised either way - a partial geometry is never passed off as a complete one silently. A record that nothing but a component's reference named, and that the service does not return, is reported the same way and has nothing to return.

https://community.thinkgeo.com/t/wfs-composite-features-resolved-atkis-geometry-and-spatial-query-limitations/12490

Properties

ServiceLocationUrl

Gets or sets the base URL of the WFS service. Read when the source opens.

public string ServiceLocationUrl { get; set; }

Property Value

string

TypeName

Gets or sets the composite feature type to read, as the service's capabilities name it - adv:AX_Strasse, say - or as {namespace}LocalName, the way WfsV2AsyncFeatureSource.TypeName takes it. Read when the source opens.

public string TypeName { get; set; }

Property Value

string

Relation

Gets or sets how the records are related to the components their geometry belongs to. Read when the source opens.

public WfsComponentRelation Relation { get; set; }

Property Value

WfsComponentRelation

WfsAxisOrder

Gets or sets the axis order used when reading coordinates from the service.

public WfsAxisOrder WfsAxisOrder { get; set; }

Property Value

WfsAxisOrder

Crs

Gets or sets the CRS features are requested in. Null, the default, takes the service's default for WfsV2CompositeAsyncFeatureSource.TypeName, and the components are requested in the same one.

public string Crs { get; set; }

Property Value

string

RecordCacheTimeout

Gets or sets how long a record completed with all of its components is reused before it is read from the service again. TimeSpan.Zero, the default, keeps none.

public TimeSpan RecordCacheTimeout { get; set; }

Property Value

TimeSpan

Remarks:

A tiled overlay reads a record once for every tile it touches, and each read fetches the whole record: a long road crossing four tiles would be completed four times. Whatever this is set to, reads that overlap share their completions - a record completed by one tile of a draw is taken as it is by the others still being read - and the source lets them go as soon as it has no read running, which is when the draw is over. This keeps complete records past that, for the next draw.

The service's data can change underneath, so a record is reused only until the timeout, and drops every one at once - after a refresh of the data, say. Closing the source drops them too. A record that could not be completed is never kept. At most 10,000 are kept; past that, no more until some expire.

ReadUntilEmptyPage

Gets or sets whether a read goes on asking for records until the service answers with none, when the service says neither how many there are nor that there are more. The default is false.

public bool ReadUntilEmptyPage { get; set; }

Property Value

bool

Remarks:

It is WfsV2AsyncFeatureSource.ReadUntilEmptyPage for every request this source makes: the pages of records, the search over an extent of every component type, and the reads that complete a record. A page of WfsV2CompositeAsyncFeatureSource.GetFeaturesPageAsync(RectangleShape, IEnumerable<string>, string, int, CancellationToken) still holds no more records than it was asked for; it is the token that no longer stops at a short response. A service that ignores STARTINDEX is detected, and the read throws.

HttpClient

Gets or sets the HTTP client used for outbound requests.

public HttpClient HttpClient { get; set; }

Property Value

HttpClient

Remarks:

Assign this to take full control of the client, including its handler pipeline and default headers. It takes precedence over WebBasedAsyncFeatureSource.HttpMessageHandler, and ownership stays with the caller: the source will not dispose a client it did not create.

HttpMessageHandler

Gets or sets an optional handler used to build the client when the source opens.

public HttpMessageHandler HttpMessageHandler { get; set; }

Property Value

HttpMessageHandler

Remarks:

Useful for Blazor WebAssembly, for DelegatingHandler chains such as Polly or OpenTelemetry, and for tests with a mock handler. Read once during open, ignored when WebBasedAsyncFeatureSource.HttpClient is already assigned, and never disposed by this source.

WebProxy

Gets or sets the proxy through which requests should be routed.

public IWebProxy WebProxy { get; set; }

Property Value

IWebProxy

TimeoutInSeconds

Gets or sets the request timeout, in seconds.

public int TimeoutInSeconds { get; set; }

Property Value

int

Credentials

Gets or sets the credentials used when authenticating requests.

public ICredentials Credentials { get; set; }

Property Value

ICredentials

UserAgent

Gets or sets the user-agent header sent with each request.

public string UserAgent { get; set; }

Property Value

string

ConcurrentAccess

Gets or sets whether this source can be read from more than one thread at once, and if it cannot, whether copying it per worker is the cheap way to read it anyway.

public ConcurrentAccess ConcurrentAccess { get; set; }

Property Value

ConcurrentAccess

Remarks:

The same declaration FeatureSource.ConcurrentAccess carries, for the same reason: whoever cuts or draws several tiles at once asks the source before deciding whether to copy it per worker. ConcurrentAccess.UnsafeLock by default; a source that answers each read from its own request says ConcurrentAccess.Safe. https://community.thinkgeo.com/t/features-cut-off-on-tile-border-with-gpu-rendering/12481

IsOpen

Gets a value indicating whether the source has been opened and is ready to answer queries.

public bool IsOpen { get; }

Property Value

bool

ProjectionConverter

Gets or sets the converter that translates between the coordinate system the service publishes and the one the map is drawn in.

public ProjectionConverter ProjectionConverter { get; set; }

Property Value

ProjectionConverter

Remarks:

Bounding boxes passed in are converted to the service's own projection before the request is issued, and the features that come back are converted to the map's projection. Where a service can reproject on request, asking it to do so is usually better than converting afterwards, because the server holds the authoritative transformation for its own grid.

GeometryType

Gets the kind of geometry the source holds - points, lines or areas - as its service declares it.

public WellKnownType GeometryType { get; protected set; }

Property Value

WellKnownType

Remarks:

Read when the source opens, at no cost of its own: WFS from the DescribeFeatureType the open already fetches, ArcGIS Server from the layer definition. It is WellKnownType.Invalid before that, after the source closes, and whenever the service does not say - OGC API - Features has no required schema, and a WFS schema may name a geometry (gml:GeometryPropertyType) without saying which. Invalid is not a promise that the source holds no geometry. https://community.thinkgeo.com/t/v15-beta160-follow-up-remaining-gpu-style-parity-source-lifecycle-wfs-v2-and-wms-gaps/12468

Constructors

WfsV2CompositeAsyncFeatureSource()

Initializes a new instance of the WfsV2CompositeAsyncFeatureSource class.

public WfsV2CompositeAsyncFeatureSource()

WfsV2CompositeAsyncFeatureSource(string, string, WfsComponentRelation)

Initializes a new instance of the WfsV2CompositeAsyncFeatureSource class.

public WfsV2CompositeAsyncFeatureSource(string serviceLocationUrl, string typeName, WfsComponentRelation relation)

Parameters

serviceLocationUrl string
The base URL of the WFS service.

typeName string
The composite feature type to read, as the service names it.

relation WfsComponentRelation
How its records are related to the components their geometry belongs to.

Methods

ClearRecordCache()

Drops every record kept by WfsV2CompositeAsyncFeatureSource.RecordCacheTimeout, so the next read of each comes from the service.

public void ClearRecordCache()

Returns

void

Remarks:

It also lets go of the records the reads running now have completed, so a read that starts after it fetches them again.

GetFeaturesPageAsync(RectangleShape, IEnumerable<string>, string, int, CancellationToken)

Returns one page of records, each with the complete geometry of its components.

public Task<FeaturePage> GetFeaturesPageAsync(RectangleShape boundingBox, IEnumerable<string> returningColumnNames, string continuationToken, int maxCount, CancellationToken cancellationToken)

Parameters

boundingBox RectangleShape
The extent to query, in the map's projection. Pass null for every record.

returningColumnNames IEnumerable<string>
The columns of the records to bring back, or null for all.

continuationToken string
The token from the previous page, or null to start.

maxCount int
The largest number of records to return.

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<FeaturePage>

        A page of records and the token for the next page, when one exists.
         is -1: the number of records a bounding box
        touches is not known until the search has been read to the end.

Remarks:

Over an extent the token carries the key of every record already returned, since that is what keeps a record whose components fall on several pages of the search from coming back twice - so it grows with the number of records read, and a deep page carries a long token. A token belongs to the read that issued it: one from a read over an extent is refused by a read without one, and the other way round.

OnComponentsUnresolved(ComponentsUnresolvedEventArgs)

Raises WfsV2CompositeAsyncFeatureSource.ComponentsUnresolved.

protected void OnComponentsUnresolved(ComponentsUnresolvedEventArgs e)

Parameters

e ComponentsUnresolvedEventArgs
The event data.

Returns

void

OpenAsyncCore(CancellationToken)

Reads the capabilities and the schemas of both feature types, and checks the relation against them.

protected Task OpenAsyncCore(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task
A task that represents the asynchronous operation.

CloseAsyncCore(CancellationToken)

Releases the metadata read at open time.

protected Task CloseAsyncCore(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task
A task that represents the asynchronous operation.

GetColumnsAsyncCore(CancellationToken)

Returns the columns of the composite type's records.

protected Task<Collection<FeatureSourceColumn>> GetColumnsAsyncCore(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<Collection<FeatureSourceColumn>>
The columns.

GetCountAsyncCore(CancellationToken)

Returns how many records the service holds, or -1 when it does not say.

protected Task<long> GetCountAsyncCore(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<long>
The record count.

GetBoundingBoxAsyncCore(CancellationToken)

Returns the extent the service advertises for the composite type, or the one the component types cover together when it advertises none.

protected Task<RectangleShape> GetBoundingBoxAsyncCore(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<RectangleShape>
The extent, or null.

GetFeaturesInsideBoundingBoxAsyncCore(RectangleShape, IEnumerable<string>, CancellationToken)

Returns the records with a component inside , each with all of its components.

protected Task<Collection<Feature>> GetFeaturesInsideBoundingBoxAsyncCore(RectangleShape boundingBox, IEnumerable<string> returningColumnNames, CancellationToken cancellationToken)

Parameters

boundingBox RectangleShape
The extent to query, in the service's own projection.

returningColumnNames IEnumerable<string>
The columns of the records to bring back.

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<Collection<Feature>>
The records.

GetFeaturesByIdsAsyncCore(IReadOnlyList<string>, IEnumerable<string>, CancellationToken)

Returns the records with the given ids, each with all of its components.

protected Task<Collection<Feature>> GetFeaturesByIdsAsyncCore(IReadOnlyList<string> ids, IEnumerable<string> returningColumnNames, CancellationToken cancellationToken)

Parameters

ids IReadOnlyList<string>
The record ids.

returningColumnNames IEnumerable<string>
The columns of the records to bring back.

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<Collection<Feature>>
The records found.

GetFeaturesByColumnValueAsyncCore(string, string, IEnumerable<string>, CancellationToken)

Returns the records whose equals , each with all of its components.

protected Task<Collection<Feature>> GetFeaturesByColumnValueAsyncCore(string columnName, string columnValue, IEnumerable<string> returningColumnNames, CancellationToken cancellationToken)

Parameters

columnName string
The column of the records to filter on.

columnValue string
The value it must equal.

returningColumnNames IEnumerable<string>
The columns of the records to bring back.

cancellationToken CancellationToken
A token that cancels the operation.

Returns

Task<Collection<Feature>>
The records found.

Events

ComponentsUnresolved

Occurs when a record's geometry could not be completed: a component names a record the service does not return, a record names a component it does not return, or a record has no components at all.

public event EventHandler<ComponentsUnresolvedEventArgs> ComponentsUnresolved;

SendingHttpRequest

Occurs before an HTTP request is dispatched so callers can inspect or cancel it.

public event EventHandler<SendingHttpRequestMessageEventArgs> SendingHttpRequest;

ReceivedHttpResponse

Occurs after an HTTP response is received.

public event EventHandler<ReceivedHttpResponseMessageEventArgs> ReceivedHttpResponse;