Why do it?
To load and visualize vector data, LuciadRIA uses a loading strategy mechanism that determines how and when data is requested from an underlying data source. Understanding how the loading strategies work and how you can tweak the mechanism can help you improve the data loading performance and achieve specific user requirements.
How do the Loading strategies work?
LuciadRIA offers two alternative loading strategies: LoadEverything
and LoadSpatially
.
-
LoadEverything
: instructs LuciadRIA to load all data at once, when a layer is added to the map. This is the preferred approach when the data set is relatively small and static. -
LoadSpatially
: queries the model for data inside the current view only, with a margin for expansion. If the map extent changes, the layer submits a new query to the model for features inside the new extent. This approach is suitable for dealing with a large amount of static or possibly dynamic data. You can tell LuciadRIA explicitly to use one of the loading strategies through the API, or you can let LuciadRIA implicitly decide which of the two strategies is selected.
If you do not explicitly configure the loading strategy on a feature layer, LuciadRIA selects the LoadSpatially
strategy if the FeatureModel
of the FeatureLayer
supports spatial querying. A FeatureModel
supports spatial querying only if its Store
implements the spatialQuery
method. Otherwise, the LoadEverything
data loading strategy is used.
On the other hand, if you explicitly configure the LoadSpatially
strategy, but the Store
of the underlying model does not implement the spatialQuery
method, LuciadRIA will switch to the LoadEverything
mechanism.
|
The role of the query provider
A loading strategy makes use of a QueryProvider
. It specifies what data must be requested from the underlying data source.
LuciadRIA uses the QueryProvider
of the loading strategy to determine a query object for specific scale ranges of the view. The query object is a query parameter
that describes a filter condition.
When a user zooms in and out on the map, the map scale changes. If it changes in such a way that it ends up in another scale
range defined by the QueryProvider
, the loading strategy directs LuciadRIA to re-load data using a query object that corresponds with that scale range.
The default QueryProvider
object requests all data for all scales.
Loading strategies and data re-loading
The data can be re-loaded in the following cases:
-
The map scale changes and crosses a scale break defined by the loading strategy’s
QueryProvider
. -
LoadingStrategy.queryProvider.invalidate()
is invoked by a user, indicating that the query must be refreshed. -
The
LoadSpatially
strategy discovers that the view extent has changed -
The loading strategy has the
refresh
property, that accepts:-
number
- the loading strategy refreshes everyn
milliseconds. -
Date
- the loading strategy will refresh once at that time. -
null
- the loading strategy never refreshes.
-
The re-loading behavior depends on the selected loading strategy:
-
LoadEverything
: All the previous data is removed when the new vector data is loaded. -
LoadSpatially
: By default, new features are handled in this way:-
Existing features that are absent in the new data are removed.
-
New features that did not exist in the previously loaded data are added.
-
New features that already existed in the previously loaded data are disregarded.
-
This default re-load approach works for static data with features that do not change.
For dynamic data, however, the updates will not be reflected. To change this default behavior for dynamic data,
you can provide a custom implementation for the LoadingStrategy.shouldUpdate
function predicate. See Use case 5: big dynamic data depending on scale for an example.
For LuciadRIA 2018.1 and more recent versions
In a 3D scene, there can be areas that represent more than one scale level, as defined by the |
How to do it?
Use case 1: small data sets
Load all vector data in one go with the LoadEverything
strategy. The default QueryProvider
instance is used implicitly.
const wfsLayer = new FeatureLayer(wfsModel, {
loadingStrategy: new LoadEverything()
});
Use case 2: Small data sets depending on scale
You can load all vector data in one go whenever scale breaks are crossed with the LoadEverything
strategy and a custom QueryProvider
.
class OGCQueryProvider extends QueryProvider {
// 2 scale breaks define 3 scale levels (scale ranges)
getQueryLevelScales(layer: FeatureLayer, map: Map) {
return [
1.0 / 20000000,
1.0 / 500000
];
};
// Provides query objects for 3 scale levels: no data requested for level 0, request with a filter object for level 1, no restriction for most detailed level
getQueryForLevel(level: number) {
if (level == 0) {
return QueryProvider.QUERY_NONE; // no data requested
} else if (level === 1) {
return {
filter: eq(property("fclass"), literal("A")) // OGC filter
}
} else {
return null; // no restriction
}
}
}
const wfsLayer = new FeatureLayer(wfsModel, {
loadingStrategy: new LoadEverything({queryProvider: new OGCQueryProvider()})
});
Use case 3: big static data
Load all vector data for the visible view extent with the LoadSpatially
strategy, using the default QueryProvider
instance implicitly.
const wfsLayer = new FeatureLayer(wfsModel, {
loadingStrategy: new LoadSpatially()
});
Use case 4: big static data depending on scale
You can load all vector data in one go whenever scale breaks are crossed using the LoadSpatially
strategy with a custom QueryProvider
.
const wfsLayer = new FeatureLayer(wfsModel, {
loadingStrategy: new LoadSpatially({queryProvider: new CustomQueryProvider()})
});
Use case 5: big dynamic data depending on scale
You can load all vector data in one go whenever scale breaks are crossed using the LoadSpatially
strategy with a custom QueryProvider
.
The LoadSpatially
instance defines a merging strategy for dynamically modified data based on a custom property.
const loadingStrategy = new LoadSpatially({queryProvider: new CustomQueryProvider()});
// will update a feature if the revision has changed
loadingStrategy.shouldUpdate = function(existingFeature, feature) {
const {revision} = feature.properties;
return revision && revision > existingFeature.properties.revision;
}
const wfsLayer = new FeatureLayer(wfsModel, { loadingStrategy });
You can consider clustering features if you still have a need for limiting the visual clutter of large amounts of data on your map.
Control data updates:
The data can be updated on request:
// no need to invalidate feature layer's painter, as updated features will be repainted automatically
wfsLayer.loadingStrategy.queryProvider.invalidate();
You can also set on a loading strategy the refresh
property that specifies a time-based refresh mode.
The example below shows how to automatically re-load data every 10
minutes.
const interval = 1000 * 60 * 10; // refresh every 10 minutes
const wfsLayer = new FeatureLayer(wfsModel, {
loadingStrategy: new LoadEverything({refresh: interval})
});
// you can change the refresh property any time
wfsLayer.loadingStrategy.refresh = null; // no automatic refreshes