diff --git a/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/DemandParser.java b/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/DemandParser.java index 9f4fc30cd..61ea37958 100644 --- a/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/DemandParser.java +++ b/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/DemandParser.java @@ -1,20 +1,32 @@ package org.opentrafficsim.road.network.factory.xml.parser; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; 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.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; import org.djunits.unit.SpeedUnit; import org.djunits.unit.Unit; +import org.djunits.value.vdouble.scalar.Acceleration; 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.LinearDensity; import org.djunits.value.vdouble.scalar.Speed; import org.djunits.value.vdouble.scalar.base.DoubleScalarRel; +import org.djutils.data.Column; +import org.djutils.data.ListTable; +import org.djutils.data.Table; +import org.djutils.data.csv.CsvData; +import org.djutils.data.serialization.TextSerializationException; import org.djutils.eval.Eval; import org.djutils.exceptions.Throw; import org.jgrapht.alg.interfaces.AStarAdmissibleHeuristic; @@ -37,18 +49,15 @@ import org.opentrafficsim.core.object.DetectorType; import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar; import org.opentrafficsim.road.gtu.generator.GeneratorPositions; +import org.opentrafficsim.road.gtu.generator.Injections; import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator; import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator.RoomChecker; +import org.opentrafficsim.road.gtu.generator.characteristics.DefaultLaneBasedGtuCharacteristicsGeneratorOd; +import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator; import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuTemplate; import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuTemplateDistribution; import org.opentrafficsim.road.gtu.generator.headway.HeadwayGenerator; -import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory; -import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModelFactory; -import org.opentrafficsim.road.gtu.lane.tactical.following.IdmPlus; -import org.opentrafficsim.road.gtu.lane.tactical.following.IdmPlusFactory; -import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLmrsPerceptionFactory; -import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs; -import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory; +import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory; import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory; import org.opentrafficsim.road.network.RoadNetwork; import org.opentrafficsim.road.network.factory.xml.XmlParserException; @@ -63,12 +72,15 @@ import org.opentrafficsim.xml.generated.Demand; import org.opentrafficsim.xml.generated.GtuTemplate; import org.opentrafficsim.xml.generated.GtuTemplateMix; +import org.opentrafficsim.xml.generated.InjectionGenerator; +import org.opentrafficsim.xml.generated.InjectionGenerator.Arrivals.Arrival; import org.opentrafficsim.xml.generated.RouteMix; import org.opentrafficsim.xml.generated.ShortestRoute; import org.opentrafficsim.xml.generated.ShortestRoute.Cost; import org.opentrafficsim.xml.generated.ShortestRouteMix; import org.opentrafficsim.xml.generated.Sink; +import nl.tudelft.simulation.dsol.SimRuntimeException; import nl.tudelft.simulation.dsol.experiment.StreamInformation; import nl.tudelft.simulation.jstats.streams.StreamInterface; @@ -197,6 +209,7 @@ else if (cost.getDistanceAndFreeFlowTime() != null) AStarAdmissibleHeuristic aStarHeuristicTime = getTimeAStarHeuristic(maxSpeed); linkWeight = new LinkWeight() { + /** {@inheritDoc} */ @Override public double getWeight(final Link link) @@ -235,7 +248,9 @@ public double getCostEstimate(final Node sourceVertex, final Node targetVertex) Route shortestRoute = otsNetwork.getShortestRouteBetween(gtuType, nodeFrom, nodeTo, nodesVia, linkWeight); Throw.when(shortestRoute == null, NetworkException.class, "Cannot find shortest route from %s to %s", nodeFrom.getId(), nodeTo.getId()); - for (Node node : shortestRoute.getNodes()) + for ( + + Node node : shortestRoute.getNodes()) { route.addNode(node); } @@ -374,6 +389,7 @@ static Map>> parseShortestRouteMix(final * @param routeMixMap map with route mix entries * @param shortestRouteMixMap map with shortest route mix entries * @param streamInformation map with stream information + * @param idGenerator id generator * @param eval expression evaluator. * @return list of created GTU generators * @throws XmlParserException when the objects cannot be inserted into the network due to inconsistencies @@ -382,7 +398,7 @@ public static List parseGenerators(final RoadNetwork otsN final Demand demand, final Map gtuTemplates, final Map>> routeMixMap, final Map>> shortestRouteMixMap, final StreamInformation streamInformation, - final Eval eval) throws XmlParserException + final IdGenerator idGenerator, final Eval eval) throws XmlParserException { OtsSimulatorInterface simulator = otsNetwork.getSimulator(); List generators = new ArrayList<>(); @@ -395,166 +411,427 @@ public static List parseGenerators(final RoadNetwork otsN String linkId = generatorTag.getLink().get(eval); String laneId = generatorTag.getLane().get(eval); - Generator routeGenerator; - if (generatorTag.getRoute() != null) + StringType routeType = generatorTag.getRoute(); + StringType routeMixType = generatorTag.getRouteMix(); + StringType shortestRouteType = generatorTag.getShortestRoute(); + StringType shortestRouteMixType = generatorTag.getShortestRouteMix(); + String errorPre = "Generator for Lane " + linkId + "." + laneId + ": "; + Generator routeGenerator = + getRouteGenerator(routeType, routeMixType, shortestRouteType, shortestRouteMixType, otsNetwork, demand, + routeMixMap, shortestRouteMixMap, streamInformation, errorPre, eval); + + StringType gtuTemplateType = generatorTag.getGtuTemplate(); + StringType gtuTemplateMixType = generatorTag.getGtuTemplateMix(); + Distribution gtuTypeDistribution = getTemplateDistribution(gtuTemplateType, + gtuTemplateMixType, routeGenerator, definitions, demand, gtuTemplates, streamInformation, stream, eval); + + RoomChecker roomChecker = ParseUtil.parseRoomChecker(generatorTag.getRoomChecker(), eval); + + Generator headwayGenerator = new HeadwayGenerator(generatorTag.getFrequency().get(eval), stream); + + CrossSectionLink link = (CrossSectionLink) otsNetwork.getLink(linkId); + Lane lane = (Lane) link.getCrossSectionElement(laneId); + Length position = ParseUtil.parseLengthBeginEnd(generatorTag.getPosition().get(eval), lane.getLength()); + Set initialLongitudinalPositions = new LinkedHashSet<>(); + initialLongitudinalPositions.add(new LanePosition(lane, position)); + + LaneBasedGtuTemplateDistribution characteristicsGenerator = + new LaneBasedGtuTemplateDistribution(gtuTypeDistribution); + generators.add(new LaneBasedGtuGenerator(lane.getFullId(), headwayGenerator, characteristicsGenerator, + GeneratorPositions.create(initialLongitudinalPositions, stream), otsNetwork, simulator, roomChecker, + idGenerator)); + } + } + catch (Exception exception) + { + throw new XmlParserException(exception); + } + return generators; + } + + /** + * Helper method to obtain route generator for Generator or InjectionGenerator. + * @param routeType route tag + * @param routeMixType route mix tag + * @param shortestRouteType shortest route tag + * @param shortestRouteMixType shortest route mix tag + * @param otsNetwork network + * @param demand demand tag + * @param routeMixMap route mix + * @param shortestRouteMixMap shortest route mix + * @param streamInformation stream info + * @param errorPre string to start error messages + * @param eval evaluator + * @return route generator + * @throws XmlParserException when a referred element does no exist + */ + private static Generator getRouteGenerator(final StringType routeType, final StringType routeMixType, + final StringType shortestRouteType, final StringType shortestRouteMixType, final RoadNetwork otsNetwork, + final Demand demand, final Map>> routeMixMap, + final Map>> shortestRouteMixMap, final StreamInformation streamInformation, + final String errorPre, final Eval eval) throws XmlParserException + { + Generator routeGenerator; + if (routeType != null) + { + String routeId = routeType.get(eval); + Route route = otsNetwork.getRoute(routeId); + Throw.when(route == null, XmlParserException.class, "%sRoute %s not found", errorPre, routeId); + routeGenerator = new FixedRouteGenerator(route); + } + + else if (routeMixType != null) + { + String routeMixId = routeMixType.get(eval); + List> routeMix = routeMixMap.get(routeMixId); + Throw.when(routeMix == null, XmlParserException.class, "RouteMix %s not found", errorPre, routeMixId); + RouteMix routeMixXml = null; + for (RouteMix mix : demand.getRouteMix()) + { + if (mix.getId().equals(routeMixId)) { - String routeId = generatorTag.getRoute().get(eval); - Route route = otsNetwork.getRoute(routeId); - Throw.when(route == null, XmlParserException.class, "Generator for Lane %s.%s: Route %s not found", linkId, - laneId, routeId); - routeGenerator = new FixedRouteGenerator(route); + routeMixXml = mix; } + } + Throw.when(routeMixXml == null, XmlParserException.class, "Route mix '%s' not defined.", routeMixId); + StreamInterface routeMixStream = ParseUtil.findStream(streamInformation, routeMixXml.getRandomStream(), eval); + try + { + routeGenerator = new ProbabilisticRouteGenerator(routeMix, routeMixStream); + } + catch (ProbabilityException exception) + { + throw new RuntimeException(errorPre + "Could not generate RouteMix " + routeMixId); + } + } - else if (generatorTag.getRouteMix() != null) + else if (shortestRouteType != null) + { + String shortestRouteId = shortestRouteType.get(eval); + Route shortestRoute = otsNetwork.getRoute(shortestRouteId); + Throw.when(shortestRoute == null, XmlParserException.class, "ShortestRoute %s not found", errorPre, + shortestRouteId); + routeGenerator = new FixedRouteGenerator(shortestRoute); + } + + else if (shortestRouteMixType != null) + { + String shortestRouteMixId = shortestRouteMixType.get(eval); + List> shortestRouteMix = shortestRouteMixMap.get(shortestRouteMixId); + Throw.when(shortestRouteMix == null, XmlParserException.class, "ShortestRouteMix %s not found", errorPre, + shortestRouteMixId); + ShortestRouteMix shortestRouteMixXml = null; + for (ShortestRouteMix mix : demand.getShortestRouteMix()) + { + if (mix.getId().equals(shortestRouteMixId)) { - String routeMixId = generatorTag.getRouteMix().get(eval); - List> routeMix = routeMixMap.get(routeMixId); - Throw.when(routeMix == null, XmlParserException.class, "Generator for Lane %s.%s: RouteMix %s not found", - linkId, laneId, routeMixId); - RouteMix routeMixXml = null; - for (RouteMix mix : demand.getRouteMix()) - { - if (mix.getId().equals(routeMixId)) - { - routeMixXml = mix; - } - } - Throw.when(routeMixXml == null, XmlParserException.class, "Route mix '%s' not defined.", routeMixId); - StreamInterface routeMixStream = - ParseUtil.findStream(streamInformation, routeMixXml.getRandomStream(), eval); - try - { - routeGenerator = new ProbabilisticRouteGenerator(routeMix, routeMixStream); - } - catch (ProbabilityException exception) - { - throw new RuntimeException( - "Generator for Lane " + linkId + "." + laneId + "Could not generate RouteMix " + routeMixId); - } + shortestRouteMixXml = mix; } + } + Throw.when(shortestRouteMixXml == null, XmlParserException.class, "Shortest route mix '%s' not defined.", + shortestRouteMixId); + StreamInterface shortestRouteMixStream = + ParseUtil.findStream(streamInformation, shortestRouteMixXml.getRandomStream(), eval); + try + { + routeGenerator = new ProbabilisticRouteGenerator(shortestRouteMix, shortestRouteMixStream); + } + catch (ProbabilityException exception) + { + throw new RuntimeException(errorPre + "Could not generate ShortestRouteMix " + shortestRouteMixId); + } + } - else if (generatorTag.getShortestRoute() != null) + else + { + throw new XmlParserException(errorPre + " No route information"); + } + return routeGenerator; + } + + /** + * Helper method to obtain LaneBasedGtuTemplate distribution for Generator or InjectionGenerator. + * @param gtuTemplateType GTU template tag + * @param gtuTemplateMixType GTU template mix tag + * @param routeGenerator route generator + * @param definitions definitions + * @param demand demand tag + * @param gtuTemplates GTU templates + * @param streamInformation stream information + * @param stream stream of demand tag + * @param eval evaluator + * @return distribution of LaneBasedGtuTemplate + * @throws ProbabilityException when a frequency is negative + * @throws XmlParserException when a referred element does no exist + */ + private static Distribution getTemplateDistribution(final StringType gtuTemplateType, + final StringType gtuTemplateMixType, final Generator routeGenerator, final Definitions definitions, + final Demand demand, final Map gtuTemplates, final StreamInformation streamInformation, + final StreamInterface stream, final Eval eval) throws ProbabilityException, XmlParserException + { + LaneBasedStrategicalRoutePlannerFactory strategicalFactory = + DefaultLaneBasedGtuCharacteristicsGeneratorOd.defaultLmrs(stream); + Distribution gtuTypeDistribution; + if (gtuTemplateType != null) + { + gtuTypeDistribution = new Distribution<>(stream); + String gtuTemplateId = gtuTemplateType.get(eval); + GtuTemplate templateTag = gtuTemplates.get(gtuTemplateId); + Throw.when(templateTag == null, XmlParserException.class, "GtuTemplate %s in generator not defined", gtuTemplateId); + LaneBasedGtuTemplate templateGtuType = parseGtuTemplate(templateTag, definitions, streamInformation, gtuTemplateId, + routeGenerator, strategicalFactory, eval); + gtuTypeDistribution.add(new FrequencyAndObject<>(1.0, templateGtuType)); + } + else if (gtuTemplateMixType != null) + { + String gtuTemplateMixId = gtuTemplateMixType.get(eval); + Throw.when(demand.getGtuTemplateMix() == null, XmlParserException.class, + "GtuTemplateMix %s cannot be found, there are no mixes defined.", gtuTemplateMixId); + GtuTemplateMix gtuTemplateMix = null; + for (GtuTemplateMix mix : demand.getGtuTemplateMix()) + { + if (gtuTemplateMixId.equals(mix.getId())) { - String shortestRouteId = generatorTag.getShortestRoute().get(eval); - Route shortestRoute = otsNetwork.getRoute(shortestRouteId); - Throw.when(shortestRoute == null, XmlParserException.class, - "Generator for Lane %s.%s: ShortestRoute %s not found", linkId, laneId, shortestRouteId); - routeGenerator = new FixedRouteGenerator(shortestRoute); + gtuTemplateMix = mix; + break; } + } + Throw.when(gtuTemplateMix == null, XmlParserException.class, "GtuTemplateMix %s is not defined.", gtuTemplateMixId); + StreamInterface mixStream = gtuTemplateMix.getRandomStream() == null ? stream + : ParseUtil.findStream(streamInformation, gtuTemplateMix.getRandomStream(), eval); + gtuTypeDistribution = new Distribution<>(mixStream); + for (org.opentrafficsim.xml.generated.GtuTemplateMix.GtuTemplate template : gtuTemplateMix.getGtuTemplate()) + { + Throw.when(!gtuTemplates.containsKey(template.getId()), XmlParserException.class, + "GtuTemplate %s is not defined.", template.getId()); + LaneBasedGtuTemplate templateGtuType = parseGtuTemplate(gtuTemplates.get(template.getId()), definitions, + streamInformation, gtuTemplateMixId, routeGenerator, strategicalFactory, eval); + gtuTypeDistribution + .add(new FrequencyAndObject(template.getWeight().get(eval), templateGtuType)); + } + } + else + { + throw new XmlParserException("No GTU information in Generator"); + } + return gtuTypeDistribution; + } - else if (generatorTag.getShortestRouteMix() != null) + /** + * Parse the Generators. + * @param otsNetwork the network to insert the parsed objects in + * @param definitions parsed definitions + * @param demand the Network tag + * @param gtuTemplates GtuTemplate tags + * @param routeMixMap map with route mix entries + * @param shortestRouteMixMap map with shortest route mix entries + * @param streamInformation map with stream information + * @param idGenerator id generator + * @param eval expression evaluator. + * @return list of created GTU generators + * @throws XmlParserException when the objects cannot be inserted into the network due to inconsistencies + */ + public static List parseInjectionGenerators(final RoadNetwork otsNetwork, + final Definitions definitions, final Demand demand, final Map gtuTemplates, + final Map>> routeMixMap, + final Map>> shortestRouteMixMap, final StreamInformation streamInformation, + final IdGenerator idGenerator, final Eval eval) throws XmlParserException + { + OtsSimulatorInterface simulator = otsNetwork.getSimulator(); + List generators = new ArrayList<>(); + try + { + int generatorNumber = 1; + for (InjectionGenerator generatorTag : demand.getInjectionGenerator()) + { + Table table = getArrivalsTable(generatorTag, eval); + + boolean generatorPositionFromInjections = containsColumn(table, Injections.LINK_COLUMN) + && containsColumn(table, Injections.LANE_COLUMN) && containsColumn(table, Injections.POSITION_COLUMN); + boolean gtuCharateristicsFromInjections = containsColumn(table, Injections.LENGTH_COLUMN) + || containsColumn(table, Injections.WIDTH_COLUMN) || containsColumn(table, Injections.FRONT_COLUMN) + || containsColumn(table, Injections.MAX_SPEED_COLUMN) + || containsColumn(table, Injections.MAX_ACCELERATION_COLUMN) + || containsColumn(table, Injections.MAX_DECELERATION_COLUMN) + || containsColumn(table, Injections.ROUTE_COLUMN) || containsColumn(table, Injections.ORIGIN_COLUMN) + || containsColumn(table, Injections.DESTINATION_COLUMN); + + // Injections + StreamInterface stream = ParseUtil.findStream(streamInformation, generatorTag.getRandomStream(), eval); + LaneBasedStrategicalPlannerFactory strategicalPlannerFactory = gtuCharateristicsFromInjections + ? DefaultLaneBasedGtuCharacteristicsGeneratorOd.defaultLmrs(stream) : null; + Duration ttc = generatorTag.getTimeToCollision() == null ? null : generatorTag.getTimeToCollision().get(eval); + Injections injections = new Injections(table, otsNetwork, definitions.getAll(GtuType.class), + strategicalPlannerFactory, stream, ttc); + + // Room checker, from injections (speed + time-to-collision) or defined + RoomChecker roomChecker; + if (generatorTag.getRoomChecker() == null) { - String shortestRouteMixId = generatorTag.getShortestRouteMix().get(eval); - List> shortestRouteMix = shortestRouteMixMap.get(shortestRouteMixId); - Throw.when(shortestRouteMix == null, XmlParserException.class, - "Generator for Lane %s.%s: ShortestRouteMix %s not found", linkId, laneId, shortestRouteMixId); - ShortestRouteMix shortestRouteMixXml = null; - for (ShortestRouteMix mix : demand.getShortestRouteMix()) - { - if (mix.getId().equals(shortestRouteMixId)) - { - shortestRouteMixXml = mix; - } - } - Throw.when(shortestRouteMixXml == null, XmlParserException.class, "Shortest route mix '%s' not defined.", - shortestRouteMixId); - StreamInterface shortestRouteMixStream = - ParseUtil.findStream(streamInformation, shortestRouteMixXml.getRandomStream(), eval); - try - { - routeGenerator = new ProbabilisticRouteGenerator(shortestRouteMix, shortestRouteMixStream); - } - catch (ProbabilityException exception) - { - throw new RuntimeException("Generator for Lane " + linkId + "." + laneId - + "Could not generate ShortestRouteMix " + shortestRouteMixId); - } + Throw.when(!containsColumn(table, Injections.SPEED_COLUMN), XmlParserException.class, + "No room checker provided and no speed data in the arrivals."); + Throw.when(ttc == null, XmlParserException.class, + "No room checker provided and no time-to-collision provided."); + roomChecker = injections; } - else { - throw new XmlParserException("Generator for Lane " + linkId + "." + laneId + ": No route information"); + roomChecker = ParseUtil.parseRoomChecker(generatorTag.getRoomChecker(), eval); } - CarFollowingModelFactory idmPlusFactory = new IdmPlusFactory(stream); - LaneBasedTacticalPlannerFactory tacticalFactory = - new LmrsFactory(idmPlusFactory, new DefaultLmrsPerceptionFactory()); - LaneBasedStrategicalRoutePlannerFactory strategicalFactory = - new LaneBasedStrategicalRoutePlannerFactory(tacticalFactory); - - // the distribution of GTUs - Distribution gtuTypeDistribution; - if (generatorTag.getGtuTemplate() != null) + // ID generator, from injections or default + Supplier idGeneratorInjections; + if (!containsColumn(table, Injections.ID_COLUMN)) { - gtuTypeDistribution = new Distribution<>(stream); - String gtuTemplateId = generatorTag.getGtuTemplate().get(eval); - GtuTemplate templateTag = gtuTemplates.get(gtuTemplateId); - Throw.when(templateTag == null, XmlParserException.class, "GtuTemplate %s in generator not defined", - gtuTemplateId); - LaneBasedGtuTemplate templateGtuType = parseGtuTemplate(templateTag, definitions, streamInformation, - gtuTemplateId, routeGenerator, strategicalFactory, eval); - gtuTypeDistribution.add(new FrequencyAndObject<>(1.0, templateGtuType)); + idGeneratorInjections = idGenerator; } - else if (generatorTag.getGtuTemplateMix() != null) + else { - String gtuTemplateMixId = generatorTag.getGtuTemplateMix().get(eval); - Throw.when(demand.getGtuTemplateMix() == null, XmlParserException.class, - "GtuTemplateMix %s cannot be found, there are no mixes defined.", gtuTemplateMixId); - GtuTemplateMix gtuTemplateMix = null; - for (GtuTemplateMix mix : demand.getGtuTemplateMix()) - { - if (gtuTemplateMixId.equals(mix.getId())) - { - gtuTemplateMix = mix; - break; - } - } - Throw.when(gtuTemplateMix == null, XmlParserException.class, "GtuTemplateMix %s is not defined.", - gtuTemplateMixId); - StreamInterface mixStream = gtuTemplateMix.getRandomStream() == null ? stream - : ParseUtil.findStream(streamInformation, gtuTemplateMix.getRandomStream(), eval); - gtuTypeDistribution = new Distribution<>(mixStream); - for (org.opentrafficsim.xml.generated.GtuTemplateMix.GtuTemplate template : gtuTemplateMix.getGtuTemplate()) - { - Throw.when(!gtuTemplates.containsKey(template.getId()), XmlParserException.class, - "GtuTemplate %s is not defined.", template.getId()); - LaneBasedGtuTemplate templateGtuType = parseGtuTemplate(gtuTemplates.get(template.getId()), definitions, - streamInformation, gtuTemplateMixId, routeGenerator, strategicalFactory, eval); - gtuTypeDistribution.add( - new FrequencyAndObject(template.getWeight().get(eval), templateGtuType)); - } + idGeneratorInjections = injections; + } + + // Generator position(s), from injections or defined + GeneratorPositions generatorPosition; + if (generatorPositionFromInjections) + { + generatorPosition = injections; } else { - throw new XmlParserException("No GTU information in Generator"); + Throw.when(generatorTag.getPosition() == null, XmlParserException.class, "No position given to injection" + + " generator, and also not all position column (link, lane position) present."); + CrossSectionLink link = + (CrossSectionLink) otsNetwork.getLink(generatorTag.getPosition().getLink().get(eval)); + Lane lane = (Lane) link.getCrossSectionElement(generatorTag.getPosition().getLane().get(eval)); + Length position = + ParseUtil.parseLengthBeginEnd(generatorTag.getPosition().getPosition().get(eval), lane.getLength()); + generatorPosition = GeneratorPositions.create(Set.of(new LanePosition(lane, position)), stream); } - RoomChecker roomChecker = ParseUtil.parseRoomChecker(generatorTag.getRoomChecker(), eval); - - Generator headwayGenerator = new HeadwayGenerator(generatorTag.getFrequency().get(eval), stream); - - CrossSectionLink link = (CrossSectionLink) otsNetwork.getLink(linkId); - Lane lane = (Lane) link.getCrossSectionElement(laneId); - Length position = ParseUtil.parseLengthBeginEnd(generatorTag.getPosition().get(eval), lane.getLength()); - Set initialLongitudinalPositions = new LinkedHashSet<>(); - initialLongitudinalPositions.add(new LanePosition(lane, position)); - - IdGenerator idGenerator = new IdGenerator(lane.getFullId()); + // GTU characteristics, from injections or defined + LaneBasedGtuCharacteristicsGenerator characteristicsGenerator; + if (gtuCharateristicsFromInjections) + { + characteristicsGenerator = injections.asLaneBasedGtuCharacteristicsGenerator(); + } + else + { + Throw.when(generatorTag.getGtuCharacteristics() == null, XmlParserException.class, + "Injection generator without GTU characteristics defined, and no information for it in the data."); + + StringType routeType = generatorTag.getGtuCharacteristics().getRoute(); + StringType routeMixType = generatorTag.getGtuCharacteristics().getRouteMix(); + StringType shortestRouteType = generatorTag.getGtuCharacteristics().getShortestRoute(); + StringType shortestRouteMixType = generatorTag.getGtuCharacteristics().getShortestRouteMix(); + String errorPre = "Injections " + (generatorNumber + 1) + ": "; + Generator routeGenerator = + getRouteGenerator(routeType, routeMixType, shortestRouteType, shortestRouteMixType, otsNetwork, + demand, routeMixMap, shortestRouteMixMap, streamInformation, errorPre, eval); + + StringType gtuTemplateType = generatorTag.getGtuCharacteristics().getGtuTemplate(); + StringType gtuTemplateMixType = generatorTag.getGtuCharacteristics().getGtuTemplateMix(); + Distribution gtuTypeDistribution = + getTemplateDistribution(gtuTemplateType, gtuTemplateMixType, routeGenerator, definitions, demand, + gtuTemplates, streamInformation, stream, eval); + + characteristicsGenerator = new LaneBasedGtuTemplateDistribution(gtuTypeDistribution); + } - LaneBasedGtuTemplateDistribution characteristicsGenerator = - new LaneBasedGtuTemplateDistribution(gtuTypeDistribution); - generators.add(new LaneBasedGtuGenerator(lane.getFullId(), headwayGenerator, characteristicsGenerator, - GeneratorPositions.create(initialLongitudinalPositions, stream), otsNetwork, simulator, roomChecker, - idGenerator)); + LaneBasedGtuGenerator generator = new LaneBasedGtuGenerator("Injections " + generatorNumber++, injections, + characteristicsGenerator, generatorPosition, otsNetwork, simulator, roomChecker, idGeneratorInjections); + generators.add(generator); } } - catch (Exception exception) + catch (TextSerializationException | IOException | ProbabilityException | SimRuntimeException | ParameterException + | NetworkException ex) { - throw new XmlParserException(exception); + throw new XmlParserException(ex); } return generators; } + /** + * Returns arrivals table from injection generator tag. + * @param generatorTag injection generator tag. + * @param eval evaluator + * @return arrivals table from injection generator tag + * @throws IOException when URI cannot be read + * @throws TextSerializationException when URI is ill formatted + */ + private static Table getArrivalsTable(final InjectionGenerator generatorTag, final Eval eval) + throws IOException, TextSerializationException + { + Table table; + if (generatorTag.getArrivals().getUri() != null && !generatorTag.getArrivals().getUri().isBlank()) + { + table = CsvData.readData(generatorTag.getArrivals().getUri(), generatorTag.getArrivals().getUri() + ".header"); + } + else + { + // gather columns + Map, Function> columnMap = new LinkedHashMap<>(); + columnMap.put(new Column<>(Injections.TIME_COLUMN, Injections.TIME_COLUMN, Duration.class), + a -> a.getValue().get(eval)); + if (!generatorTag.getArrivals().getArrival().isEmpty()) + { + Arrival arrival = generatorTag.getArrivals().getArrival().get(0); + addColumn(arrival, columnMap, Injections.ID_COLUMN, String.class, a -> a.getId().get(eval)); + addColumn(arrival, columnMap, Injections.SPEED_COLUMN, Speed.class, a -> a.getSpeed().get(eval)); + addColumn(arrival, columnMap, Injections.LINK_COLUMN, String.class, a -> a.getLink().get(eval)); + addColumn(arrival, columnMap, Injections.LANE_COLUMN, String.class, a -> a.getLane().get(eval)); + addColumn(arrival, columnMap, Injections.POSITION_COLUMN, String.class, a -> a.getPosition().get(eval)); + addColumn(arrival, columnMap, Injections.LENGTH_COLUMN, Length.class, a -> a.getLength().get(eval)); + addColumn(arrival, columnMap, Injections.WIDTH_COLUMN, Length.class, a -> a.getWidth().get(eval)); + addColumn(arrival, columnMap, Injections.FRONT_COLUMN, Length.class, a -> a.getFront().get(eval)); + addColumn(arrival, columnMap, Injections.MAX_SPEED_COLUMN, Speed.class, a -> a.getMaxSpeed().get(eval)); + addColumn(arrival, columnMap, Injections.MAX_ACCELERATION_COLUMN, Acceleration.class, + a -> a.getMaxAcceleration().get(eval)); + addColumn(arrival, columnMap, Injections.MAX_DECELERATION_COLUMN, Acceleration.class, + a -> a.getMaxDeceleration().get(eval)); + addColumn(arrival, columnMap, Injections.ROUTE_COLUMN, String.class, a -> a.getRoute().get(eval)); + addColumn(arrival, columnMap, Injections.ORIGIN_COLUMN, String.class, a -> a.getOrigin().get(eval)); + addColumn(arrival, columnMap, Injections.DESTINATION_COLUMN, String.class, a -> a.getDestination().get(eval)); + } + ListTable tab = new ListTable("injections", "injections", columnMap.keySet()); + table = tab; + for (Arrival arrival : generatorTag.getArrivals().getArrival()) + { + tab.addRow(columnMap.entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().apply(arrival)))); + } + } + return table; + } + + /** + * Add column to map, if it is given in the arrival. + * @param type of column + * @param arrival example arrival to check whether data is given + * @param columnMap column map + * @param columnId id of the column + * @param clazz class of the column type + * @param supplier value supplier from an arrival + */ + private static void addColumn(final Arrival arrival, final Map, Function> columnMap, + final String columnId, final Class clazz, final Function supplier) + { + if (supplier.apply(arrival) != null) + { + columnMap.put(new Column<>(columnId, columnId, clazz), supplier); + } + } + + /** + * Returns whether the table contains the column with given id. + * @param table table + * @param column column id + * @return whether the table contains the column with given id + */ + private static boolean containsColumn(final Table table, final String column) + { + return Arrays.stream(table.getColumnIds()).anyMatch(column::equals); + } + /** * Parse a GtuTemplate. * @param templateTag tag of the GTU template. @@ -588,6 +865,8 @@ private static LaneBasedGtuTemplate parseGtuTemplate(final GtuTemplate templateT /** * Parse a unit-based distribution into a Generator. + * @param djunits type + * @param unit type * @param streamMap the map with predefined streams * @param distribution the tag to parse (sub class of ConstantDistType) * @param unit unit as taken from the tag diff --git a/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/XmlParser.java b/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/XmlParser.java index bf3ee70aa..fee609b19 100644 --- a/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/XmlParser.java +++ b/ots-parser-xml/src/main/java/org/opentrafficsim/road/network/factory/xml/parser/XmlParser.java @@ -36,6 +36,7 @@ import org.opentrafficsim.core.geometry.Flattener; import org.opentrafficsim.core.gtu.GtuException; import org.opentrafficsim.core.gtu.GtuType; +import org.opentrafficsim.core.idgenerator.IdGenerator; import org.opentrafficsim.core.network.LinkType; import org.opentrafficsim.core.network.NetworkException; import org.opentrafficsim.core.network.route.Route; @@ -276,9 +277,15 @@ private static ExperimentRunControl build(final Ots ots, final RoadNet Map>> routeMixMap = DemandParser.parseRouteMix(otsNetwork, demand, eval); Map>> shortestRouteMixMap = DemandParser.parseShortestRouteMix(otsNetwork, demand, eval); + + IdGenerator idGenerator = new IdGenerator(""); List generators = DemandParser.parseGenerators(otsNetwork, definitions, demand, gtuTemplates, - routeMixMap, shortestRouteMixMap, streamInformation, eval); + routeMixMap, shortestRouteMixMap, streamInformation, idGenerator, eval); System.out.println("Created " + generators.size() + " generators based on explicit generator definitions"); + generators = DemandParser.parseInjectionGenerators(otsNetwork, definitions, demand, gtuTemplates, routeMixMap, + shortestRouteMixMap, streamInformation, idGenerator, eval); + System.out + .println("Created " + generators.size() + " generators based on explicit injection generator definitions"); DemandParser.parseSinks(otsNetwork, demand, definitions, eval); } diff --git a/ots-parser-xml/src/main/resources/xsd/bindings.xml b/ots-parser-xml/src/main/resources/xsd/bindings.xml index 59a878e59..f3340c8e4 100644 --- a/ots-parser-xml/src/main/resources/xsd/bindings.xml +++ b/ots-parser-xml/src/main/resources/xsd/bindings.xml @@ -69,6 +69,8 @@ adapter="org.opentrafficsim.xml.bindings.PositiveTimeAdapter" /> + - + @@ -44,7 +44,7 @@ - + @@ -271,6 +271,104 @@ + + + + + + + Provide a Position if there is no Link, Lane or Position (on lane) + data. + + + + + + + + + + + + Provide Gtu Characteristics if there is no data on GTU size, speed, + acceleration and route. + + + + + + + + + + + + + + + + + + + + + Test. + + + + + Provide a Room Checker if there is no speed in the data. + + + + + + Provide a Time To Collision if there is speed in the data. + + + + + + + + + + Provide link to arrivals file (csv with csv.header) or specify Arrivals. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -287,29 +385,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -322,7 +397,7 @@ - + diff --git a/ots-parser-xml/src/main/resources/xsd/ots.xsd b/ots-parser-xml/src/main/resources/xsd/ots.xsd index e81acddcb..5cafcf6b5 100644 --- a/ots-parser-xml/src/main/resources/xsd/ots.xsd +++ b/ots-parser-xml/src/main/resources/xsd/ots.xsd @@ -193,7 +193,7 @@ - + @@ -241,6 +241,12 @@ + + + + + + @@ -510,46 +516,76 @@ - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +