package org.opentrafficsim.draw.graphs; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.djunits.value.vdouble.scalar.Length; import org.djunits.value.vdouble.scalar.Speed; import org.djunits.value.vdouble.scalar.Time; import org.djutils.exceptions.Throw; import org.djutils.immutablecollections.Immutable; import org.djutils.immutablecollections.ImmutableArrayList; import org.djutils.immutablecollections.ImmutableList; import org.opentrafficsim.base.WeightedMeanAndSum; import org.opentrafficsim.kpi.sampling.KpiLaneDirection; import org.opentrafficsim.kpi.sampling.Sampler; import org.opentrafficsim.kpi.sampling.SpaceTimeRegion; /** * A {@code GraphPath} defines the spatial dimension of graphs. It has a number of sections, each of which may have one or more * source objects depending on the number of series. For example, a 3-lane road may result in a few sections each having 3 * series. Graphs can aggregate the series, or show multiple series. *

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

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 okt. 2018
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param underlying type of path sections */ public class GraphPath extends AbstractGraphSpace { /** Sections. */ private final List> sections; /** Start distance per section. */ private final List startDistances = new ArrayList<>(); /** Total path length. */ private final Length totalLength; /** Mean speed limit over the entire path. */ private final Speed speedLimit; /** * Constructor for a one-series path. * @param name String; name * @param sections List<Section<S>>; sections */ public GraphPath(final String name, final List> sections) { this(new ArrayList() { /** */ private static final long serialVersionUID = 20181020L; { add(name); } }, sections); } /** * Constructor. * @param seriesNames List<String>; names of series * @param sections List<Section<S>>; sections */ public GraphPath(final List seriesNames, final List> sections) { super(seriesNames); this.sections = sections; Length cumulativeLength = Length.ZERO; for (Section section : sections) { this.startDistances.add(cumulativeLength); cumulativeLength = cumulativeLength.plus(section.getLength()); } this.totalLength = cumulativeLength; WeightedMeanAndSum mean = new WeightedMeanAndSum<>(); for (Section section : sections) { mean.add(section.getSpeedLimit().si, section.getLength().si); } this.speedLimit = Speed.instantiateSI(mean.getMean()); } /** * Returns the start distance of the section. * @param section Section<S>; Section<S> section * @return Length; start distance of the section */ public final Length getStartDistance(final Section section) { int index = this.sections.indexOf(section); Throw.when(index == -1, IllegalArgumentException.class, "Section is not part of the path."); return this.startDistances.get(index); } /** * Returns the total path length. * @return Length; total path length */ public final Length getTotalLength() { return this.totalLength; } /** * Returns the mean speed over the entire section. * @return Speed; mean speed over the entire section */ public final Speed getSpeedLimit() { return this.speedLimit; } /** * Returns a section. * @param index int; index of section * @return Section<S>; section */ public Section get(final int index) { return this.sections.get(index); } /** {@inheritDoc} */ @Override public Iterator iterator() { return new Iterator() { /** Section iterator. */ private Iterator> sectionIterator = getSections().iterator(); /** Source object iterator per section. */ private Iterator sourceIterator = this.sectionIterator.hasNext() ? this.sectionIterator.next().iterator() : null; /** {@inheritDoc} */ @Override public boolean hasNext() { if (this.sourceIterator != null && this.sourceIterator.hasNext()) { return true; } while (this.sectionIterator.hasNext()) { Iterator it = this.sectionIterator.next().iterator(); if (it.hasNext()) { this.sourceIterator = it; return true; } } this.sourceIterator = null; return false; } /** {@inheritDoc} */ @Override public S next() { Throw.when(!hasNext(), NoSuchElementException.class, "No more element left."); return this.sourceIterator.next(); } }; } /** {@inheritDoc} */ @Override public Iterator iterator(final int series) { List list = new ArrayList<>(); for (Section section : this.sections) { list.add(section.getSource(series)); } return new ImmutableArrayList<>(list, Immutable.WRAP).iterator(); } /** * Returns an immutable list of the sections. * @return ImmutableList<Section<S>>; sections */ public ImmutableList> getSections() { return new ImmutableArrayList<>(this.sections, Immutable.WRAP); } /** {@inheritDoc} */ @Override public String toString() { return "GraphPath [sections=" + this.sections + ", startDistances=" + this.startDistances + ", totalLength=" + this.totalLength + ", speedLimit=" + this.speedLimit + "]"; } /** * Start recording along path. * @param sampler Sampler<?>; sampler * @param path GraphPath<KpiLaneDirection>; path */ public static void initRecording(final Sampler sampler, final GraphPath path) { for (Section section : path.getSections()) { for (KpiLaneDirection kpiLaneDirection : section) { sampler.registerSpaceTimeRegion(new SpaceTimeRegion(kpiLaneDirection, Length.ZERO, kpiLaneDirection.getLaneData().getLength(), Time.ZERO, Time.instantiateSI(Double.MAX_VALUE))); } } } /** * Interface for sections. *

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

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 20 okt. 2018
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param underlying type */ public interface Section extends Iterable { /** * Returns the section length. * @return Length; section length */ Length getLength(); /** * Returns the speed limit on the section. * @return Speed; speed limit on the section */ Speed getSpeedLimit(); /** * Returns the source object. * @param series int; number * @return S; underlying object of the series */ S getSource(int series); } }