This article is part of a series of tutorials that show you how to develop your first application:
Goal
In this tutorial, you learn how to use filtering to prevent the visualization of objects.
When you zoom out of the map, the view can clutter up with lots of overlapping icons. That clutter decreases visibility and situational awareness. You have some options to prevent icons from cluttering the view. This tutorial and the clustering tutorial discuss those options.
Starting point
We take the code written in the Drawing labels tutorial as our starting point, and expand from there.
Step 1 - Create the layer loading strategy
We want to restrict the amount of visualized data when the user sees the map from a larger distance, after zooming out for instance. The WFS that serves the vector data, supports OGC Filter parameters, so we use OGC Filters to define the restriction. By sending a query filter parameter to a WFS service, we can request filtered vector data: a subset of an otherwise much larger data set.
For more information, see Refining queries to WFS servers with OGC filters.
Before we create a filter query though, we need to discuss the concept of loading strategies. When a feature layer is added
to a map, LuciadRIA triggers a query to load vector data, through the model Store
set on the layer.
That request adheres to a particular loading strategy.
Each feature layer has an instance of LoadStrategy
, which is either set explicitly or implicitly.
So far, we used a default loading strategy, but now we’re going to create a new one. We configure it to pass a specific filter object with the query request to the underlying WFS store.
You can choose between two options when you create a loading strategy object:
-
LoadSpatially
- this strategy retrieves only the data within the visible extent of the map from the model. -
LoadEverything
- this strategy loads all data for the whole map.
You can use |
Let’s explicitly set an instance of the LoadSpatially
loading strategy in the city layer.
vectorData.ts
import {LoadSpatially} from "@luciad/ria/view/feature/loadingstrategy/LoadSpatially.js";
// ...
function createCityLoadingStrategy() {
return new LoadSpatially();
}
export function createCitiesLayer(map: Map) {
// ...
const featureLayer = new FeatureLayer(featureModel, {
label: "US Cities",
layerType: LayerType.STATIC,
painter: addSelection(new CityPainter()),
selectable: true,
loadingStrategy: createCityLoadingStrategy()
});
}
If a feature layer uses a model containing a |
For more information about loading strategies, see Dealing with large feature data sets. |
Step 2 - Configure a loading strategy with QueryProvider
You can configure the loading strategy with a QueryProvider
object that must implement these callback functions:
-
getQueryLevelScales()
- Must return an array of map scale thresholds. If you don’t define thresholds, the same query object is used at all map scales. -
getQueryForLevel(scaleLevel)
- Returns a query object to use on the backingStore
for the given scale level.
Let’s create a QueryProvider
instance that defines two scale levels with a distinct query object for each.
The getQueryLevelScales
function defines two scale levels:
-
Level 0: when a map scale is below the threshold value (1/50000000), the query request isn’t executed at all. The value
QueryProvider.QUERY_NONE
means that there’s no query. -
Level 1: when the map scale is above the threshold, the query request to WFS doesn’t specify any filters.
import {LoadSpatially} from "@luciad/ria/view/feature/loadingstrategy/LoadSpatially.js";
import {QueryProvider} from "@luciad/ria/view/feature/QueryProvider.js";
class CityQueryProvider extends QueryProvider {
getQueryLevelScales() {
return [1 / 50000000];
}
getQueryForLevel(level: number) {
return level === 0 ? QueryProvider.QUERY_NONE : null;
}
}
function createCityLoadingStrategy() {
// use dedicated
return new LoadSpatially({queryProvider: new CityQueryProvider()});
}
Now, when you zoom out, yo see no cities displayed. When you zoom in, they re-appear.
Step 3 - Create a query filter object
This isn’t what we wanted to achieve, so let’s continue modifying the CityQueryProvider
to request a subset
of city features based on an OGC Query Filter parameter.
Instead of using QueryProvider.QUERY_NONE
, we want to create a filter object that a WFS service can process.
We can do use the utility functions from the FilterFactory
for that.
Our aim is to limit our request to the big cities only when we zoom out to scale level 0.
The snippet below shows you how to create a filter object for features that have a "TOT_POP" property value greater than or equal to 1000000.
import {gte, literal, property} from "@luciad/ria/ogc/filter/FilterFactory.js";
const BIG_CITY_FILTER = gte(property("TOT_POP"), literal(1000000));
Let’s use the big city filter in the QueryProvider
for a scale level of 0.
const BIG_CITY_FILTER = gte(property("TOT_POP"), literal(1000000));
class CityQueryProvider extends QueryProvider {
getQueryLevelScales() {
return [1 / 50000000];
}
getQueryForLevel(level: number) {
return level === 0 ? {filter: BIG_CITY_FILTER} : null;
}
}
Final result
The map now behaves in such a way that when we zoom out, we see just the big cities. After zooming in, we see all the cities.
Full code
vectorData
import {FeatureLayer} from "@luciad/ria/view/feature/FeatureLayer.js";
import {FeatureModel} from "@luciad/ria/model/feature/FeatureModel.js";
import {LayerType} from "@luciad/ria/view/LayerType.js";
import {Map} from "@luciad/ria/view/Map.js";
import {WFSFeatureStore} from "@luciad/ria/model/store/WFSFeatureStore.js";
import {CityPainter} from "./CityPainter.js";
import {addSelection} from "@luciad/ria/view/feature/FeaturePainterUtil.js";
import {LoadSpatially} from "@luciad/ria/view/feature/loadingstrategy/LoadSpatially.js";
import {QueryProvider} from "@luciad/ria/view/feature/QueryProvider.js";
import {gte, literal, property} from "@luciad/ria/ogc/filter/FilterFactory.js";
const BIG_CITY_FILTER = gte(property("TOT_POP"), literal(1000000));
class CityQueryProvider extends QueryProvider {
getQueryLevelScales() {
return [1 / 50000000];
}
getQueryForLevel(level: number) {
return level === 0 ? {filter: BIG_CITY_FILTER} : null;
}
}
function createCityLoadingStrategy() {
return new LoadSpatially({queryProvider: new CityQueryProvider()});
}
export function createCitiesLayer(map: Map) {
const url = "https://sampleservices.luciad.com/wfs";
const featureTypeName = "cities";
// Create a WFS store
return WFSFeatureStore.createFromURL(url, featureTypeName).then(
(wfsSore: WFSFeatureStore) => {
// Create a model based on the created store
const featureModel = new FeatureModel(wfsSore);
// Create a feature layer
const featureLayer = new FeatureLayer(featureModel, {
label: "US Cities",
layerType: LayerType.STATIC,
painter: addSelection(new CityPainter()),
selectable: true,
loadingStrategy: createCityLoadingStrategy()
});
// Add a layer to the map
map.layerTree.addChild(featureLayer);
return featureLayer;
});
}