diff --git a/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java b/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java index 4e4c6860d63..2425a0f1491 100644 --- a/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java +++ b/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java @@ -413,6 +413,8 @@ public static void runWithDefaults(Class clazz, Str if (ApplicationUtils.isRunFromDesktop() && args.length == 0) { + System.setProperty("MATSIM_GUI_DESKTOP", "true"); + if (defaultArgs.length > 0) { String value = String.join(ARGS_DELIMITER, defaultArgs); System.setProperty("MATSIM_GUI_ARGS", value); diff --git a/contribs/application/src/main/java/org/matsim/application/ShowGUI.java b/contribs/application/src/main/java/org/matsim/application/ShowGUI.java index e394bfd7b79..6400262eb95 100644 --- a/contribs/application/src/main/java/org/matsim/application/ShowGUI.java +++ b/contribs/application/src/main/java/org/matsim/application/ShowGUI.java @@ -40,6 +40,13 @@ public Integer call() throws Exception { Gui gui = f.get(); + // Set the current working directory to be used in the gui, when run from the command line + // If the gui is run from desktop, the working directory is not overwritten + + // Assumption is that starting something from command line, the user expects that the working directory remains the same + if (!System.getProperty("MATSIM_GUI_DESKTOP", "false").equals("true")) + gui.setWorkingDirectory(new File("")); + while (gui.isShowing()) Thread.sleep(250); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java index c5927fcdb04..6730cae8996 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java @@ -1,5 +1,7 @@ package org.matsim.application.analysis.noise; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.locationtech.jts.geom.Envelope; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Scenario; @@ -8,6 +10,7 @@ import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.InputOptions; import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.SampleOptions; import org.matsim.application.options.ShpOptions; import org.matsim.contrib.noise.NoiseConfigGroup; import org.matsim.contrib.noise.NoiseOfflineCalculation; @@ -38,6 +41,8 @@ ) public class NoiseAnalysis implements MATSimAppCommand { + private static final Logger log = LogManager.getLogger(NoiseAnalysis.class); + @CommandLine.Mixin private final InputOptions input = InputOptions.ofCommand(NoiseAnalysis.class); @CommandLine.Mixin @@ -46,7 +51,11 @@ public class NoiseAnalysis implements MATSimAppCommand { @CommandLine.Mixin private final ShpOptions shp = new ShpOptions(); - @CommandLine.Option(names = "--consider-activities", split = ",", description = "Considered activities for noise calculation", defaultValue = "h,w,home,work") + @CommandLine.Mixin + private final SampleOptions sampleOptions = new SampleOptions(); + + @CommandLine.Option(names = "--consider-activities", split = ",", description = "Considered activities for noise calculation." + + " Use asterisk ('*') for acttype prefixes, if all such acts shall be considered.", defaultValue = "h,w,home*,work*") private Set considerActivities; @CommandLine.Option(names = "--noise-barrier", description = "Path to the noise barrier File", defaultValue = "") @@ -88,6 +97,16 @@ public Integer call() throws Exception { noiseParameters.setNoiseBarriersFilePath(noiseBarrierFile); } + if(! sampleOptions.isSet() && noiseParameters.getScaleFactor() == 1d){ + log.warn("You didn't provide the simulation sample size via command line option --sample-size! This means, noise damages are not scaled!!!"); + } else if (noiseParameters.getScaleFactor() == 1d){ + if (sampleOptions.getSample() == 1d){ + log.warn("Be aware that the noise output is not scaled. This might be unintended. If so, assure to provide the sample size via command line option --sample-size, in the SimWrapperConfigGroup," + + "or provide the scaleFactor (the inverse of the sample size) in the NoiseConfigGroup!!!"); + } + noiseParameters.setScaleFactor(sampleOptions.getUpscaleFactor()); + } + Scenario scenario = ScenarioUtils.loadScenario(config); String outputFilePath = output.getPath().getParent() == null ? "." : output.getPath().getParent().toString(); @@ -119,6 +138,7 @@ private Config prepareConfig() { config.facilities().setInputFile(null); config.eventsManager().setNumberOfThreads(null); config.eventsManager().setEstimatedNumberOfEvents(null); + //ts, aug '24: not sure if and why we need to set 1 thread config.global().setNumberOfThreads(1); return config; diff --git a/contribs/freight/src/main/java/org/matsim/freight/carriers/controler/CarrierVehicleReRouter.java b/contribs/freight/src/main/java/org/matsim/freight/carriers/controler/CarrierVehicleReRouter.java index 4a75e5c607d..cd03cb5fd94 100644 --- a/contribs/freight/src/main/java/org/matsim/freight/carriers/controler/CarrierVehicleReRouter.java +++ b/contribs/freight/src/main/java/org/matsim/freight/carriers/controler/CarrierVehicleReRouter.java @@ -36,6 +36,7 @@ import com.graphhopper.jsprit.io.algorithm.AlgorithmConfigXmlReader; import com.graphhopper.jsprit.io.algorithm.VehicleRoutingAlgorithms; import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.roadpricing.RoadPricingScheme; import org.matsim.core.replanning.ReplanningContext; import org.matsim.core.replanning.modules.GenericPlanStrategyModule; import org.matsim.core.router.util.TravelTime; @@ -58,7 +59,7 @@ class CarrierVehicleReRouter implements GenericPlanStrategyModule{ private final VehicleRoutingActivityCosts vehicleRoutingActivityCosts; - public CarrierVehicleReRouter( Network network, CarrierVehicleTypes vehicleTypes, TravelTime travelTimes, String vrpAlgoConfigFile, VehicleTypeDependentRoadPricingCalculator roadPricing ) { + public CarrierVehicleReRouter( Network network, CarrierVehicleTypes vehicleTypes, TravelTime travelTimes, String vrpAlgoConfigFile, RoadPricingScheme roadPricing ) { this.network = network; vehicleRoutingTransportCosts = getNetworkBasedTransportCosts(network,vehicleTypes,travelTimes,roadPricing); vehicleRoutingActivityCosts = new VehicleRoutingActivityCosts() { @@ -146,7 +147,7 @@ public void handlePlan(CarrierPlan carrierPlan) { } - private NetworkBasedTransportCosts getNetworkBasedTransportCosts(Network network, CarrierVehicleTypes vehicleTypes, TravelTime travelTimes, VehicleTypeDependentRoadPricingCalculator roadPricing) { + private NetworkBasedTransportCosts getNetworkBasedTransportCosts(Network network, CarrierVehicleTypes vehicleTypes, TravelTime travelTimes, RoadPricingScheme roadPricing ) { //****** //Define transport-costs //****** @@ -157,7 +158,7 @@ private NetworkBasedTransportCosts getNetworkBasedTransportCosts(Network network //sets time-dependent travelTimes tpcostsBuilder.setTravelTime(travelTimes); - if(roadPricing != null) tpcostsBuilder.setRoadPricingCalculator(roadPricing); + if(roadPricing != null) tpcostsBuilder.setRoadPricingScheme(roadPricing ); //sets time-slice to build time-dependent tpcosts and travelTime matrices tpcostsBuilder.setTimeSliceWidth(900); diff --git a/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCosts.java b/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCosts.java index f332b84cb0b..e6d8e1a0e45 100644 --- a/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCosts.java +++ b/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCosts.java @@ -30,6 +30,8 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.roadpricing.RoadPricingScheme; +import org.matsim.contrib.roadpricing.RoadPricingSchemeImpl; import org.matsim.core.router.speedy.SpeedyALTFactory; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.LeastCostPathCalculator.Path; @@ -83,6 +85,8 @@ */ public class NetworkBasedTransportCosts implements VRPTransportCosts { + private final RoadPricingScheme roadPricingScheme; + public interface InternalLeastCostPathCalculatorListener { void startCalculation(long routerId); @@ -316,27 +320,34 @@ private static class VehicleTypeVarCosts { */ static class VehicleTransportCostsIncludingToll implements TravelDisutility { -// private static Logger logger = LogManager.getLogger(VehicleTransportCostsIncludingToll.class); private final TravelDisutility baseTransportDisutility; - private final VehicleTypeDependentRoadPricingCalculator vehicleTypeDependentPricingCalculator; + private final RoadPricingScheme roadPricingScheme; - public VehicleTransportCostsIncludingToll(TravelDisutility baseTransportDisutility, - VehicleTypeDependentRoadPricingCalculator vehicleTypeDependentPricingCalculator) { + public VehicleTransportCostsIncludingToll( TravelDisutility baseTransportDisutility, + RoadPricingScheme roadPricingScheme ) { super(); this.baseTransportDisutility = baseTransportDisutility; - this.vehicleTypeDependentPricingCalculator = vehicleTypeDependentPricingCalculator; -// System.out.println("huuuuuuuuuuuuuuuuuuuu - initialize transport costs with toll"); + this.roadPricingScheme = roadPricingScheme; } @Override public double getLinkTravelDisutility(Link link, double time, Person person, org.matsim.vehicles.Vehicle vehicle) { double costs = baseTransportDisutility.getLinkTravelDisutility(link, time, person, vehicle); - Id typeId = vehicle.getType().getId(); - double toll = vehicleTypeDependentPricingCalculator.getTollAmount(typeId, link, time); -// System.out.println("huuuuuuuuuuuuuuuuuuuu - paid toll"); + + RoadPricingSchemeImpl.Cost costInfo; + if (person == null) { + costInfo = roadPricingScheme.getLinkCostInfo( link.getId(), time, null, vehicle.getId() ); + } else { + costInfo = roadPricingScheme.getLinkCostInfo( link.getId(), time, person.getId(), vehicle.getId() ); + } + + double toll = 0.; + if ( costInfo != null ){ + toll = costInfo.amount; + } return costs + toll; } @@ -377,7 +388,8 @@ public static Builder newInstance(Network network) { private LeastCostPathCalculatorFactory leastCostPathCalculatorFactory = (network, travelCosts, travelTimes) -> new SpeedyALTFactory().createPathCalculator(network, travelCosts, travelTime); - private VehicleTypeDependentRoadPricingCalculator roadPricingCalculator = new VehicleTypeDependentRoadPricingCalculator(); +// private VehicleTypeDependentRoadPricingCalculator roadPricingScheme = new VehicleTypeDependentRoadPricingCalculator(); + private RoadPricingScheme roadPricingScheme; private boolean withToll = false; @@ -393,7 +405,7 @@ public static Builder newInstance(Network network) { * Creates the builder requiring {@link Network} and a collection of * {@link VehicleType}. * - * @param network + * @param network the MATSim network * @param vehicleTypes must be all vehicleTypes and their assigned * costInformation in the system. */ @@ -414,7 +426,7 @@ private void retrieveTypeSpecificCosts(Collection vehicleTypes) { * Sets the travelTime. By default, travelTime is based on * link.getFreespeed();. * - * @param travelTime + * @param travelTime the travelTime to set * @return this builder */ public Builder setTravelTime(TravelTime travelTime) { @@ -463,7 +475,7 @@ public Builder setFIFO(boolean isFIFO) { *

* By default, it use {@link SpeedyALTFactory} * - * @param {@link {@link LeastCostPathCalculatorFactory} + * @param leastCostPathCalcFactory {@link LeastCostPathCalculatorFactory} * @return this builder */ public Builder setThreadSafeLeastCostPathCalculatorFactory( @@ -472,9 +484,9 @@ public Builder setThreadSafeLeastCostPathCalculatorFactory( return this; } - public Builder setRoadPricingCalculator(VehicleTypeDependentRoadPricingCalculator calculator) { + public Builder setRoadPricingScheme( RoadPricingScheme roadPricingScheme) { withToll = true; - this.roadPricingCalculator = calculator; + this.roadPricingScheme = roadPricingScheme; return this; } @@ -501,7 +513,7 @@ public NetworkBasedTransportCosts build() { baseDisutility = new BaseVehicleTransportCosts(typeSpecificCosts, travelTime); } if (withToll) { - finalDisutility = new VehicleTransportCostsIncludingToll(baseDisutility, roadPricingCalculator); + finalDisutility = new VehicleTransportCostsIncludingToll(baseDisutility, roadPricingScheme ); } else finalDisutility = baseDisutility; return new NetworkBasedTransportCosts(this); @@ -511,10 +523,10 @@ public NetworkBasedTransportCosts build() { * Adds type-specific costs. If typeId already exists, existing entry is * overwritten. * - * @param typeId - * @param fix - * @param perSecond - * @param perMeter + * @param typeId the vehicleType-id as String + * @param fix fix costs for the vehicle + * @param perSecond variable costs per second + * @param perMeter variable costs per meter */ public void addVehicleTypeSpecificCosts(String typeId, double fix, double perSecond, double perMeter) { typeSpecificCosts.put(typeId, new VehicleTypeVarCosts(perMeter, perSecond)); @@ -551,8 +563,6 @@ public void addVehicleTypeSpecificCosts(String typeId, double fix, double perSec private final Map matsimVehicles = new HashMap<>(); - private final VehicleTypeDependentRoadPricingCalculator roadPricingCalc; - /** * by default sets the {@link SpeedyALTFactory} */ @@ -568,7 +578,7 @@ private NetworkBasedTransportCosts(Builder builder) { this.travelTime = builder.travelTime; this.network = builder.network; this.leastCostPathCalculatorFactory = builder.leastCostPathCalculatorFactory; - this.roadPricingCalc = builder.roadPricingCalculator; + this.roadPricingScheme = builder.roadPricingScheme; this.timeSliceWidth = builder.timeSliceWidth; this.defaultTypeId = builder.defaultTypeId; this.ttMemorizedCounter = new Counter("#TransportCostValues cached "); @@ -585,7 +595,7 @@ private NetworkBasedTransportCosts(Builder builder) { * cached travel-time. If not, it computes and caches new values with the * leastCostPathCalc defined in here. * - * @Throws {@link IllegalStateException} if vehicle is null + * @exception IllegalStateException if vehicle is null */ @Override public double getTransportTime(Location fromId, Location toId, double departureTime, Driver driver, @@ -666,7 +676,7 @@ private void informStartCalc() { * cached travel-cost value. If not, it computes and caches new values with the * leastCostPathCalc defined in here. * - * @Throws {@link IllegalStateException} if vehicle is null + * @exception IllegalStateException if vehicle is null */ @Override public double getTransportCost(Location fromId, Location toId, double departureTime, Driver driver, @@ -734,7 +744,7 @@ public double getTransportCost(Location fromId, Location toId, double departureT * cached distance. If not, it computes and caches new values with the * leastCostPathCalc defined in here. * - * @Throws {@link IllegalStateException} if vehicle is null + * @exception IllegalStateException if vehicle is null */ @Override public double getDistance(Location fromId, Location toId, double departureTime, Vehicle vehicle) { @@ -799,7 +809,7 @@ public Collection getInternalListeners( * This is a rather bad approximation. If you require this, you should implement * another {@link VehicleRoutingTransportCosts} * - * @Throws {@link IllegalStateException} if vehicle is null + * @exception IllegalStateException if vehicle is null */ @Override public double getBackwardTransportCost(Location fromId, Location toId, double arrivalTime, Driver driver, @@ -815,7 +825,7 @@ public double getBackwardTransportCost(Location fromId, Location toId, double ar * This is a rather bad approximation. If you require this, you should implement * another {@link VehicleRoutingTransportCosts}. * - * @Throws {@link IllegalStateException} if vehicle is null + * @exception IllegalStateException if vehicle is null */ @Override public double getBackwardTransportTime(Location fromId, Location toId, double arrivalTime, Driver driver, @@ -862,7 +872,7 @@ private int getTimeSlice(double time) { /** * Gets the network the calculation is based on. * - * @return + * @return the network */ public Network getNetwork() { return network; @@ -877,13 +887,4 @@ public TravelTime getTravelTime() { return travelTime; } - /** - * Gets the {@link VehicleTypeDependentRoadPricingCalculator} - * - * @return {@link VehicleTypeDependentRoadPricingCalculator} - */ - public VehicleTypeDependentRoadPricingCalculator getRoadPricingCalculator() { - return roadPricingCalc; - } - } diff --git a/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/VehicleTypeDependentRoadPricingCalculator.java b/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/VehicleTypeDependentRoadPricingCalculator.java index ed5d3694720..6175780ab9a 100644 --- a/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/VehicleTypeDependentRoadPricingCalculator.java +++ b/contribs/freight/src/main/java/org/matsim/freight/carriers/jsprit/VehicleTypeDependentRoadPricingCalculator.java @@ -35,6 +35,7 @@ * @author stefan schröder * */ +@Deprecated // use RoadPricingScheme public class VehicleTypeDependentRoadPricingCalculator { interface TollCalculator { diff --git a/contribs/freight/src/test/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCostsTest.java b/contribs/freight/src/test/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCostsTest.java index ccdb7095162..64e7eeba641 100644 --- a/contribs/freight/src/test/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCostsTest.java +++ b/contribs/freight/src/test/java/org/matsim/freight/carriers/jsprit/NetworkBasedTransportCostsTest.java @@ -27,16 +27,21 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.roadpricing.*; import org.matsim.core.config.Config; import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.misc.Time; +import org.matsim.freight.carriers.CarrierVehicle; import org.matsim.testcases.MatsimTestUtils; import org.matsim.vehicles.CostInformation; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.VehiclesFactory; import java.util.Arrays; @@ -47,6 +52,8 @@ public class NetworkBasedTransportCostsTest { + private static final String TYPE_1 = "type1"; + private static final String TYPE_2 = "type2"; @RegisterExtension public final MatsimTestUtils utils = new MatsimTestUtils(); @@ -60,21 +67,21 @@ void test_whenAddingTwoDifferentVehicleTypes_itMustAccountForThem(){ Network network = scenario.getNetwork(); NetworkBasedTransportCosts.Builder builder = NetworkBasedTransportCosts.Builder.newInstance(network); - builder.addVehicleTypeSpecificCosts("type1", 10.0, 0.0, 2.0); - builder.addVehicleTypeSpecificCosts("type2", 20.0, 0.0, 4.0); + builder.addVehicleTypeSpecificCosts(TYPE_1, 10.0, 0.0, 2.0); + builder.addVehicleTypeSpecificCosts(TYPE_2, 20.0, 0.0, 4.0); NetworkBasedTransportCosts c = builder.build(); Vehicle vehicle1 = mock(Vehicle.class); com.graphhopper.jsprit.core.problem.vehicle.VehicleType type1 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); when(type1.getMaxVelocity()).thenReturn(5.0); - when(type1.getTypeId()).thenReturn("type1"); + when(type1.getTypeId()).thenReturn(TYPE_1); when(vehicle1.getType()).thenReturn(type1); when(vehicle1.getId()).thenReturn("vehicle1"); Vehicle vehicle2 = mock(Vehicle.class); com.graphhopper.jsprit.core.problem.vehicle.VehicleType type2 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); when(type2.getMaxVelocity()).thenReturn(5.0); - when(type2.getTypeId()).thenReturn("type2"); + when(type2.getTypeId()).thenReturn(TYPE_2); when(vehicle2.getType()).thenReturn(type2); when(vehicle2.getId()).thenReturn("vehicle2"); @@ -94,8 +101,8 @@ void test_whenVehicleTypeNotKnow_throwException(){ Network network = scenario.getNetwork(); NetworkBasedTransportCosts.Builder builder = NetworkBasedTransportCosts.Builder.newInstance(network); - builder.addVehicleTypeSpecificCosts("type1", 10.0, 0.0, 2.0); - builder.addVehicleTypeSpecificCosts("type2", 20.0, 0.0, 4.0); + builder.addVehicleTypeSpecificCosts(TYPE_1, 10.0, 0.0, 2.0); + builder.addVehicleTypeSpecificCosts(TYPE_2, 20.0, 0.0, 4.0); NetworkBasedTransportCosts c = builder.build(); Vehicle vehicle2 = mock(Vehicle.class); @@ -120,14 +127,14 @@ void test_whenAddingTwoVehicleTypesViaConstructor_itMustAccountForThat(){ String NETWORK_FILENAME = utils.getClassInputDirectory() + "network.xml"; new MatsimNetworkReader(scenario.getNetwork()).readFile(NETWORK_FILENAME); - VehicleType vehType1 = VehicleUtils.getFactory().createVehicleType(Id.create( "type1", VehicleType.class )); + VehicleType vehType1 = VehicleUtils.getFactory().createVehicleType(Id.create(TYPE_1, VehicleType.class )); CostInformation costInformation1 = vehType1.getCostInformation() ; costInformation1.setFixedCost( 0.0 ); costInformation1.setCostsPerMeter( 2.0 ); costInformation1.setCostsPerSecond( 0.0 ); - VehicleType vehType2 = VehicleUtils.getFactory().createVehicleType(Id.create( "type2", VehicleType.class )); + VehicleType vehType2 = VehicleUtils.getFactory().createVehicleType(Id.create(TYPE_2, VehicleType.class )); CostInformation costInformation = vehType2.getCostInformation() ; costInformation.setFixedCost( 0.0 ); @@ -136,20 +143,20 @@ void test_whenAddingTwoVehicleTypesViaConstructor_itMustAccountForThat(){ Network network = scenario.getNetwork(); NetworkBasedTransportCosts.Builder builder = - NetworkBasedTransportCosts.Builder.newInstance(network,Arrays.asList(vehType1,vehType2)); + NetworkBasedTransportCosts.Builder.newInstance(network,Arrays.asList(vehType1,vehType2)); NetworkBasedTransportCosts networkBasedTransportCosts = builder.build(); Vehicle vehicle1 = mock(Vehicle.class); com.graphhopper.jsprit.core.problem.vehicle.VehicleType type1 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); when(type1.getMaxVelocity()).thenReturn(5.0); - when(type1.getTypeId()).thenReturn("type1"); + when(type1.getTypeId()).thenReturn(TYPE_1); when(vehicle1.getType()).thenReturn(type1); when(vehicle1.getId()).thenReturn("vehicle1"); Vehicle vehicle2 = mock(Vehicle.class); com.graphhopper.jsprit.core.problem.vehicle.VehicleType type2 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); when(type2.getMaxVelocity()).thenReturn(5.0); - when(type2.getTypeId()).thenReturn("type2"); + when(type2.getTypeId()).thenReturn(TYPE_2); when(vehicle2.getType()).thenReturn(type2); when(vehicle2.getId()).thenReturn("vehicle2"); @@ -159,4 +166,211 @@ void test_whenAddingTwoVehicleTypesViaConstructor_itMustAccountForThat(){ Assertions.assertEquals(20000.0, networkBasedTransportCosts.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle2), 0.01); } + + /** + * This test is a modified version of {@link #test_whenAddingTwoDifferentVehicleTypes_itMustAccountForThem} + * In addition, there is added a road pricing scheme to toll all vehicles + */ + @Test + void test_whenAddingTwoDifferentVehicleTypes_tollAllTypes(){ + Config config = new Config(); + config.addCoreModules(); + Scenario scenario = ScenarioUtils.createScenario(config); + new MatsimNetworkReader(scenario.getNetwork()).readFile(utils.getClassInputDirectory() + "network.xml"); + + //Create Rp Scheme from code. + RoadPricingSchemeImpl scheme = RoadPricingUtils.addOrGetMutableRoadPricingScheme(scenario ); + /* Configure roadpricing scheme. */ + RoadPricingUtils.setName(scheme, "DemoToll4Test"); + RoadPricingUtils.setType(scheme, RoadPricingScheme.TOLL_TYPE_LINK); + RoadPricingUtils.setDescription(scheme, "Tolling scheme for test."); + + /* Add general link based toll for one link */ + RoadPricingUtils.addLink(scheme, Id.createLinkId("21")); + RoadPricingUtils.createAndAddGeneralCost(scheme, Time.parseTime("00:00:00"), Time.parseTime("72:00:00"), 99.99); + + NetworkBasedTransportCosts.Builder builder = NetworkBasedTransportCosts.Builder.newInstance(scenario.getNetwork()); + builder.addVehicleTypeSpecificCosts(TYPE_1, 10.0, 0.0, 2.0); + builder.addVehicleTypeSpecificCosts(TYPE_2, 20.0, 0.0, 4.0); + builder.setRoadPricingScheme(scheme); + NetworkBasedTransportCosts c = builder.build(); + + Vehicle vehicle1 = mock(Vehicle.class); + com.graphhopper.jsprit.core.problem.vehicle.VehicleType type1 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); + when(type1.getMaxVelocity()).thenReturn(5.0); + when(type1.getTypeId()).thenReturn(TYPE_1); + when(vehicle1.getType()).thenReturn(type1); + when(vehicle1.getId()).thenReturn("vehicle1"); + + Vehicle vehicle2 = mock(Vehicle.class); + com.graphhopper.jsprit.core.problem.vehicle.VehicleType type2 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); + when(type2.getMaxVelocity()).thenReturn(5.0); + when(type2.getTypeId()).thenReturn(TYPE_2); + when(vehicle2.getType()).thenReturn(type2); + when(vehicle2.getId()).thenReturn("vehicle2"); + + //vehicle1: includes toll + Assertions.assertEquals(20099.99, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle1), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle1), 0.01); + + //vehicle 2: no toll + Assertions.assertEquals(40099.99, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle2), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle2), 0.01); + } + + /** + * This test is a modified version of {@link #test_whenAddingTwoDifferentVehicleTypes_itMustAccountForThem} + * In addition, there is added a road pricing scheme. + * The scheme is only set for one vehicle type: type1. + * So, only the vehicle using that type (vehicle1) should be tolled, the other (vehicle2) not. + * This test is build, in a way, that it uses the MATSim infrastructure for filtering the vehicles in the toll factor. + * To see it just on jsprit setting, please refer to {@link #test_whenAddingTwoDifferentVehicleTypes_tollBasedOnVehicleId} + */ + @Test + void test_whenAddingTwoDifferentVehicleTypes_tollOneTypeTollFactor(){ + Config config = new Config(); + config.addCoreModules(); + Scenario scenario = ScenarioUtils.createScenario(config); + new MatsimNetworkReader(scenario.getNetwork()).readFile(utils.getClassInputDirectory() + "network.xml"); + + //Create RoadPricing Scheme from code. + RoadPricingSchemeImpl scheme = RoadPricingUtils.addOrGetMutableRoadPricingScheme(scenario ); + /* Configure roadpricing scheme. */ + RoadPricingUtils.setName(scheme, "DemoToll4Test"); + RoadPricingUtils.setType(scheme, RoadPricingScheme.TOLL_TYPE_LINK); + RoadPricingUtils.setDescription(scheme, "Tolling scheme for test."); + + /* Add general link based toll for one link */ + RoadPricingUtils.addLink(scheme, Id.createLinkId("21")); + RoadPricingUtils.createAndAddGeneralCost(scheme, Time.parseTime("00:00:00"), Time.parseTime("72:00:00"), 99.99); + + //Use toll factor to only toll vehicles of type1. + TollFactor tollFactor = (personId, vehicleId, linkId, time) -> { + double tollFactor1 = 0.; + var vehTypeIdString = VehicleUtils.findVehicle(vehicleId, scenario).getType().getId().toString(); //This needs, the vehicles registered in the scenario. + if (TYPE_1.equals(vehTypeIdString)) { + tollFactor1 = 1.; + } + return tollFactor1; + }; + RoadPricingScheme rpSchemeWTollFactor = new RoadPricingSchemeUsingTollFactor( scheme , tollFactor ); + + /// End creating roadPricing scheme from Code + + //Build MATSim vehicles and convert them to jsprit Vehicles ... just to see, that this works across the whole chain and + //that we can filter/search by (MATSim) vehicle types in TollFactor usage + VehiclesFactory vf = scenario.getVehicles().getFactory(); + final Vehicle vehicle1; + { + VehicleType vehType1 = vf.createVehicleType( Id.create( TYPE_1, VehicleType.class ) ); + vehType1.getCostInformation().setFixedCost(10.0); + vehType1.getCostInformation().setCostsPerSecond(0.0); + vehType1.getCostInformation().setCostsPerMeter(2.0); + scenario.getVehicles().addVehicleType( vehType1 ); + CarrierVehicle matsimVehicle1 = CarrierVehicle.newInstance( Id.createVehicleId( "vehicle1" ), Id.createLinkId("20"), vehType1 ); + scenario.getVehicles().addVehicle( matsimVehicle1 ); + vehicle1 = MatsimJspritFactory.createJspritVehicle( matsimVehicle1, new Coord() ); + } + + final Vehicle vehicle2; + { + VehicleType vehType2 = vf.createVehicleType(Id.create(TYPE_2, VehicleType.class)); + vehType2.getCostInformation().setFixedCost(20.0); + vehType2.getCostInformation().setCostsPerSecond(0.0); + vehType2.getCostInformation().setCostsPerMeter(4.0); + scenario.getVehicles().addVehicleType(vehType2); + + CarrierVehicle matsimVehicle2 = CarrierVehicle.newInstance(Id.createVehicleId("vehicle2"), Id.createLinkId("20"), vehType2); + scenario.getVehicles().addVehicle(matsimVehicle2); + vehicle2 = MatsimJspritFactory.createJspritVehicle(matsimVehicle2, new Coord()); + } + + //Build the NetbasedTransportCosts object with the roadpricing scheme. + NetworkBasedTransportCosts c = NetworkBasedTransportCosts.Builder.newInstance( + scenario.getNetwork(), + scenario.getVehicles().getVehicleTypes().values() ) + .setRoadPricingScheme(rpSchemeWTollFactor) + .build() ; + + //vehicle1: includes toll + Assertions.assertEquals(20099.99, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle1), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle1), 0.01); + + //vehicle 2: no toll + Assertions.assertEquals(40000.0, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle2), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle2), 0.01); + } + + + /** + * This test is a modified version of {@link #test_whenAddingTwoDifferentVehicleTypes_itMustAccountForThem} + * In addition, there is added a road pricing scheme. + * With using a toll factor, the tolling can be set differently for the two vehicles. + * This test is build, in a way, that it uses the JSPRIT vehicle directly. + * To see it with the MATSim settings, please refer to {@link #test_whenAddingTwoDifferentVehicleTypes_tollOneTypeTollFactor()} + */ + @Test + void test_whenAddingTwoDifferentVehicleTypes_tollBasedOnVehicleId(){ + Config config = new Config(); + config.addCoreModules(); + Scenario scenario = ScenarioUtils.createScenario(config); + new MatsimNetworkReader(scenario.getNetwork()).readFile(utils.getClassInputDirectory() + "network.xml"); + + //Create Rp Scheme from code. + RoadPricingSchemeImpl scheme = RoadPricingUtils.addOrGetMutableRoadPricingScheme(scenario ); + /* Configure roadpricing scheme. */ + RoadPricingUtils.setName(scheme, "DemoToll4TestType1"); + RoadPricingUtils.setType(scheme, RoadPricingScheme.TOLL_TYPE_LINK); + RoadPricingUtils.setDescription(scheme, "Tolling scheme for test."); + + /* Add general link based toll for one link */ + RoadPricingUtils.addLink(scheme, Id.createLinkId("21")); + RoadPricingUtils.createAndAddGeneralCost(scheme, Time.parseTime("00:00:00"), Time.parseTime("72:00:00"), 99.99); + + //Use a factor to take into account the different types. type2 gehts tolled with 50% of the toll of type1 + TollFactor tollFactor = (personId, vehicleId, linkId, time) -> { + //No information about the vehicleType available anywhere, because it is not registered centrally. + // -> Use the vehicleId to distinguish the types. + var vehTypeIdString = vehicleId.toString(); + if (vehTypeIdString.equals("vehicle1")) { + return 1; + } else if (vehTypeIdString.equals("vehicle2")) { + return 0.5; + } else { + return 0; + } + }; + RoadPricingSchemeUsingTollFactor rpSchemeWTollFactor = new RoadPricingSchemeUsingTollFactor( scheme , tollFactor ); + + ///___ End creating toll scheme from code + + NetworkBasedTransportCosts.Builder builder = NetworkBasedTransportCosts.Builder.newInstance(scenario.getNetwork()); + builder.addVehicleTypeSpecificCosts(TYPE_1, 10.0, 0.0, 2.0); + builder.addVehicleTypeSpecificCosts(TYPE_2, 20.0, 0.0, 4.0); + builder.setRoadPricingScheme(rpSchemeWTollFactor); //add the rpCalculator to activate the tolling. + NetworkBasedTransportCosts c = builder.build(); + + Vehicle vehicle1 = mock(Vehicle.class); + com.graphhopper.jsprit.core.problem.vehicle.VehicleType type1 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); + when(type1.getMaxVelocity()).thenReturn(5.0); + when(type1.getTypeId()).thenReturn(TYPE_1); + when(vehicle1.getType()).thenReturn(type1); + when(vehicle1.getId()).thenReturn("vehicle1"); + + Vehicle vehicle2 = mock(Vehicle.class); + com.graphhopper.jsprit.core.problem.vehicle.VehicleType type2 = mock( com.graphhopper.jsprit.core.problem.vehicle.VehicleType.class ); + when(type2.getMaxVelocity()).thenReturn(5.0); + when(type2.getTypeId()).thenReturn(TYPE_2); + when(vehicle2.getType()).thenReturn(type2); + when(vehicle2.getId()).thenReturn("vehicle2"); + + //vehicle1: includes toll of 99.99 for entering the final link + Assertions.assertEquals(20099.99, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle1), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle1), 0.01); + + //vehicle 2: includes toll of 49.995 (50% of 99.99) for entering the final link + Assertions.assertEquals(40049.995, c.getTransportCost(Location.newInstance("20"), Location.newInstance("21"), 0.0, mock(Driver.class), vehicle2), 0.01); + Assertions.assertEquals(20000.0, c.getDistance(Location.newInstance("6"), Location.newInstance("21"), 0.0, vehicle2), 0.01); + } + } diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index 5917f032e3e..f58a572c939 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -11,7 +11,7 @@ 4.27.2 - 1.65.0 + 1.65.1 diff --git a/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/prepare/CreateDataDistributionOfStructureData.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/prepare/CreateDataDistributionOfStructureData.java index 477231505c1..dcd4c3732d9 100644 --- a/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/prepare/CreateDataDistributionOfStructureData.java +++ b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/prepare/CreateDataDistributionOfStructureData.java @@ -29,7 +29,7 @@ public class CreateDataDistributionOfStructureData implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(CreateDataDistributionOfStructureData.class); - private static LanduseDataConnectionCreator landuseDataConnectionCreator; + private final LanduseDataConnectionCreator landuseDataConnectionCreator; private enum LanduseConfiguration { useOnlyOSMLanduse, useOSMBuildingsAndLanduse @@ -78,16 +78,16 @@ private enum LanduseConfiguration { private final Map>> buildingsPerZone = new HashMap<>(); public CreateDataDistributionOfStructureData(LanduseDataConnectionCreator landuseDataConnectionCreator) { - CreateDataDistributionOfStructureData.landuseDataConnectionCreator = landuseDataConnectionCreator; + this.landuseDataConnectionCreator = landuseDataConnectionCreator; log.info("Using LanduseDataConnectionCreator {} to connect the types of the landuse data to the categories of the small scale commercial traffic generation", landuseDataConnectionCreator.getClass().getSimpleName()); } public CreateDataDistributionOfStructureData() { - landuseDataConnectionCreator = new LanduseDataConnectionCreatorForOSM_Data(); + this.landuseDataConnectionCreator = new LanduseDataConnectionCreatorForOSM_Data(); log.info("Using default LanduseDataConnectionCreatorForOSM_Data to connect the types of the landuse data to the categories of the small scale commercial traffic generation"); } public static void main(String[] args) { - System.exit(new CommandLine(new CreateDataDistributionOfStructureData(landuseDataConnectionCreator)).execute(args)); + System.exit(new CommandLine(new CreateDataDistributionOfStructureData()).execute(args)); } @Override diff --git a/contribs/vsp/src/main/java/playground/vsp/pt/fare/DistanceBasedPtFareParams.java b/contribs/vsp/src/main/java/playground/vsp/pt/fare/DistanceBasedPtFareParams.java index a86cd173c14..442d956bf3c 100644 --- a/contribs/vsp/src/main/java/playground/vsp/pt/fare/DistanceBasedPtFareParams.java +++ b/contribs/vsp/src/main/java/playground/vsp/pt/fare/DistanceBasedPtFareParams.java @@ -22,6 +22,7 @@ public class DistanceBasedPtFareParams extends ReflectiveConfigGroup { public static final String LONG_DISTANCE_TRIP_THRESHOLD = "longDistanceTripThreshold"; public static final String LONG_DISTANCE_TRIP_SLOPE = "longDistanceTripSlope"; public static final String LONG_DISTANCE_TRIP_INTERCEPT = "longDistanceTripIntercept"; + public static final String FARE_ZONE_SHP = "fareZoneShp"; @PositiveOrZero private double minFare = 2.0; @@ -35,6 +36,7 @@ public class DistanceBasedPtFareParams extends ReflectiveConfigGroup { private double longDistanceTripIntercept = 30.0; @PositiveOrZero private double longDistanceTripSlope = 0.00025; + private String fareZoneShp; public DistanceBasedPtFareParams() { super(SET_NAME); @@ -52,6 +54,7 @@ public Map getComments() { map.put(LONG_DISTANCE_TRIP_THRESHOLD, "Threshold of the long trips in meters. Below this value, " + "the trips are considered as normal trips. Above this value, the trips are considered as " + "inter-city trips"); + map.put(FARE_ZONE_SHP, "Shp file with fare zone(s). This parameter is only used for PtFareCalculationModel 'fareZoneBased'."); return map; } @@ -114,4 +117,14 @@ public double getLongDistanceTripThreshold() { public void setLongDistanceTripThreshold(double longDistanceTripThreshold) { this.longDistanceTripThreshold = longDistanceTripThreshold; } + + @StringGetter(FARE_ZONE_SHP) + public String getFareZoneShp() { + return fareZoneShp; + } + + @StringSetter(FARE_ZONE_SHP) + public void setFareZoneShp(String fareZoneShp) { + this.fareZoneShp = fareZoneShp; + } } diff --git a/contribs/vsp/src/main/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandler.java b/contribs/vsp/src/main/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandler.java new file mode 100644 index 00000000000..962a18fac57 --- /dev/null +++ b/contribs/vsp/src/main/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandler.java @@ -0,0 +1,163 @@ +package playground.vsp.pt.fare; + +import com.google.inject.Inject; +import org.apache.commons.math.stat.regression.SimpleRegression; +import org.geotools.api.feature.simple.SimpleFeature; +import org.locationtech.jts.geom.Geometry; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.ActivityStartEvent; +import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.population.Person; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.router.StageActivityTypeIdentifier; +import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.pt.PtConstants; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FareZoneBasedPtFareHandler implements ActivityStartEventHandler { + @Inject + private EventsManager events; + + public static final String FARE = "fare"; + public static final String PT_FARE_ZONE_BASED = "fare zone based pt fare"; + public static final String PT_GERMANWIDE_FARE_BASED = "german-wide fare based pt fare"; + + private final ShpOptions shp; + + private final Map, Coord> personDepartureCoordMap = new HashMap<>(); + private final Map, Coord> personArrivalCoordMap = new HashMap<>(); + + public FareZoneBasedPtFareHandler(DistanceBasedPtFareParams params) { + this.shp = new ShpOptions(params.getFareZoneShp(), null, null); + } + + @Override + public void handleEvent(ActivityStartEvent event) { + + if (event.getActType().equals(PtConstants.TRANSIT_ACTIVITY_TYPE)) { + personDepartureCoordMap.computeIfAbsent(event.getPersonId(), c -> event.getCoord()); // The departure place is fixed to the place of first pt interaction an agent has in the whole leg + personArrivalCoordMap.put(event.getPersonId(), event.getCoord()); // The arrival stop will keep updating until the agent start a real activity (i.e. finish the leg) + } + + if (!StageActivityTypeIdentifier.isStageActivity(event.getActType())) { + Id personId = event.getPersonId(); + if (personDepartureCoordMap.containsKey(personId)) { + double distance = CoordUtils.calcEuclideanDistance + (personDepartureCoordMap.get(personId), personArrivalCoordMap.get(personId)); + + SimpleFeature departureZone = determineFareZone(personDepartureCoordMap.get(personId), shp.readFeatures()); + SimpleFeature arrivalZone = determineFareZone(personArrivalCoordMap.get(personId), shp.readFeatures()); + + Map.Entry fareEntry = computeFare(distance, departureZone, arrivalZone); + // charge fare to the person + events.processEvent( + new PersonMoneyEvent(event.getTime(), event.getPersonId(), -fareEntry.getValue(), + PtFareConfigGroup.PT_FARE, fareEntry.getKey(), event.getPersonId().toString())); + + personDepartureCoordMap.remove(personId); + personArrivalCoordMap.remove(personId); + } + } + } + + public static Map.Entry computeFare(double distance, SimpleFeature departureZone, SimpleFeature arrivalZone) { + + if (departureZone != null && arrivalZone != null) { +// if both zones are not null -> departure and arrival point are inside of one of the tarifzonen + if (departureZone.getID().equals(arrivalZone.getID())) { + return new AbstractMap.SimpleEntry<>(PT_FARE_ZONE_BASED ,(double) departureZone.getAttribute(FARE)); + } + } +// in every other case return german wide fare / Deutschlandtarif + return getGermanWideFare(distance); + } + + private static Map.Entry getGermanWideFare(double distance) { + + SimpleRegression regression = new SimpleRegression(); + +// in Deutschlandtarif, the linear function for the prices above 100km seem to have a different steepness +// hence the following difference in data points +// prices taken from https://deutschlandtarifverbund.de/wp-content/uploads/2024/07/20231201_TBDT_J_10_Preisliste_V07.pdf + if (distance / 1000 <= 100.) { + regression.addData(1, 1.70); + regression.addData(2,1.90); + regression.addData(3,2.00); + regression.addData(4,2.10); + regression.addData(5,2.20); + regression.addData(6,3.20); + regression.addData(7,3.70); + regression.addData(8,3.80); + regression.addData(9,3.90); + regression.addData(10,4.10); + regression.addData(11,5.00); + regression.addData(12,5.40); + regression.addData(13,5.60); + regression.addData(14,5.80); + regression.addData(15,5.90); + regression.addData(16,6.40); + regression.addData(17,6.50); + regression.addData(18,6.60); + regression.addData(19,6.70); + regression.addData(20,6.90); + regression.addData(30,9.90); + regression.addData(40,13.70); + regression.addData(50,16.30); + regression.addData(60,18.10); + regression.addData(70,20.10); + regression.addData(80,23.20); + regression.addData(90,26.20); + regression.addData(100,28.10); + } else { + regression.addData(100,28.10); + regression.addData(200,47.20); + regression.addData(300,59.70); + regression.addData(400,71.70); + regression.addData(500,83.00); + regression.addData(600,94.60); + regression.addData(700,106.30); + regression.addData(800,118.20); + regression.addData(900,130.10); + regression.addData(1000,141.00); + regression.addData(1100,148.60); + regression.addData(1200,158.10); + regression.addData(1300,169.20); + regression.addData(1400,179.80); + regression.addData(1500,190.10); + regression.addData(1600,201.50); + regression.addData(1700,212.80); + regression.addData(1800,223.30); + regression.addData(1900,233.90); + regression.addData(2000,244.00); + } + return new AbstractMap.SimpleEntry<>(PT_GERMANWIDE_FARE_BASED, regression.getSlope() * distance / 1000 + regression.getIntercept()); + } + + static SimpleFeature determineFareZone(Coord coord, List features) { + SimpleFeature zone = null; + + for (SimpleFeature ft : features) { + Geometry geom = (Geometry) ft.getDefaultGeometry(); + + if (MGC.coord2Point(coord).within(geom)) { + zone = ft; + break; + } + } + return zone; + } + + @Override + public void reset(int iteration) { + personArrivalCoordMap.clear(); + personDepartureCoordMap.clear(); + } +} diff --git a/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareConfigGroup.java b/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareConfigGroup.java index 20c9291dc1b..8abf76dcb37 100644 --- a/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareConfigGroup.java +++ b/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareConfigGroup.java @@ -13,7 +13,7 @@ public class PtFareConfigGroup extends ReflectiveConfigGroup { public static final String APPLY_UPPER_BOUND = "applyUpperBound"; public static final String UPPER_BOUND_FACTOR = "upperBoundFactor"; - public enum PtFareCalculationModels {distanceBased} // More to come (e.g. zone based, hybrid...) + public enum PtFareCalculationModels {distanceBased, fareZoneBased} // More to come (e.g. zone based, hybrid...) private static final String PT_FARE_CALCULATION_CMT = "PT fare calculation scheme. Current implementation: distanceBased (more to come...)"; public static final String UPPER_BOUND_FACTOR_CMT = "When upper bound is applied, upperBound = upperBoundFactor * max Fare of the day. " + diff --git a/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareModule.java b/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareModule.java index 9bd25cc81f4..4da41c16612 100644 --- a/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareModule.java +++ b/contribs/vsp/src/main/java/playground/vsp/pt/fare/PtFareModule.java @@ -11,12 +11,15 @@ public void install() { getConfig().scoring().getModes().get(TransportMode.pt).setDailyMonetaryConstant(0); getConfig().scoring().getModes().get(TransportMode.pt).setMarginalUtilityOfDistance(0); PtFareConfigGroup ptFareConfigGroup = ConfigUtils.addOrGetModule(this.getConfig(), PtFareConfigGroup.class); + DistanceBasedPtFareParams distanceBasedPtFareParams = ConfigUtils.addOrGetModule(this.getConfig(), DistanceBasedPtFareParams.class); + if (ptFareConfigGroup.getPtFareCalculation() == PtFareConfigGroup.PtFareCalculationModels.distanceBased) { - DistanceBasedPtFareParams distanceBasedPtFareParams = ConfigUtils.addOrGetModule(this.getConfig(), DistanceBasedPtFareParams.class); - addEventHandlerBinding().toInstance(new DistanceBasedPtFareHandler(distanceBasedPtFareParams)); - } else { + addEventHandlerBinding().toInstance(new DistanceBasedPtFareHandler(distanceBasedPtFareParams)); + } else if (ptFareConfigGroup.getPtFareCalculation() == PtFareConfigGroup.PtFareCalculationModels.fareZoneBased) { + addEventHandlerBinding().toInstance(new FareZoneBasedPtFareHandler(distanceBasedPtFareParams)); + } else { throw new RuntimeException("Please choose from the following fare Calculation method: [" + - PtFareConfigGroup.PtFareCalculationModels.distanceBased + "]"); + PtFareConfigGroup.PtFareCalculationModels.distanceBased + ", " + PtFareConfigGroup.PtFareCalculationModels.fareZoneBased + "]"); } if (ptFareConfigGroup.getApplyUpperBound()) { diff --git a/contribs/vsp/src/test/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandlerTest.java b/contribs/vsp/src/test/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandlerTest.java new file mode 100644 index 00000000000..ec486ec2cee --- /dev/null +++ b/contribs/vsp/src/test/java/playground/vsp/pt/fare/FareZoneBasedPtFareHandlerTest.java @@ -0,0 +1,124 @@ +package playground.vsp.pt.fare; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.analysis.personMoney.PersonMoneyEventsAnalysisModule; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.population.*; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.scenario.MutableScenario; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.examples.ExamplesUtils; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static org.matsim.application.ApplicationUtils.globFile; + +public class FareZoneBasedPtFareHandlerTest { + + @RegisterExtension + private MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + void testFareZoneBasedPtFareHandler() { + + URL context = ExamplesUtils.getTestScenarioURL("kelheim"); + Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(context, "config.xml")); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + + PtFareConfigGroup ptFareConfigGroup = ConfigUtils.addOrGetModule(config, PtFareConfigGroup.class); + ptFareConfigGroup.setPtFareCalculationModel(PtFareConfigGroup.PtFareCalculationModels.fareZoneBased); + + DistanceBasedPtFareParams fareParams = ConfigUtils.addOrGetModule(config, DistanceBasedPtFareParams.class); + fareParams.setFareZoneShp(IOUtils.extendUrl(context, "ptTestArea/pt-area.shp").toString()); + + + ScoringConfigGroup scoring = ConfigUtils.addOrGetModule(config, ScoringConfigGroup.class); + + ScoringConfigGroup.ActivityParams homeParams = new ScoringConfigGroup.ActivityParams("home"); + ScoringConfigGroup.ActivityParams workParams = new ScoringConfigGroup.ActivityParams("work"); + homeParams.setTypicalDuration(8 * 3600.); + workParams.setTypicalDuration(8 * 3600.); + scoring.addActivityParams(homeParams); + scoring.addActivityParams(workParams); + + + MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); + + Population population = ScenarioUtils.createScenario(ConfigUtils.createConfig()).getPopulation(); + PopulationFactory fac = population.getFactory(); + + Person person = fac.createPerson(Id.createPersonId("fareTestPerson")); + Plan plan = fac.createPlan(); + + Activity home = fac.createActivityFromCoord("home", new Coord(710300.624,5422165.737)); +// bus to Saal (Donau) work location departs at 09:14 + home.setEndTime(9 * 3600.); + Activity work = fac.createActivityFromCoord("work", new Coord(714940.65,5420707.78)); +// rb17 to regensburg 2nd home location departs at 13:59 + work.setEndTime(13 * 3600. + 45 * 60); + Activity home2 = fac.createActivityFromCoord("home", new Coord(726634.40,5433508.07)); + + Leg leg = fac.createLeg(TransportMode.pt); + + plan.addActivity(home); + plan.addLeg(leg); + plan.addActivity(work); + plan.addLeg(leg); + plan.addActivity(home2); + + person.addPlan(plan); + population.addPerson(person); + scenario.setPopulation(population); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + install(new PtFareModule()); + install(new PersonMoneyEventsAnalysisModule()); + } + }); + controler.run(); + + Assertions.assertTrue(Files.exists(Path.of(utils.getOutputDirectory()))); + +// read personMoneyEvents.tsv and check if both fare entries do have the correct fare type + String filePath = globFile(Path.of(utils.getOutputDirectory()), "*output_personMoneyEvents.tsv*").toString(); + String line; + List events = new ArrayList<>(); + + try (BufferedReader br = IOUtils.getBufferedReader(filePath)) { +// skip header + br.readLine(); + + while ((line = br.readLine()) != null) { + events.add(line.split(";")); + } + } catch (IOException e) { + e.printStackTrace(); + } + + Assertions.assertEquals(2, events.size()); + Assertions.assertEquals(FareZoneBasedPtFareHandler.PT_FARE_ZONE_BASED, events.get(0)[4]); + Assertions.assertEquals(FareZoneBasedPtFareHandler.PT_GERMANWIDE_FARE_BASED, events.get(1)[4]); + } +} diff --git a/examples/scenarios/kelheim/ptTestArea/pt-area.cpg b/examples/scenarios/kelheim/ptTestArea/pt-area.cpg new file mode 100644 index 00000000000..3ad133c048f --- /dev/null +++ b/examples/scenarios/kelheim/ptTestArea/pt-area.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/examples/scenarios/kelheim/ptTestArea/pt-area.dbf b/examples/scenarios/kelheim/ptTestArea/pt-area.dbf new file mode 100644 index 00000000000..e17541dab99 Binary files /dev/null and b/examples/scenarios/kelheim/ptTestArea/pt-area.dbf differ diff --git a/examples/scenarios/kelheim/ptTestArea/pt-area.prj b/examples/scenarios/kelheim/ptTestArea/pt-area.prj new file mode 100644 index 00000000000..bd846aeb220 --- /dev/null +++ b/examples/scenarios/kelheim/ptTestArea/pt-area.prj @@ -0,0 +1 @@ +PROJCS["ETRS_1989_UTM_Zone_32N",GEOGCS["GCS_ETRS_1989",DATUM["D_ETRS_1989",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",9.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/examples/scenarios/kelheim/ptTestArea/pt-area.shp b/examples/scenarios/kelheim/ptTestArea/pt-area.shp new file mode 100644 index 00000000000..a798ec1b8bf Binary files /dev/null and b/examples/scenarios/kelheim/ptTestArea/pt-area.shp differ diff --git a/examples/scenarios/kelheim/ptTestArea/pt-area.shx b/examples/scenarios/kelheim/ptTestArea/pt-area.shx new file mode 100644 index 00000000000..534dab6a968 Binary files /dev/null and b/examples/scenarios/kelheim/ptTestArea/pt-area.shx differ diff --git a/matsim/pom.xml b/matsim/pom.xml index 17556c8ac08..3f2321ada6d 100644 --- a/matsim/pom.xml +++ b/matsim/pom.xml @@ -118,7 +118,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 3.3.0 + 3.3.1 diff --git a/matsim/src/main/java/org/matsim/run/gui/Gui.java b/matsim/src/main/java/org/matsim/run/gui/Gui.java index 4b826b14842..1ca4a9afc8d 100644 --- a/matsim/src/main/java/org/matsim/run/gui/Gui.java +++ b/matsim/src/main/java/org/matsim/run/gui/Gui.java @@ -91,6 +91,11 @@ public class Gui extends JFrame { private File configFile; private File lastUsedDirectory; + + /** + * This is the working directory for the simulation. If it is null, the working directory is the directory of the config file. + */ + private File workingDirectory = null; private ConfigEditor editor = null; private ScheduleValidatorWindow transitValidator = null; @@ -439,6 +444,8 @@ private void startMATSim() { textStdOut.setText(""); textErrOut.setText(""); + String cwd = workingDirectory == null ? new File(txtConfigfilename.getText()).getParent() : workingDirectory.getAbsolutePath(); + new Thread(() -> { String classpath = System.getProperty("java.class.path"); String[] cpParts = classpath.split(File.pathSeparator); @@ -457,8 +464,7 @@ private void startMATSim() { "--add-exports", "java.desktop/sun.java2d=ALL-UNNAMED", mainClass, txtConfigfilename.getText() }; // see https://jogamp.org/bugzilla/show_bug.cgi?id=1317#c21 and/or https://github.com/matsim-org/matsim-libs/pull/2940 - exeRunner = ExeRunner.run(cmdArgs, textStdOut, textErrOut, - new File(txtConfigfilename.getText()).getParent()); + exeRunner = ExeRunner.run(cmdArgs, textStdOut, textErrOut, cwd); int exitcode = exeRunner.waitForFinish(); exeRunner = null; @@ -575,6 +581,10 @@ public static void main(String[] args) { Gui.show("MATSim", RunMatsim.class, args.length == 1 ? new File(args[0]) : null); } + public void setWorkingDirectory(File cwd) { + this.workingDirectory = cwd; + } + // Is it a problem to make the following available to the outside? If so, why? Would it // be better to rather copy/paste the above code and start from there? kai, jun/aug'18 diff --git a/pom.xml b/pom.xml index 3f97fb8c5d3..404796a50b1 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ commons-codec commons-codec - 1.17.0 + 1.17.1 org.apache.commons @@ -205,7 +205,7 @@ com.google.errorprone error_prone_annotations - 2.28.0 + 2.29.2 @@ -420,7 +420,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins