All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentrafficsim.kpi.sampling.Query Maven / Gradle / Ivy

package org.opentrafficsim.kpi.sampling;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import org.djunits.unit.LengthUnit;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Frequency;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Time;
import org.djutils.exceptions.Throw;
import org.djutils.immutablecollections.ImmutableIterator;
import org.opentrafficsim.base.Identifiable;
import org.opentrafficsim.kpi.interfaces.GtuData;
import org.opentrafficsim.kpi.interfaces.LaneData;
import org.opentrafficsim.kpi.interfaces.LinkData;
import org.opentrafficsim.kpi.sampling.meta.FilterDataSet;
import org.opentrafficsim.kpi.sampling.meta.FilterDataType;

/**
 * A query defines which subset of trajectory information should be included. This is in terms of space-time regions, and in
 * terms of filter data of trajectories, e.g. only include trajectories of trucks.
 * 

* Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License. *

* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param gtu data type * @param lane data type */ public final class Query implements Identifiable { /** unique id. */ private final String id; /** Sampling. */ private final Sampler sampler; /** Description. */ private final String description; /** Filter data set. */ private final FilterDataSet filterDataSet; /** Update frequency. */ private final Frequency updateFrequency; /** Interval to gather statistics over. */ private final Duration interval; /** List of space-time regions of this query. */ private final List> spaceTimeRegions = new ArrayList<>(); /** * Constructor. * @param sampler Sampler<G, L>; sampler * @param id String; id * @param description String; description * @param filterDataSet FilterDataSet; filter data * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String id, final String description, final FilterDataSet filterDataSet) { this(sampler, description, filterDataSet, null, null); } /** * Constructor with time interval. * @param sampler Sampler<G, L>; sampler * @param id String; id * @param description String; description * @param filterDataSet FilterDataSet; filter data * @param interval Duration; interval to gather statistics over * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String id, final String description, final FilterDataSet filterDataSet, final Duration interval) { this(sampler, id, description, filterDataSet, null, interval); } /** * Constructor with update frequency. * @param sampler Sampler<G, L>; sampler * @param id String; id * @param description String; description * @param filterDataSet FilterDataSet; filter data * @param updateFrequency Frequency; update frequency * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String id, final String description, final FilterDataSet filterDataSet, final Frequency updateFrequency) { this(sampler, id, description, filterDataSet, updateFrequency, null); } /** * Constructor with time interval and update frequency. * @param sampler Sampler<G, L>; sampler * @param id String; id * @param description String; description * @param filterDataSet FilterDataSet; filter data * @param updateFrequency Frequency; update frequency * @param interval Duration; interval to gather statistics over * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String id, final String description, final FilterDataSet filterDataSet, final Frequency updateFrequency, final Duration interval) { Throw.whenNull(sampler, "Sampling may not be null."); Throw.whenNull(description, "Description may not be null."); Throw.whenNull(filterDataSet, "Meta data may not be null."); this.sampler = sampler; this.filterDataSet = new FilterDataSet(filterDataSet); this.id = id == null ? UUID.randomUUID().toString() : id; this.description = description; this.updateFrequency = updateFrequency; this.interval = interval; } /** * Constructor without id. * @param sampler Sampler<G, L>; sampler * @param description String; description * @param filterDataSet FilterDataSet; filter data * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String description, final FilterDataSet filterDataSet) { this(sampler, null, description, filterDataSet, null, null); } /** * Constructor without id, with time interval. * @param sampler Sampler<G, L>; sampler * @param description String; description * @param filterDataSet filterDataSet; filter data * @param interval Duration; interval to gather statistics over * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String description, final FilterDataSet filterDataSet, final Duration interval) { this(sampler, null, description, filterDataSet, null, interval); } /** * Constructor without id, with time update frequency. * @param sampler Sampler<G, L>; sampler * @param description String; description * @param filterDataSet filterDataSet; filter data * @param updateFrequency Frequency; update frequency * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String description, final FilterDataSet filterDataSet, final Frequency updateFrequency) { this(sampler, null, description, filterDataSet, updateFrequency, null); } /** * Constructor without id, with time interval and update frequency. * @param sampler Sampler<G, L>; sampler * @param description String; description * @param filterDataSet filterDataSet; filter data * @param updateFrequency Frequency; update frequency * @param interval Duration; interval to gather statistics over * @throws NullPointerException if sampling, description or filterDataSet is null */ public Query(final Sampler sampler, final String description, final FilterDataSet filterDataSet, final Frequency updateFrequency, final Duration interval) { this(sampler, null, description, filterDataSet, updateFrequency, interval); } /** * Returns the unique id for the query. * @return String; the unique id for the query */ @Override public String getId() { return this.id.toString(); } /** * Returns the description. * @return String; description */ public String getDescription() { return this.description; } /** * Returns the update frequency. * @return Frequency; updateFrequency. */ public Frequency getUpdateFrequency() { return this.updateFrequency; } /** * Returns the time interval. * @return Duration; interval. */ public Duration getInterval() { return this.interval; } /** * Returns the number of filter datas. * @return int; number of filter data entries */ public int filterSize() { return this.filterDataSet.size(); } /** * Returns an iterator over the filter datas and the related data sets. * @return Iterator<Entry<FilterDataType<?>, Set<?>>>; iterator over filter data entries, removal is * not allowed */ public Iterator, Set>> getFilterDataSetIterator() { return this.filterDataSet.getFilterDataSetIterator(); } /** * Defines a region in space and time for which this query is valid. All lanes in the link are included. * @param link LinkData<? extends L>; link * @param startPosition Length; start position * @param endPosition Length; end position * @param startTime Time; start time * @param endTime Time; end time */ public void addSpaceTimeRegionLink(final LinkData link, final Length startPosition, final Length endPosition, final Time startTime, final Time endTime) { Throw.whenNull(link, "Link may not be null."); Throw.whenNull(startPosition, "Start position may not be null."); Throw.whenNull(endPosition, "End position may not be null."); Throw.whenNull(startTime, "Start time may not be null."); Throw.whenNull(endTime, "End time may not be null."); Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class, "End position should be greater than start position."); Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time."); for (L lane : link.getLaneDatas()) { Length x0 = new Length(lane.getLength().si * startPosition.si / link.getLength().si, LengthUnit.SI); Length x1 = new Length(lane.getLength().si * endPosition.si / link.getLength().si, LengthUnit.SI); addSpaceTimeRegion(lane, x0, x1, startTime, endTime); } } /** * Defines a region in space and time for which this query is valid. * @param lane L; lane * @param startPosition Length; start position * @param endPosition Length; end position * @param startTime Time; start time * @param endTime Time; end time */ public void addSpaceTimeRegion(final L lane, final Length startPosition, final Length endPosition, final Time startTime, final Time endTime) { Throw.whenNull(lane, "Lane direction may not be null."); Throw.whenNull(startPosition, "Start position may not be null."); Throw.whenNull(endPosition, "End position may not be null."); Throw.whenNull(startTime, "Start time may not be null."); Throw.whenNull(endTime, "End time may not be null."); Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class, "End position should be greater than start position."); Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time."); SpaceTimeRegion spaceTimeRegion = new SpaceTimeRegion<>(lane, startPosition, endPosition, startTime, endTime); this.sampler.registerSpaceTimeRegion(spaceTimeRegion); this.spaceTimeRegions.add(spaceTimeRegion); } /** * Returns the number of space-time regions. * @return int; number of space-time regions */ public int spaceTimeRegionSize() { return this.spaceTimeRegions.size(); } /** * Returns an iterator over the space-time regions. * @return Iterator<SpaceTimeRegion<? extends L>>; iterator over space-time regions, removal is not allowed */ public Iterator> getSpaceTimeIterator() { return new ImmutableIterator<>(this.spaceTimeRegions.iterator()); } /** * Returns a list of TrajectoryGroups in accordance with the query. Each {@code TrajectoryGroup} contains {@code Trajectory} * objects pertaining to a {@code SpaceTimeRegion} from the query. A {@code Trajectory} is only included if all the filter * data of this query accepts the trajectory. This method uses {@code Time.ZERO} as start. * @param endTime Time; end time of interval to get trajectory groups for * @param underlying class of filter data type and its value * @return List<TrajectoryGroup<G>>; list of trajectory groups in accordance with the query */ public List> getTrajectoryGroups(final Time endTime) { return getTrajectoryGroups(Time.ZERO, endTime); } /** * Returns a list of TrajectoryGroups in accordance with the query. Each {@code TrajectoryGroup} contains {@code Trajectory} * objects pertaining to a {@code SpaceTimeRegion} from the query. A {@code Trajectory} is only included if all the filter * data of this query accepts the trajectory. * @param startTime Time; start time of interval to get trajectory groups for * @param endTime Time; start time of interval to get trajectory groups for * @param underlying class of filter data type and its value * @return List<TrajectoryGroup<G>>; list of trajectory groups in accordance with the query */ @SuppressWarnings("unchecked") public List> getTrajectoryGroups(final Time startTime, final Time endTime) { Throw.whenNull(startTime, "Start t may not be null."); Throw.whenNull(endTime, "End t may not be null."); // Step 1) gather trajectories per GTU, truncated over space and time Map trajectoryAcceptLists = new LinkedHashMap<>(); List> trajectoryGroupList = new ArrayList<>(); for (SpaceTimeRegion spaceTimeRegion : this.spaceTimeRegions) { Time start = startTime.gt(spaceTimeRegion.getStartTime()) ? startTime : spaceTimeRegion.getStartTime(); Time end = endTime.lt(spaceTimeRegion.getEndTime()) ? endTime : spaceTimeRegion.getEndTime(); TrajectoryGroup trajectoryGroup; if (this.sampler.getSamplerData().getTrajectoryGroup(spaceTimeRegion.getLane()) == null) { trajectoryGroup = new TrajectoryGroup<>(start, spaceTimeRegion.getLane()); } else { trajectoryGroup = this.sampler.getSamplerData().getTrajectoryGroup(spaceTimeRegion.getLane()) .getTrajectoryGroup(spaceTimeRegion.getStartPosition(), spaceTimeRegion.getEndPosition(), start, end); } for (Trajectory trajectory : trajectoryGroup.getTrajectories()) { if (!trajectoryAcceptLists.containsKey(trajectory.getGtuId())) { trajectoryAcceptLists.put(trajectory.getGtuId(), new TrajectoryAcceptList()); } trajectoryAcceptLists.get(trajectory.getGtuId()).addTrajectory(trajectory, trajectoryGroup); } trajectoryGroupList.add(trajectoryGroup); } // Step 2) accept per GTU Iterator iterator = trajectoryAcceptLists.keySet().iterator(); while (iterator.hasNext()) { String gtuId = iterator.next(); TrajectoryAcceptList trajectoryAcceptListCombined = copyTrajectoryAcceptList(trajectoryAcceptLists.get(gtuId)); trajectoryAcceptListCombined.acceptAll(); // refuse only if any filter data type refuses for (FilterDataType filterDataType : this.filterDataSet.getMetaDataTypes()) { // create safe copy per filter data type, with defaults accepts = false TrajectoryAcceptList trajectoryAcceptListCopy = copyTrajectoryAcceptList(trajectoryAcceptLists.get(gtuId)); // request filter data type to accept or reject ((FilterDataType) filterDataType).accept(trajectoryAcceptListCopy, (Set) new LinkedHashSet<>(this.filterDataSet.get(filterDataType))); // combine acceptance/rejection of filter data types so far for (int i = 0; i < trajectoryAcceptListCopy.size(); i++) { Trajectory trajectory = trajectoryAcceptListCopy.getTrajectory(i); trajectoryAcceptListCombined.acceptTrajectory(trajectory, trajectoryAcceptListCombined.isAccepted(trajectory) && trajectoryAcceptListCopy.isAccepted(trajectory)); } } } // Step 3) filter trajectories List> out = new ArrayList<>(); for (TrajectoryGroup full : trajectoryGroupList) { TrajectoryGroup filtered = new TrajectoryGroup<>(full.getStartTime(), full.getLane()); for (Trajectory trajectory : full.getTrajectories()) { String gtuId = trajectory.getGtuId(); if (trajectoryAcceptLists.get(gtuId).isAccepted(trajectory)) { filtered.addTrajectory(trajectory); } } out.add(filtered); } return out; } /** * Returns a copy of the trajectory accept list, with all assumed not accepted. * @param trajectoryAcceptList TrajectoryAcceptList; trajectory accept list to copy. * @return TrajectoryAcceptList; copy of the trajectory accept list, with all assumed not accepted. */ private TrajectoryAcceptList copyTrajectoryAcceptList(final TrajectoryAcceptList trajectoryAcceptList) { TrajectoryAcceptList trajectoryAcceptListCopy = new TrajectoryAcceptList(); for (int i = 0; i < trajectoryAcceptList.size(); i++) { trajectoryAcceptListCopy.addTrajectory(trajectoryAcceptList.getTrajectory(i), trajectoryAcceptList.getTrajectoryGroup(i)); } return trajectoryAcceptListCopy; } /** * Returns the sampler. * @return Sampler<G, L>; sampler. */ public Sampler getSampler() { return this.sampler; } /** {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.description == null) ? 0 : this.description.hashCode()); result = prime * result + ((this.interval == null) ? 0 : this.interval.hashCode()); result = prime * result + ((this.filterDataSet == null) ? 0 : this.filterDataSet.hashCode()); result = prime * result + ((this.sampler == null) ? 0 : this.sampler.hashCode()); result = prime * result + ((this.spaceTimeRegions == null) ? 0 : this.spaceTimeRegions.hashCode()); result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); result = prime * result + ((this.updateFrequency == null) ? 0 : this.updateFrequency.hashCode()); return result; } /** {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Query other = (Query) obj; if (this.description == null) { if (other.description != null) { return false; } } else if (!this.description.equals(other.description)) { return false; } if (this.interval == null) { if (other.interval != null) { return false; } } else if (!this.interval.equals(other.interval)) { return false; } if (this.filterDataSet == null) { if (other.filterDataSet != null) { return false; } } else if (!this.filterDataSet.equals(other.filterDataSet)) { return false; } if (this.sampler == null) { if (other.sampler != null) { return false; } } else if (!this.sampler.equals(other.sampler)) { return false; } if (this.spaceTimeRegions == null) { if (other.spaceTimeRegions != null) { return false; } } else if (!this.spaceTimeRegions.equals(other.spaceTimeRegions)) { return false; } if (this.id == null) { if (other.id != null) { return false; } } else if (!this.id.equals(other.id)) { return false; } if (this.updateFrequency == null) { if (other.updateFrequency != null) { return false; } } else if (!this.updateFrequency.equals(other.updateFrequency)) { return false; } return true; } /** {@inheritDoc} */ @Override public String toString() { return "Query (" + this.description + ")"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy