Filtering |
Modern datasets can contain thousands, if not hundreds of thousands of unique points of interest. While computer hardware has scaled to meet increasing demand, the process of combing through data to separate important findings from background noise is still vital. Tracking Library provides a mechanism to automatically filter entity data as it is received and select only those entities that match the criteria specified.
Filtering can be thought of as passive analysis, and can be as simple as checking a single value, or as complicated as an AccessQuery which might take into account communications quality, terrain, navigation data, or a variety of other calculations.
Note |
---|
The functionality described in this topic requires a license for the Tracking Library. |
A filter is an object that derives from EntityFilter<TEntity>. There are two provided implementations:
AccessEntityFilter<TEntity> - filters based on the results of an AccessQuery.
DelegateEntityFilter<TEntity> - filters based on a provided , delegate, which is useful for very simple filters on specific criteria, such as Force == Friendly.
While a filter provides the criteria for selection, an EntityFilterChain<TEntity> is used to actually process groups of filters.
The following code sample creates an EntityFilterChain<TEntity> containing two DelegateEntityFilter<TEntity> objects that separate entities into friendly and hostile categories based on the value of the Affiliation property. The EntityFilterChain<TEntity> constructor takes two parameters: the EntitySet<TEntity> to filter and the MatchingStrategy, which indicates whether an entity should be placed in multiple filters, or only in the first filter it matches.
DelegateEntityFilter<ExampleEntity> friendlyFilter = new DelegateEntityFilter<>(inputEntities.getContext(), IsMatchCallback.of((transaction, entity) -> entity.getAffiliation().getValue(transaction) == Force.FRIENDLY)); DelegateEntityFilter<ExampleEntity> hostileFilter = new DelegateEntityFilter<>(inputEntities.getContext(), IsMatchCallback.of((transaction, entity) -> entity.getAffiliation().getValue(transaction) == Force.HOSTILE)); EntityFilterChain<ExampleEntity> filterChain = new EntityFilterChain<>(inputEntities, MatchingStrategy.FIRST); filterChain.getFilters().add(friendlyFilter); filterChain.getFilters().add(hostileFilter); filterChain.applyChanges(); EntitySet<ExampleEntity> friendlyEntities = friendlyFilter.getMatchingEntities(); EntitySet<ExampleEntity> hostileEntities = hostileFilter.getMatchingEntities(); EntitySet<ExampleEntity> unknownEntities = filterChain.getHomelessEntities();
After configuring the EntityFilterChain<TEntity>, the three entity sets will remain empty until the redistributeEntities method is called to process the filters, as shown in the code sample below:
context.doTransactionally(Action1.of(filterChain::redistributeEntities));
Once this method returns, each of the entity sets will contain the entities that correspond to the value of their Affiliation property, and all entities that match neither filter are placed in the HomelessEntities (get) entity set. The entity set used as the input is never modified by the filter chain, and is expected to be managed by an outside source. Furthermore, the Changed (add / remove) event is raised whenever entities are added to or removed from an entity set managed by a filter.
As mentioned above, MatchingStrategy indicates if an EntityFilterChain<TEntity> will place each entity in only the first EntityFilter<TEntity> it matches or all matching filters. MatchingStrategy.FIRST is recommended unless there is a specific reason for an entity to be in multiple filters. This will not only improve performance, but is ideal when using a filter chain for visualization, since the resulting entity sets will not overlap.
Filters can make use of Evaluator Parameterization to filter entities based on complex analysis. The following code sample shows the use of an AccessQuery to create a filter that only contains entities with line of sight to the surface of the Earth at zero latitude and longitude.
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth(); EntityParameter<ExampleEntity> entityParameter = new EntityParameter<ExampleEntity>(); TransactionParameter transactionParameter = new TransactionParameter(); Platform transmitter = new Platform(); transmitter.setLocationPoint(new EntityPoint<>(new TypeLiteral<ExampleEntity>() {}, entityParameter, transactionParameter)); transmitter.setOrientationAxes(new EntityAxes<>(new TypeLiteral<ExampleEntity>() {}, entityParameter, transactionParameter)); Platform receiver = new Platform(); receiver.setLocationPoint(new PointCartographic(earth, new Cartographic(0.0, 0.0, 0.0))); receiver.setOrientationAxes(earth.getFixedFrame().getAxes()); LinkInstantaneous link = new LinkInstantaneous(transmitter, receiver); CentralBodyObstructionConstraint constraint = new CentralBodyObstructionConstraint(link, earth); EvaluatorGroup group = new EvaluatorGroup(); AccessEntityFilter<ExampleEntity> filterByAccess = new AccessEntityFilter<>(context, entityParameter, transactionParameter, constraint, group);
The above is a very simple AccessQuery, but the same concept can be extended to use any of the analysis capabilities of DME Component Libraries.
Since filtering makes use of the transaction system, the filtering can be done on a separate thread, taking advantage of modern multi-core processors. One approach to achieve this is to call redistributeEntities repeatedly in the background at a predetermined interval. RecurringTaskScheduler facilitates running a requested task as fairly as possible across all available threads. Each task has an associated frequency and all attempts are made to run the task at the desired interval.
The following code sample runs redistributeEntities in a background thread every tenth of a second:
RecurringTaskScheduler recurringTasks = new RecurringTaskScheduler(); recurringTasks.addTask(Duration.fromSeconds(0.1), RecurringTask.of(filterChain::redistributeEntities)); recurringTasks.start();
The same approach can also be achieved using other task scheduling systems, either built-in to Java, or available as third-party libraries. RecurringTaskScheduler does not have any special capabilities specific to DME Component Libraries.