diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml index bdf0d7bfcde..8a7054763c5 100644 --- a/.github/workflows/code-coverage.yaml +++ b/.github/workflows/code-coverage.yaml @@ -26,7 +26,7 @@ jobs: run: mvn verify -P jacoco --batch-mode --also-make --projects matsim -Dmaven.test.redirectTestOutputToFile -Dmatsim.preferLocalDtds=true - name: Push coverage to CodeCov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: ./matsim/target/site/jacoco/jacoco.xml diff --git a/.github/workflows/deploy-on-pr-merge.yaml b/.github/workflows/deploy-on-pr-merge.yaml index ef046e1bdb0..11a60d440a6 100644 --- a/.github/workflows/deploy-on-pr-merge.yaml +++ b/.github/workflows/deploy-on-pr-merge.yaml @@ -46,7 +46,7 @@ jobs: - name: Submit Dependency Graph # Generate a complete dependency graph and submit the graph to the GitHub repository. # The goal is to improve security alerts from dependabot, because dependabot is not able to compute the complete dependency graph. - uses: advanced-security/maven-dependency-submission-action@v3 + uses: advanced-security/maven-dependency-submission-action@v4 env: MAVEN_OPTS: -Xmx2g diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml index a4eb837efcb..a0d383b992d 100644 --- a/.github/workflows/verify-push.yaml +++ b/.github/workflows/verify-push.yaml @@ -75,7 +75,7 @@ jobs: - name: Detect changes against master # we only want to build matsim (module) if changes are not limited to contribs id: detect-changes - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v3 if: ${{matrix.module == 'matsim'}} with: filters: | diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java index db9adc47ccd..43420197e8e 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java @@ -39,7 +39,7 @@ import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.TransportModeNetworkFilter; import org.matsim.core.utils.geometry.CoordUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.facilities.*; import org.opengis.feature.simple.SimpleFeature; @@ -225,7 +225,7 @@ public static final ActivityFacilities createFacilityForEachLink(String facility public static final ActivityFacilities createFacilityFromBuildingShapefile(String shapeFileName, String identifierCaption, String numberOfHouseholdsCaption) { - ShapeFileReader shapeFileReader = new ShapeFileReader(); + GeoFileReader shapeFileReader = new GeoFileReader(); Collection features = shapeFileReader.readFileAndInitialize(shapeFileName); ActivityFacilities facilities = FacilitiesUtils.createActivityFacilities("DensitiyFacilities"); diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/VoronoiExample.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/VoronoiExample.java index 24544971132..382e13b68f7 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/VoronoiExample.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/VoronoiExample.java @@ -28,17 +28,17 @@ import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.triangulate.VoronoiDiagramBuilder; import org.matsim.contrib.matrixbasedptrouter.utils.BoundingBox; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; /** * @author dziemke */ class VoronoiExample { - + public static void main(String[] args) { GeometryFactory geometryFactory = new GeometryFactory(); - + Collection sites = new ArrayList<>(); sites.add(new Coordinate(70, 70)); sites.add(new Coordinate(50, 150)); @@ -48,16 +48,16 @@ public static void main(String[] args) { sites.add(new Coordinate(250, 150)); sites.add(new Coordinate(350, 50)); sites.add(new Coordinate(370, 170)); - + VoronoiDiagramBuilder voronoiDiagramBuilder = new VoronoiDiagramBuilder(); - voronoiDiagramBuilder.setSites(sites); - + voronoiDiagramBuilder.setSites(sites); + List polygons = voronoiDiagramBuilder.getSubdivision().getVoronoiCellPolygons(geometryFactory); - + BoundingBox boundingBox = BoundingBox.createBoundingBox(0, 0, 400, 200); Polygon boundingPolygon = VoronoiGeometryUtils.createBoundingPolygon(boundingBox); Collection cutGeometries = VoronoiGeometryUtils.cutPolygonsByBoundary(polygons, boundingPolygon); Collection features = VoronoiGeometryUtils.createFeaturesFromPolygons(cutGeometries); - ShapeFileWriter.writeGeometries(features, "/Users/dominik/voronoi_test.shp"); + GeoFileWriter.writeGeometries(features, "/Users/dominik/voronoi_test.shp"); } } diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GeoserverUpdater.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GeoserverUpdater.java index 2f25f641a4c..a5799e30983 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GeoserverUpdater.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GeoserverUpdater.java @@ -47,7 +47,7 @@ import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.facilities.ActivityFacility; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; @@ -74,7 +74,7 @@ public GeoserverUpdater (String crs, String name, Map, Geom this.pushing2Geoserver = pushing2Geoserver; this.createQGisOutput = createQGisOutput; } - + private Map, Map> accessibilitiesMap = new HashMap<>() ; @Override @@ -95,16 +95,16 @@ public void finish() { SimpleFeatureTypeBuilder featureTypeBuilder = createFeatureTypeBuilder(); SimpleFeatureType featureType = featureTypeBuilder.buildFeatureType(); DefaultFeatureCollection featureCollection = createFeatureCollection(geometryFactory, featureType); - + if (outputDirectory != null) { File file = new File(outputDirectory); file.mkdirs(); } - + if (createQGisOutput) { - ShapeFileWriter.writeGeometries(featureCollection, outputDirectory + "/result.shp"); + GeoFileWriter.writeGeometries(featureCollection, outputDirectory + "/result.shp"); } - + if (pushing2Geoserver) { updateOnGeoserver(featureType, featureCollection); } @@ -130,7 +130,7 @@ private DefaultFeatureCollection createFeatureCollection(GeometryFactory geometr LOG.info("Start creating features from accessibility data."); DefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal", featureType); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); - + CoordinateTransformation transformation = TransformationFactory.getCoordinateTransformation(this.crs, TransformationFactory.WGS84); for (Entry, Map> entry : accessibilitiesMap.entrySet()) { @@ -145,10 +145,10 @@ private DefaultFeatureCollection createFeatureCollection(GeometryFactory geometr i++; } featureBuilder.add(geometryFactory.createPolygon(transformedCoordinates)); - + featureBuilder.add(Integer.parseInt(entry.getKey().getFirst().getId().toString())); featureBuilder.add(entry.getKey().getSecond()); - + for (Modes4Accessibility modeEnum : Modes4Accessibility.values()) { String mode = modeEnum.toString(); Double accessibility = entry.getValue().get(mode); @@ -188,14 +188,14 @@ private void updateOnGeoserver(SimpleFeatureType featureType, DefaultFeatureColl // There have been errors with the data store if the dependency "gt-jdbc-postgis", version 13.0 was missing! DataStore dataStore = DataStoreFinder.getDataStore(params); LOG.info("dataStore = " + dataStore); - + // Remove schema in case it already exists try { dataStore.removeSchema(name); } catch (IllegalArgumentException e) { LOG.warn("Could not remove schema. Probably, it has not existed yet."); } - + dataStore.createSchema(featureType); SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(name); featureStore.addFeatures(featureCollection); diff --git a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/AccidentsNetworkModification.java b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/AccidentsNetworkModification.java index 6794cd558d6..7388ce718d5 100644 --- a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/AccidentsNetworkModification.java +++ b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/AccidentsNetworkModification.java @@ -44,7 +44,7 @@ import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; /** @@ -59,26 +59,26 @@ public class AccidentsNetworkModification { public AccidentsNetworkModification(Scenario scenario) { this.scenario = scenario; } - + public Network setLinkAttributsBasedOnOSMFile(String landuseOsmFile, String osmCRS, String[] tunnelLinkIDs, String[] planfreeLinkIDs) throws MalformedURLException, IOException { - + AccidentsConfigGroup accidentsCfg = (AccidentsConfigGroup) scenario.getConfig().getModules().get(AccidentsConfigGroup.GROUP_NAME); - + Map landUseFeaturesBB = new HashMap<>(); Map landUseDataBB = new HashMap<>(); - + log.info("Initializing all link-specific information..."); - + if (landuseOsmFile == null) { log.warn("Landuse shape file is null. Using default values..."); } else { SimpleFeatureSource ftsLandUseBB; if (!landuseOsmFile.startsWith("http")) { - ftsLandUseBB = ShapeFileReader.readDataFile(landuseOsmFile); + ftsLandUseBB = GeoFileReader.readDataFile(landuseOsmFile); } else { ftsLandUseBB = FileDataStoreFinder.getDataStore(new URL(landuseOsmFile)).getFeatureSource(); - } + } try (SimpleFeatureIterator itLandUseBB = ftsLandUseBB.getFeatures().features()) { while (itLandUseBB.hasNext()) { SimpleFeature ftLandUseBB = itLandUseBB.next(); @@ -95,23 +95,23 @@ public Network setLinkAttributsBasedOnOSMFile(String landuseOsmFile, String osmC e.printStackTrace(); } } - + int linkCounter = 0; for (Link link : this.scenario.getNetwork().getLinks().values()) { - + if (linkCounter % 100 == 0) { log.info("Link #" + linkCounter + " (" + (int) ((double) linkCounter / this.scenario.getNetwork().getLinks().size() * 100) + "%)"); } - linkCounter++; - + linkCounter++; + link.getAttributes().putAttribute(accidentsCfg.getAccidentsComputationMethodAttributeName(), AccidentsConfigGroup.AccidentsComputationMethod.BVWP.toString()); - + ArrayList bvwpRoadType = new ArrayList<>(); - + // 'plangleich', 'planfrei' or tunnel? bvwpRoadType.add(0, 1); - + for(int j=0; j < planfreeLinkIDs.length; j++){ if(planfreeLinkIDs[j].equals(String.valueOf(link.getId()))){ bvwpRoadType.set(0, 0); // Change to Plan free @@ -119,7 +119,7 @@ public Network setLinkAttributsBasedOnOSMFile(String landuseOsmFile, String osmC break; } } - + for(int i=0; i < tunnelLinkIDs.length; i++){ if(tunnelLinkIDs[i].equals(String.valueOf(link.getId()))){ bvwpRoadType.set(0, 2); // Change to Tunnel @@ -127,25 +127,25 @@ public Network setLinkAttributsBasedOnOSMFile(String landuseOsmFile, String osmC break; } } - + // builtup or not builtup area? String osmLandUseFeatureBBId = getOSMLandUseFeatureBBId(link, landUseFeaturesBB, osmCRS); - - if (osmLandUseFeatureBBId == null) { + + if (osmLandUseFeatureBBId == null) { log.warn("No area type found for link " + link.getId() + ". Using default value: not built-up area."); if (link.getFreespeed() > 16.) { bvwpRoadType.add(1, 0); } else { bvwpRoadType.add(1, 2); } - + } else { String landUseTypeBB = landUseDataBB.get(osmLandUseFeatureBBId); if (landUseTypeBB.matches("commercial|industrial|recreation_ground|residential|retail")) { //built-up area if (link.getFreespeed() > 16.) { bvwpRoadType.add(1, 1); } else { - bvwpRoadType.add(1, 3); + bvwpRoadType.add(1, 3); } } else { if (link.getFreespeed() > 16.) { @@ -163,85 +163,85 @@ public Network setLinkAttributsBasedOnOSMFile(String landuseOsmFile, String osmC numberOfLanesBVWP = (int) link.getNumberOfLanes(); } bvwpRoadType.add(2, numberOfLanesBVWP); - + link.getAttributes().putAttribute( AccidentsConfigGroup.BVWP_ROAD_TYPE_ATTRIBUTE_NAME, bvwpRoadType.get(0) + "," + bvwpRoadType.get(1) + "," + bvwpRoadType.get(2)); } log.info("Initializing all link-specific information... Done."); return scenario.getNetwork(); } - + private String getOSMLandUseFeatureBBId(Link link, Map landUseFeaturesBB, String osmCRS) { - + if (landUseFeaturesBB == null || landUseFeaturesBB.isEmpty()) return null; - + CoordinateTransformation ctScenarioCRS2osmCRS = TransformationFactory.getCoordinateTransformation(this.scenario.getConfig().global().getCoordinateSystem(), osmCRS); - + Coord linkCoordinateTransformedToOSMCRS = ctScenarioCRS2osmCRS.transform(link.getCoord()); // this Method gives the middle point of the link back Point pMiddle = MGC.xy2Point(linkCoordinateTransformedToOSMCRS.getX(), linkCoordinateTransformedToOSMCRS.getY()); - + Coord linkStartCoordinateTransformedToOSMCRS = ctScenarioCRS2osmCRS.transform(link.getFromNode().getCoord()); Point pStart = MGC.xy2Point(linkStartCoordinateTransformedToOSMCRS.getX(), linkStartCoordinateTransformedToOSMCRS.getY()); - + Coord linkEndCoordinateTransformedToOSMCRS = ctScenarioCRS2osmCRS.transform(link.getToNode().getCoord()); Point pEnd = MGC.xy2Point(linkEndCoordinateTransformedToOSMCRS.getX(), linkEndCoordinateTransformedToOSMCRS.getY()); - + String osmLandUseFeatureBBId = null; - + for (SimpleFeature feature : landUseFeaturesBB.values()) { if (((Geometry) feature.getDefaultGeometry()).contains(pMiddle)) { return osmLandUseFeatureBBId = feature.getAttribute("osm_id").toString(); } } - + for (SimpleFeature feature : landUseFeaturesBB.values()) { if (((Geometry) feature.getDefaultGeometry()).contains(pStart)) { return osmLandUseFeatureBBId = feature.getAttribute("osm_id").toString(); } } - + for (SimpleFeature feature : landUseFeaturesBB.values()) { if (((Geometry) feature.getDefaultGeometry()).contains(pEnd)) { return osmLandUseFeatureBBId = feature.getAttribute("osm_id").toString(); } } - + // look around the link - + GeometryFactory geoFac = new GeometryFactory(); CoordinateTransformation cTosmCRSToGK4 = TransformationFactory.getCoordinateTransformation(osmCRS, "EPSG:31468"); - - double distance = 10.0; - + + double distance = 10.0; + while (osmLandUseFeatureBBId == null && distance <= 500) { Coord coordGK4 = cTosmCRSToGK4.transform(MGC.coordinate2Coord(pMiddle.getCoordinate())); Point pGK4 = geoFac.createPoint(MGC.coord2Coordinate(coordGK4)); - + Point pRightGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() + distance, pGK4.getY())); Point pRight = transformPointFromGK4ToOSMCRS(pRightGK4, osmCRS); - + Point pDownGK4 = geoFac.createPoint(new Coordinate(pGK4.getX(), pGK4.getY() - distance)); Point pDown = transformPointFromGK4ToOSMCRS(pDownGK4, osmCRS); - + Point pLeftGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() - distance, pGK4.getY())); Point pLeft = transformPointFromGK4ToOSMCRS(pLeftGK4, osmCRS); - + Point pUpGK4 = geoFac.createPoint(new Coordinate(pGK4.getX(), pGK4.getY() + distance)); Point pUp = transformPointFromGK4ToOSMCRS(pUpGK4, osmCRS); - + Point pUpRightGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() + distance, pGK4.getY() + distance)); Point pUpRight = transformPointFromGK4ToOSMCRS(pUpRightGK4, osmCRS); - + Point pDownRightGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() + distance, pGK4.getY() - distance)); Point pDownRight = transformPointFromGK4ToOSMCRS(pDownRightGK4, osmCRS); - + Point pDownLeftGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() - distance, pGK4.getY() - distance)); Point pDownLeft = transformPointFromGK4ToOSMCRS(pDownLeftGK4, osmCRS); - + Point pUpLeftGK4 = geoFac.createPoint(new Coordinate(pGK4.getX() - distance, pGK4.getY() + distance)); Point pUpLeft = transformPointFromGK4ToOSMCRS(pUpLeftGK4, osmCRS); - + for (SimpleFeature feature : landUseFeaturesBB.values()) { - + if (((Geometry) feature.getDefaultGeometry()).contains(pRight)) { osmLandUseFeatureBBId = feature.getAttribute("osm_id").toString(); return osmLandUseFeatureBBId; @@ -268,17 +268,17 @@ private String getOSMLandUseFeatureBBId(Link link, Map la return osmLandUseFeatureBBId; } } - + distance += 10.0; } - + log.warn("No area type found. Returning null..."); return null; } private Point transformPointFromGK4ToOSMCRS(Point pointGK4, String osmCRS) { CoordinateTransformation ctGK4toOSMCRS = TransformationFactory.getCoordinateTransformation("EPSG:31468", osmCRS); - + Coord coordGK4 = MGC.coordinate2Coord(pointGK4.getCoordinate()); Point pointOSMCRS = new GeometryFactory().createPoint(MGC.coord2Coordinate(ctGK4toOSMCRS.transform(coordGK4))); return pointOSMCRS; diff --git a/contribs/analysis/pom.xml b/contribs/analysis/pom.xml index 3894c728d93..8238a60b8bb 100644 --- a/contribs/analysis/pom.xml +++ b/contribs/analysis/pom.xml @@ -32,7 +32,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.3 + 5.3.1 diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java index 21e616f6896..c1f1f9ea36e 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java @@ -41,7 +41,7 @@ import org.matsim.core.trafficmonitoring.TimeBinUtils; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PolylineFeatureFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.core.utils.io.IOUtils; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -190,7 +190,7 @@ private void writeRows(BufferedWriter timesWriter, boolean absolute) throws IOEx public void writeAbsoluteSHPTravelTimes(String file, CoordinateReferenceSystem crs, boolean ignoreExitLinks) { try { Collection ft = generateSHPFileData(crs, this.network, true, ignoreExitLinks); - ShapeFileWriter.writeGeometries(ft, file); + GeoFileWriter.writeGeometries(ft, file); } catch (Exception e) { throw new RuntimeException(e); } @@ -199,7 +199,7 @@ public void writeAbsoluteSHPTravelTimes(String file, CoordinateReferenceSystem c public void writeRelativeSHPTravelTimes(String file, CoordinateReferenceSystem crs, boolean ignoreExitLinks) { try { Collection ft = generateSHPFileData(crs, this.network, false, ignoreExitLinks); - ShapeFileWriter.writeGeometries(ft, file); + GeoFileWriter.writeGeometries(ft, file); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/contribs/application/pom.xml b/contribs/application/pom.xml index 546d465e7d2..f15326461c8 100644 --- a/contribs/application/pom.xml +++ b/contribs/application/pom.xml @@ -79,6 +79,7 @@ gtfs2matsim master-33809c4f0f-1 + org.geotools * @@ -87,6 +88,18 @@ org.matsim * + + com.amazonaws + * + + + com.graphql-java + * + + + org.postgresql + * + diff --git a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java index bc9c97f08b3..083a315ecb5 100644 --- a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java +++ b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java @@ -1,5 +1,9 @@ package org.matsim.application; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; @@ -7,11 +11,14 @@ import org.matsim.application.options.InputOptions; import org.matsim.application.options.OutputOptions; import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigAliases; +import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import picocli.CommandLine; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -21,13 +28,20 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; public class ApplicationUtils { private static final Logger log = LogManager.getLogger(ApplicationUtils.class); + /** + * This encoding indicates command line was used to start the jar. + */ + private static final String WIN_CLI_ENCODING = "cp850"; + private ApplicationUtils() { } @@ -46,7 +60,197 @@ public static String[] mergeArgs(String[] args, String... defaultArgs) { } /** - * Extends a context (usually config location) with an relative filename. + * Utility method to check if a jar might be run from the desktop (using double-click). + */ + public static boolean isRunFromDesktop() { + + // check if gui was explicitly enabled + String env = System.getenv().getOrDefault("RUN_GUI", "undefined"); + if (env.equalsIgnoreCase("true") || env.equals("1")) + return true; + else if (env.equalsIgnoreCase("false") || env.equals("0")) + return false; + + String property = System.getProperty("RUN_GUI", "undefined"); + if (property.equalsIgnoreCase("true") || property.equals("1")) + return true; + else if (property.equalsIgnoreCase("false") || property.equals("0")) + return false; + + String macIdentifier = System.getenv().getOrDefault("__CFBundleIdentifier", "none"); + + if (macIdentifier.equals("com.apple.java.JarLauncher") || macIdentifier.equals("com.apple.JavaLauncher")) + return true; + + String os = System.getProperty("os.name"); + + if (os.toLowerCase().startsWith("windows")) { + + // presence of the prompt variable indicates that the jar was run from the command line + boolean hasPrompt = System.getenv().containsKey("PROMPT"); + + // this prompt is not set in PowerShell, so we need another check + if (hasPrompt) + return false; + + // stdout.encoding from CLI are usually cp850 + String encoding = System.getProperty("stdout.encoding", "none"); + String sunEncoding = System.getProperty("sun.stdout.encoding", "none"); + + if (encoding.equals(WIN_CLI_ENCODING) || sunEncoding.equals(WIN_CLI_ENCODING)) + return false; + + // Run from intelij, will not start the gui by default + if (System.getenv().containsKey("IDEA_INITIAL_DIRECTORY")) + return false; + // also file.encoding=UTF-8, seems to be set by default in IntelliJ + + // if no other cli indicators are present, we have to assume that the jar was run from the desktop + return true; + } + + return false; + + } + + /** + * Apply run configuration in yaml format. + */ + public static void applyConfigUpdate(Config config, Path yaml) { + + if (!Files.exists(yaml)) { + throw new IllegalArgumentException("Given config yaml does not exist: " + yaml); + } + + ObjectMapper mapper = new ObjectMapper(new YAMLFactory() + .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)); + + ConfigAliases aliases = new ConfigAliases(); + Deque emptyStack = new ArrayDeque<>(); + + try (BufferedReader reader = Files.newBufferedReader(yaml)) { + + JsonNode node = mapper.readTree(reader); + + Iterator> fields = node.fields(); + + while (fields.hasNext()) { + Map.Entry field = fields.next(); + String configGroupName = aliases.resolveAlias(field.getKey(), emptyStack); + ConfigGroup group = config.getModules().get(configGroupName); + if (group == null) { + group = new ConfigGroup(configGroupName); + config.addModule(group); + } + + applyNodeToConfigGroup(field.getValue(), group); + } + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + } + + /** + * Sets the json config into + */ + private static void applyNodeToConfigGroup(JsonNode node, ConfigGroup group) { + + Iterator> fields = node.fields(); + + while (fields.hasNext()) { + Map.Entry field = fields.next(); + + if (isParameterSet(field.getValue())) { + + // store the current parameters sets, newly added sets are not merged with each other + List params = new ArrayList<>(group.getParameterSets(field.getKey())); + + for (JsonNode item : field.getValue()) { + + + // Special case of parameter sets that have only one entry + if (field.getValue().size() == 1 && params.size() == 1 && field.getValue().get(0).isObject()) { + + applyNodeToConfigGroup(field.getValue().get(0), params.get(0)); + + } else { + + applyNodeAsParameterSet(field.getKey(), item, group, params); + } + } + } else { + + if (field.getValue().isTextual()) + group.addParam(field.getKey(), field.getValue().textValue()); + else if (field.getValue().isArray()) { + // arrays are joined using "," + Stream stream = StreamSupport.stream(field.getValue().spliterator(), false); + String string = stream.map(n -> n.isTextual() ? n.textValue() : n.toString()).collect(Collectors.joining(",")); + group.addParam(field.getKey(), string); + } else + group.addParam(field.getKey(), field.getValue().toString()); + } + } + } + + /** + * Any array of complex object can be considered a config group. + */ + private static boolean isParameterSet(JsonNode node) { + + if (!node.isArray()) + return false; + + // any object can be assigned as parameter set + for (JsonNode el : node) { + if (!el.isObject()) + return false; + } + + return true; + } + + /** + * Handle possible update and creation of parameter sets within a config group. + */ + private static void applyNodeAsParameterSet(String groupName, JsonNode item, ConfigGroup group, List params) { + + Iterator> it = item.fields(); + + // There was at least one matching group + boolean matched = false; + + while (!params.isEmpty() && it.hasNext()) { + + Map.Entry attr = it.next(); + List candidates = params.stream() + .filter(p -> p.getParams().containsKey(attr.getKey())) + .filter(p -> p.getParams().get(attr.getKey()) + .equals(attr.getValue().isTextual() ? attr.getValue().textValue() : attr.getValue().toString())) + .toList(); + + if (candidates.isEmpty()) + break; + + matched = true; + params = candidates; + } + + if (params.size() > 1) { + throw new IllegalArgumentException("Ambiguous parameter set: " + item); + } else if (params.size() == 1 && matched) { + applyNodeToConfigGroup(item, params.get(0)); + } else { + ConfigGroup newGroup = group.createParameterSet(groupName); + group.addParameterSet(newGroup); + applyNodeToConfigGroup(item, newGroup); + } + } + + /** + * Extends a context (usually config location) with a relative filename. * If the results is a local file, the path will be returned. Otherwise, it will be an url. * The results can be used as input for command line parameter or {@link IOUtils#resolveFileOrResource(String)}. * @@ -75,10 +279,11 @@ public static Path globFile(Path path, String pattern) { PathMatcher m = path.getFileSystem().getPathMatcher("glob:" + pattern); try { - return Files.list(path) - .filter(p -> m.matches(p.getFileName())) - .findFirst() - .orElseThrow(() -> new IllegalStateException("No " + pattern + " file found.")); + try (Stream list = Files.list(path)) { + return list.filter(p -> m.matches(p.getFileName())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No " + pattern + " file found.")); + } } catch (IOException e) { throw new RuntimeException(e); } 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 c1951c1b2a4..7d63518e323 100644 --- a/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java +++ b/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java @@ -1,17 +1,10 @@ package org.matsim.application; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import com.google.common.collect.Lists; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; -import org.matsim.application.commands.RunScenario; -import org.matsim.application.commands.ShowGUI; import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigAliases; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.ControllerConfigGroup; @@ -23,15 +16,12 @@ import picocli.CommandLine; import javax.annotation.Nullable; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; -import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.concurrent.Callable; @@ -78,6 +68,8 @@ public abstract class MATSimApplication implements Callable, CommandLin private static final Logger log = LogManager.getLogger(MATSimApplication.class); + private static final String ARGS_DELIMITER = "ยง$"; + public static final String COLOR = "@|bold,fg(81) "; static final String DEFAULT_NAME = "MATSimApplication"; static final String HEADER = COLOR + @@ -170,7 +162,7 @@ public Integer call() throws Exception { Objects.requireNonNull(config); if (specs != null) - applySpecs(config, specs); + ApplicationUtils.applyConfigUpdate(config, specs); if (remainingArgs != null) { String[] args = remainingArgs.stream().map(s -> s.replace("-c:", "--config:")).toArray(String[]::new); @@ -210,99 +202,22 @@ public Integer call() throws Exception { } catch (Exception e) { log.error("Error running post-processing", e); } - } - } return 0; } - /** - * Apply given specs to config. - */ - private static void applySpecs(Config config, Path specs) { - - if (!Files.exists(specs)) { - throw new IllegalArgumentException("Desired run config does not exist:" + specs); - } - - ObjectMapper mapper = new ObjectMapper(new YAMLFactory() - .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)); - - ConfigAliases aliases = new ConfigAliases(); - Deque emptyStack = new ArrayDeque<>(); - - try (BufferedReader reader = Files.newBufferedReader(specs)) { - - JsonNode node = mapper.readTree(reader); - - Iterator> fields = node.fields(); - - while (fields.hasNext()) { - Map.Entry field = fields.next(); - String configGroupName = aliases.resolveAlias(field.getKey(), emptyStack); - ConfigGroup group = config.getModules().get(configGroupName); - if (group == null) { - log.warn("Config group not found: {}", configGroupName); - continue; - } - - applyNodeToConfigGroup(field.getValue(), group); - } - - } catch (IOException e) { - e.printStackTrace(); - } - + File getConfigPath() { + return configPath; } - /** - * Sets the json config into - */ - private static void applyNodeToConfigGroup(JsonNode node, ConfigGroup group) { - - Iterator> fields = node.fields(); - - while (fields.hasNext()) { - Map.Entry field = fields.next(); - - if (field.getValue().isArray()) { - - Collection params = group.getParameterSets(field.getKey()); - - // single node and entry merged directly - if (field.getValue().size() == 1 && params.size() == 1) { - applyNodeToConfigGroup(field.getValue().get(0), params.iterator().next()); - } else { - - for (JsonNode item : field.getValue()) { - - Map.Entry first = item.fields().next(); - Optional m = params.stream().filter(p -> p.getParams().get(first.getKey()).equals(first.getValue().textValue())).findFirst(); - if (m.isEmpty()) - throw new IllegalArgumentException("Could not find matching param by key" + first); - - applyNodeToConfigGroup(item, m.get()); - } - } - - } else { - - if (!field.getValue().isValueNode()) - throw new IllegalArgumentException("Received complex value type instead of primitive: " + field.getValue()); - - - if (field.getValue().isTextual()) - group.addParam(field.getKey(), field.getValue().textValue()); - else - group.addParam(field.getKey(), field.getValue().toString()); - } - } + @Nullable + String getDefaultScenario() { + return defaultScenario; } - /** * Custom module configs that will be added to the {@link Config} object. * @@ -429,6 +344,13 @@ public static void run(Class clazz, String... args) l.addAll(Arrays.asList(args)); args = l.toArray(new String[0]); + + // Pass stored args to the instance as well + if (System.getenv().containsKey("MATSIM_GUI_ARGS")) { + String[] guiArgs = System.getenv("MATSIM_GUI_ARGS").split(ARGS_DELIMITER); + if (guiArgs.length > 0) + args = ApplicationUtils.mergeArgs(args, guiArgs); + } } prepareArgs(args); @@ -451,6 +373,50 @@ public static void run(Class clazz, String... args) System.exit(code); } + /** + * Convenience method to run a scenario from code or automatically with gui when desktop application is detected. + * This method may also be used to predefine some default arguments. + * @param clazz class of the scenario to run + * @param args pass arguments from the main method + * @param defaultArgs predefined default arguments that will always be present + */ + public static void runWithDefaults(Class clazz, String[] args, String... defaultArgs) { + + if (ApplicationUtils.isRunFromDesktop() && args.length == 0) { + + if (defaultArgs.length > 0) { + String value = String.join(ARGS_DELIMITER, defaultArgs); + System.setProperty("MATSIM_GUI_ARGS", value); + } + + // args are empty when run from desktop and is not used + run(clazz, "gui"); + + } else { + // run if no other command is present + if (args.length > 0) { + // valid command is present + if (args[0].equals("run") || args[0].equals("prepare") || args[0].equals("analysis") || args[0].equals("gui") ){ + // If this is a run command, the default args can be applied + if (args[0].equals("run")) + args = ApplicationUtils.mergeArgs(args, defaultArgs); + + } else { + // Automatically add run command + String[] runArgs = ApplicationUtils.mergeArgs(new String[]{"run"}, defaultArgs); + args = ApplicationUtils.mergeArgs(runArgs, args); + } + + } else + // Automatically add run command + args = ApplicationUtils.mergeArgs(new String[]{"run"}, defaultArgs); + + log.info("Running {} with: {}", clazz.getSimpleName(), String.join(" ", args)); + + run(clazz, args); + } + } + /** * Calls an application class and forwards any exceptions. * @@ -512,7 +478,7 @@ public static Controler prepare(MATSimApplication app, Config config, String... config = tmp != null ? tmp : config; if (app.specs != null) { - applySpecs(config, app.specs); + ApplicationUtils.applyConfigUpdate(config, app.specs); } if (app.remainingArgs != null) { @@ -744,7 +710,7 @@ public Integer call() throws Exception { } /** - * Option to switch post processing behavour + * Option to switch post-processing behaviour */ public enum PostProcessOption { diff --git a/contribs/application/src/main/java/org/matsim/application/commands/RunScenario.java b/contribs/application/src/main/java/org/matsim/application/RunScenario.java similarity index 67% rename from contribs/application/src/main/java/org/matsim/application/commands/RunScenario.java rename to contribs/application/src/main/java/org/matsim/application/RunScenario.java index 3221069de95..460d5585ee5 100644 --- a/contribs/application/src/main/java/org/matsim/application/commands/RunScenario.java +++ b/contribs/application/src/main/java/org/matsim/application/RunScenario.java @@ -1,12 +1,11 @@ -package org.matsim.application.commands; +package org.matsim.application; -import org.matsim.application.MATSimApplication; import picocli.CommandLine; import java.util.concurrent.Callable; @CommandLine.Command(name = "run", description = "Run the scenario") -public class RunScenario implements Callable { +class RunScenario implements Callable { @CommandLine.ParentCommand private MATSimApplication app; diff --git a/contribs/application/src/main/java/org/matsim/application/commands/ShowGUI.java b/contribs/application/src/main/java/org/matsim/application/ShowGUI.java similarity index 65% rename from contribs/application/src/main/java/org/matsim/application/commands/ShowGUI.java rename to contribs/application/src/main/java/org/matsim/application/ShowGUI.java index 443f6a5cccb..e394bfd7b79 100644 --- a/contribs/application/src/main/java/org/matsim/application/commands/ShowGUI.java +++ b/contribs/application/src/main/java/org/matsim/application/ShowGUI.java @@ -1,14 +1,14 @@ -package org.matsim.application.commands; +package org.matsim.application; -import org.matsim.application.MATSimApplication; import org.matsim.run.gui.Gui; import picocli.CommandLine; +import java.io.File; import java.util.concurrent.Callable; import java.util.concurrent.Future; @CommandLine.Command(name = "gui", description = "Run the scenario through the MATSim GUI") -public class ShowGUI implements Callable { +class ShowGUI implements Callable { @CommandLine.ParentCommand private MATSimApplication parent; @@ -26,7 +26,17 @@ public Integer call() throws Exception { name = name.substring(MATSimApplication.COLOR.length(), name.length() - 4); } - Future f = Gui.show(name, parent.getClass()); + File configFile = null; + + // Try to load default config file + if (parent.getDefaultScenario() != null && new File(parent.getDefaultScenario()).exists()) + configFile = new File(parent.getDefaultScenario()); + + // override the default if present + if (parent.getConfigPath() != null && parent.getConfigPath().exists()) + configFile = parent.getConfigPath(); + + Future f = Gui.show(name, parent.getClass(), configFile); Gui gui = f.get(); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java index 35f11ab09d6..bc656e79a2f 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java @@ -16,6 +16,7 @@ import org.locationtech.jts.geom.Point; import org.matsim.application.CommandSpec; import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CsvOptions; import org.matsim.application.options.InputOptions; import org.matsim.application.options.OutputOptions; import org.matsim.application.options.ShpOptions; @@ -26,11 +27,12 @@ import tech.tablesaw.joining.DataFrameJoiner; import tech.tablesaw.selection.Selection; -import java.io.IOException; +import java.io.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.file.Files; import java.util.*; +import java.util.zip.GZIPInputStream; import static tech.tablesaw.aggregate.AggregateFunctions.count; @@ -93,7 +95,7 @@ public Integer call() throws Exception { Table persons = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("persons.csv"))) .columnTypesPartial(Map.of("person", ColumnType.TEXT)) .sample(false) - .separator(';').build()); + .separator(new CsvOptions().detectDelimiter(input.getPath("persons.csv"))).build()); int total = persons.rowCount(); @@ -134,8 +136,7 @@ public Integer call() throws Exception { Table trips = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("trips.csv"))) .columnTypesPartial(columnTypes) .sample(false) - .separator(';').build()); - + .separator(CsvOptions.detectDelimiter(input.getPath("trips.csv"))).build()); // Trip filter with start and end if (shp.isDefined() && filter == LocationFilter.trip_start_and_end) { diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/LinkStats.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/LinkStats.java index 09c3e910238..f5e2abbb323 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/LinkStats.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/LinkStats.java @@ -1,6 +1,6 @@ package org.matsim.application.analysis.traffic; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; diff --git a/contribs/application/src/main/java/org/matsim/application/options/CsvOptions.java b/contribs/application/src/main/java/org/matsim/application/options/CsvOptions.java index 64740d7346b..a022a6f5771 100644 --- a/contribs/application/src/main/java/org/matsim/application/options/CsvOptions.java +++ b/contribs/application/src/main/java/org/matsim/application/options/CsvOptions.java @@ -3,9 +3,11 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.lang.StringUtils; import org.matsim.core.utils.io.IOUtils; import picocli.CommandLine; +import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -40,24 +42,49 @@ public CsvOptions(CSVFormat.Predefined csvFormat) { this.csvFormat = csvFormat; } - /** - * Constructor with all available options. - */ - public CsvOptions(CSVFormat.Predefined csvFormat, Character csvDelimiter, Charset csvCharset) { - this.csvFormat = csvFormat; - this.csvDelimiter = csvDelimiter; - this.csvCharset = csvCharset; - } + /** + * Constructor with all available options. + */ + public CsvOptions(CSVFormat.Predefined csvFormat, Character csvDelimiter, Charset csvCharset) { + this.csvFormat = csvFormat; + this.csvDelimiter = csvDelimiter; + this.csvCharset = csvCharset; + } + + /** + * Detects possibly used delimiter from the header of a csv or tsv file. + */ + public static Character detectDelimiter(String path) throws IOException { + try (BufferedReader reader = IOUtils.getBufferedReader(path)) { + String firstLine = reader.readLine(); + + int comma = StringUtils.countMatches(firstLine, ","); + int semicolon = StringUtils.countMatches(firstLine, ";"); + int tab = StringUtils.countMatches(firstLine, "\t"); + + if (comma == 0 && semicolon == 0 && tab == 0) { + throw new IllegalArgumentException("No delimiter found in the first line of the file."); + } - /** + // Comma is preferred as the more likely format + if (comma >= semicolon && comma >= tab) { + return ','; + } else if (tab >= semicolon) + return '\t'; + else + return ';'; + } + } + + /** * Get the CSV format defined by the options. */ public CSVFormat getFormat() { - CSVFormat format = this.csvFormat.getFormat().withFirstRecordAsHeader(); + CSVFormat.Builder format = this.csvFormat.getFormat().builder().setSkipHeaderRecord(true); if (csvDelimiter != null) - format = format.withDelimiter(csvDelimiter); + format = format.setDelimiter(csvDelimiter); - return format; + return format.build(); } /** @@ -71,7 +98,7 @@ public CSVParser createParser(Path path) throws IOException { * Creates a new csv writer. */ public CSVPrinter createPrinter(Path path) throws IOException { - return new CSVPrinter(IOUtils.getBufferedWriter(path.toUri().toURL(), csvCharset, false), getFormat()); + return new CSVPrinter(IOUtils.getBufferedWriter(path.toUri().toURL(), csvCharset, false), getFormat()); } } diff --git a/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java b/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java index a541548a7d9..ab40fd1f3c8 100644 --- a/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java +++ b/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java @@ -18,7 +18,7 @@ import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.IdentityTransformation; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; @@ -161,7 +161,7 @@ public List readFeatures() { if (shpCharset != null) ds.setCharset(shpCharset); - return ShapeFileReader.getSimpleFeatures(ds); + return GeoFileReader.getSimpleFeatures(ds); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -341,13 +341,27 @@ public final class Index { } /** - * Query the index for first feature including a certain point. + * Query the index for first feature including matching the coordinate and return specified attribute. * * @return null when no features was found that contains the point */ @Nullable @SuppressWarnings("unchecked") - public String query(Coord coord) { + public T query(Coord coord) { + SimpleFeature ft = queryFeature(coord); + if (ft != null) + return (T) ft.getAttribute(attr); + + return null; + // throw new NoSuchElementException(String.format("No matching entry found for x:%f y:%f %s", x, y, p)); + } + + /** + * Query the index and return the whole feature. + */ + @Nullable + @SuppressWarnings("unchecked") + public SimpleFeature queryFeature(Coord coord) { // Because we can not easily transform the feature geometry with MATSim we have to do it the other way around... Coordinate p = MGC.coord2Coordinate(ct.transform(coord)); @@ -355,16 +369,16 @@ public String query(Coord coord) { for (SimpleFeature ft : result) { Geometry geom = (Geometry) ft.getDefaultGeometry(); if (geom.contains(MGC.coordinate2Point(p))) - return (String) ft.getAttribute(attr); + return ft; } return null; - // throw new NoSuchElementException(String.format("No matching entry found for x:%f y:%f %s", x, y, p)); } /** * Checks whether a coordinate is contained in any of the features. */ + @SuppressWarnings("unchecked") public boolean contains(Coord coord) { Coordinate p = MGC.coord2Coordinate(ct.transform(coord)); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/CreateLandUseShp.java b/contribs/application/src/main/java/org/matsim/application/prepare/CreateLandUseShp.java index c2abbfe1ae3..4f10c61e745 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/CreateLandUseShp.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/CreateLandUseShp.java @@ -1,6 +1,5 @@ package org.matsim.application.prepare; -import com.beust.jcommander.internal.Lists; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.geotools.data.DataStore; @@ -24,6 +23,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -90,7 +90,7 @@ public Integer call() throws Exception { STRtree index = new STRtree(); boolean built = false; - List paths = Lists.newArrayList(); + List paths =new ArrayList<>(); if (input.toString().endsWith("zip")) { FileSystem fs = FileSystems.newFileSystem(input, ClassLoader.getSystemClassLoader()); for (String l : layer) { diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java b/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java index 8318bef7292..b89303a23bb 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java @@ -26,7 +26,7 @@ import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; import picocli.CommandLine; @@ -110,7 +110,7 @@ public Integer call() throws Exception { } // Read shape file // TODO this is acutally not needed. Just testing the functionality of reading shape file from URL. Delete afterwards!!! - List nutsFeatures = ShapeFileReader.getAllFeatures(URI.create(nutsPath).toURL()). + List nutsFeatures = GeoFileReader.getAllFeatures(URI.create(nutsPath).toURL()). stream().filter(f -> relevantNutsIds.contains(f.getAttribute("NUTS_ID").toString())). collect(Collectors.toList()); System.out.println("There are " + nutsFeatures.size() + " relevant NUTS regions"); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java index 878cfad8156..8b3e621ac22 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java @@ -11,8 +11,8 @@ import java.util.Set; @CommandLine.Command( - name = "clean-network", - description = "Ensures that all links in the network are strongly connected." + name = "clean-network", + description = "Ensures that all links in the network are strongly connected." ) public class CleanNetwork implements MATSimAppCommand { @@ -22,7 +22,7 @@ public class CleanNetwork implements MATSimAppCommand { @CommandLine.Option(names = "--output", description = "Output path", required = true) private Path output; - @CommandLine.Option(names = "--modes", description = "List of modes to clean", defaultValue = TransportMode.car) + @CommandLine.Option(names = "--modes", description = "List of modes to clean", split = ",", defaultValue = TransportMode.car) private Set modes; @Override diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java index 296dd6abb7a..1a7272bf277 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java @@ -74,7 +74,15 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { - SumoNetworkConverter converter = SumoNetworkConverter.newInstance(input, output, Path.of(shp.getShapeFile()), crs.getInputCRS(), crs.getTargetCRS(), freeSpeedFactor); +// since ShpOptions.getShapeFile() no longer is a path but a string, we have to check if it is defined before creating SumoNetworkConverter to +// preserve the possibility to run the converter without a shp file, otherwise, when calling Path.of(shp.getShapeFile) a NullPointerException is caused -sme0324 + Path path = null; + + if (shp.isDefined()) { + path = Path.of(shp.getShapeFile()); + } + + SumoNetworkConverter converter = SumoNetworkConverter.newInstance(input, output, path, crs.getInputCRS(), crs.getTargetCRS(), freeSpeedFactor); Network network = NetworkUtils.createNetwork(); Lanes lanes = LanesUtils.createLanesContainer(); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/XYToLinks.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/XYToLinks.java index ec9ada3bad5..83aefa033e0 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/XYToLinks.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/XYToLinks.java @@ -1,15 +1,20 @@ package org.matsim.application.prepare.population; +import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; +import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.TransportModeNetworkFilter; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; import org.matsim.core.population.algorithms.XY2Links; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.facilities.ActivityFacilities; import org.matsim.facilities.FacilitiesUtils; +import org.matsim.facilities.MatsimFacilitiesReader; import picocli.CommandLine; import java.nio.file.Path; @@ -29,6 +34,9 @@ public class XYToLinks implements MATSimAppCommand { @CommandLine.Option(names = "--car-only", description = "Convert to car-only network", defaultValue = "false") private boolean carOnly; + @CommandLine.Option(names = "--facilities", description = "Input facilities. Necessary if activities already were assigned facility ids.") + private Path inputFacilities; + @Override public Integer call() throws Exception { @@ -42,7 +50,16 @@ public Integer call() throws Exception { network = carOnlyNetwork; } - XY2Links algo = new XY2Links(network, FacilitiesUtils.createActivityFacilities()); + ActivityFacilities facilities = FacilitiesUtils.createActivityFacilities(); + + if (inputFacilities != null) { + Scenario scenario = ScenarioUtils.loadScenario(ConfigUtils.createConfig()); + MatsimFacilitiesReader reader = new MatsimFacilitiesReader(scenario); + reader.parse(inputFacilities.toUri().toURL()); + facilities = scenario.getActivityFacilities(); + } + + XY2Links algo = new XY2Links(network, facilities); Population population = PopulationUtils.readPopulation(input.toString()); diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java index b8dc592921e..7939a9dab9a 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java @@ -27,6 +27,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; +import org.matsim.application.options.ShpOptions; import org.matsim.freight.carriers.*; import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; import org.matsim.core.utils.geometry.CoordinateTransformation; @@ -41,7 +42,7 @@ /** * This CarrierReaderFromCSV reads all carrier information given in the read CSV - * file and creates the carriers. While the process of creating the carriers the + * file and creates the carriers. While the process of creating the carriers, the * consistency of the information will be checked. * * @author Ricardo Ewert @@ -55,11 +56,11 @@ public final class CarrierReaderFromCSV { * file. For one carrier several CarrierInformationElement can be read in. This * is necessary for creating different configurations of the vehicles. Not every * parameter should be set for creating the carrier. While the process of - * creating the carriers the consistency of the information will be checked. + * creating the carriers, the consistency of the information will be checked. */ static class CarrierInformationElement { /** - * Name of carrier of this information element. + * Name of the carrier of this information element. */ private final String carrierName; /** @@ -97,8 +98,8 @@ static class CarrierInformationElement { private final int jspritIterations; /** * Sets a fixed number of vehicles per vehicleType and location. If this - * number is e.g. 3.: for each vehicleType 3 vehicles at each location will be - * created and the fleetsize is finite. + * number is e.g., 3.: for each vehicleType 3 vehicles at each location will be + * created, and the fleetsize is finite. */ private int fixedNumberOfVehiclePerTypeAndLocation; @@ -236,20 +237,20 @@ public void setFixedNumberOfVehiclePerTypeAndLocation(int fixedNumberOfVehiclePe * @param scenario * @param freightCarriersConfigGroup * @param csvLocationCarrier - * @param polygonsInShape + * @param indexShape * @param defaultJspritIterations * @param crsTransformationNetworkAndShape * @param shapeCategory * @throws IOException */ public static void readAndCreateCarrierFromCSV(Scenario scenario, FreightCarriersConfigGroup freightCarriersConfigGroup, - Path csvLocationCarrier, Collection polygonsInShape, int defaultJspritIterations, + Path csvLocationCarrier, ShpOptions.Index indexShape, int defaultJspritIterations, CoordinateTransformation crsTransformationNetworkAndShape, String shapeCategory) throws IOException { Set allNewCarrierInformation = readCarrierInformation(csvLocationCarrier); - checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, polygonsInShape, shapeCategory); + checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, indexShape, shapeCategory); log.info("The read carrier information from the csv are checked without errors."); - createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, polygonsInShape, + createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, indexShape, defaultJspritIterations, crsTransformationNetworkAndShape); } @@ -302,16 +303,16 @@ else if (!record.get("fleetSize").isBlank()) } /** - * Checks if the read carrier information are consistent. + * Checks if the read carrier information is consistent. * * @param allNewCarrierInformation * @param freightCarriersConfigGroup * @param scenario - * @param polygonsInShape + * @param indexShape * @param shapeCategory */ static void checkNewCarrier(Set allNewCarrierInformation, - FreightCarriersConfigGroup freightCarriersConfigGroup, Scenario scenario, Collection polygonsInShape, String shapeCategory) { + FreightCarriersConfigGroup freightCarriersConfigGroup, Scenario scenario, ShpOptions.Index indexShape, String shapeCategory) { CarriersUtils.addOrGetCarriers(scenario); for (CarrierInformationElement carrierElement : allNewCarrierInformation) { @@ -355,12 +356,12 @@ static void checkNewCarrier(Set allNewCarrierInformat log.warn( "No possible area for additional depot given. Random choice in the hole network of a possible position"); if (carrierElement.getAreaOfAdditionalDepots() != null) { - if (polygonsInShape == null) + if (indexShape == null) throw new RuntimeException("For carrier " + carrierElement.getName() + " a certain area for depots is selected, but no shape is read in. Please check."); for (String depotArea : carrierElement.getAreaOfAdditionalDepots()) { boolean isInShape = false; - for (SimpleFeature singlePolygon : polygonsInShape) { + for (SimpleFeature singlePolygon : indexShape.getShp().readFeatures()) { if (singlePolygon.getAttribute(shapeCategory).equals(depotArea)) { isInShape = true; break; @@ -409,14 +410,14 @@ static void checkNewCarrier(Set allNewCarrierInformat * @param scenario * @param allNewCarrierInformation * @param freightCarriersConfigGroup - * @param polygonsInShape + * @param indexShape * @param defaultJspritIterations * @param crsTransformationNetworkAndShape */ static void createNewCarrierAndAddVehicleTypes(Scenario scenario, - Set allNewCarrierInformation, FreightCarriersConfigGroup freightCarriersConfigGroup, - Collection polygonsInShape, int defaultJspritIterations, - CoordinateTransformation crsTransformationNetworkAndShape) { + Set allNewCarrierInformation, FreightCarriersConfigGroup freightCarriersConfigGroup, + ShpOptions.Index indexShape, int defaultJspritIterations, + CoordinateTransformation crsTransformationNetworkAndShape) { Carriers carriers = CarriersUtils.addOrGetCarriers(scenario); CarrierVehicleTypes carrierVehicleTypes = new CarrierVehicleTypes(); @@ -457,7 +458,7 @@ static void createNewCarrierAndAddVehicleTypes(Scenario scenario, && !link.getId().toString().contains("pt") && (!link.getAttributes().getAsMap().containsKey("type") || !link.getAttributes().getAsMap().get("type").toString().contains("motorway")) - && FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, + && FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, singleNewCarrier.getAreaOfAdditionalDepots(), crsTransformationNetworkAndShape)) { singleNewCarrier.getVehicleDepots().add(link.getId().toString()); } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index 8c1f113a6d0..d991eaa9a6f 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -24,17 +24,18 @@ import org.apache.commons.csv.CSVRecord; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.locationtech.jts.geom.Point; 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.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; -import org.matsim.freight.carriers.*; +import org.matsim.application.options.ShpOptions; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.router.TripStructureUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; -import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.freight.carriers.*; import org.opengis.feature.simple.SimpleFeature; import java.io.IOException; @@ -45,7 +46,7 @@ /** * This DemandReaderFromCSV reads all demand information given in the read CSV * file and creates the demand for the carriers. While the process of creating - * the demand the consistency of the information will be checked. + * the demand, the consistency of the information will be checked. * * @author Ricardo Ewert */ @@ -58,9 +59,9 @@ public final class DemandReaderFromCSV { * file. Several DemandInformationElement can be read in for one carrier. This * is necessary for creating configurations of the demand. Not every parameter * should be set for creating the demand. While the process of creating the - * demand the consistency of the information will be checked. If this demand - * creates a service the information for the firstJobElement should be set. If - * this demands creates a shipment the firstJobElement is the pickup and the + * demand, the consistency of the information will be checked. If this demand + * creates a service, the information for the firstJobElement should be set. If + * this demand creates a shipment, the firstJobElement is the pickup and the * secondJobElement is the delivery. */ static class DemandInformationElement { @@ -317,7 +318,7 @@ public String getTypeOfDemand() { * * @param scenario * @param csvLocationDemand - * @param polygonsInShape + * @param indexShape * @param combineSimilarJobs * @param crsTransformationNetworkAndShape * @param population @@ -325,18 +326,18 @@ public String getTypeOfDemand() { * @throws IOException */ static void readAndCreateDemand(Scenario scenario, Path csvLocationDemand, - Collection polygonsInShape, boolean combineSimilarJobs, + ShpOptions.Index indexShape, boolean combineSimilarJobs, CoordinateTransformation crsTransformationNetworkAndShape, Population population, String shapeCategory) throws IOException { Set demandInformation = readDemandInformation(csvLocationDemand); - checkNewDemand(scenario, demandInformation, polygonsInShape, shapeCategory); - createDemandForCarriers(scenario, polygonsInShape, demandInformation, population, combineSimilarJobs, + checkNewDemand(scenario, demandInformation, indexShape, shapeCategory); + createDemandForCarriers(scenario, indexShape, demandInformation, population, combineSimilarJobs, crsTransformationNetworkAndShape); } /** * Reads the demand information from the csv file and checks if the information - * are consistent + * is consistent * * @param csvLocationDemand * @return @@ -404,11 +405,11 @@ static Set readDemandInformation(Path csvLocationDeman * * @param scenario * @param demandInformation - * @param polygonsInShape + * @param indexShape * @param shapeCategory */ static void checkNewDemand(Scenario scenario, Set demandInformation, - Collection polygonsInShape, String shapeCategory) { + ShpOptions.Index indexShape, String shapeCategory) { for (DemandInformationElement newDemand : demandInformation) { Carriers carriers = (Carriers) scenario.getScenarioElement("carriers"); @@ -439,13 +440,13 @@ static void checkNewDemand(Scenario scenario, Set dema throw new RuntimeException("For the carrier " + newDemand.getCarrierName() + ": Select either share of population or number of locations"); if (newDemand.getAreasFirstJobElement() != null) { - if (polygonsInShape == null) + if (indexShape == null) throw new RuntimeException("You selected a certain area for the carrier" + newDemand.getCarrierName() + " although no shape file is loaded."); for (String demandArea : newDemand.getAreasFirstJobElement()) { boolean isInShape = false; - for (SimpleFeature singlePolygon : polygonsInShape) + for (SimpleFeature singlePolygon : indexShape.getAllFeatures()) if ((singlePolygon.getAttribute(shapeCategory) != null && singlePolygon.getAttribute(shapeCategory).equals(demandArea))) { isInShape = true; @@ -501,12 +502,12 @@ static void checkNewDemand(Scenario scenario, Set dema throw new RuntimeException("For the carrier " + newDemand.getCarrierName() + ": The percentage of the population should be more than 0 and maximum 100pct. Please check!"); if (newDemand.getAreasSecondJobElement() != null) { - if (polygonsInShape == null) + if (indexShape == null) throw new RuntimeException("You selected a certain area for the carrier" + newDemand.getCarrierName() + " although no shape file is loaded."); for (String demand : newDemand.getAreasSecondJobElement()) { boolean isInShape = false; - for (SimpleFeature singlePolygon : polygonsInShape) + for (SimpleFeature singlePolygon : indexShape.getAllFeatures()) if (singlePolygon.getAttribute(shapeCategory).equals(demand)) { isInShape = true; break; @@ -537,22 +538,22 @@ static void checkNewDemand(Scenario scenario, Set dema * Creates for every demand information the services/shipments for the carriers * * @param scenario - * @param polygonsInShape + * @param indexShape * @param demandInformation * @param population * @param combineSimilarJobs * @param crsTransformationNetworkAndShape */ - static void createDemandForCarriers(Scenario scenario, Collection polygonsInShape, + static void createDemandForCarriers(Scenario scenario, ShpOptions.Index indexShape, Set demandInformation, Population population, boolean combineSimilarJobs, CoordinateTransformation crsTransformationNetworkAndShape) { for (DemandInformationElement newDemandInformationElement : demandInformation) { if (newDemandInformationElement.getTypeOfDemand().equals("service")) - createServices(scenario, newDemandInformationElement, polygonsInShape, population, combineSimilarJobs, + createServices(scenario, newDemandInformationElement, indexShape, population, combineSimilarJobs, crsTransformationNetworkAndShape); else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) - createShipments(scenario, newDemandInformationElement, polygonsInShape, population, combineSimilarJobs, + createShipments(scenario, newDemandInformationElement, indexShape, population, combineSimilarJobs, crsTransformationNetworkAndShape); } @@ -563,14 +564,14 @@ else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) * * @param scenario * @param newDemandInformationElement - * @param polygonsInShape + * @param indexShape * @param population * @param combineSimilarJobs * @param crsTransformationNetworkAndShape */ private static void createServices(Scenario scenario, DemandInformationElement newDemandInformationElement, - Collection polygonsInShape, Population population, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape) { + ShpOptions.Index indexShape, Population population, boolean combineSimilarJobs, + CoordinateTransformation crsTransformationNetworkAndShape) { int countOfLinks = 1; int distributedDemand = 0; @@ -598,7 +599,7 @@ else if (population == null) String samplingOption = String.valueOf(population.getAttributes().getAttribute("samplingOption")); if (areasForServiceLocations != null) - possiblePersonsForService = findPossiblePersons(population, areasForServiceLocations, polygonsInShape, + possiblePersonsForService = findPossiblePersons(population, areasForServiceLocations, indexShape, crsTransformationNetworkAndShape); else possiblePersonsForService.putAll(population.getPersons()); @@ -619,7 +620,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { numberOfServiceLocations = numberPossibleServices; } // find possible links for the services - HashMap, Link> possibleLinksForService = findAllPossibleLinks(scenario, polygonsInShape, + HashMap, Link> possibleLinksForService = findAllPossibleLinks(scenario, indexShape, crsTransformationNetworkAndShape, numberOfServiceLocations, areasForServiceLocations, locationsOfServices, possiblePersonsForService, nearestLinkPerPerson); @@ -634,7 +635,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { if (possibleLinksForService.size() > demandToDistribute) { for (int i = 0; i < demandToDistribute; i++) { - Link link = findNextUsedLink(scenario, polygonsInShape, possibleLinksForService, numberOfJobs, + Link link = findNextUsedLink(scenario, indexShape, possibleLinksForService, numberOfJobs, areasForServiceLocations, locationsOfServices, usedServiceLocations, possiblePersonsForService, nearestLinkPerPerson, crsTransformationNetworkAndShape, i); double serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit(); @@ -706,7 +707,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { if (locationsOfServices != null && locationsOfServices.length > i) { link = scenario.getNetwork().getLinks().get(Id.createLinkId(locationsOfServices[i])); } else - link = findNextUsedLink(scenario, polygonsInShape, possibleLinksForService, + link = findNextUsedLink(scenario, indexShape, possibleLinksForService, numberOfServiceLocations, areasForServiceLocations, locationsOfServices, usedServiceLocations, possiblePersonsForService, nearestLinkPerPerson, crsTransformationNetworkAndShape, i); @@ -755,14 +756,14 @@ else if (samplingOption.equals("changeDemandOnLocation")) { * * @param scenario * @param newDemandInformationElement - * @param polygonsInShape + * @param indexShape * @param population * @param combineSimilarJobs * @param crsTransformationNetworkAndShape */ private static void createShipments(Scenario scenario, DemandInformationElement newDemandInformationElement, - Collection polygonsInShape, Population population, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape) { + ShpOptions.Index indexShape, Population population, boolean combineSimilarJobs, + CoordinateTransformation crsTransformationNetworkAndShape) { int countOfLinks = 1; int distributedDemand = 0; @@ -797,12 +798,12 @@ else if (population == null) String samplingOption = String.valueOf(population.getAttributes().getAttribute("samplingOption")); if (areasForPickupLocations != null) - possiblePersonsPickup = findPossiblePersons(population, areasForPickupLocations, polygonsInShape, + possiblePersonsPickup = findPossiblePersons(population, areasForPickupLocations, indexShape, crsTransformationNetworkAndShape); else possiblePersonsPickup.putAll(population.getPersons()); if (areasForDeliveryLocations != null) - possiblePersonsDelivery = findPossiblePersons(population, areasForDeliveryLocations, polygonsInShape, + possiblePersonsDelivery = findPossiblePersons(population, areasForDeliveryLocations, indexShape, crsTransformationNetworkAndShape); else possiblePersonsDelivery.putAll(population.getPersons()); @@ -861,10 +862,10 @@ else if (population == null) numberOfDeliveryLocations = numberPossibleJobsDelivery; } // find possible Links for delivery and pickup - HashMap, Link> possibleLinksPickup = findAllPossibleLinks(scenario, polygonsInShape, + HashMap, Link> possibleLinksPickup = findAllPossibleLinks(scenario, indexShape, crsTransformationNetworkAndShape, numberOfPickupLocations, areasForPickupLocations, setLocationsOfPickup, possiblePersonsPickup, nearestLinkPerPersonPickup); - HashMap, Link> possibleLinksDelivery = findAllPossibleLinks(scenario, polygonsInShape, + HashMap, Link> possibleLinksDelivery = findAllPossibleLinks(scenario, indexShape, crsTransformationNetworkAndShape, numberOfDeliveryLocations, areasForDeliveryLocations, setLocationsOfDelivery, possiblePersonsDelivery, nearestLinkPerPersonPickup); @@ -882,22 +883,25 @@ else if (population == null) + " for pickup is not part of the possible links for pickup. Please check!"); if (setLocationsOfDelivery != null) - for (String selectedLinkIdDelivery : setLocationsOfDelivery) - if (!possibleLinksDelivery.containsKey(Id.createLinkId(selectedLinkIdDelivery))) - throw new RuntimeException("The selected link " + selectedLinkIdDelivery + if (numberOfDeliveryLocations < setLocationsOfDelivery.length) + log.warn("You selected more certain locations than the set number of locations. Randomly selected locations will be used."); + else + for (String selectedLinkIdDelivery : setLocationsOfDelivery) + if (!possibleLinksDelivery.containsKey(Id.createLinkId(selectedLinkIdDelivery))) + throw new RuntimeException("The selected link " + selectedLinkIdDelivery + " for delivery is not part of the possible links for delivery. Please check!"); - // distribute the demand over the network because no number of jobs are selected + // distribute the demand over the network because no number of jobs is selected if (numberOfJobs == null) { // creates shipments with a demand of 1 if (possibleLinksPickup.size() > demandToDistribute || possibleLinksDelivery.size() > demandToDistribute) { for (int i = 0; i < demandToDistribute; i++) { Link linkPickup; Link linkDelivery; - linkPickup = findNextUsedLink(scenario, polygonsInShape, possibleLinksPickup, + linkPickup = findNextUsedLink(scenario, indexShape, possibleLinksPickup, numberOfPickupLocations, areasForPickupLocations, setLocationsOfPickup, usedPickupLocations, possiblePersonsPickup, nearestLinkPerPersonPickup, crsTransformationNetworkAndShape, i); - linkDelivery = findNextUsedLink(scenario, polygonsInShape, possibleLinksDelivery, + linkDelivery = findNextUsedLink(scenario, indexShape, possibleLinksDelivery, numberOfDeliveryLocations, areasForDeliveryLocations, setLocationsOfDelivery, usedDeliveryLocations, possiblePersonsDelivery, nearestLinkPerPersonDelivery, crsTransformationNetworkAndShape, i); @@ -970,12 +974,12 @@ else if (numberOfPickupLocations != null) { } if (pickupIsDemandBase) { linkPickup = demandBasedLink; - linkDelivery = findNextUsedLink(scenario, polygonsInShape, possibleLinksDelivery, + linkDelivery = findNextUsedLink(scenario, indexShape, possibleLinksDelivery, numberOfDeliveryLocations, areasForDeliveryLocations, setLocationsOfDelivery, usedDeliveryLocations, possiblePersonsDelivery, nearestLinkPerPersonDelivery, crsTransformationNetworkAndShape, countOfLinks - 1); while (usedDeliveryLocations.contains(linkDelivery.getId().toString())) { - linkDelivery = findNextUsedLink(scenario, polygonsInShape, possibleLinksDelivery, + linkDelivery = findNextUsedLink(scenario, indexShape, possibleLinksDelivery, numberOfDeliveryLocations, areasForDeliveryLocations, setLocationsOfDelivery, usedDeliveryLocations, possiblePersonsDelivery, nearestLinkPerPersonDelivery, crsTransformationNetworkAndShape, countOfLinks - 1); @@ -986,12 +990,12 @@ else if (numberOfPickupLocations != null) { } } else { linkDelivery = demandBasedLink; - linkPickup = findNextUsedLink(scenario, polygonsInShape, possibleLinksPickup, + linkPickup = findNextUsedLink(scenario, indexShape, possibleLinksPickup, numberOfPickupLocations, areasForPickupLocations, setLocationsOfPickup, usedPickupLocations, possiblePersonsPickup, nearestLinkPerPersonPickup, crsTransformationNetworkAndShape, countOfLinks - 1); while (usedPickupLocations.contains(linkPickup.getId().toString())) { - linkPickup = findNextUsedLink(scenario, polygonsInShape, possibleLinksPickup, + linkPickup = findNextUsedLink(scenario, indexShape, possibleLinksPickup, numberOfPickupLocations, areasForPickupLocations, setLocationsOfPickup, usedPickupLocations, possiblePersonsPickup, nearestLinkPerPersonPickup, crsTransformationNetworkAndShape, countOfLinks - 1); @@ -1036,10 +1040,10 @@ else if (numberOfPickupLocations != null) { if (demandToDistribute != 0 && demandToDistribute < numberOfJobs) throw new RuntimeException( "The resulting number of jobs is not feasible, because the demand is smaller then the number of jobs. Please check!"); - Link linkPickup = findNextUsedLink(scenario, polygonsInShape, possibleLinksPickup, + Link linkPickup = findNextUsedLink(scenario, indexShape, possibleLinksPickup, numberOfPickupLocations, areasForPickupLocations, setLocationsOfPickup, usedPickupLocations, possiblePersonsPickup, nearestLinkPerPersonPickup, crsTransformationNetworkAndShape, i); - Link linkDelivery = findNextUsedLink(scenario, polygonsInShape, possibleLinksDelivery, + Link linkDelivery = findNextUsedLink(scenario, indexShape, possibleLinksDelivery, numberOfDeliveryLocations, areasForDeliveryLocations, setLocationsOfDelivery, usedDeliveryLocations, possiblePersonsDelivery, nearestLinkPerPersonDelivery, crsTransformationNetworkAndShape, i); @@ -1088,8 +1092,8 @@ else if (numberOfPickupLocations != null) { } /** - * Creates a job Id for a new job. If a certain Id is already used a number will - * be added at the end until no existing job was the same Id. + * Creates a job Id for a new job. + * If a certain Id is already used, a number will be added at the end until no existing job was the same Id. * * @param scenario * @param newDemandInformationElement @@ -1127,8 +1131,8 @@ private static String createJobId(Scenario scenario, DemandInformationElement ne } /** - * If jobs of a carrier have the same characteristics (time window, location) - * they will be combined to one job, + * If jobs of a carrier have the same characteristics (time window, location), + * they will be combined to one job. * * @param scenario * @param newDemandInformationElement @@ -1240,7 +1244,7 @@ private static void reduceNumberOfJobsIfSameCharacteristics(Scenario scenario, * Finds and returns all possible links for this job. * * @param scenario - * @param polygonsInShape + * @param indexShape * @param crsTransformationNetworkAndShape * @param numberOfLocations * @param areasForLocations @@ -1250,22 +1254,24 @@ private static void reduceNumberOfJobsIfSameCharacteristics(Scenario scenario, * @return */ private static HashMap, Link> findAllPossibleLinks(Scenario scenario, - Collection polygonsInShape, CoordinateTransformation crsTransformationNetworkAndShape, - Integer numberOfLocations, String[] areasForLocations, String[] setLocations, - HashMap, Person> possiblePersons, - HashMap, HashMap> nearestLinkPerPerson) { + ShpOptions.Index indexShape, CoordinateTransformation crsTransformationNetworkAndShape, + Integer numberOfLocations, String[] areasForLocations, String[] setLocations, + HashMap, Person> possiblePersons, + HashMap, HashMap> nearestLinkPerPerson) { HashMap, Link> possibleLinks = new HashMap, Link>(); if (numberOfLocations == null) { for (Link link : scenario.getNetwork().getLinks().values()) - if (!link.getId().toString().contains("pt") && (!link.getAttributes().getAsMap().containsKey("type") || !link.getAttributes().getAsMap().get("type").toString().contains("motorway")) && FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, - areasForLocations, crsTransformationNetworkAndShape)) { + if (!link.getId().toString().contains("pt") && (!link.getAttributes().getAsMap().containsKey( + "type") || !link.getAttributes().getAsMap().get("type").toString().contains( + "motorway")) && FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, + areasForLocations, crsTransformationNetworkAndShape)) { possibleLinks.put(link.getId(), link); } } else { - Link newPossibleLink = null; + Link newPossibleLink; while (possibleLinks.size() < numberOfLocations) { newPossibleLink = findPossibleLinkForDemand(possibleLinks, possiblePersons, nearestLinkPerPerson, - polygonsInShape, areasForLocations, numberOfLocations, scenario, setLocations, + indexShape, areasForLocations, numberOfLocations, scenario, setLocations, crsTransformationNetworkAndShape); if (!possibleLinks.containsKey(newPossibleLink.getId())) possibleLinks.put(newPossibleLink.getId(), newPossibleLink); @@ -1279,7 +1285,7 @@ private static HashMap, Link> findAllPossibleLinks(Scenario scenario, * Finds the next link which can be used as a location. * * @param scenario - * @param polygonsInShape + * @param indexShape * @param possibleLinks * @param selectedNumberOfLocations * @param areasForLocations @@ -1291,7 +1297,7 @@ private static HashMap, Link> findAllPossibleLinks(Scenario scenario, * @param i * @return */ - private static Link findNextUsedLink(Scenario scenario, Collection polygonsInShape, + private static Link findNextUsedLink(Scenario scenario, ShpOptions.Index indexShape, HashMap, Link> possibleLinks, Integer selectedNumberOfLocations, String[] areasForLocations, String[] selectedLocations, ArrayList usedLocations, HashMap, Person> possiblePersons, HashMap, HashMap> nearestLinkPerPerson, @@ -1305,7 +1311,7 @@ private static Link findNextUsedLink(Scenario scenario, Collection usedLocations.size() && usedLocations.contains(link.getId().toString()))) link = findPossibleLinkForDemand(possibleLinks, possiblePersons, nearestLinkPerPerson, - polygonsInShape, areasForLocations, selectedNumberOfLocations, scenario, selectedLocations, + indexShape, areasForLocations, selectedNumberOfLocations, scenario, selectedLocations, crsTransformationNetworkAndShape); } else { link = scenario.getNetwork().getLinks() @@ -1315,30 +1321,26 @@ private static Link findNextUsedLink(Scenario scenario, Collection, Person> findPossiblePersons(Population population, - String[] areasForServiceLocations, Collection polygonsInShape, + String[] areasForServiceLocations, ShpOptions.Index indexShape, CoordinateTransformation crsTransformationNetworkAndShape) { HashMap, Person> possiblePersons = new HashMap, Person>(); for (Person person : population.getPersons().values()) { - Point p = MGC.xy2Point((double) person.getAttributes().getAttribute("homeX"), - (double) person.getAttributes().getAttribute("homeY")); - Coord coord; + Coord coord = getHomeCoord(person); if (crsTransformationNetworkAndShape != null) - coord = crsTransformationNetworkAndShape.transform(MGC.point2Coord(p)); - else - coord = MGC.point2Coord(p); + coord = crsTransformationNetworkAndShape.transform(coord); - if (FreightDemandGenerationUtils.checkPositionInShape(null, MGC.coord2Point(coord), polygonsInShape, + if (FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, areasForServiceLocations, crsTransformationNetworkAndShape)) possiblePersons.put(person.getId(), person); } @@ -1352,15 +1354,13 @@ private static HashMap, Person> findPossiblePersons(Population popula * @param nearestLinkPerPerson * @param person */ - static void findLinksForPersons(Scenario scenario, - HashMap, HashMap> nearestLinkPerPerson, Person person) { - + static void findLinksForPerson(Scenario scenario, + HashMap, HashMap> nearestLinkPerPerson, Person person) { + Coord homePoint = getHomeCoord(person); for (Link link : scenario.getNetwork().getLinks().values()) if (!link.getId().toString().contains("pt") && (!link.getAttributes().getAsMap().containsKey("type") || !link.getAttributes().getAsMap().get("type").toString().contains("motorway"))) { - Coord homePoint = MGC.point2Coord(MGC.xy2Point((double) person.getAttributes().getAttribute("homeX"), - (double) person.getAttributes().getAttribute("homeY"))); Coord middlePointLink = FreightDemandGenerationUtils.getCoordOfMiddlePointOfLink(link); double distance = NetworkUtils.getEuclideanDistance(homePoint, middlePointLink); if (!nearestLinkPerPerson.containsKey(person.getId()) @@ -1371,13 +1371,37 @@ static void findLinksForPersons(Scenario scenario, } } + /** + * Method to get the home coordinate of a person. + * The default is to get the home coordinate from one home activity of the selected plan. + * If the selected plan does not contain a home activity, the home coordinate is read from the attributes of the person. + * + * @param person The person for which the home coordinate should be returned. + * @return + */ + private static Coord getHomeCoord(Person person) { + Coord homeCoord = null; + if (person.getSelectedPlan() != null) + homeCoord = PopulationUtils.getActivities(person.getSelectedPlan(), + TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream().filter( + activity -> activity.getType().contains("home")).findFirst().get().getCoord(); + if (homeCoord == null) { + double home_x = (double) person.getAttributes().getAsMap().entrySet().stream().filter( + entry -> entry.getKey().contains("home") && entry.getKey().contains("X")).findFirst().get().getValue(); + double home_y = (double) person.getAttributes().getAsMap().entrySet().stream().filter( + entry -> entry.getKey().contains("home") && entry.getKey().contains("Y")).findFirst().get().getValue(); + homeCoord = new Coord(home_x, home_y); + } + return homeCoord; + } + /** * Searches a possible link for the demand. * * @param possibleLinks * @param possiblePersons * @param nearestLinkPerPerson - * @param polygonsInShape + * @param indexShape * @param areasForTheDemand * @param selectedNumberOfLocations * @param scenario @@ -1386,10 +1410,10 @@ static void findLinksForPersons(Scenario scenario, * @return */ private static Link findPossibleLinkForDemand(HashMap, Link> possibleLinks, - HashMap, Person> possiblePersons, - HashMap, HashMap> nearestLinkPerPerson, - Collection polygonsInShape, String[] areasForTheDemand, Integer selectedNumberOfLocations, - Scenario scenario, String[] selectedLocations, CoordinateTransformation crsTransformationNetworkAndShape) { + HashMap, Person> possiblePersons, + HashMap, HashMap> nearestLinkPerPerson, + ShpOptions.Index indexShape, String[] areasForTheDemand, Integer selectedNumberOfLocations, + Scenario scenario, String[] selectedLocations, CoordinateTransformation crsTransformationNetworkAndShape) { Link selectedlink = null; Link newLink = null; if (selectedNumberOfLocations == null) @@ -1406,16 +1430,7 @@ private static Link findPossibleLinkForDemand(HashMap, Link> possibleLi newLink = scenario.getNetwork().getLinks().values().stream() .skip(rand.nextInt(scenario.getNetwork().getLinks().size())).findFirst().get(); else { - Person person = possiblePersons.values().stream().skip(rand.nextInt(possiblePersons.size())) - .findFirst().get(); - if (nearestLinkPerPerson.containsKey(person.getId())) - newLink = scenario.getNetwork().getLinks().get(Id - .createLinkId(nearestLinkPerPerson.get(person.getId()).values().iterator().next())); - else { - findLinksForPersons(scenario, nearestLinkPerPerson, person); - newLink = scenario.getNetwork().getLinks().get(Id - .createLinkId(nearestLinkPerPerson.get(person.getId()).values().iterator().next())); - } + newLink = getNewLinkForPerson(possiblePersons, nearestLinkPerPerson, scenario); } } } else { @@ -1423,25 +1438,29 @@ private static Link findPossibleLinkForDemand(HashMap, Link> possibleLi newLink = possibleLinks.values().stream().skip(rand.nextInt(possibleLinks.size())).findFirst() .get(); } else { - Person person = possiblePersons.values().stream().skip(rand.nextInt(possiblePersons.size())) - .findFirst().get(); - if (nearestLinkPerPerson.containsKey(person.getId())) - newLink = scenario.getNetwork().getLinks().get( - Id.createLinkId(nearestLinkPerPerson.get(person.getId()).values().iterator().next())); - else { - findLinksForPersons(scenario, nearestLinkPerPerson, person); - newLink = scenario.getNetwork().getLinks().get( - Id.createLinkId(nearestLinkPerPerson.get(person.getId()).values().iterator().next())); - } + newLink = getNewLinkForPerson(possiblePersons, nearestLinkPerPerson, scenario); } } if (!newLink.getId().toString().contains("pt") && (!newLink.getAttributes().getAsMap().containsKey("type") || !newLink.getAttributes().getAsMap().get("type").toString().contains("motorway")) - && (polygonsInShape == null || FreightDemandGenerationUtils.checkPositionInShape(newLink, null, - polygonsInShape, areasForTheDemand, crsTransformationNetworkAndShape))) + && (indexShape == null || FreightDemandGenerationUtils.checkPositionInShape(newLink, null, + indexShape, areasForTheDemand, crsTransformationNetworkAndShape))) selectedlink = newLink; } return selectedlink; } + + private static Link getNewLinkForPerson(HashMap, Person> possiblePersons, + HashMap, HashMap> nearestLinkPerPerson, Scenario scenario) { + Link newLink; + Person person = possiblePersons.values().stream().skip(rand.nextInt(possiblePersons.size())) + .findFirst().get(); + if (!nearestLinkPerPerson.containsKey(person.getId())) { + findLinksForPerson(scenario, nearestLinkPerPerson, person); + } + newLink = scenario.getNetwork().getLinks().get( + Id.createLinkId(nearestLinkPerPerson.get(person.getId()).values().iterator().next())); + return newLink; + } } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java index caec3bbf367..d43ee64522e 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java @@ -26,14 +26,6 @@ import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.ShpOptions; -import org.matsim.freight.carriers.FreightCarriersConfigGroup; -import org.matsim.freight.carriers.Carrier; -import org.matsim.freight.carriers.CarrierPlanWriter; -import org.matsim.freight.carriers.CarriersUtils; -import org.matsim.freight.carriers.Carriers; -import org.matsim.freight.carriers.controler.CarrierModule; -import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory; -import org.matsim.freight.carriers.usecases.chessboard.CarrierScoringFunctionFactoryImpl; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.ControllerConfigGroup; @@ -44,14 +36,15 @@ import org.matsim.core.population.PopulationUtils; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; -import org.opengis.feature.simple.SimpleFeature; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.controler.CarrierModule; +import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory; +import org.matsim.freight.carriers.usecases.chessboard.CarrierScoringFunctionFactoryImpl; import picocli.CommandLine; import javax.management.InvalidAttributeValueException; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; import java.util.Objects; import java.util.concurrent.ExecutionException; @@ -129,7 +122,7 @@ private enum OptionsOfVRPSolutions { private ShpOptions shp = new ShpOptions(shapeFilePath, shapeCRS, null); @CommandLine.Option(names = "--populationFileLocation", description = "Path to the population file.", defaultValue = "") - private Path populationFilePath; + private String populationFilePath; @CommandLine.Option(names = "--populationCRS", description = "CRS of the input network (e.g.\"EPSG:31468\")") private String populationCRS; @@ -169,8 +162,6 @@ public static void main(String[] args) { public Integer call() throws IOException, InvalidAttributeValueException, ExecutionException, InterruptedException { String vehicleTypesFileLocation = carrierVehicleFilePath.toString(); - String carriersFileLocation = carrierFilePath.toString(); - String populationFile = populationFilePath.toString(); CoordinateTransformation crsTransformationFromNetworkToShape = null; // create and prepare MATSim config @@ -194,20 +185,20 @@ public Integer call() throws IOException, InvalidAttributeValueException, Execut // load or create carrier Scenario scenario = ScenarioUtils.loadScenario(config); - Collection polygonsInShape = null; + ShpOptions.Index indexShape = null; shp = new ShpOptions(shapeFilePath, shapeCRS, null); if (shp.isDefined()) { log.warn("Use of shpFile. Locations for the carriers and the demand only in shp: " + shp.getShapeFile()); - polygonsInShape = shp.readFeatures(); + indexShape = shp.createIndex(shapeCategory); crsTransformationFromNetworkToShape = shp.createTransformation(networkCRS); } log.info("Start creating carriers. Selected option: " + selectedCarrierInputOption); - createCarrier(scenario, selectedCarrierInputOption, carriersFileLocation, csvCarrierPath, polygonsInShape, + createCarrier(scenario, selectedCarrierInputOption, csvCarrierPath, indexShape, defaultJspritIterations, crsTransformationFromNetworkToShape); // create the demand log.info("Start creating the demand. Selected option: " + selectedCarrierInputOption); - createDemand(selectedDemandGenerationOption, scenario, csvDemandPath, polygonsInShape, populationFile, + createDemand(selectedDemandGenerationOption, scenario, csvDemandPath, indexShape, populationFilePath, selectedPopulationSamplingOption, selectedPopulationOption, Boolean.getBoolean(combineSimilarJobs), crsTransformationFromNetworkToShape); @@ -243,6 +234,8 @@ private Config prepareConfig(int lastMATSimIteration, String coordinateSystem) { FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); freightCarriersConfigGroup.setTravelTimeSliceWidth(1800); freightCarriersConfigGroup.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.enforceBeginnings); + if (carrierFilePath != null) + freightCarriersConfigGroup.setCarriersFile(carrierFilePath.toString()); return config; } @@ -258,12 +251,12 @@ private Config prepareConfig(int lastMATSimIteration, String coordinateSystem) { private static void setNetworkAndNetworkChangeEvents(Config config, String networkPathOfOtherNetwork, String networkChangeEventsFileLocation) throws RuntimeException { - if (networkPathOfOtherNetwork.equals("")) + if (networkPathOfOtherNetwork.isEmpty()) throw new RuntimeException("no correct network path network"); else { config.network().setInputFile(networkPathOfOtherNetwork); log.info("The following input network is selected: imported network from " + networkPathOfOtherNetwork); - if (networkChangeEventsFileLocation.equals("")) + if (networkChangeEventsFileLocation.isEmpty()) log.info("No networkChangeEvents selected"); else { log.info("Setting networkChangeEventsInput file: " + networkChangeEventsFileLocation); @@ -295,15 +288,14 @@ private static void prepareVehicles(Config config, String vehicleTypesFileLocati * * @param scenario * @param selectedCarrierInputOption - * @param carriersFileLocation * @param csvLocationCarrier - * @param polygonsInShape + * @param indexShape * @param defaultJspritIterations * @param crsTransformationNetworkAndShape * @throws IOException */ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrierInputOption, - String carriersFileLocation, Path csvLocationCarrier, Collection polygonsInShape, + Path csvLocationCarrier, ShpOptions.Index indexShape, int defaultJspritIterations, CoordinateTransformation crsTransformationNetworkAndShape) throws IOException { FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), @@ -312,42 +304,40 @@ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrie case addCSVDataToExistingCarrierFileData -> { // reads an existing carrier file and adds the information based on the read csv // carrier file - if (Objects.equals(carriersFileLocation, "")) + if (freightCarriersConfigGroup.getCarriersFile() == null) throw new RuntimeException("No path to the carrier file selected"); else { - freightCarriersConfigGroup.setCarriersFile(carriersFileLocation); CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); - log.info("Load carriers from: " + carriersFileLocation); + log.info("Load carriers from: " + freightCarriersConfigGroup.getCarriersFile()); CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightCarriersConfigGroup, csvLocationCarrier, - polygonsInShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); + indexShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); } } case readCarrierFile -> { // reads only a carrier file as the carrier import. - if (Objects.equals(carriersFileLocation, "")) + if (freightCarriersConfigGroup.getCarriersFile() == null) throw new RuntimeException("No path to the carrier file selected"); else { - freightCarriersConfigGroup.setCarriersFile(carriersFileLocation); CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); - log.info("Load carriers from: " + carriersFileLocation); + log.info("Load carriers from: " + freightCarriersConfigGroup.getCarriersFile()); } } case createCarriersFromCSV -> // creates all carriers based on the given information in the read carrier csv CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightCarriersConfigGroup, csvLocationCarrier, - polygonsInShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); + indexShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); default -> throw new RuntimeException("no method to create or read carrier selected."); } } /** - * Differs between the different options of creating the demand.. + * Differs between the different options of creating the demand. * * @param selectedDemandGenerationOption * @param scenario * @param csvLocationDemand - * @param polygonsInShape - * @param populationFile + * @param indexShape + * @param populationFilePath * @param selectedSamplingOption * @param selectedPopulationOption * @param combineSimilarJobs @@ -355,31 +345,31 @@ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrie * @throws IOException */ private void createDemand(DemandGenerationOptions selectedDemandGenerationOption, Scenario scenario, - Path csvLocationDemand, Collection polygonsInShape, String populationFile, - PopulationSamplingOption selectedSamplingOption, PopulationOptions selectedPopulationOption, - boolean combineSimilarJobs, CoordinateTransformation crsTransformationNetworkAndShape) throws IOException { + Path csvLocationDemand, ShpOptions.Index indexShape, String populationFilePath, + PopulationSamplingOption selectedSamplingOption, PopulationOptions selectedPopulationOption, + boolean combineSimilarJobs, CoordinateTransformation crsTransformationNetworkAndShape) throws IOException { switch (selectedDemandGenerationOption) { case createDemandFromCSV -> // creates the demand by using the information given in the read csv file - DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, polygonsInShape, combineSimilarJobs, + DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, combineSimilarJobs, crsTransformationNetworkAndShape, null, shapeCategory); case createDemandFromCSVAndUsePopulation -> { /* * Option creates the demand by using the information given in the read csv file * and uses a population for finding demand locations */ - Population population = PopulationUtils.readPopulation(populationFile); + Population population = PopulationUtils.readPopulation(populationFilePath); switch (selectedSamplingOption) { /* - * this option is important if the sample of the population and the sample of + * This option is important if the sample of the population and the sample of * the resulting demand is different. For example, you can create with a 10pct * sample a 100pct demand modal for the waste collection. */ case createMoreLocations -> /* * If the demand sample is higher than the population sample, more demand - * location are created related to the given share of persons of the population + * locations are created related to the given share of persons in the population * with this demand. */ FreightDemandGenerationUtils.preparePopulation(population, sampleSizeInputPopulation, @@ -398,14 +388,14 @@ private void createDemand(DemandGenerationOptions selectedDemandGenerationOption break; case useHolePopulation: // uses the hole population as possible demand locations - DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, polygonsInShape, + DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory); break; case usePopulationInShape: // uses only the population with home location in the given shape file FreightDemandGenerationUtils.reducePopulationToShapeArea(population, shp.createIndex(populationCRS, "_")); - DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, polygonsInShape, + DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory); break; default: @@ -460,7 +450,7 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution Controler controler) throws ExecutionException, InterruptedException { switch (selectedSolution) { case runJspritAndMATSim -> { - // solves the VRP with jsprit and runs MATSim afterwards + // solves the VRP with jsprit and runs MATSim afterward new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, false); @@ -469,8 +459,7 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution .write(config.controller().getOutputDirectory() + "/output_carriersWithPlans.xml"); } case runJspritAndMATSimWithDistanceConstraint -> { - // solves the VRP with jsprit by using the distance constraint and runs MATSim - // afterwards + // solves the VRP with jsprit by using the distance constraint and runs MATSim afterward new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, true); diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java index b2d70951be8..234d657c0b6 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java @@ -21,29 +21,30 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.Point; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.*; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; import org.matsim.application.options.ShpOptions; +import org.matsim.core.controler.Controler; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.utils.geometry.CoordinateTransformation; +import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.freight.carriers.Carrier; import org.matsim.freight.carriers.CarrierService; import org.matsim.freight.carriers.CarrierShipment; import org.matsim.freight.carriers.CarriersUtils; -import org.matsim.core.controler.Controler; -import org.matsim.core.utils.geometry.CoordinateTransformation; -import org.matsim.core.utils.geometry.geotools.MGC; -import org.opengis.feature.simple.SimpleFeature; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Objects; /** * Collection of different methods for the FreightDemandGeneration. @@ -75,16 +76,39 @@ static void preparePopulation(Population population, double sampleSizeInputPopul personsToRemove.add(person.getId()); continue; } - for (Plan plan : person.getPlans()) - for (PlanElement element : plan.getPlanElements()) - if (element instanceof Activity) - if (((Activity) element).getType().contains("home")) { - double x = ((Activity) element).getCoord().getX(); - double y = ((Activity) element).getCoord().getY(); - person.getAttributes().putAttribute("homeX", x); - person.getAttributes().putAttribute("homeY", y); - break; - } + Coord homeCoord = null; + if (person.getSelectedPlan() != null) { + if (PopulationUtils.getActivities(person.getSelectedPlan(), + TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream().anyMatch( + activity -> activity.getType().contains("home"))) { + homeCoord = PopulationUtils.getActivities(person.getSelectedPlan(), + TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream().filter( + activity -> activity.getType().contains("home")).findFirst().get().getCoord(); + } + } else if (!person.getPlans().isEmpty()) + for (Plan plan : person.getPlans()) + if (PopulationUtils.getActivities(plan, + TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream().anyMatch( + activity -> activity.getType().contains("home"))) { + homeCoord = PopulationUtils.getActivities(plan, + TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream().filter( + activity -> activity.getType().contains("home")).findFirst().get().getCoord(); + } + if (homeCoord == null){ + double home_x = (double) person.getAttributes().getAsMap().entrySet().stream().filter( + entry -> entry.getKey().contains("home") && entry.getKey().contains("X") || entry.getKey().contains("x")).findFirst().get().getValue(); + double home_y = (double) person.getAttributes().getAsMap().entrySet().stream().filter( + entry -> entry.getKey().contains("home") && (entry.getKey().contains("Y") || entry.getKey().contains("y"))).findFirst().get().getValue(); + homeCoord = new Coord(home_x, home_y); + } + + + if (homeCoord != null) { + person.getAttributes().putAttribute("homeX", homeCoord.getX()); + person.getAttributes().putAttribute("homeY", homeCoord.getY()); + } else { + log.warn("No home found for person " + person.getId()); + } person.removePlan(person.getSelectedPlan()); } for (Id id : personsToRemove) @@ -164,42 +188,34 @@ static void reducePopulationToShapeArea(Population population, ShpOptions.Index * Checks if a link is one of the possible areas. * * @param link - * @param point - * @param polygonsInShape + * @param givenCoord + * @param indexShape * @param possibleAreas * @param crsTransformationNetworkAndShape * @return */ - static boolean checkPositionInShape(Link link, Point point, Collection polygonsInShape, - String[] possibleAreas, CoordinateTransformation crsTransformationNetworkAndShape) { - - if (polygonsInShape == null) + static boolean checkPositionInShape(Link link, Coord givenCoord, ShpOptions.Index indexShape, + String[] possibleAreas, CoordinateTransformation crsTransformationNetworkAndShape) { + if (indexShape == null) return true; - boolean isInShape = false; - Point p = null; - if (link != null && point == null) { + Coord coordToCheck = null; + if (link != null && givenCoord == null) { if (crsTransformationNetworkAndShape != null) - p = MGC.coord2Point(crsTransformationNetworkAndShape.transform(getCoordOfMiddlePointOfLink(link))); + coordToCheck = crsTransformationNetworkAndShape.transform(getCoordOfMiddlePointOfLink(link)); else - p = MGC.coord2Point(getCoordOfMiddlePointOfLink(link)); - } else if (link == null && point != null) - p = point; - for (SimpleFeature singlePolygon : polygonsInShape) { - if (possibleAreas != null) { - for (String area : possibleAreas) { - if (area.equals(singlePolygon.getAttribute("Ortsteil")) - || area.equals(singlePolygon.getAttribute("BEZNAME"))) - if (((Geometry) singlePolygon.getDefaultGeometry()).contains(p)) { - return true; - } - } - } else { - if (((Geometry) singlePolygon.getDefaultGeometry()).contains(p)) { + coordToCheck = getCoordOfMiddlePointOfLink(link); + } else if (link == null && givenCoord != null) + coordToCheck = givenCoord; + if (possibleAreas != null) { + for (String area : possibleAreas) { + if (Objects.equals(indexShape.query(coordToCheck), area)) { return true; } } + } else { + return indexShape.contains(coordToCheck); } - return isInShape; + return false; } /** diff --git a/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java b/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java new file mode 100644 index 00000000000..8c6c4768b22 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/ConfigYamlUpdateTest.java @@ -0,0 +1,239 @@ +package org.matsim.application; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigGroup; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.ReflectiveConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.testcases.MatsimTestUtils; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class ConfigYamlUpdateTest { + + @RegisterExtension + private MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + void params() { + + Path input = Path.of(utils.getClassInputDirectory()); + + Config config = ConfigUtils.loadConfig(input.resolve("config.xml").toString()); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("params.yml") + ); + + ScoringConfigGroup scoring = ConfigUtils.addOrGetModule(config, ScoringConfigGroup.class); + + assertThat(scoring.getModes()) + .hasSize(7); + + assertThat(scoring.getPerforming_utils_hr()) + .isEqualTo(6.88); + + ScoringConfigGroup.ModeParams car = scoring.getModes().get(TransportMode.car); + + assertThat(car.getConstant()).isEqualTo(-0.62); + assertThat(car.getMarginalUtilityOfTraveling()).isEqualTo(0); + + } + + @Test + void standard() { + + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("standard.yml") + ); + + assertThat(config.controller().getRunId()).isEqualTo("567"); + assertThat(config.global().getNumberOfThreads()).isEqualTo(8); + + assertThat(config.scoring().getOrCreateModeParams("car").getConstant()).isEqualTo(-1); + assertThat(config.scoring().getOrCreateModeParams("bike").getConstant()).isEqualTo(-2); + } + + @Test + void createParamSet() { + + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + TestConfigGroup testGroup = ConfigUtils.addOrGetModule(config, TestConfigGroup.class); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("multiLevel.yml") + ); + + testGroup.addParam("values", "1, 2, 3"); + + assertThat(testGroup.values) + .containsExactly(1, 2, 3); + + Collection params = testGroup.getParameterSets("params"); + + assertThat(params).hasSize(2); + + Iterator it = params.iterator(); + TestParamSet next = (TestParamSet) it.next(); + + assertThat(next.getParams().get("mode")).isEqualTo("car"); + assertThat(next.getParams().get("values")).isEqualTo("-1.0, -2.0"); + assertThat(next.values).containsExactly(-1d, -2d); + + next = (TestParamSet) it.next(); + + assertThat(next.getParams().get("mode")).isEqualTo("bike"); + assertThat(next.getParams().get("values")).isEqualTo("3.0, 4.0"); + assertThat(next.getParams().get("extra")).isEqualTo("extra"); + } + + @Test + void createGroup() { + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("multiLevel.yml") + ); + + + TestConfigGroup test = ConfigUtils.addOrGetModule(config, TestConfigGroup.class); + + assertThat(test.values).containsExactly(1, 2, 3); + + } + + + @Test + void multiLevel() { + + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + TestConfigGroup testGroup = ConfigUtils.addOrGetModule(config, TestConfigGroup.class); + + testGroup.addParameterSet(new TestParamSet("car", "person", "work")); + testGroup.addParameterSet(new TestParamSet("bike", "person", "work")); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("multiLevel.yml") + ); + + Collection params = testGroup.getParameterSets("params"); + assertThat(params).hasSize(2); + + Iterator it = params.iterator(); + ConfigGroup next = it.next(); + + // These parameters are recognized as lists correctly + assertThat(next.getParams().get("values")).isEqualTo("-1.0, -2.0"); + + next = it.next(); + assertThat(next.getParams().get("values")).isEqualTo("3.0, 4.0"); + assertThat(next.getParams().get("extra")).isEqualTo("extra"); + + } + + @Test + void updateOne() { + + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + TestConfigGroup testGroup = ConfigUtils.addOrGetModule(config, TestConfigGroup.class); + + testGroup.addParameterSet(new TestParamSet("car", "person", "work")); + + ApplicationUtils.applyConfigUpdate( + config, input.resolve("multiLevel.yml") + ); + + Collection params = testGroup.getParameterSets("params"); + assertThat(params).hasSize(2); + + Iterator it = params.iterator(); + ConfigGroup next = it.next(); + + assertThat(next.getParams().get("mode")).isEqualTo("car"); + assertThat(next.getParams().get("values")).isEqualTo("-1.0, -2.0"); + + next = it.next(); + assertThat(next.getParams().get("mode")).isEqualTo("bike"); + assertThat(next.getParams().get("values")).isEqualTo("3.0, 4.0"); + assertThat(next.getParams().get("extra")).isEqualTo("extra"); + } + + @Test + void ambiguous() { + + Config config = ConfigUtils.createConfig(); + Path input = Path.of(utils.getClassInputDirectory()); + + TestConfigGroup testGroup = ConfigUtils.addOrGetModule(config, TestConfigGroup.class); + + testGroup.addParameterSet(new TestParamSet("car", "person", "work")); + testGroup.addParameterSet(new TestParamSet("car", "person", "home")); + + // This should fail because the parameter set is ambiguous + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + ApplicationUtils.applyConfigUpdate( + config, input.resolve("multiLevel.yml") + ); + }); + } + + + public static final class TestConfigGroup extends ReflectiveConfigGroup { + + @Parameter + private List values; + + public TestConfigGroup() { + super("test"); + } + + @Override + public ConfigGroup createParameterSet(String type) { + if (type.equals("params")) { + return new TestParamSet(); + } + + return super.createParameterSet(type); + } + } + + + public static final class TestParamSet extends ReflectiveConfigGroup { + + @Parameter + private List values; + + public TestParamSet() { + super("params", true); + } + + public TestParamSet(String mode, String subpopulation, String activity) { + super("params", true); + + this.addParam("mode", mode); + this.addParam("subpopulation", subpopulation); + this.addParam("activity", activity); + } + } + +} diff --git a/contribs/application/src/test/java/org/matsim/application/options/CsvOptionsTest.java b/contribs/application/src/test/java/org/matsim/application/options/CsvOptionsTest.java index 65299cb0e81..a406deca398 100644 --- a/contribs/application/src/test/java/org/matsim/application/options/CsvOptionsTest.java +++ b/contribs/application/src/test/java/org/matsim/application/options/CsvOptionsTest.java @@ -3,11 +3,13 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -20,18 +22,27 @@ public class CsvOptionsTest { @Test void output() throws IOException { - CsvOptions csv = new CsvOptions(CSVFormat.Predefined.TDF); + List delimiters = new ArrayList<>(); - Path tmp = f.resolve("test.csv"); + delimiters.add(new CsvOptions(CSVFormat.Predefined.TDF)); + delimiters.add(new CsvOptions(CSVFormat.Predefined.Default)); + delimiters.add(new CsvOptions(CSVFormat.Predefined.Default, ';', StandardCharsets.UTF_8)); - CSVPrinter printer = csv.createPrinter(tmp); + for (CsvOptions csv : delimiters) { + Path tmp = f.resolve("test.csv"); - printer.printRecord("header", "column"); - printer.printRecord("1", "2"); - printer.close(); + CSVPrinter printer = csv.createPrinter(tmp); - assertThat(tmp) - .hasContent("header\tcolumn\n1\t2"); + String delimiter = csv.getFormat().getDelimiterString(); + printer.printRecord("header", "column"); + printer.printRecord("1", "2"); + printer.close(); + + assertThat(tmp) + .hasContent("header" + delimiter + "column\n1" + delimiter + "2"); + + assertThat(delimiter).isEqualTo(CsvOptions.detectDelimiter(tmp.toString()).toString()); + } } } diff --git a/contribs/application/src/test/java/org/matsim/application/options/ShpOptionsTest.java b/contribs/application/src/test/java/org/matsim/application/options/ShpOptionsTest.java index 7742e1bdc2e..9b4b662733f 100644 --- a/contribs/application/src/test/java/org/matsim/application/options/ShpOptionsTest.java +++ b/contribs/application/src/test/java/org/matsim/application/options/ShpOptionsTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.locationtech.jts.geom.Geometry; +import org.matsim.api.core.v01.Coord; import org.matsim.testcases.MatsimTestUtils; import org.opengis.feature.simple.SimpleFeature; @@ -41,6 +42,30 @@ void readZip() { } + @Test + void get() { + + Path input = Path.of(utils.getClassInputDirectory() + .replace("ShpOptionsTest", "CreateLandUseShpTest") + .replace("options", "prepare")) + .resolve("andorra-latest-free.shp.zip"); + + Assumptions.assumeTrue(Files.exists(input)); + + ShpOptions shp = new ShpOptions(input, null, null); + + ShpOptions.Index index = shp.createIndex(shp.getShapeCrs(), "name"); + + SimpleFeature result = index.queryFeature(new Coord(1.5333461, 42.555388)); + + assertThat(result) + .isNotNull(); + + String name = index.query(new Coord(1.5333461, 42.555388)); + assertThat(name) + .isEqualTo("Museu de la Miniatura"); + } + @Test void all() { diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java index 1bcb22e6b80..96e405eefd3 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java @@ -1,14 +1,7 @@ package org.matsim.freightDemandGeneration; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -17,21 +10,21 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; -import org.matsim.freight.carriers.FreightCarriersConfigGroup; -import org.matsim.freight.carriers.Carrier; -import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; -import org.matsim.freight.carriers.CarriersUtils; -import org.matsim.freight.carriers.CarrierVehicle; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; +import org.matsim.freight.carriers.CarrierVehicle; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; import org.matsim.freightDemandGeneration.CarrierReaderFromCSV.CarrierInformationElement; import org.matsim.testcases.MatsimTestUtils; -import org.opengis.feature.simple.SimpleFeature; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; /** * @author Ricardo Ewert @@ -54,13 +47,13 @@ void carrierCreation() throws IOException { Path carrierCSVLocation = Path.of(utils.getPackageInputDirectory() + "testCarrierCSV.csv"); Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); ShpOptions shp = new ShpOptions(shapeFilePath, "WGS84", null); - Collection polygonsInShape = shp.readFeatures(); Set allNewCarrierInformation = CarrierReaderFromCSV .readCarrierInformation(carrierCSVLocation); String shapeCategory = "Ortsteil"; - CarrierReaderFromCSV.checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, polygonsInShape, shapeCategory); + ShpOptions.Index indexShape = shp.createIndex(shapeCategory); + CarrierReaderFromCSV.checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, indexShape, shapeCategory); CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, - polygonsInShape, 1, null); + indexShape, 1, null); Assertions.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size()); Assertions.assertTrue( CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); @@ -146,9 +139,9 @@ void carrierCreation() throws IOException { if (!depot.equals("j(2,6)R")) { Link link = network.getLinks().get(Id.createLinkId(depot)); Assertions.assertTrue( - FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, null, null)); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area1"}, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area2"}, null)); + FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area1"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area2"}, null)); } } diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java index da396bb7a9e..c9133b6b56b 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java @@ -51,7 +51,7 @@ void testLinkForPerson() throws IOException { FreightDemandGenerationUtils.preparePopulation(population, 1.0, 1.0, "changeNumberOfLocationsWithDemand"); HashMap, HashMap> nearestLinkPerPerson = new HashMap<>(); for (Person person : population.getPersons().values()) { - DemandReaderFromCSV.findLinksForPersons(scenario, nearestLinkPerPerson, person); + DemandReaderFromCSV.findLinksForPerson(scenario, nearestLinkPerPerson, person); } Assertions.assertEquals("j(1,8)",nearestLinkPerPerson.get(Id.createPersonId("person1")).values().iterator().next()); Assertions.assertEquals("j(3,3)",nearestLinkPerPerson.get(Id.createPersonId("person2")).values().iterator().next()); @@ -78,9 +78,10 @@ void demandCreation() throws IOException { freightCarriersConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); Path carrierCSVLocation = Path.of(utils.getPackageInputDirectory() + "testCarrierCSV.csv"); Path demandCSVLocation = Path.of(utils.getPackageInputDirectory() + "testDemandCSV.csv"); - String shapeCategory = "Ortsteil"; Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); ShpOptions shp = new ShpOptions(shapeFilePath, "WGS84", null); + String shapeCategory = "Ortsteil"; + ShpOptions.Index indexShape = shp.createIndex("Ortsteil"); Collection polygonsInShape = shp.readFeatures(); String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); @@ -90,10 +91,10 @@ void demandCreation() throws IOException { Set allNewCarrierInformation = CarrierReaderFromCSV .readCarrierInformation(carrierCSVLocation); CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, - polygonsInShape, 1, null); + indexShape, 1, null); Set demandInformation = DemandReaderFromCSV.readDemandInformation(demandCSVLocation); - DemandReaderFromCSV.checkNewDemand(scenario, demandInformation, polygonsInShape, shapeCategory); - DemandReaderFromCSV.createDemandForCarriers(scenario, polygonsInShape, demandInformation, population, false, + DemandReaderFromCSV.checkNewDemand(scenario, demandInformation, indexShape, shapeCategory); + DemandReaderFromCSV.createDemandForCarriers(scenario, indexShape, demandInformation, population, false, null); Assertions.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size()); Assertions.assertTrue( @@ -142,10 +143,10 @@ void demandCreation() throws IOException { for (String locationsOfServiceElement : locationsPerServiceElement.get("serviceElement1")) { Link link = network.getLinks().get(Id.createLinkId(locationsOfServiceElement)); Assertions.assertTrue( - FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, null, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, + FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[] { "area1" }, null)); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[] { "area2" }, null)); } Assertions.assertEquals(4, locationsPerServiceElement.get("serviceElement2").size()); @@ -230,10 +231,10 @@ void demandCreation() throws IOException { for (String locationsOfShipmentElement : locationsPerShipmentElement.get("ShipmenElement1_delivery")) { Link link = network.getLinks().get(Id.createLinkId(locationsOfShipmentElement)); Assertions.assertTrue( - FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, null, null)); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, + FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[] { "area1" }, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[] { "area2" }, null)); } } diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtilsTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtilsTest.java index e1528caf871..4015178449d 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtilsTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtilsTest.java @@ -1,12 +1,8 @@ package org.matsim.freightDemandGeneration; -import java.nio.file.Path; -import java.util.Collection; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.locationtech.jts.geom.Point; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -16,10 +12,10 @@ import org.matsim.application.options.ShpOptions; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.testcases.MatsimTestUtils; -import org.opengis.feature.simple.SimpleFeature; - + +import java.nio.file.Path; + /** * @author Ricardo Ewert * @@ -27,9 +23,9 @@ public class FreightDemandGenerationUtilsTest { @RegisterExtension - private MatsimTestUtils utils = new MatsimTestUtils(); - - @Test + private MatsimTestUtils utils = new MatsimTestUtils(); + + @Test void testPreparePopulation() { String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); @@ -78,9 +74,9 @@ void testPreparePopulation() { Assertions.assertEquals(5200.0,person.getAttributes().getAttribute("homeY")); Assertions.assertEquals(0, person.getPlans().size()); Assertions.assertNull(person.getSelectedPlan()); - } - - @Test + } + + @Test void testCoordOfMiddlePointOfLink() { Network network = NetworkUtils.readNetwork("https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); Link link = network.getLinks().get(Id.createLinkId("i(8,8)")); @@ -91,9 +87,9 @@ void testCoordOfMiddlePointOfLink() { Coord middlePoint2 = FreightDemandGenerationUtils.getCoordOfMiddlePointOfLink(link2); Assertions.assertEquals(5000, middlePoint2.getX(), MatsimTestUtils.EPSILON); Assertions.assertEquals(7500, middlePoint2.getY(), MatsimTestUtils.EPSILON); - } - - @Test + } + + @Test void testReducePopulationToShapeArea() { String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); @@ -105,38 +101,38 @@ void testReducePopulationToShapeArea() { Assertions.assertEquals(6, population.getPersons().size()); Assertions.assertFalse(population.getPersons().containsKey(Id.createPersonId("person2"))); Assertions.assertFalse(population.getPersons().containsKey(Id.createPersonId("person4"))); - } - - @Test + } + + @Test void testCheckPositionInShape_link() { Network network = NetworkUtils.readNetwork("https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); Link link = network.getLinks().get(Id.createLinkId("i(8,8)")); Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); ShpOptions shp = new ShpOptions(shapeFilePath,"WGS84", null); - Collection polygonsInShape = shp.readFeatures(); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, null, null)); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area1"}, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area2"}, null)); + ShpOptions.Index indexShape = shp.createIndex("Ortsteil"); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area1"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area2"}, null)); link = network.getLinks().get(Id.createLinkId("i(6,3)R")); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, null, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area1"}, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, polygonsInShape, new String[]{"area2"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area1"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, new String[]{"area2"}, null)); + + } - } - - @Test + @Test void testCheckPositionInShape_point() { Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); ShpOptions shp = new ShpOptions(shapeFilePath,"WGS84", null); - Collection polygonsInShape = shp.readFeatures(); - Point point = MGC.xy2Point(6000, 6000); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, null, null)); - Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, new String[]{"area1"}, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, new String[]{"area2"}, null)); - point = MGC.xy2Point(2000, 2000); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, null, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, new String[]{"area1"}, null)); - Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, point, polygonsInShape, new String[]{"area2"}, null)); + ShpOptions.Index indexShape = shp.createIndex("Ortsteil"); + Coord coord = new Coord(6000, 6000); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, null, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, new String[]{"area1"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, new String[]{"area2"}, null)); + coord = new Coord(2000, 2000); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, null, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, new String[]{"area1"}, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(null, coord, indexShape, new String[]{"area2"}, null)); } } diff --git a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/config.xml b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/config.xml new file mode 100644 index 00000000000..c67cc24de58 --- /dev/null +++ b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/config.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml new file mode 100644 index 00000000000..f1d3c105534 --- /dev/null +++ b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/multiLevel.yml @@ -0,0 +1,11 @@ + +test: + values: [1,2,3] + params: + - mode: car + subpopulation: person + values: ["-1", "-2"] + - mode: bike + subpopulation: person + values: [3, 4] + extra: "extra" \ No newline at end of file diff --git a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/params.yml b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/params.yml new file mode 100644 index 00000000000..31d0f926bd2 --- /dev/null +++ b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/params.yml @@ -0,0 +1,13 @@ +scoring: + scoringParameters: + - modeParams: + - mode: walk + constant: 0.0 + - mode: car + constant: -0.62 + - mode: pt + constant: -0.25 + - mode: bike + constant: -2.23 + - mode: ride + constant: -1.37 \ No newline at end of file diff --git a/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/standard.yml b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/standard.yml new file mode 100644 index 00000000000..dc25f14bdf3 --- /dev/null +++ b/contribs/application/test/input/org/matsim/application/ConfigYamlUpdateTest/standard.yml @@ -0,0 +1,14 @@ +controler: + runId: 567 + +global: + numberOfThreads: 8 + +planCalcScore: + + scoringParameters: + - modeParams: + - mode: car + constant: -1 + - mode: bike + constant: -2 \ No newline at end of file diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml b/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml deleted file mode 100644 index bc64ce1501a..00000000000 --- a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/util/WeightedRandomSelection.java b/contribs/common/src/main/java/org/matsim/contrib/common/util/WeightedRandomSelection.java index 6e305111108..148174d67af 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/util/WeightedRandomSelection.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/util/WeightedRandomSelection.java @@ -67,6 +67,10 @@ public int size() { return entryList.size(); } + public double getTotalWeight() { + return totalWeight; + } + private record Entry(E e, double cumulativeWeight) implements Comparable> { public int compareTo(Entry o) { double diff = this.cumulativeWeight - o.cumulativeWeight; diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/components/constraints/TransitWalkConstraint.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/components/constraints/TransitWalkConstraint.java index 7bfd6d0b61b..5c63102b7a9 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/components/constraints/TransitWalkConstraint.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/components/constraints/TransitWalkConstraint.java @@ -13,6 +13,7 @@ import org.matsim.contribs.discrete_mode_choice.model.trip_based.TripConstraintFactory; import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.RoutedTripCandidate; import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate; +import org.matsim.pt.routes.TransitPassengerRoute; /** * This contraint forbids "pt" trips that only consist of walk legs, i.e. there @@ -29,7 +30,7 @@ public boolean validateAfterEstimation(DiscreteModeChoiceTrip trip, TripCandidat // Go through all plan elments for (PlanElement element : ((RoutedTripCandidate) candidate).getRoutedPlanElements()) { if (element instanceof Leg) { - if (((Leg) element).getMode().equals(TransportMode.pt)) { + if (((Leg) element).getRoute() instanceof TransitPassengerRoute) { // If we find at least one pt leg, we're good return true; } diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/model/tour_based/TourBasedModel.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/model/tour_based/TourBasedModel.java index 962ce8a6ee3..295e71e94c4 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/model/tour_based/TourBasedModel.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/model/tour_based/TourBasedModel.java @@ -93,7 +93,7 @@ public List chooseModes(Person person, List chooseModes(Person person, List allFeatures = ShapeFileReader.getAllFeatures(areaFile); + Collection allFeatures = GeoFileReader.getAllFeatures(areaFile); //do not convert coordinates! all MATSim output should be in the same CRS. if input was not in correct CRS, simulation would have crashed... - ShapeFileWriter.writeGeometries(allFeatures, output.getPath("serviceArea.shp").toString()); + GeoFileWriter.writeGeometries(allFeatures, output.getPath("serviceArea.shp").toString()); //needs to be a DoubleColumn because transposing later forces us to have the same column type for all (new) value columns tableSupplyKPI.addColumns(DoubleColumn.create("Number of areas", new Integer[]{allFeatures.size()})); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java index db6168ac2e1..3481b7ec7a2 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java @@ -20,7 +20,7 @@ /** * Aggregate class for informed-mode-choice that makes sure to invoke the correct estimator for each drt mode. */ -public class MultiModalDrtLegEstimator implements LegEstimator { +public class MultiModalDrtLegEstimator implements LegEstimator { private static final Logger log = LogManager.getLogger(MultiModalDrtLegEstimator.class); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java index 58705f89cb3..6ce932ecebc 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java @@ -15,6 +15,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.controler.MatsimServices; @@ -94,13 +95,16 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id linkId) { Leg currentLeg = (Leg) planAgent.getCurrentPlanElement(); Gbl.assertIf(this.fissConfigGroup.sampledModes.contains(currentLeg.getMode())); NetworkRoute networkRoute = (NetworkRoute) currentLeg.getRoute(); + Person person = planAgent.getCurrentPlan().getPerson(); + Vehicle vehicle = this.matsimServices.getScenario().getVehicles().getVehicles() + .get(networkRoute.getVehicleId()); // update travel time with travel times of last iteration double newTravelTime = 0.0; - // start and end link are not consideres in NetworkRoutingModule for travel time + // start and end link are not considered in NetworkRoutingModule for travel time for (Id routeLinkId : networkRoute.getLinkIds()) { newTravelTime += this.travelTime.getLinkTravelTime(network.getLinks().get(routeLinkId), - now + newTravelTime, null, null); + now + newTravelTime, person, vehicle); } LOG.debug("New travelTime: {}, was {}", newTravelTime, networkRoute.getTravelTime().orElseGet(() -> Double.NaN)); @@ -108,8 +112,8 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id linkId) { } // remove vehicle of teleported agent from parking spot QVehicle removedVehicle = null; - if (agent instanceof MobsimDriverAgent) { - Id vehicleId = ((MobsimDriverAgent) agent).getPlannedVehicleId(); + if (agent instanceof MobsimDriverAgent driverAgent) { + Id vehicleId = driverAgent.getPlannedVehicleId(); QVehicle vehicle = qNetsimEngine.getVehicles().get(vehicleId); QLinkI qLinkI = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(linkId); removedVehicle = qLinkI.removeParkedVehicle(vehicleId); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java index 4a7cb879f50..8a6b6e4098c 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java @@ -20,13 +20,6 @@ package org.matsim.contrib.drt.extension.companions; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; - import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -44,6 +37,13 @@ import org.matsim.testcases.MatsimTestUtils; import org.matsim.vis.otfvis.OTFVisConfigGroup; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + /** * @author Steffen Axer */ @@ -74,8 +74,9 @@ void testRunDrtWithCompanions() { Controler controler = DrtCompanionControlerCreator.createControler(config); controler.run(); - int actualRides = getTotalNumberOfDrtRides(); - Assertions.assertThat(actualRides).isEqualTo(706); + int[] actualRides = getTotalNumberOfDrtRides(); + Assertions.assertThat(actualRides[0]).isEqualTo(378); + Assertions.assertThat(actualRides[1]).isEqualTo(706); } @Test @@ -101,11 +102,12 @@ void testRunDrtWithCompanionsMultiThreaded() { Controler controler = DrtCompanionControlerCreator.createControler(config); controler.run(); - int actualRides = getTotalNumberOfDrtRides(); - Assertions.assertThat(actualRides).isEqualTo(699); + int[] actualRides = getTotalNumberOfDrtRides(); + Assertions.assertThat(actualRides[0]).isEqualTo(375); + Assertions.assertThat(actualRides[1]).isEqualTo(699); } - private int getTotalNumberOfDrtRides() { + private int[] getTotalNumberOfDrtRides() { String filename = utils.getOutputDirectory() + "/drt_customer_stats_drt.csv"; final List collect; @@ -119,8 +121,12 @@ private int getTotalNumberOfDrtRides() { List keys = List.of(collect.get(0).split(";")); List lastIterationValues = List.of(collect.get(size - 1).split(";")); - int index = keys.indexOf("rides"); - String value = lastIterationValues.get(index); - return Integer.parseInt(value); + int ridesRequestIndex = keys.indexOf("rides"); + int ridesRequest = Integer.parseInt(lastIterationValues.get(ridesRequestIndex)); + + int ridesPaxIndex = keys.indexOf("rides_pax"); + int ridesPax = Integer.parseInt(lastIterationValues.get(ridesPaxIndex)); + + return new int[]{ridesRequest, ridesPax}; } } diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java index 48a6c4aeeea..2ced56d6edf 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java @@ -109,10 +109,10 @@ void testWithPrebooking() { controller.run(); - assertEquals(74, tracker.immediateScheduled); - assertEquals(198, tracker.prebookedScheduled); - assertEquals(116, tracker.immediateRejected); - assertEquals(7, tracker.prebookedRejected); + assertEquals(112, tracker.immediateScheduled); + assertEquals(182, tracker.prebookedScheduled); + assertEquals(94, tracker.immediateRejected); + assertEquals(23, tracker.prebookedRejected); } static private class PassengerPickUpTracker implements PassengerPickedUpEventHandler { diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java index 69febd3aab0..be42758f698 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java @@ -283,7 +283,7 @@ void testRangeConstraintWithCustomInstances() { } assertEquals(1470, distanceCalculator.calculatedDistances); - assertEquals(5288, distanceApproximator.calculatedDistances); + assertEquals(5280, distanceApproximator.calculatedDistances); } @Test @@ -319,7 +319,7 @@ protected void configureQSim() { } assertEquals(1470, distanceCalculator.calculatedDistances); - assertEquals(5288, distanceApproximator.calculatedDistances); + assertEquals(5280, distanceApproximator.calculatedDistances); } static class CustomDistanceCalculator extends CustomCalculator { @@ -464,9 +464,9 @@ void testVehicleDistanceObjective() { controller.run(); assertEquals(22, handler.rejectedRequests); - assertEquals(2066658.0, handler.fleetDistance, 1e-3); - assertEquals(694149.0, handler.activeTime(), 1e-3); - assertEquals(280.61475, handler.meanWaitTime(), 1e-3); + assertEquals(2070663.0, handler.fleetDistance, 1e-3); + assertEquals(699076.0, handler.activeTime(), 1e-3); + assertEquals(279.37704, handler.meanWaitTime(), 1e-3); } @Test diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java index 399804f9aee..edc20fb87b7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java @@ -20,6 +20,8 @@ package org.matsim.contrib.drt.analysis; import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.apache.logging.log4j.LogManager; @@ -332,7 +334,7 @@ private void writeIterationPassengerStats(String summarizeLegs, int it) { try (var bw = getAppendingBufferedWriter("drt_customer_stats", ".csv")) { if (!headerWritten) { headerWritten = true; - bw.write(line("runId", "iteration", "rides", "wait_average", "wait_max", "wait_p95", "wait_p75", "wait_median", + bw.write(line("runId", "iteration", "rides", "rides_pax", "groupSize_mean", "wait_average", "wait_max", "wait_p95", "wait_p75", "wait_median", "percentage_WT_below_10", "percentage_WT_below_15", "inVehicleTravelTime_mean", "distance_m_mean", "directDistance_m_mean", "totalTravelTime_mean", "fareAllReferences_mean", "rejections", "rejectionRate")); } @@ -569,10 +571,13 @@ private static String summarizeLegs(List legs, Map, Double> format.setMaximumFractionDigits(2); format.setGroupingUsed(false); + Multiset> servedRides = HashMultiset.create(); + for (DrtLeg leg : legs) { if (leg.toLinkId == null) { continue; } + servedRides.add(leg.request); waitStats.addValue(leg.waitTime); rideStats.addValue(leg.arrivalTime - leg.departureTime - leg.waitTime); distanceStats.addValue(travelDistances.get(leg.request)); @@ -580,7 +585,9 @@ private static String summarizeLegs(List legs, Map, Double> traveltimes.addValue(leg.arrivalTime - leg.departureTime); } - return String.join(delimiter, format.format(waitStats.getValues().length) + "",// + return String.join(delimiter, format.format(servedRides.entrySet().size()) + "",// + format.format(servedRides.size()) + "",// + format.format(((double) servedRides.size()) / servedRides.entrySet().size()) + "",// format.format(waitStats.getMean()) + "",// format.format(waitStats.getMax()) + "",// format.format(waitStats.getPercentile(95)) + "",// diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java index 40c885b5531..129c952ec09 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java @@ -24,13 +24,7 @@ import java.io.IOException; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.*; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.apache.logging.log4j.LogManager; @@ -49,7 +43,7 @@ import org.matsim.core.controler.listener.IterationEndsListener; import org.matsim.core.controler.listener.ShutdownListener; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.core.utils.io.IOUtils; import org.opengis.feature.simple.SimpleFeature; @@ -159,10 +153,12 @@ private Map createZonalStats() { public void notifyShutdown(ShutdownEvent event) { String crs = event.getServices().getConfig().global().getCoordinateSystem(); Collection features = convertGeometriesToSimpleFeatures(crs); - String fileName = event.getServices() + if(!features.isEmpty()) { + String fileName = event.getServices() .getControlerIO() - .getOutputFilename("drt_waitStats" + "_" + drtCfg.getMode() + "_zonal.shp"); - ShapeFileWriter.writeGeometries(features, fileName); + .getOutputFilename("drt_waitStats" + "_" + drtCfg.getMode() + "_zonal.gpkg"); + GeoFileWriter.writeGeometries(features, fileName); + } } private Collection convertGeometriesToSimpleFeatures(String targetCoordinateSystem) { @@ -172,9 +168,10 @@ private Collection convertGeometriesToSimpleFeatures(String targe } catch (IllegalArgumentException e) { log.warn("Coordinate reference system \"" + targetCoordinateSystem - + "\" is unknown. Please set a crs in config global. Will try to create drt_waitStats_" + + "\" is unknown. Please set a crs in config global. Will not create drt_waitStats_" + drtCfg.getMode() - + "_zonal.shp anyway."); + + "_zonal.gpkg."); + return Collections.emptyList(); } simpleFeatureBuilder.setName("drtZoneFeature"); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java index 1b16c140d50..139d5262b12 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java @@ -48,6 +48,12 @@ public DefaultInsertionCostCalculator(CostCalculationStrategy costCalculationStr */ @Override public double calculate(DrtRequest drtRequest, Insertion insertion, DetourTimeInfo detourTimeInfo) { + // check if the max riding time constraints is violated (with default config, the max ride duration + // is infinity) + if (violatesMaxRideDuration(drtRequest, detourTimeInfo)) { + return INFEASIBLE_SOLUTION_COST; + } + var vEntry = insertion.vehicleEntry; // in case of prebooking, we may have intermediate stay times after pickup @@ -55,7 +61,7 @@ public double calculate(DrtRequest drtRequest, Insertion insertion, DetourTimeIn // dropoff insertion point double effectiveDropoffTimeLoss = InsertionDetourTimeCalculator.calculateRemainingPickupTimeLossAtDropoff( insertion, detourTimeInfo.pickupDetourInfo) + detourTimeInfo.dropoffDetourInfo.dropoffTimeLoss; - + if (vEntry.getSlackTime(insertion.pickup.index) < detourTimeInfo.pickupDetourInfo.pickupTimeLoss || vEntry.getSlackTime(insertion.dropoff.index) < effectiveDropoffTimeLoss) { return INFEASIBLE_SOLUTION_COST; @@ -63,4 +69,10 @@ public double calculate(DrtRequest drtRequest, Insertion insertion, DetourTimeIn return costCalculationStrategy.calcCost(drtRequest, insertion, detourTimeInfo); } + + private boolean violatesMaxRideDuration(DrtRequest drtRequest, InsertionDetourTimeCalculator.DetourTimeInfo detourTimeInfo) { + // Check if the max travel time constraint for the newly inserted request is violated + double rideDuration = detourTimeInfo.dropoffDetourInfo.arrivalTime - detourTimeInfo.pickupDetourInfo.departureTime; + return drtRequest.getMaxRideDuration() < rideDuration; + } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java index 60e27e4bc5a..2d1bd2aa13e 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java @@ -186,7 +186,8 @@ public List generateInsertions(DrtRequest drtRequest, V // task here on the same link (maybe a pickup followed by its dropoff) but much // earlier. In that case it is actually a valid insertion. - if (drtRequest.getEarliestStartTime() < nextStop.getArrivalTime()) { + boolean viableSameLink = vEntry.getPrecedingStayTime(i) > 0.0; + if (viableSameLink && drtRequest.getEarliestStartTime() < nextStop.getArrivalTime()) { // the new request wants to depart before the start of the next stop, which may // be a viable insertion. Note that if the requested wanted to depart after the // start of the next stop, but before its end, this is a special case that is @@ -235,6 +236,16 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, addInsertion(insertions, createInsertionWithDetourData(request, vEntry, pickupInsertion, fromPickupTT, pickupDetourInfo, j)); + } else { + // special case: inserting dropoff before prebooked task on the same link + // see the reasoning in generateInsertions + + boolean viableSameLink = vEntry.getPrecedingStayTime(j) > 0.0; + if (viableSameLink && earliestPickupStartTime + fromPickupTT < nextStop(vEntry, j).getArrivalTime()) { + addInsertion(insertions, + createInsertionWithDetourData(request, vEntry, pickupInsertion, fromPickupTT, pickupDetourInfo, + j)); + } } } @@ -274,6 +285,16 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, addInsertion(insertions, createInsertionWithDetourData(request, vEntry, pickupInsertion, fromPickupTT, pickupDetourInfo, j)); + } else { + // special case: inserting dropoff before prebooked task on the same link + // see the reasoning in generateInsertions + + boolean viableSameLink = vEntry.getPrecedingStayTime(j) > 0.0; + if (viableSameLink && earliestPickupStartTime + fromPickupTT < nextStop(vEntry, j).getArrivalTime()) { + addInsertion(insertions, + createInsertionWithDetourData(request, vEntry, pickupInsertion, fromPickupTT, pickupDetourInfo, + j)); + } } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/MaxDetourInsertionCostCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/MaxDetourInsertionCostCalculator.java new file mode 100644 index 00000000000..e8bedc9282d --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/MaxDetourInsertionCostCalculator.java @@ -0,0 +1,33 @@ +package org.matsim.contrib.drt.optimizer.insertion; + +import org.matsim.contrib.drt.passenger.DrtRequest; + +/** + * This insertion cost calculator performs additional check on the maximum ride duration, on top of the original InsertionCostCalculator + * @author: Nico Kรผhnel (nkuehnel), Chengqi Lu (luchengqi7) + * + * The function is now implemented in the DefaultInsertionCostCalculator + * */ +@Deprecated +public class MaxDetourInsertionCostCalculator implements InsertionCostCalculator { + private final InsertionCostCalculator delegate; + + public MaxDetourInsertionCostCalculator(InsertionCostCalculator delegate) { + this.delegate = delegate; + } + + @Override + public double calculate(DrtRequest drtRequest, InsertionGenerator.Insertion insertion, InsertionDetourTimeCalculator.DetourTimeInfo detourTimeInfo) { + if (violatesDetour(drtRequest, detourTimeInfo)) { + return INFEASIBLE_SOLUTION_COST; + } + return delegate.calculate(drtRequest, insertion, detourTimeInfo); + } + + private boolean violatesDetour(DrtRequest drtRequest, InsertionDetourTimeCalculator.DetourTimeInfo detourTimeInfo) { + // Check if the max travel time constraint for the newly inserted request is violated + double rideDuration = detourTimeInfo.dropoffDetourInfo.arrivalTime - detourTimeInfo.pickupDetourInfo.departureTime; + return drtRequest.getMaxRideDuration() < rideDuration; + } + +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java index c58eaeb9bdc..7bcec87ac9b 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java @@ -28,14 +28,14 @@ import java.util.stream.Collectors; import org.matsim.contrib.drt.optimizer.VehicleEntry; -import org.matsim.contrib.drt.optimizer.insertion.*; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator; import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; +import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.stops.StopTimeCalculator; -import org.matsim.contrib.zone.skims.TravelTimeMatrix; -import org.matsim.core.router.util.TravelTime; import com.google.common.annotations.VisibleForTesting; @@ -44,12 +44,9 @@ */ class ExtensiveInsertionProvider { static ExtensiveInsertionProvider create(DrtConfigGroup drtCfg, InsertionCostCalculator insertionCostCalculator, - TravelTimeMatrix travelTimeMatrix, TravelTime travelTime, ForkJoinPool forkJoinPool, - StopTimeCalculator stopTimeCalculator) { - var insertionParams = (ExtensiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(); - var admissibleTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( - insertionParams.admissibleBeelineSpeedFactor, travelTimeMatrix, travelTime); - return new ExtensiveInsertionProvider((ExtensiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(), + ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator, + DetourTimeEstimator admissibleTimeEstimator) { + return new ExtensiveInsertionProvider((ExtensiveInsertionSearchParams) drtCfg.getDrtInsertionSearchParams(), insertionCostCalculator, new InsertionGenerator(stopTimeCalculator, admissibleTimeEstimator), forkJoinPool); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java index cfeaa94ec38..53153700371 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java @@ -22,10 +22,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpModes; @@ -48,11 +48,19 @@ public ExtensiveInsertionSearchQSimModule(DrtConfigGroup drtCfg) { @Override protected void configureQSim() { + bindModal(DetourTimeEstimator.class).toProvider(modalProvider(getter -> { + var insertionParams = (ExtensiveInsertionSearchParams) drtCfg.getDrtInsertionSearchParams(); + var admissibleTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( + insertionParams.admissibleBeelineSpeedFactor, getter.getModal(TravelTimeMatrix.class), + getter.getModal(TravelTime.class)); + return admissibleTimeEstimator; + })); + bindModal(DrtInsertionSearch.class).toProvider(modalProvider(getter -> { var insertionCostCalculator = getter.getModal(InsertionCostCalculator.class); var provider = ExtensiveInsertionProvider.create(drtCfg, insertionCostCalculator, - getter.getModal(TravelTimeMatrix.class), getter.getModal(TravelTime.class), - getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(StopTimeCalculator.class)); + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(StopTimeCalculator.class), getter.getModal(DetourTimeEstimator.class)); return new ExtensiveInsertionSearch(provider, getter.getModal(MultiInsertionDetourPathCalculator.class), insertionCostCalculator, getter.getModal(StopTimeCalculator.class)); })).asEagerSingleton(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java index aa3e2d8db06..9e2be45d6bf 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java @@ -19,17 +19,7 @@ package org.matsim.contrib.drt.optimizer.insertion.repeatedselective; -import com.google.common.annotations.VisibleForTesting; -import org.matsim.contrib.drt.optimizer.VehicleEntry; -import org.matsim.contrib.drt.optimizer.insertion.*; -import org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder.InsertionWithCost; import static org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder.INSERTION_WITH_COST_COMPARATOR; -import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.drt.stops.StopTimeCalculator; -import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrix; -import org.matsim.core.router.util.TravelTime; - import static org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator.INFEASIBLE_SOLUTION_COST; import java.util.Collection; @@ -37,11 +27,21 @@ import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder.InsertionWithCost; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.stops.StopTimeCalculator; + +import com.google.common.annotations.VisibleForTesting; + /** * @author steffenaxer */ class RepeatedSelectiveInsertionProvider { - private static final double SPEED_FACTOR = 1.; private final InsertionCostCalculator insertionCostCalculator; private final InsertionGenerator insertionGenerator; private final ForkJoinPool forkJoinPool; @@ -54,14 +54,11 @@ class RepeatedSelectiveInsertionProvider { this.forkJoinPool = forkJoinPool; } - public static RepeatedSelectiveInsertionProvider create( InsertionCostCalculator insertionCostCalculator, AdaptiveTravelTimeMatrix updatableTravelTimeMatrix, - TravelTime travelTime, - ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator) { - var detourTimeEstimatorWithUpdatedTravelTimes = DetourTimeEstimatorWithAdaptiveTravelTimes.create( - SPEED_FACTOR, updatableTravelTimeMatrix, travelTime); - return new RepeatedSelectiveInsertionProvider(insertionCostCalculator, - new InsertionGenerator(stopTimeCalculator, detourTimeEstimatorWithUpdatedTravelTimes), forkJoinPool); - } + public static RepeatedSelectiveInsertionProvider create(InsertionCostCalculator insertionCostCalculator, + ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator, DetourTimeEstimator detourTimeEstimator) { + return new RepeatedSelectiveInsertionProvider(insertionCostCalculator, + new InsertionGenerator(stopTimeCalculator, detourTimeEstimator), forkJoinPool); + } public List getInsertions(DrtRequest drtRequest, Collection vehicleEntries) { // Parallel outer stream over vehicle entries. The inner stream (flatmap) is diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java index 2f15a6638d8..234bbed7481 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java @@ -22,6 +22,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.optimizer.insertion.selective.SingleInsertionDetourPathCalculator; @@ -41,6 +42,8 @@ * @author steffenaxer */ public class RepeatedSelectiveInsertionSearchQSimModule extends AbstractDvrpModeQSimModule { + private static final double SPEED_FACTOR = 1.; + private final DrtConfigGroup drtCfg; public RepeatedSelectiveInsertionSearchQSimModule(DrtConfigGroup drtCfg) { @@ -50,11 +53,17 @@ public RepeatedSelectiveInsertionSearchQSimModule(DrtConfigGroup drtCfg) { @Override protected void configureQSim() { + bindModal(DetourTimeEstimator.class).toProvider(modalProvider(getter -> { + var detourTimeEstimatorWithUpdatedTravelTimes = DetourTimeEstimatorWithAdaptiveTravelTimes.create( + SPEED_FACTOR, getter.getModal(AdaptiveTravelTimeMatrix.class), getter.getModal(TravelTime.class)); + return detourTimeEstimatorWithUpdatedTravelTimes; + })); + addModalComponent(RepeatedSelectiveInsertionSearch.class, modalProvider(getter -> { - RepeatedSelectiveInsertionProvider provider = RepeatedSelectiveInsertionProvider.create( - getter.getModal(InsertionCostCalculator.class), getter.getModal(AdaptiveTravelTimeMatrix.class), - getter.getModal(TravelTime.class), getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), - getter.getModal(StopTimeCalculator.class)); + RepeatedSelectiveInsertionProvider provider = RepeatedSelectiveInsertionProvider.create( + getter.getModal(InsertionCostCalculator.class), + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(StopTimeCalculator.class), getter.getModal(DetourTimeEstimator.class)); var insertionCostCalculator = getter.getModal(InsertionCostCalculator.class); return new RepeatedSelectiveInsertionSearch(provider, getter.getModal(SingleInsertionDetourPathCalculator.class), diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java index b6efbb923f8..0988d929260 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java @@ -25,13 +25,13 @@ import java.util.concurrent.ForkJoinPool; import org.matsim.contrib.drt.optimizer.VehicleEntry; -import org.matsim.contrib.drt.optimizer.insertion.*; +import org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator; +import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.stops.StopTimeCalculator; -import org.matsim.contrib.zone.skims.TravelTimeMatrix; -import org.matsim.core.router.util.TravelTime; import com.google.common.annotations.VisibleForTesting; @@ -39,12 +39,9 @@ * @author michalm */ class SelectiveInsertionProvider { - public static SelectiveInsertionProvider create(DrtConfigGroup drtCfg, - InsertionCostCalculator insertionCostCalculator, TravelTimeMatrix travelTimeMatrix, TravelTime travelTime, - ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator) { - var insertionParams = (SelectiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(); - var restrictiveDetourTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( - insertionParams.restrictiveBeelineSpeedFactor, travelTimeMatrix, travelTime); + public static SelectiveInsertionProvider create(InsertionCostCalculator insertionCostCalculator, + ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator, + DetourTimeEstimator restrictiveDetourTimeEstimator) { return new SelectiveInsertionProvider(new BestInsertionFinder(insertionCostCalculator), new InsertionGenerator(stopTimeCalculator, restrictiveDetourTimeEstimator), forkJoinPool); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java index f92cc82413c..7d8b045f173 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java @@ -22,10 +22,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpModes; @@ -49,10 +49,19 @@ public SelectiveInsertionSearchQSimModule(DrtConfigGroup drtCfg) { @Override protected void configureQSim() { + bindModal(DetourTimeEstimator.class).toProvider(modalProvider(getter -> { + var insertionParams = (SelectiveInsertionSearchParams) drtCfg.getDrtInsertionSearchParams(); + var restrictiveDetourTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( + insertionParams.restrictiveBeelineSpeedFactor, getter.getModal(TravelTimeMatrix.class), + getter.getModal(TravelTime.class)); + return restrictiveDetourTimeEstimator; + })); + addModalComponent(SelectiveInsertionSearch.class, modalProvider(getter -> { - SelectiveInsertionProvider provider = SelectiveInsertionProvider.create(drtCfg, - getter.getModal(InsertionCostCalculator.class), getter.getModal(TravelTimeMatrix.class), - getter.getModal(TravelTime.class), getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(StopTimeCalculator.class)); + SelectiveInsertionProvider provider = SelectiveInsertionProvider.create( + getter.getModal(InsertionCostCalculator.class), + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(StopTimeCalculator.class), getter.getModal(DetourTimeEstimator.class)); // Use 0 as the cost for the selected insertion: // - In the selective strategy, there is at most 1 insertion pre-selected. So no need to compute as there is // no other insertion to compare with. diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java index d053f6e74b9..1437d6286ca 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java @@ -38,6 +38,7 @@ public static AcceptedDrtRequest createFromOriginalRequest(DrtRequest request) { .earliestStartTime(request.getEarliestStartTime()) .latestStartTime(request.getLatestStartTime()) .latestArrivalTime(request.getLatestArrivalTime()) + .maxRideDuration(request.getMaxRideDuration()) .build(); } @@ -46,12 +47,14 @@ public static AcceptedDrtRequest createFromOriginalRequest(DrtRequest request) { private final double earliestStartTime; private final double latestStartTime; private final double latestArrivalTime; + private final double maxRideDuration; private AcceptedDrtRequest(Builder builder) { request = builder.request; earliestStartTime = builder.earliestStartTime; latestStartTime = builder.latestStartTime; latestArrivalTime = builder.latestArrivalTime; + maxRideDuration = builder.maxRideDuration; } public static Builder newBuilder() { @@ -64,6 +67,7 @@ public static Builder newBuilder(AcceptedDrtRequest copy) { builder.earliestStartTime = copy.getEarliestStartTime(); builder.latestStartTime = copy.getLatestStartTime(); builder.latestArrivalTime = copy.getLatestArrivalTime(); + builder.maxRideDuration = copy.getMaxRideDuration(); return builder; } @@ -82,6 +86,9 @@ public double getLatestStartTime() { public double getLatestArrivalTime() { return latestArrivalTime; } + public double getMaxRideDuration() { + return maxRideDuration; + } public Id getId() { return request.getId(); @@ -126,6 +133,7 @@ public static final class Builder { private double earliestStartTime; private double latestStartTime; private double latestArrivalTime; + private double maxRideDuration; private Builder() { } @@ -150,6 +158,11 @@ public Builder latestArrivalTime(double val) { return this; } + public Builder maxRideDuration(double val) { + this.maxRideDuration = val; + return this; + } + public AcceptedDrtRequest build() { return new AcceptedDrtRequest(this); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java index e1c034f2a92..510ad458013 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java @@ -39,6 +39,7 @@ public class DrtRequest implements PassengerRequest { private final double earliestStartTime; private final double latestStartTime; private final double latestArrivalTime; + private final double maxRideDuration; private final List> passengerIds = new ArrayList<>(); private final String mode; @@ -52,6 +53,7 @@ private DrtRequest(Builder builder) { earliestStartTime = builder.earliestStartTime; latestStartTime = builder.latestStartTime; latestArrivalTime = builder.latestArrivalTime; + maxRideDuration = builder.maxRideDuration; passengerIds.addAll(builder.passengerIds); mode = builder.mode; fromLink = builder.fromLink; @@ -69,6 +71,7 @@ public static Builder newBuilder(DrtRequest copy) { builder.earliestStartTime = copy.getEarliestStartTime(); builder.latestStartTime = copy.getLatestStartTime(); builder.latestArrivalTime = copy.getLatestArrivalTime(); + builder.maxRideDuration = copy.getMaxRideDuration(); builder.passengerIds = new ArrayList<>(copy.getPassengerIds()); builder.mode = copy.getMode(); builder.fromLink = copy.getFromLink(); @@ -100,6 +103,10 @@ public double getLatestArrivalTime() { return latestArrivalTime; } + public double getMaxRideDuration() { + return maxRideDuration; + } + @Override public Link getFromLink() { return fromLink; @@ -133,6 +140,7 @@ public String toString() { .add("earliestStartTime", earliestStartTime) .add("latestStartTime", latestStartTime) .add("latestArrivalTime", latestArrivalTime) + .add("maxRideDuration", maxRideDuration) .add("passengerIds", passengerIds.stream().map(Object::toString).collect(Collectors.joining(","))) .add("mode", mode) .add("fromLink", fromLink) @@ -146,6 +154,7 @@ public static final class Builder { private double earliestStartTime; private double latestStartTime; private double latestArrivalTime; + private double maxRideDuration; private List> passengerIds = new ArrayList<>(); private String mode; private Link fromLink; @@ -179,6 +188,11 @@ public Builder latestArrivalTime(double val) { return this; } + public Builder maxRideDuration(double maxRideDuration) { + this.maxRideDuration = maxRideDuration; + return this; + } + public Builder passengerIds(List> val) { passengerIds = new ArrayList<>(val); return this; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java index 8a2cff7227e..f076fa6c525 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java @@ -52,10 +52,11 @@ public DrtRequest createRequest(Id id, List> passengerIds, R DrtRoute drtRoute = (DrtRoute)route; double latestDepartureTime = departureTime + drtRoute.getMaxWaitTime(); double latestArrivalTime = departureTime + drtRoute.getTravelTime().seconds(); + double maxRideDuration = drtRoute.getMaxRideTime(); eventsManager.processEvent( new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerIds, fromLink.getId(), toLink.getId(), - drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime)); + drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime, maxRideDuration)); DrtRequest request = DrtRequest.newBuilder() .id(id) @@ -66,6 +67,7 @@ public DrtRequest createRequest(Id id, List> passengerIds, R .earliestStartTime(departureTime) .latestStartTime(latestDepartureTime) .latestArrivalTime(latestArrivalTime) + .maxRideDuration(maxRideDuration) .submissionTime(submissionTime) .build(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/MaxDetourOfferAcceptor.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/MaxDetourOfferAcceptor.java new file mode 100644 index 00000000000..fe2dfb25834 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/MaxDetourOfferAcceptor.java @@ -0,0 +1,23 @@ +package org.matsim.contrib.drt.passenger; + +import java.util.Optional; + +public class MaxDetourOfferAcceptor implements DrtOfferAcceptor{ + private final double maxAllowedPickupDelay; + + public MaxDetourOfferAcceptor(double maxAllowedPickupDelay) { + this.maxAllowedPickupDelay = maxAllowedPickupDelay; + } + + @Override + public Optional acceptDrtOffer(DrtRequest request, double departureTime, double arrivalTime) { + double updatedLatestStartTime = Math.min(departureTime + + maxAllowedPickupDelay, request.getLatestStartTime()); + return Optional.of(AcceptedDrtRequest + .newBuilder() + .request(request) + .earliestStartTime(request.getEarliestStartTime()) + .latestArrivalTime(Math.min(updatedLatestStartTime + request.getMaxRideDuration(), request.getLatestArrivalTime())) + .latestStartTime(updatedLatestStartTime).build()); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/PromisedPickupTimeWindowDrtOfferAcceptor.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/PromisedPickupTimeWindowDrtOfferAcceptor.java index 78e8e34bfe1..90732eb0b74 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/PromisedPickupTimeWindowDrtOfferAcceptor.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/PromisedPickupTimeWindowDrtOfferAcceptor.java @@ -4,7 +4,9 @@ /** * @author nkuehnel / MOIA + * The function is now realized by the MaxDetourOfferAcceptor -Chengqi (luchengqi7) */ +@Deprecated public final class PromisedPickupTimeWindowDrtOfferAcceptor implements DrtOfferAcceptor { private final double promisedPickupTimeWindow; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java index 09f04e814d9..6a5a320df1c 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java @@ -40,6 +40,7 @@ public class DrtRequestSubmittedEvent extends PassengerRequestSubmittedEvent { public static final String ATTRIBUTE_EARLIEST_DEPARTURE_TIME = "earliestDepartureTime"; public static final String ATTRIBUTE_LATEST_PICKUP_TIME = "latestPickupTime"; public static final String ATTRIBUTE_LATEST_DROPOFF_TIME = "latestDropoffTime"; + public static final String ATTRIBUTE_MAX_RIDE_DURATION = "maxRideDuration"; private final double unsharedRideTime; private final double unsharedRideDistance; @@ -47,16 +48,18 @@ public class DrtRequestSubmittedEvent extends PassengerRequestSubmittedEvent { private final double earliestDepartureTime; private final double latestPickupTime; private final double latestDropoffTime; + private final double maxRideDuration; public DrtRequestSubmittedEvent(double time, String mode, Id requestId, List> personIds, Id fromLinkId, Id toLinkId, double unsharedRideTime, double unsharedRideDistance, - double earliestDepartureTime, double latestPickupTime, double latestDropoffTime) { + double earliestDepartureTime, double latestPickupTime, double latestDropoffTime, double maxRideDuration ) { super(time, mode, requestId, personIds, fromLinkId, toLinkId); this.unsharedRideTime = unsharedRideTime; this.unsharedRideDistance = unsharedRideDistance; this.earliestDepartureTime = earliestDepartureTime; this.latestPickupTime = latestPickupTime; this.latestDropoffTime = latestDropoffTime; + this.maxRideDuration = maxRideDuration; } @Override @@ -90,6 +93,9 @@ public final double getLatestDropoffTime() { return latestDropoffTime; } + public double getMaxRideDuration() { + return maxRideDuration; + } @Override public Map getAttributes() { Map attr = super.getAttributes(); @@ -98,6 +104,7 @@ public Map getAttributes() { attr.put(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, earliestDepartureTime + ""); attr.put(ATTRIBUTE_LATEST_PICKUP_TIME, latestPickupTime + ""); attr.put(ATTRIBUTE_LATEST_DROPOFF_TIME, latestDropoffTime + ""); + attr.put(ATTRIBUTE_MAX_RIDE_DURATION, maxRideDuration + ""); return attr; } @@ -118,7 +125,8 @@ public static DrtRequestSubmittedEvent convert(GenericEvent event) { double earliestDepartureTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, "NaN")); double latestPickupTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_PICKUP_TIME, "NaN")); double latestDropoffTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_DROPOFF_TIME, "NaN")); + double maxRideDuration = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_MAX_RIDE_DURATION, "NaN")); return new DrtRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId, unsharedRideTime, - unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime); + unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime, maxRideDuration); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java index 3fef9b0c932..747e1ae77f2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java @@ -68,7 +68,9 @@ public PrebookingStopActivity(PassengerHandler passengerHandler, DynAgent driver @Override protected boolean isLastStep(double now) { - return updatePickupRequests(now) && leaveTimes.size() == 0 && now >= endTime.get(); + boolean pickupsReady = updatePickupRequests(now); + boolean dropoffsReady = updateDropoffRequests(now); + return pickupsReady && dropoffsReady && now >= endTime.get(); } @Override @@ -83,10 +85,10 @@ private void initDropoffRequests(double now) { leaveTimes.put(request.getId(), leaveTime); } - processDropoffRequests(now); + updateDropoffRequests(now); } - private void processDropoffRequests(double now) { + private boolean updateDropoffRequests(double now) { var iterator = leaveTimes.entrySet().iterator(); while (iterator.hasNext()) { @@ -98,6 +100,8 @@ private void processDropoffRequests(double now) { iterator.remove(); } } + + return leaveTimes.size() == 0; } private boolean updatePickupRequests(double now) { @@ -154,12 +158,9 @@ public void notifyPassengersAreReadyForDeparture(List pass queuePickup(request, now); } - private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { - return pickupRequests.values() - .stream() - .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); + return pickupRequests.values().stream().filter( + r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) + .findAny().orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRoute.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRoute.java index 72f21260119..c375aee64a6 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRoute.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRoute.java @@ -72,6 +72,10 @@ public double getMaxTravelTime() { return getTravelTime().seconds(); // currently DrtRoute.travelTime is set to the max allowed travel time } + public double getMaxRideTime() { + return routeDescription.getMaxRideTime(); + } + public void setDirectRideTime(double directRideTime) { this.routeDescription.setDirectRideTime(directRideTime); } @@ -86,6 +90,10 @@ public void setUnsharedPath(VrpPathWithTravelData unsharedPath) { this.routeDescription.setUnsharedPath(links); } + public void setMaxRideTime(double maxRideTime) { + this.routeDescription.setMaxRideTime(maxRideTime); + } + @Override public String getRouteDescription() { @@ -149,12 +157,18 @@ public static class RouteDescription { private OptionalTime maxWaitTime = OptionalTime.undefined(); private OptionalTime directRideTime = OptionalTime.undefined(); private List unsharedPath = new ArrayList(); + private OptionalTime maxRideTime = OptionalTime.undefined(); @JsonProperty("directRideTime") public double getDirectRideTime() { return directRideTime.isUndefined() ? OptionalTime.undefined().seconds() : directRideTime.seconds(); } + @JsonProperty("maxRideTime") + public double getMaxRideTime() { + return maxRideTime.seconds(); + } + @JsonProperty("maxWaitTime") public double getMaxWaitTime() { return maxWaitTime.isUndefined() ? OptionalTime.undefined().seconds() : maxWaitTime.seconds(); @@ -169,6 +183,10 @@ public void setDirectRideTime(double directRideTime) { this.directRideTime = OptionalTime.defined(directRideTime); } + public void setMaxRideTime(double maxRideTime) { + this.maxRideTime = OptionalTime.defined(maxRideTime); + } + public void setMaxWaitTime(double maxWaitTime) { this.maxWaitTime = OptionalTime.defined(maxWaitTime); } @@ -176,6 +194,5 @@ public void setMaxWaitTime(double maxWaitTime) { public void setUnsharedPath(List unsharedPath) { this.unsharedPath = unsharedPath; } - } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRouteCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRouteCreator.java index 2c668199c5e..15f9bb77530 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRouteCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DrtRouteCreator.java @@ -61,8 +61,18 @@ public DrtRouteCreator(DrtConfigGroup drtCfg, Network modalNetwork, * @return maximum travel time */ static double getMaxTravelTime(DrtConfigGroup drtCfg, double unsharedRideTime) { - return Math.min(unsharedRideTime + drtCfg.maxAbsoluteDetour, - drtCfg.maxTravelTimeAlpha * unsharedRideTime + drtCfg.maxTravelTimeBeta); + return drtCfg.maxTravelTimeAlpha * unsharedRideTime + drtCfg.maxTravelTimeBeta; + } + + /** + * Calculates the maximum ride time defined as: drtCfg.maxDetourAlpha * unsharedRideTime + drtCfg.maxDetourBeta + * + * @param drtCfg + * @param unsharedRideTime ride time of the direct (shortest-time) route + * @return maximum ride time + */ + static double getMaxRideTime(DrtConfigGroup drtCfg, double unsharedRideTime) { + return Math.min(unsharedRideTime + drtCfg.maxAbsoluteDetour, drtCfg.maxDetourAlpha * unsharedRideTime + drtCfg.maxDetourBeta); } public Route createRoute(double departureTime, Link accessActLink, Link egressActLink, Person person, @@ -71,11 +81,13 @@ public Route createRoute(double departureTime, Link accessActLink, Link egressAc router, travelTime); double unsharedRideTime = unsharedPath.getTravelTime();//includes first & last link double maxTravelTime = getMaxTravelTime(drtCfg, unsharedRideTime); + double maxRideDuration = getMaxRideTime(drtCfg, unsharedRideTime); double unsharedDistance = VrpPaths.calcDistance(unsharedPath);//includes last link DrtRoute route = routeFactories.createRoute(DrtRoute.class, accessActLink.getId(), egressActLink.getId()); route.setDistance(unsharedDistance); route.setTravelTime(maxTravelTime); + route.setMaxRideTime(maxRideDuration); route.setDirectRideTime(unsharedRideTime); route.setMaxWaitTime(drtCfg.maxWaitTime); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java index 03b9c551970..7fb685bd92e 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java @@ -107,12 +107,36 @@ public static DrtConfigGroup getSingleModeDrtConfig(Config config) { @Parameter @Comment( - "Defines the maximum allowed absolute detour in seconds of the maxTravelTime estimation function (optimisation constraint), i.e. " - + "min(unsharedRideTime + maxAbsoluteDetour, maxTravelTimeAlpha * unsharedRideTime + maxTravelTimeBeta). " - + "maxAbsoluteDetour should not be smaller than 0. and should be higher than the offset maxTravelTimeBeta.") + "Defines the maximum allowed absolute detour in seconds. Note that the detour is computed from the latest promised pickup time. " + + "To enable the max detour constraint, maxAllowedPickupDelay has to be specified. maxAbsoluteDetour should not be smaller than 0, " + + "and should be higher than the offset maxDetourBeta. By default, this limit is disabled (i.e. set to Inf)") @PositiveOrZero public double maxAbsoluteDetour = Double.POSITIVE_INFINITY;// [s] + @Parameter + @Comment( + "Defines the maximum allowed absolute detour based on the unsharedRideTime. Note that the detour is computed from the latest promised " + + "pickup time. To enable the max detour constraint, maxAllowedPickupDelay has to be specified. A linear combination similar to travel " + + "time constrain is used. This is the ratio part. By default, this limit is disabled (i.e. set to Inf, together with maxDetourBeta).") + @DecimalMin("1.0") + public double maxDetourAlpha = Double.POSITIVE_INFINITY; + + @Parameter + @Comment( + "Defines the maximum allowed absolute detour based on the unsharedRideTime. Note that the detour is computed from the latest promised " + + "pickup time. To enable the max detour constraint, maxAllowedPickupDelay has to be specified. A linear combination similar to travel " + + "time constrain is used. This is the constant part. By default, this limit is disabled (i.e. set to Inf, together with maxDetourAlpha).") + @PositiveOrZero + public double maxDetourBeta = Double.POSITIVE_INFINITY;// [s] + + @Parameter + @Comment( + "Defines the maximum delay allowed from the initial scheduled pick up time. Once the initial pickup time is offered, the latest promised" + + "pickup time is calculated based on initial scheduled pickup time + maxAllowedPickupDelay. " + + "By default, this limit is disabled. If enabled, a value between 120 and 240 is a good choice.") + @PositiveOrZero + public double maxAllowedPickupDelay = Double.POSITIVE_INFINITY;// [s] + @Parameter @Comment("If true, the max travel and wait times of a submitted request" + " are considered hard constraints (the request gets rejected if one of the constraints is violated)." @@ -250,7 +274,7 @@ private void initSingletonParameterSets() { addDefinition(DrtRequestInsertionRetryParams.SET_NAME, DrtRequestInsertionRetryParams::new, () -> drtRequestInsertionRetryParams, params -> drtRequestInsertionRetryParams = (DrtRequestInsertionRetryParams)params); - + //prebooking (optional) addDefinition(PrebookingParams.SET_NAME, PrebookingParams::new, () -> prebookingParams, diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java index a8551b6ab9b..8b3755ba082 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java @@ -313,8 +313,13 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour // prebooking case when we are already at the stop location, but next stop task happens in the future Task afterPickupTask = insertWait(vehicleEntry.vehicle, pickupStopTask, nextBeginTime); - scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, - afterPickupTask.getEndTime()); + // update timings + if (pickupIdx != dropoffIdx) { + // update schedule when inserting the dropoff, otherwise we will illegally shift + // the begin time of a following prebooked stop if there is one + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, + afterPickupTask.getEndTime()); + } } else { VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink, pickupStopTask.getEndTime(), detourData.detourFromPickup, travelTime); @@ -323,9 +328,12 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour Task afterPickupTask = insertDriveWithWait(vehicleEntry.vehicle, pickupStopTask, vrpPath, nextBeginTime); // update timings - // TODO should be enough to update the timeline only till dropoffIdx... - scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, - afterPickupTask.getEndTime()); + if (pickupIdx != dropoffIdx) { + // update schedule when inserting the dropoff, otherwise we will illegally shift + // the begin time of a following prebooked stop if there is one + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, + afterPickupTask.getEndTime()); + } } return pickupStopTask; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java index 1eee457066d..f4dad777fed 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java @@ -50,17 +50,14 @@ public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRe @Override public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, DrtRequest request) { - final double stopDuration = provider.calcDropoffDuration(vehicle, request); - - // we need to take into account the general shift of the task and adding the new - // stop - - // first, we apply shiftEndTime, TODO: can we do this without the complex - // calculation? - double shiftedEndTime = shiftEndTime(vehicle, stop, insertionTime); + double stopDuration = provider.calcDropoffDuration(vehicle, request); + // first, check when we can start with the dropoff + double earliestStartTime = stop.getBeginTime(); + earliestStartTime = Math.max(earliestStartTime, insertionTime); + // second, we add the dropoff - return Math.max(stop.getEndTime(), Math.max(shiftedEndTime, insertionTime + stopDuration)); + return Math.max(stop.getEndTime(), earliestStartTime + stopDuration); } /** diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java index 35e41f5d018..9e7e01e6780 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java @@ -72,7 +72,7 @@ public void reset(int iteration) { { var requestId = Id.create(0, Request.class); events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("12"), - Id.createLinkId("23"), 240, 1000, 0.0, 0.0, 0.0)); + Id.createLinkId("23"), 240, 1000, 0.0, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.flush(); @@ -83,7 +83,7 @@ public void reset(int iteration) { // test minFarePerTrip var requestId = Id.create(1, Request.class); events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("45"), - Id.createLinkId("56"), 24, 100, 0.0, 0.0, 0.0)); + Id.createLinkId("56"), 24, 100, 0.0, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.finishProcessing(); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/MaxDetourConstraintTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/MaxDetourConstraintTest.java new file mode 100644 index 00000000000..895f3a096f2 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/MaxDetourConstraintTest.java @@ -0,0 +1,64 @@ +package org.matsim.contrib.drt.optimizer; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.contrib.drt.passenger.DrtOfferAcceptor; +import org.matsim.contrib.drt.passenger.MaxDetourOfferAcceptor; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.DrtControlerCreator; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.examples.ExamplesUtils; +import org.matsim.testcases.MatsimTestUtils; +import org.matsim.vis.otfvis.OTFVisConfigGroup; + +import java.net.URL; + +public class MaxDetourConstraintTest { + @RegisterExtension + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void testMaxDetourConstraint() { + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), "mielec_drt_config.xml"); + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup()); + MultiModeDrtConfigGroup multiModeDrtConfigGroup = MultiModeDrtConfigGroup.get(config); + DrtConfigGroup drtConfigGroup = DrtConfigGroup.getSingleModeDrtConfig(config); + + // Max wait time + drtConfigGroup.maxWaitTime = 300; + + // Turn on the max detour constraint + drtConfigGroup.maxDetourAlpha = 1.5; + drtConfigGroup.maxDetourBeta = 300; + drtConfigGroup.maxAllowedPickupDelay = 180; + drtConfigGroup.maxAbsoluteDetour = 1200; + + // Make the max total travel time constraints very loose (i.e., make it not active) + drtConfigGroup.maxTravelTimeAlpha = 10; + drtConfigGroup.maxTravelTimeBeta = 7200; + + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + Controler controler = DrtControlerCreator.createControler(config, false); + + for (DrtConfigGroup drtCfg : multiModeDrtConfigGroup.getModalElements()) { + controler.addOverridingQSimModule(new AbstractDvrpModeQSimModule(drtCfg.mode) { + @Override + protected void configureQSim() { + bindModal(DrtOfferAcceptor.class).toProvider(modalProvider(getter -> new MaxDetourOfferAcceptor(drtCfg.maxAllowedPickupDelay))); + } + }); + } + + controler.run(); + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java index 5a44c12c118..6ffde35326c 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java @@ -378,7 +378,8 @@ void startEmpty_onlineRequest_beforeAlreadyPrebookedOtherRequest() { Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); Waypoint.Stop stop0 = stop(200, fromLink, 1); Waypoint.Stop stop1 = stop(400, link("stop"), 0); - VehicleEntry entry = entry(start, stop0, stop1); + List precedingStayTimes = Arrays.asList(100.0, 0.0); + VehicleEntry entry = entry(start, precedingStayTimes, stop0, stop1); assertInsertionsOnly(drtRequest, entry, new Insertion(drtRequest, entry, 0, 0), new Insertion(drtRequest, entry, 0, 1), @@ -524,9 +525,13 @@ private Waypoint.Stop stop(double beginTime, Link link, int outgoingOccupancy) { } private VehicleEntry entry(Waypoint.Start start, Waypoint.Stop... stops) { + List precedingStayTimes = Collections.nCopies(stops.length, 0.0); + return entry(start, precedingStayTimes, stops); + } + + private VehicleEntry entry(Waypoint.Start start, List precedingStayTimes, Waypoint.Stop... stops) { var slackTimes = new double[stops.length + 2]; Arrays.fill(slackTimes, Double.POSITIVE_INFINITY); - List precedingStayTimes = Collections.nCopies(stops.length, 0.0); return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), slackTimes, precedingStayTimes, 0); } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java index cb149fc1f3b..152ac850766 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java @@ -7,6 +7,9 @@ import org.matsim.contrib.drt.prebooking.PrebookingTestEnvironment.RequestInfo; import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.core.controler.Controler; import org.matsim.testcases.MatsimTestUtils; @@ -204,8 +207,8 @@ void twoSequentialRequests_inverseSubmission() { { RequestInfo requestInfo = environment.getRequestInfo().get("lateRequest"); assertEquals(0.0, requestInfo.submissionTime, 1e-3); - assertEquals(4000.0 + 60.0, requestInfo.pickupTime, 1e-3); - assertEquals(4103.0, requestInfo.dropoffTime, 1e-3); + assertEquals(4000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(4104.0, requestInfo.dropoffTime, 1e-3); } } @@ -479,4 +482,136 @@ void sameTrip_inverseSubmission_splitPickup() { // Three stops, 2x pickup, 1x dropoff assertEquals(3, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); } + + @Test + void interactionTimes() { + /*- + * Here we test prebookings in combination with non-zero interaction times for pickup and dropoff + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + // 1800 indicated but only departing 2000 + .addRequest("personA", 0, 0, 5, 5, 2000.0, 0.0, 2000.0 - 200.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { + @Override + public void install() { + bindModal(PassengerStopDurationProvider.class) + .toInstance(StaticPassengerStopDurationProvider.of(60.0, 30.0)); + } + }); + + controller.run(); + + RequestInfo requestInfo = environment.getRequestInfo().get("personA"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2060.0, requestInfo.pickupTime, 1e-3); + assertEquals(2301.0, requestInfo.dropoffTime, 1e-3); + + var taskInfo = environment.getTaskInfo().get("vehicleA"); + assertEquals("STAY", taskInfo.get(0).type); + assertEquals("DRIVE", taskInfo.get(1).type); + assertEquals("STAY", taskInfo.get(2).type); + assertEquals("STOP", taskInfo.get(3).type); + assertEquals("DRIVE", taskInfo.get(4).type); + assertEquals("STOP", taskInfo.get(5).type); + + assertEquals(1.0, taskInfo.get(1).startTime, 1e-3); // Pickup drive + assertEquals(86.0, taskInfo.get(2).startTime, 1e-3); // Starting to wait + assertEquals(1800.0, taskInfo.get(3).startTime, 1e-3); // Starting stop + assertEquals(2060.0, taskInfo.get(3).endTime, 1e-3); // Ending stop (260s duration) + assertEquals(2060.0, taskInfo.get(4).startTime, 1e-3); // Starting drive (ending stop) + } + + @Test + void destinationEqualsPrebookedOrigin_twoRequests() { + /*- + * In this test, we have two prebooked requests: + * P[A] ---------> D[A] P[B] --------> D[B] + * + * The dropoff of A happens at the same place as the pickup of B. Then we dispatch a new request C + * traveling the same trip as A. Without an implemented fix, inserting the dropfof between D[A] and P[B] + * was not an option as P[B] was the same link as the destination of C. The only viable dropoff insertion + * was after P[B], which, however, was too late. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 4, 4, 1000.0, 0.0) // + .addRequest("requestB", 4, 4, 8, 8, 8000.0, 1.0) // + .addRequest("requestC", 1, 1, 4, 4, 1000.0, 2.0) // + .configure(300.0, 2.0, 1800.0, 60.0) // + .endTime(12.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(1000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(1188.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1.0, requestInfo.submissionTime, 1e-3); + assertEquals(8000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(8230.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestC"); + assertEquals(2.0, requestInfo.submissionTime, 1e-3); + assertEquals(1000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(1188.0, requestInfo.dropoffTime, 1e-3); + } + + assertEquals(4, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + void destinationEqualsPrebookedOrigin_oneRequest() { + /*- + * In this test, we aprebooked requests: + * P[A] ---------> D[A] + * + * Then we dispatch a new request C before A. The destination of C is the origin of A. Without an implemented fix, + * inserting the dropoff before P[A] was not allowed as it is the same link, but inserting after D[A] was too late. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 4, 4, 8, 8, 4000.0, 1.0) // + .addRequest("requestB", 1, 1, 4, 4, 1000.0, 2.0) // + .configure(300.0, 2.0, 1800.0, 60.0) // + .endTime(12.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1.0, requestInfo.submissionTime, 1e-3); + assertEquals(4000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(4230.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(2.0, requestInfo.submissionTime, 1e-3); + assertEquals(1000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(1188.0, requestInfo.dropoffTime, 1e-3); + } + + assertEquals(4, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java index e3ac88d4985..652d66ecf19 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java @@ -304,16 +304,12 @@ public void install() { controller.run(); - // sh, 11/08/2023: updated after introducing prebookg, basically we generate a - // new feasible insertion (see InsertionGenerator) that previously did not - // exist, but has the same cost (pickup loss + drop-off loss) as the original - // one var expectedStats = Stats.newBuilder() .rejectionRate(0.04) .rejections(16) - .waitAverage(278.76) - .inVehicleTravelTimeMean(384.93) - .totalTravelTimeMean(663.68) + .waitAverage(278.92) + .inVehicleTravelTimeMean(384.6) + .totalTravelTimeMean(663.52) .build(); verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); @@ -342,17 +338,17 @@ void testRunDrtWithPrebooking() { controller.run(); - assertEquals(157, tracker.immediateScheduled); + assertEquals(169, tracker.immediateScheduled); assertEquals(205, tracker.prebookedScheduled); - assertEquals(26, tracker.immediateRejected); + assertEquals(14, tracker.immediateRejected); assertEquals(0, tracker.prebookedRejected); var expectedStats = Stats.newBuilder() - .rejectionRate(0.07) - .rejections(26) - .waitAverage(232.76) - .inVehicleTravelTimeMean(389.09) - .totalTravelTimeMean(621.85) + .rejectionRate(0.04) + .rejections(14) + .waitAverage(232.47) + .inVehicleTravelTimeMean(389.16) + .totalTravelTimeMean(621.63) .build(); verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java index 9601d9fa418..b467a732727 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java @@ -42,7 +42,7 @@ public void testDrtSharingFactorHandler() { var requestId = Id.create(0, Request.class); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId1), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId1), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId, personId1, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId1, vehicleId)); events.flush(); @@ -62,8 +62,8 @@ public void testDrtSharingFactorHandler() { Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(300.0, mode, requestId2, personId2, vehicleId)); @@ -91,8 +91,8 @@ public void testDrtSharingFactorHandler() { Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); @@ -121,8 +121,8 @@ public void testDrtSharingFactorHandler() { Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); @@ -152,8 +152,8 @@ public void testDrtSharingFactorHandler() { Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); @@ -182,7 +182,7 @@ public void testDrtSharingFactorHandler() { var requestId1 = Id.create(0, Request.class); Assertions.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1, personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1, personId2), null, null, 0, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId2, vehicleId)); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java index 1f08cf7c993..1bba2ad2e5a 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java @@ -275,7 +275,7 @@ private EventSequence eventSequence(String id, double submittedTime, double wait var personId = Id.create(id, Person.class); var submittedEvent = new DrtRequestSubmittedEvent(submittedTime, MODE, requestId, List.of(personId), linkAB.getId(), - linkBC.getId(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); + linkBC.getId(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); var pickupEvent = new PassengerPickedUpEvent(submittedTime + waitTime, MODE, requestId, null, null); double rideTime = DistanceUtils.calculateDistance(linkBC, linkAB) / inVehicleSpeed; var dropoffEvent = new PassengerDroppedOffEvent(submittedTime + waitTime + rideTime, MODE, requestId, null, diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java index 6ba4b41e0c8..1eb290b2d8a 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java @@ -65,7 +65,7 @@ public class DrtEventsReadersTest { //standard dvrp events are tested in DvrpEventsReadersTest private final List drtEvents = List.of( - new DrtRequestSubmittedEvent(0, mode, request, List.of(person), link1, link2, 111, 222, 0.0, 412.0, 512.0),// + new DrtRequestSubmittedEvent(0, mode, request, List.of(person), link1, link2, 111, 222, 0.0, 412.0, 512.0, 555),// taskStarted(10, DrtDriveTask.TYPE, 0, link1),// taskEnded(30, DefaultDrtStopTask.TYPE, 1, link2), // taskStarted(50, DrtStayTask.TYPE, 2, link1),// diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java index 7e11e5e8dd8..7406ceaa359 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java @@ -53,6 +53,8 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler { private final String mode; + private final Set departureModes; + private final MobsimTimer mobsimTimer; private final EventsManager eventsManager; @@ -79,9 +81,10 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR private final Map, List> groupDepartureStage = new LinkedHashMap<>(); - DefaultPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, + DefaultPassengerEngine(String mode, Set departureModes, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, AdvanceRequestProvider advanceRequestProvider, PassengerGroupIdentifier passengerGroupIdentifier) { this.mode = mode; + this.departureModes = departureModes; this.mobsimTimer = mobsimTimer; this.requestCreator = requestCreator; this.optimizer = optimizer; @@ -197,7 +200,7 @@ public void afterSim() { @Override public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { - if (!agent.getMode().equals(mode)) { + if (!departureModes.contains(agent.getMode())) { return false; } @@ -285,8 +288,12 @@ public void handleEvent(PassengerRequestRejectedEvent event) { rejectedRequestsEvents.add(event); } } - + public static Provider createProvider(String mode) { + return createProvider(mode, Collections.singleton(mode)); + } + + public static Provider createProvider(String mode, Set departureModes) { return new ModalProviders.AbstractProvider<>(mode, DvrpModes::mode) { @Inject private EventsManager eventsManager; @@ -296,7 +303,7 @@ public static Provider createProvider(String mode) { @Override public DefaultPassengerEngine get() { - return new DefaultPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), + return new DefaultPassengerEngine(getMode(), departureModes, eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), getModalInstance(AdvanceRequestProvider.class), getModalInstance(PassengerGroupIdentifier.class)); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpReader.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpReader.java index 9913f890326..d47dac4c588 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpReader.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpReader.java @@ -26,7 +26,7 @@ import org.locationtech.jts.geom.MultiPolygon; import org.matsim.api.core.v01.Id; import org.matsim.contrib.zone.Zone; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; public class ZoneShpReader { @@ -41,7 +41,7 @@ public void readZones(URL url) { } public void readZones(URL url, String idHeader) { - Collection features = ShapeFileReader.getAllFeatures(url); + Collection features = GeoFileReader.getAllFeatures(url); if (features.size() != zones.size()) { throw new RuntimeException("Features#: " + features.size() + "; zones#: " + zones.size()); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpWriter.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpWriter.java index 1e3fde7b279..ba5bb104c9d 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpWriter.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneShpWriter.java @@ -51,6 +51,6 @@ public void write(String shpFile) { features.add(factory.createPolygon(z.getMultiPolygon(), new Object[] { id }, id)); } - ShapeFileWriter.writeGeometries(features, shpFile); + GeoFileWriter.writeGeometries(features, shpFile); } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java index 1f87d091b7b..cc0b601cdd6 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java @@ -41,6 +41,7 @@ import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.events.MobsimScopeEventHandler; import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.mobsim.qsim.QSim; import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine; import org.matsim.vehicles.Vehicle; @@ -77,9 +78,10 @@ private boolean isOnFirstLink() { private final Queue linkLeaveEvents = new ConcurrentLinkedQueue<>(); private final Queue trafficLeaveEvents = new ConcurrentLinkedQueue<>(); - + private final QSim qsim; @Inject - DriveDischargingHandler(ElectricFleet data, Network network, EventsManager eventsManager) { + DriveDischargingHandler(QSim qsim, ElectricFleet data, Network network, EventsManager eventsManager) { + this.qsim = qsim; this.network = network; this.eventsManager = eventsManager; eVehicles = data.getElectricVehicles(); @@ -116,7 +118,7 @@ public void onPrepareSim() { @Override public void afterSim() { // process remaining events - doSimStep(Double.POSITIVE_INFINITY); + doSimStep(this.qsim.getSimTimer().getTimeOfDay()); } @Override diff --git a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTest/output_events.xml.gz b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTest/output_events.xml.gz index 3b1fb60c3eb..305d78e582e 100644 Binary files a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTest/output_events.xml.gz and b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTest/output_events.xml.gz differ diff --git a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTestWithChargingDurationEnforcement/output_events.xml.gz b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTestWithChargingDurationEnforcement/output_events.xml.gz index 8589f8c2b83..c5cee38e5c7 100644 Binary files a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTestWithChargingDurationEnforcement/output_events.xml.gz and b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleTest/runTestWithChargingDurationEnforcement/output_events.xml.gz differ diff --git a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest/runTest/output_events.xml.gz b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest/runTest/output_events.xml.gz index f93dab62299..ae05badd1be 100644 Binary files a/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest/runTest/output_events.xml.gz and b/contribs/ev/test/input/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModelTest/runTest/output_events.xml.gz differ diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index c29fd00cf58..57b42a70d8a 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -10,8 +10,8 @@ hybridsim - 3.25.2 - 1.61.0 + 3.25.3 + 1.62.2 diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/CandidateGenerator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/CandidateGenerator.java index d31e30c2ffd..be52b42752c 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/CandidateGenerator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/CandidateGenerator.java @@ -3,7 +3,7 @@ import org.matsim.api.core.v01.population.Plan; import javax.annotation.Nullable; -import java.util.Collection; +import java.util.List; import java.util.Set; /** @@ -15,7 +15,7 @@ public interface CandidateGenerator { /** * Generate plan candidates, ordered by their natural comparator. */ - default Collection generate(PlanModel planModel) { + default List generate(PlanModel planModel) { return generate(planModel, null, null); } @@ -23,6 +23,7 @@ default Collection generate(PlanModel planModel) { * Generate plan candidates, ordered by their natural comparator. * @param consideredModes if not null, will restrict usable modes to these present in the set * @param mask if not null, only include these trips with a true entry at their respective index. + * @return list of candidates, this list must be mutable */ - Collection generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask); + List generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask); } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java index a6229390386..e681025ef92 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java @@ -7,6 +7,7 @@ import com.google.inject.multibindings.MapBinder; import com.google.inject.multibindings.Multibinder; import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.listener.ControlerListener; import org.matsim.core.router.PlanRouter; import org.matsim.core.router.TripRouter; import org.matsim.core.utils.timing.TimeInterpretation; @@ -48,6 +49,10 @@ private InformedModeChoiceModule(Builder builder) { this.builder = builder; } + public static Builder newBuilder() { + return new Builder(); + } + @Override public void install() { @@ -80,7 +85,12 @@ public void install() { MapBinder pBinder = MapBinder.newMapBinder(binder(), String.class, CandidatePruner.class); for (Map.Entry e : builder.pruner.entrySet()) { - pBinder.addBinding(e.getKey()).toInstance(e.getValue()); + CandidatePruner instance = e.getValue(); + + pBinder.addBinding(e.getKey()).toInstance(instance); + + if (instance instanceof ControlerListener cl) + addControlerListenerBinding().toInstance(cl); } addPlanStrategyBinding(SELECT_BEST_K_PLAN_MODES_STRATEGY).toProvider(SelectBestKPlanModesStrategyProvider.class); @@ -131,20 +141,16 @@ private void bindAllModes(Map> map, TypeLiteral>> fixedCosts = new HashMap<>(); - private final Map>> legEstimators = new HashMap<>(); - private final Map>> tripEstimators = new HashMap<>(); + private final Map> fixedCosts = new HashMap<>(); + private final Map> legEstimators = new HashMap<>(); + private final Map> tripEstimators = new HashMap<>(); - private final Map>> options = new HashMap<>(); + private final Map> options = new HashMap<>(); private final Set>> constraints = new LinkedHashSet<>(); @@ -155,7 +161,7 @@ public static final class Builder { /** * Adds a fixed cost to one or more modes. */ - public > Builder withFixedCosts(Class> estimator, String... modes) { + public Builder withFixedCosts(Class estimator, String... modes) { for (String mode : modes) { fixedCosts.put(mode, estimator); @@ -167,8 +173,8 @@ public > Builder withFixedCosts(Class> Builder withLegEstimator(Class> estimator, Class> option, - String... modes) { + public Builder withLegEstimator(Class estimator, Class option, + String... modes) { for (String mode : modes) { @@ -187,8 +193,8 @@ public > Builder withLegEstimator(Class> Builder withTripEstimator(Class> estimator, Class> option, - String... modes) { + public Builder withTripEstimator(Class estimator, Class option, + String... modes) { for (String mode : modes) { diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeAvailability.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeAvailability.java index ccbc7b8cf2f..0470fbceb6e 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeAvailability.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeAvailability.java @@ -1,11 +1,28 @@ package org.matsim.modechoice; /** - * Default mode availability enumeration with two different options. + * Mode availability enumeration with different options. + * Different options are required in case there a different pricing schemes, depending on the usage of the mode. */ public enum ModeAvailability { YES, - NO; + NO, + + MONTHLY_SUBSCRIPTION, + YEARLY_SUBSCRIPTION, + DAILY_TICKET, + + /** + * May be used if the other options are not applicable. + */ + OTHER; + + /** + * True for all options except {@link #NO}. + */ + public boolean isModeAvailable() { + return this != NO; + } } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeEstimate.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeEstimate.java index a73a2b9cbac..6b08caba569 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeEstimate.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeEstimate.java @@ -13,7 +13,7 @@ public final class ModeEstimate { private final String mode; - private final Enum option; + private final ModeAvailability option; private final double[] est; private final double[] tripEst; @@ -35,7 +35,7 @@ public final class ModeEstimate { * @param isMin whether these are minimum estimates * @param storeTripEst whether trip est needs to be stored */ - ModeEstimate(String mode, Enum option, int n, boolean isUsable, boolean storeTripEst, boolean isMin) { + ModeEstimate(String mode, ModeAvailability option, int n, boolean isUsable, boolean storeTripEst, boolean isMin) { this.mode = mode; this.option = option; this.min = isMin; @@ -48,7 +48,7 @@ public String getMode() { return mode; } - public Enum getOption() { + public ModeAvailability getOption() { return option; } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeOptions.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeOptions.java index 62932b758c6..f0eb03bfd73 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeOptions.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/ModeOptions.java @@ -6,28 +6,30 @@ import java.util.List; /** - * Interface to determine which mode options are available to an agent and considered when computing best options. + * Interface to determine which {@link ModeAvailability} are available to an agent and considered when computing best options. *

* This interface also contains the default implementations. * - * @param enum listing the possible options */ -public interface ModeOptions> { +public interface ModeOptions { /** * Determine options for one agent. */ - List get(Person person); + List get(Person person); /** - * Return whether an option allows to use the mode. Normally only one of the option should forbid using the mode at all. + * Return whether an option allows to use the mode. */ - boolean allowUsage(T option); + default boolean allowUsage(ModeAvailability option) { + return option.isModeAvailable(); + } /** * The mode is always available and considered. + * This also means that there should be no daily costs associated with the mode. */ - final class AlwaysAvailable implements ModeOptions { + final class AlwaysAvailable implements ModeOptions { private static final List YES = List.of(ModeAvailability.YES); private static final List NO = List.of(ModeAvailability.NO); @@ -37,16 +39,12 @@ public List get(Person person) { return YES; } - @Override - public boolean allowUsage(ModeAvailability option) { - return option == ModeAvailability.YES; - } } /** * Plans are considered with and without this mode. */ - final class ConsiderYesAndNo implements ModeOptions { + final class ConsiderYesAndNo implements ModeOptions { private static final List BOTH = List.of(ModeAvailability.YES, ModeAvailability.NO); @@ -55,17 +53,13 @@ public List get(Person person) { return BOTH; } - @Override - public boolean allowUsage(ModeAvailability option) { - return option == ModeAvailability.YES; - } } /** * Consider both options if car is available, otherwise none. */ - final class ConsiderIfCarAvailable implements ModeOptions { + final class ConsiderIfCarAvailable implements ModeOptions { @Override public List get(Person person) { @@ -75,10 +69,5 @@ public List get(Person person) { return AlwaysAvailable.NO; } - - @Override - public boolean allowUsage(ModeAvailability option) { - return option == ModeAvailability.YES; - } } } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanCandidate.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanCandidate.java index 9a3cc6ccebd..418dc8e744c 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanCandidate.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanCandidate.java @@ -38,7 +38,8 @@ public double getUtility() { } /** - * Get mode for trip i. + * Get mode for trip i. Indexing starts at 0. + * @see #size() */ public String getMode(int i) { return modes[i]; @@ -51,6 +52,25 @@ public String[] getModes() { return Arrays.copyOf(modes, modes.length); } + /** + * Check whether a certain mode is present at least once. + */ + public boolean containsMode(String mode) { + for (String m : modes) { + if (mode.equals(m)) { + return true; + } + } + return false; + } + + /** + * Number of trips. + */ + public int size() { + return modes.length; + } + /** * Return features vector with number of occurrences per mode. */ diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModel.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModel.java index f0de060d997..cfb4c3f7966 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModel.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModel.java @@ -81,6 +81,7 @@ public Person getPerson() { } public Plan getPlan() { + // TODO: This should better be removed, memory usage by keeping these plans is increased return plan; } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java index 93c2ca21a0f..85f80664279 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java @@ -28,10 +28,10 @@ public final class PlanModelService implements StartupListener { @Inject - private Map> legEstimators; + private Map legEstimators; @Inject - private Map> tripEstimator; + private Map tripEstimator; @Inject private Set> constraints; @@ -49,10 +49,10 @@ public final class PlanModelService implements StartupListener { private ControlerListenerManager controlerListenerManager; private final InformedModeChoiceConfigGroup config; - private final Map> options; + private final Map options; @Inject - private PlanModelService(InformedModeChoiceConfigGroup config, Map> options) { + private PlanModelService(InformedModeChoiceConfigGroup config, Map options) { this.config = config; this.options = options; @@ -92,14 +92,14 @@ public void initEstimates(EstimatorContext context, PlanModel planModel) { for (String mode : config.getModes()) { - ModeOptions> t = (ModeOptions>) this.options.get(mode); + ModeOptions t = this.options.get(mode); - List> modeOptions = t.get(planModel.getPerson()); + List modeOptions = t.get(planModel.getPerson()); List c = new ArrayList<>(); - for (Enum modeOption : modeOptions) { - TripEstimator> te = (TripEstimator>) tripEstimator.get(mode); + for (ModeAvailability modeOption : modeOptions) { + TripEstimator te = tripEstimator.get(mode); boolean usable = t.allowUsage(modeOption); @@ -123,7 +123,7 @@ public void initEstimates(EstimatorContext context, PlanModel planModel) { /** * Return the modes an estimator was registered for. */ - public List modesForEstimator(LegEstimator est) { + public List modesForEstimator(LegEstimator est) { return legEstimators.entrySet().stream().filter(e -> e.getValue().equals(est)) .map(Map.Entry::getKey) .distinct() @@ -139,10 +139,10 @@ public List allowedModes(PlanModel planModel) { for (String mode : config.getModes()) { - ModeOptions> t = (ModeOptions>) this.options.get(mode); - List> modeOptions = t.get(planModel.getPerson()); + ModeOptions t = this.options.get(mode); + List modeOptions = t.get(planModel.getPerson()); - for (Enum modeOption : modeOptions) { + for (ModeAvailability modeOption : modeOptions) { boolean usable = t.allowUsage(modeOption); if (usable) { @@ -181,7 +181,7 @@ public void calculateEstimates(EstimatorContext context, PlanModel planModel) { continue; } - TripEstimator> tripEst = (TripEstimator>) tripEstimator.get(c.getMode()); + TripEstimator tripEst = tripEstimator.get(c.getMode()); // some options may produce equivalent results, but are re-estimated // however, the more expensive computation is routing and only done once @@ -265,7 +265,7 @@ public boolean isValidOption(PlanModel model, String[] modes) { /** * Return the trip estimator for one specific mode. */ - public TripEstimator getTripEstimator(String mode) { + public TripEstimator getTripEstimator(String mode) { return tripEstimator.get(mode); } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/commands/GenerateChoiceSet.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/commands/GenerateChoiceSet.java index 46e295edce7..3c05d8e43b5 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/commands/GenerateChoiceSet.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/commands/GenerateChoiceSet.java @@ -27,6 +27,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -42,7 +43,7 @@ public class GenerateChoiceSet implements MATSimAppCommand, PersonAlgorithm { @CommandLine.Mixin private ScenarioOptions scenario; - @CommandLine.Option(names = "--subpopulation", description = "Subpopulation filter", defaultValue = "person") + @CommandLine.Option(names = "--subpopulation", description = "Subpopulation filter") private String subpopulation; @CommandLine.Option(names = "--top-k", description = "Use top k estimates") @@ -103,7 +104,7 @@ public Integer call() throws Exception { // copy the original plan, so no modifications are made for (Person person : controler.getScenario().getPopulation().getPersons().values()) { String subpop = PopulationUtils.getSubpopulation(person); - if (subpopulation != null && !subpop.equals(subpopulation)) + if (subpopulation != null && !Objects.equals(subpop, subpopulation)) continue; Plan selected = person.getSelectedPlan(); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/DefaultLegScoreEstimator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/DefaultLegScoreEstimator.java index e84b670f33d..b66f27782d5 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/DefaultLegScoreEstimator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/DefaultLegScoreEstimator.java @@ -8,7 +8,7 @@ /** * Default estimator using MATSim scoring config. */ -public class DefaultLegScoreEstimator implements LegEstimator { +public class DefaultLegScoreEstimator implements LegEstimator { @Override diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/FixedCostsEstimator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/FixedCostsEstimator.java index 6104b0f3e38..8bcf5deefe6 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/FixedCostsEstimator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/FixedCostsEstimator.java @@ -4,17 +4,17 @@ import org.matsim.modechoice.EstimatorContext; import org.matsim.modechoice.ModeAvailability; -public interface FixedCostsEstimator> { +public interface FixedCostsEstimator { /** * The usage utility is added to the total score estimate, if this mode was used at least one tine. */ - double usageUtility(EstimatorContext context, String mode, T option); + double usageUtility(EstimatorContext context, String mode, ModeAvailability option); /** * The fixed utility is always added to the score estimate, regardless of usage. */ - default double fixedUtility(EstimatorContext context, String mode, T option) { + default double fixedUtility(EstimatorContext context, String mode, ModeAvailability option) { return 0; } @@ -22,12 +22,12 @@ default double fixedUtility(EstimatorContext context, String mode, T option) { /** * Default implementation that uses the daily constant as fixed costs. */ - final class DailyConstant implements FixedCostsEstimator { + final class DailyConstant implements FixedCostsEstimator { @Override public double usageUtility(EstimatorContext context, String mode, ModeAvailability option) { - if (option == ModeAvailability.YES) { + if (option.isModeAvailable()) { ModeUtilityParameters params = context.scoring.modeParams.get(mode); if (params == null) throw new IllegalStateException("Scoring parameter for mode " + mode + " not configured."); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/LegEstimator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/LegEstimator.java index 927f52c314f..b868bd45e04 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/LegEstimator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/LegEstimator.java @@ -2,13 +2,14 @@ import org.matsim.api.core.v01.population.Leg; import org.matsim.modechoice.EstimatorContext; +import org.matsim.modechoice.ModeAvailability; /** * Estimator for a single leg. * * @param enumeration of possible ownerships */ -public interface LegEstimator> { +public interface LegEstimator { /** * Calculate an estimate of utility รญf this mode would be used. @@ -20,7 +21,7 @@ public interface LegEstimator> { * @param option used mode availability * @return Estimated utility */ - double estimate(EstimatorContext context, String mode, Leg leg, T option); + double estimate(EstimatorContext context, String mode, Leg leg, ModeAvailability option); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/PtTripEstimator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/PtTripEstimator.java index 073326c431d..bf8eba848b4 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/PtTripEstimator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/PtTripEstimator.java @@ -26,7 +26,7 @@ *

* Based on the estimator in the discrete_mode_choice module. */ -public class PtTripEstimator implements TripEstimator { +public class PtTripEstimator implements TripEstimator { private static final Logger log = LogManager.getLogger(PtTripEstimator.class); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/TripEstimator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/TripEstimator.java index d66628651ed..853a5da61a4 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/TripEstimator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/estimators/TripEstimator.java @@ -2,6 +2,7 @@ import org.matsim.api.core.v01.population.Leg; import org.matsim.modechoice.EstimatorContext; +import org.matsim.modechoice.ModeAvailability; import org.matsim.modechoice.PlanModel; import java.util.List; @@ -11,9 +12,8 @@ * the final estimate can not be given until the whole plan is known. * This is usually the case with zone based fare systems or pt pricing based on the distance of the entire day. * - * @param enumeration of possible ownerships */ -public interface TripEstimator> { +public interface TripEstimator { /** * Calculate an estimate of utility รญf this mode would be used. This method will be called with all legs of a trip. @@ -26,7 +26,7 @@ public interface TripEstimator> { * @param option used mode availability * @return Estimated utility */ - MinMaxEstimate estimate(EstimatorContext context, String mode, PlanModel plan, List trip, T option); + MinMaxEstimate estimate(EstimatorContext context, String mode, PlanModel plan, List trip, ModeAvailability option); /** * Provide an estimate for the whole plan. This function must only estimate the cost by using its mode. @@ -37,14 +37,14 @@ public interface TripEstimator> { * @param option mode availability * @return Estimated utility */ - default double estimate(EstimatorContext context, String mode, String[] modes, PlanModel plan, T option) { + default double estimate(EstimatorContext context, String mode, String[] modes, PlanModel plan, ModeAvailability option) { throw new UnsupportedOperationException("providesMinEstimate returned true, but estimate function for the whole plan is not implemented yet."); } /** * Indicate whether an estimate will be uncertain, so that it requires an additional minimum estimation. */ - default boolean providesMinEstimate(EstimatorContext context, String mode, T option) { + default boolean providesMinEstimate(EstimatorContext context, String mode, ModeAvailability option) { return false; } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/CandidatePruner.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/CandidatePruner.java index 439caeab260..ef2cc7af044 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/CandidatePruner.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/CandidatePruner.java @@ -1,23 +1,53 @@ package org.matsim.modechoice.pruning; +import org.matsim.core.controler.listener.ControlerListener; +import org.matsim.modechoice.PlanCandidate; import org.matsim.modechoice.PlanModel; +import java.util.List; +import java.util.Random; + /** * Class used for pruning unpromising candidates, i.e. their score is below the calculated threshold. + * All methods of implementations must be thread safe! They will be invoked in parallel during planning! + *

+ * Implementations can also implement {@link ControlerListener} to get notified simulation progress. */ public interface CandidatePruner { + /** + * Remove candidates from a given collection. This method is expected to modify the collection directly. + * This method of pruning can be used for more complex strategies, but requires that the necesarry amount of candidates is generated first. + * The threshold based pruning {@link #planThreshold(PlanModel)} is usually more efficient. + */ + default void pruneCandidates(PlanModel model, List candidates, Random rnd) { + } + + /** + * Called when a candidate was selected. + * + * @param model the model used for planning + * @param candidate the selected candidate + */ + default void onSelectCandidate(PlanModel model, PlanCandidate candidate) { + } + /** * Calculate threshold to be applied on the best known plan estimate. Candidates with a larger difference to the best, than this threshold are discarded. + * * @return positive threshold, if negative it will not be applied */ - double planThreshold(PlanModel planModel); + default double planThreshold(PlanModel planModel) { + return -1; + } /** * Calculate threshold to be applied on a single trip. Modes worse than this threshold on this trip will be discarded. + * * @return positive threshold, if negative it will not be applied */ - double tripThreshold(PlanModel planModel, int idx); - + default double tripThreshold(PlanModel planModel, int idx) { + return -1; + } } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/ChangeExpBetaPruner.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/ChangeExpBetaPruner.java new file mode 100644 index 00000000000..12f7763c104 --- /dev/null +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/pruning/ChangeExpBetaPruner.java @@ -0,0 +1,44 @@ +package org.matsim.modechoice.pruning; + +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.modechoice.PlanModel; + +/** + * Prunes candidates based on the probability calculated using change exp beta formula. + *

+ * E.g. using percentile of 0.9 will remove plans for which the probability of being changed to is less than 10% + * + * The smaller p, the more plans are removed. + * + * + * @see org.matsim.core.replanning.selectors.ExpBetaPlanChanger + */ +public class ChangeExpBetaPruner implements CandidatePruner { + + private final double threshold; + + /** + * Create pruner with the desired percentile of plans to keep. + * @param p percentile larger than 0.01 and 1. + */ + public ChangeExpBetaPruner(ScoringConfigGroup config, double p) { + + // because of hard-coded 0.01 in ExpBetaPlanChanger + if (p <= 0.01 || p >= 1) { + throw new IllegalArgumentException("p must be larger than 0.01 and smaller than 1"); + } + + // Calculate utility threshold based on desired percentage + threshold = 2 * Math.log(100 * p) / config.getBrainExpBeta(); + } + + @Override + public double planThreshold(PlanModel planModel) { + return threshold; + } + + @Override + public double tripThreshold(PlanModel planModel, int idx) { + return threshold; + } +} diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectBestKPlanModesStrategyProvider.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectBestKPlanModesStrategyProvider.java index 2852b788656..1278ee3c22a 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectBestKPlanModesStrategyProvider.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectBestKPlanModesStrategyProvider.java @@ -1,7 +1,6 @@ package org.matsim.modechoice.replanning; import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.replanning.PlanStrategy; import org.matsim.core.replanning.PlanStrategyImpl; import org.matsim.core.replanning.modules.ReRoute; @@ -9,15 +8,12 @@ import org.matsim.core.router.TripRouter; import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.facilities.ActivityFacilities; -import org.matsim.modechoice.ModeChoiceWeightScheduler; -import org.matsim.modechoice.PlanCandidate; -import org.matsim.modechoice.search.TopKChoicesGenerator; import jakarta.inject.Inject; import jakarta.inject.Provider; /** - * Provider for {@link SelectFromGeneratorStrategy}. + * Provider for {@link SimplePlanSelectionStrategy}. */ public class SelectBestKPlanModesStrategyProvider implements Provider { @@ -31,7 +27,7 @@ public class SelectBestKPlanModesStrategyProvider implements Provider generator; + private Provider generator; @Inject private Provider selector; @@ -41,7 +37,7 @@ public PlanStrategy get() { PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); - builder.addStrategyModule(new SimplePlanSelectionStrategyModule(globalConfigGroup, generator, selector)); + builder.addStrategyModule(new SimplePlanSelectionStrategy(globalConfigGroup, generator, selector)); builder.addStrategyModule(new ReRoute(facilities, tripRouterProvider, globalConfigGroup, timeInterpretation)); return builder.build(); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectFromGeneratorStrategy.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectFromGeneratorStrategy.java deleted file mode 100644 index 833c787b343..00000000000 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectFromGeneratorStrategy.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.matsim.modechoice.replanning; - -import org.matsim.api.core.v01.population.Plan; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.modechoice.CandidateGenerator; -import org.matsim.modechoice.PlanCandidate; -import org.matsim.modechoice.PlanModel; - -import java.util.Collection; - -/** - * Choose route using a generator and selector. - */ -public class SelectFromGeneratorStrategy implements PlanAlgorithm { - - private final CandidateGenerator generator; - private final PlanSelector selector; - - public SelectFromGeneratorStrategy(CandidateGenerator generator, PlanSelector selector) { - this.generator = generator; - this.selector = selector; - } - - @Override - public void run(Plan plan) { - - Collection candidates = generator.generate(PlanModel.newInstance(plan)); - PlanCandidate candidate = selector.select(candidates); - - if (candidate != null) { - candidate.applyTo(plan); - } - } -} diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSingleTripModeStrategy.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSingleTripModeStrategy.java index 0ee937c25ea..89b50019936 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSingleTripModeStrategy.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSingleTripModeStrategy.java @@ -83,8 +83,13 @@ public void run(Plan plan) { PlanCandidate c = chooseCandidate(model, null); - if (c != null) + if (c != null) { + if (pruner != null) { + pruner.onSelectCandidate(model, c); + } + c.applyTo(plan); + } } @@ -141,7 +146,7 @@ public PlanCandidate chooseCandidate(PlanModel model, @Nullable Collection candidates = generator.generate(model, modes, mask); + List candidates = generator.generate(model, modes, mask); // Remove based on threshold if (pruner != null) { @@ -153,6 +158,8 @@ public PlanCandidate chooseCandidate(PlanModel model, @Nullable Collection c.getUtility() < threshold); } + + pruner.pruneCandidates(model, candidates, rnd); } // Remove options that are the same as the current mode diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSubtourModeStrategy.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSubtourModeStrategy.java index 2d1e6f37945..21863fe785e 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSubtourModeStrategy.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SelectSubtourModeStrategy.java @@ -156,16 +156,19 @@ public void run(Plan plan) { continue; } - Set candidates = new HashSet<>(); + Set candidateSet = new LinkedHashSet<>(); // Single modes are also added - candidates.addAll(singleModeCandidates); + candidateSet.addAll(singleModeCandidates); // one could either allow all modes here or only non chain based // config switch might be useful to investigate which option is better // execute best k modes - candidates.addAll(ctx.generator.generate(model, nonChainBasedModes, mask)); + candidateSet.addAll(ctx.generator.generate(model, nonChainBasedModes, mask)); + + // candidates are unique after this + List candidates = new ArrayList<>(candidateSet); if (config.isRequireDifferentModes()) candidates.removeIf(c -> Arrays.equals(c.getModes(), model.getCurrentModesMutable())); @@ -177,12 +180,19 @@ public void run(Plan plan) { if (!Double.isNaN(threshold) && threshold > 0) { candidates.removeIf(c -> c.getUtility() < singleModeCandidates.get(0).getUtility() - threshold); } + + // Only applied at the end + ctx.pruner.pruneCandidates(model, candidates, rnd); } if (!candidates.isEmpty()) { PlanCandidate select = ctx.selector.select(candidates); if (select != null) { + if (ctx.pruner != null) { + ctx.pruner.onSelectCandidate(model, select); + } + select.applyTo(plan); break; } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategy.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategy.java new file mode 100644 index 00000000000..37f48e67cad --- /dev/null +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategy.java @@ -0,0 +1,69 @@ +package org.matsim.modechoice.replanning; + +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.gbl.MatsimRandom; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; + +import jakarta.inject.Provider; +import org.matsim.modechoice.PlanCandidate; +import org.matsim.modechoice.PlanModel; + +import java.util.List; +import java.util.Random; + +/** + * Strategy that simply select from configured generator.. + */ +public class SimplePlanSelectionStrategy extends AbstractMultithreadedModule { + + private final Provider generator; + private final Provider selector; + + public SimplePlanSelectionStrategy(GlobalConfigGroup globalConfigGroup, + Provider generator, + Provider selector) { + super(globalConfigGroup); + this.generator = generator; + this.selector = selector; + } + + @Override + public PlanAlgorithm getPlanAlgoInstance() { + return new Algorithm(generator.get()); + } + + private static final class Algorithm implements PlanAlgorithm { + + private final GeneratorContext ctx; + private final Random rnd; + + public Algorithm(GeneratorContext ctx) { + this.ctx = ctx; + this.rnd = MatsimRandom.getLocalInstance(); + } + + @Override + public void run(Plan plan) { + + PlanModel planModel = PlanModel.newInstance(plan); + List candidates = ctx.generator.generate(planModel); + + if (ctx.pruner != null) { + ctx.pruner.pruneCandidates(planModel, candidates, rnd); + } + + PlanCandidate candidate = ctx.selector.select(candidates); + + if (candidate != null) { + if (ctx.pruner != null) { + ctx.pruner.onSelectCandidate(planModel, candidate); + } + + candidate.applyTo(plan); + } + } + } + +} diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategyModule.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategyModule.java deleted file mode 100644 index 2621291d4e2..00000000000 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/replanning/SimplePlanSelectionStrategyModule.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.matsim.modechoice.replanning; - -import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.replanning.modules.AbstractMultithreadedModule; -import org.matsim.modechoice.CandidateGenerator; - -import jakarta.inject.Provider; - -/** - * Module for {@link SelectFromGeneratorStrategy}. - */ -public class SimplePlanSelectionStrategyModule extends AbstractMultithreadedModule { - - private final Provider generator; - private final Provider selector; - - public SimplePlanSelectionStrategyModule(GlobalConfigGroup globalConfigGroup, Provider generator, Provider selector) { - super(globalConfigGroup); - this.generator = generator; - this.selector = selector; - } - - @Override - public PlanAlgorithm getPlanAlgoInstance() { - return new SelectFromGeneratorStrategy(generator.get(), selector.get()); - } - -} diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/AbstractCandidateGenerator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/AbstractCandidateGenerator.java index 54fd77228fc..fe1c92afee2 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/AbstractCandidateGenerator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/AbstractCandidateGenerator.java @@ -19,10 +19,10 @@ abstract class AbstractCandidateGenerator implements CandidateGenerator { @Inject - protected Map> tripEstimator; + protected Map tripEstimator; @Inject - protected Map> fixedCosts; + protected Map fixedCosts; @Inject protected ScoringParametersForPerson params; diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/BestChoiceGenerator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/BestChoiceGenerator.java index 3085da656e1..c7f2ded08ea 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/BestChoiceGenerator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/BestChoiceGenerator.java @@ -26,14 +26,14 @@ public class BestChoiceGenerator extends TopKChoicesGenerator { @Override - public Collection generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask) { + public List generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask) { List candidates = new ArrayList<>(generate(planModel, consideredModes, mask, 10, 0, Double.NaN)); if (candidates.isEmpty()) - return Set.of(); + return candidates; - return List.of(candidates.stream().max(Comparator.comparingDouble(PlanCandidate::getUtility)).orElseThrow()); + return candidates.subList(0, 1); } } diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/SingleTripChoicesGenerator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/SingleTripChoicesGenerator.java index 70d6ea4edcc..5ba80a11740 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/SingleTripChoicesGenerator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/SingleTripChoicesGenerator.java @@ -19,7 +19,7 @@ public SingleTripChoicesGenerator(InformedModeChoiceConfigGroup config) { } @Override - public Collection generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask) { + public List generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask) { if (mask == null) throw new IllegalArgumentException("Mask must be provided"); diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/TopKChoicesGenerator.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/TopKChoicesGenerator.java index 4cb0dce962a..27ed135b3b2 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/TopKChoicesGenerator.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/search/TopKChoicesGenerator.java @@ -35,7 +35,7 @@ public class TopKChoicesGenerator extends AbstractCandidateGenerator { super(config); } - public Collection generate(PlanModel planModel, Set consideredModes, boolean[] mask) { + public List generate(PlanModel planModel, Set consideredModes, boolean[] mask) { CandidatePruner p = pruner.get(); double threshold = -1; @@ -54,7 +54,7 @@ public Collection generate(PlanModel planModel, Set consi * @param diffThreshold allowed difference to the best solution (if positive) * @param absThreshold minimal required estimate score (use NaN or negative infinity if not needed) */ - public Collection generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask, + public List generate(PlanModel planModel, @Nullable Set consideredModes, @Nullable boolean[] mask, int topK, double diffThreshold, double absThreshold) { EstimatorContext context = new EstimatorContext(planModel.getPerson(), params.getScoringParameters(planModel.getPerson())); @@ -88,7 +88,7 @@ protected final void prepareModel(PlanModel planModel, EstimatorContext context) } - private Collection generateCandidate(EstimatorContext context, PlanModel planModel, boolean[] mask, int topK, double diffThreshold, double absThreshold, + private List generateCandidate(EstimatorContext context, PlanModel planModel, boolean[] mask, int topK, double diffThreshold, double absThreshold, Set consideredModes, Set consolidateModes, List> constraints) { ModeChoiceSearch search = new ModeChoiceSearch(planModel.trips(), planModel.modes()); @@ -255,7 +255,7 @@ private double computePlanEstimate(EstimatorContext context, PlanModel planModel // Add the fixed costs estimate if a mode has been used for (ModeEstimate mode : options) { - FixedCostsEstimator> f = (FixedCostsEstimator>) fixedCosts.get(mode.getMode()); + FixedCostsEstimator f = fixedCosts.get(mode.getMode()); // Fixed costs are not required for each mode if (f == null) @@ -273,7 +273,7 @@ private double computePlanEstimate(EstimatorContext context, PlanModel planModel for (ModeEstimate mode : options) { if (mode.getMode() == consolidateMode && usedModes.contains(consolidateMode)) { - TripEstimator> f = (TripEstimator>) tripEstimator.get(mode.getMode()); + TripEstimator f = tripEstimator.get(mode.getMode()); // subtract all the trip estimates that have been made before for (int i = 0; i < result.length; i++) { diff --git a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/estimators/ComplexTripEstimator.java b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/estimators/ComplexTripEstimator.java index c50b8dfe7dd..05edcbc745f 100644 --- a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/estimators/ComplexTripEstimator.java +++ b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/estimators/ComplexTripEstimator.java @@ -14,7 +14,7 @@ /** * Complex estimator, using MATSim infrastructure */ -public final class ComplexTripEstimator implements TripEstimator, LinkEnterEventHandler, IterationEndsListener { +public final class ComplexTripEstimator implements TripEstimator, LinkEnterEventHandler, IterationEndsListener { private int iters = 0; private int events = 0; diff --git a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/CandidatePrunerTest.java b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/CandidatePrunerTest.java new file mode 100644 index 00000000000..f264be63ce8 --- /dev/null +++ b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/CandidatePrunerTest.java @@ -0,0 +1,38 @@ +package org.matsim.modechoice.pruning; + +import org.junit.jupiter.api.Test; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.modechoice.PlanCandidate; +import org.matsim.modechoice.PlanModel; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static org.assertj.core.api.Assertions.assertThat; + +class CandidatePrunerTest { + + private final CandidatePruner pruner = new ExamplePruner(TransportMode.drt); + + @Test + void example() { + + PlanModel model = Mockito.mock(PlanModel.class); + + List candidates = new ArrayList<>(List.of( + new PlanCandidate(new String[]{"car", "car", "car", "car"}, Double.NaN), + new PlanCandidate(new String[]{"car", "bike", "pt", "car"}, Double.NaN), + new PlanCandidate(new String[]{"car", "drt", "car", "car"}, Double.NaN), + new PlanCandidate(new String[]{"drt"}, Double.NaN) + )); + + pruner.pruneCandidates(model, candidates, new Random()); + + assertThat(candidates) + .hasSize(2); + + } + +} diff --git a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/ExamplePruner.java b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/ExamplePruner.java new file mode 100644 index 00000000000..ea92034fec6 --- /dev/null +++ b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/pruning/ExamplePruner.java @@ -0,0 +1,32 @@ +package org.matsim.modechoice.pruning; + +import org.matsim.modechoice.PlanCandidate; +import org.matsim.modechoice.PlanModel; + +import java.util.List; +import java.util.Random; + +public class ExamplePruner implements CandidatePruner { + + private final String mode; + + public ExamplePruner(String mode) { + this.mode = mode; + } + + @Override + public void pruneCandidates(PlanModel model, List candidates, Random rnd) { + + candidates.removeIf(candidate -> candidate.containsMode(mode)); + } + + @Override + public double planThreshold(PlanModel planModel) { + return Double.POSITIVE_INFINITY; + } + + @Override + public double tripThreshold(PlanModel planModel, int idx) { + return Double.POSITIVE_INFINITY; + } +} diff --git a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/search/TopKMinMaxTest.java b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/search/TopKMinMaxTest.java index 01efd0381a4..04cfe9860e2 100644 --- a/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/search/TopKMinMaxTest.java +++ b/contribs/informed-mode-choice/src/test/java/org/matsim/modechoice/search/TopKMinMaxTest.java @@ -209,7 +209,7 @@ else if (invocationOnMock.getArgument(0).equals(TransportMode.walk)) { FacilitiesUtils.createActivityFacilities(), TimeInterpretation.create(PlansConfigGroup.ActivityDurationInterpretation.minOfDurationAndEndTime, PlansConfigGroup.TripDurationHandling.shiftActivityEndTimes))); - MapBinder> optionBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>(){}); + MapBinder optionBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() {}, new TypeLiteral<>(){}); optionBinder.addBinding(TransportMode.car).toInstance(new ModeOptions.AlwaysAvailable()); optionBinder.addBinding(TransportMode.walk).toInstance(new ModeOptions.AlwaysAvailable()); @@ -217,20 +217,20 @@ else if (invocationOnMock.getArgument(0).equals(TransportMode.walk)) { Multibinder> tcBinder = Multibinder.newSetBinder(binder(), new TypeLiteral<>() { }); - MapBinder> fcBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { + MapBinder fcBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { }, new TypeLiteral<>() { }); fcBinder.addBinding(TransportMode.car).toInstance((context, mode, option) -> -1); - MapBinder> legBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { + MapBinder legBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { }, new TypeLiteral<>() { }); legBinder.addBinding(TransportMode.walk).toInstance((context, mode, leg, option) -> -0.5); - MapBinder> tripBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { + MapBinder tripBinder = MapBinder.newMapBinder(binder(), new TypeLiteral<>() { }, new TypeLiteral<>() { }); @@ -251,7 +251,7 @@ protected CandidatePruner pruner() { } // Provides fixed estimates for testing - private class CarTripEstimator implements TripEstimator { + private class CarTripEstimator implements TripEstimator { @Override public MinMaxEstimate estimate(EstimatorContext context, String mode, PlanModel plan, List trip, ModeAvailability option) { diff --git a/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStops.java b/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStops.java index a9838bc4e12..52c5a6fe36d 100644 --- a/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStops.java +++ b/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStops.java @@ -44,7 +44,7 @@ import org.matsim.contrib.minibus.PConfigGroup; import org.matsim.core.network.algorithms.NetworkCalcTopoType; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.matsim.pt.transitSchedule.TransitScheduleFactoryImpl; import org.matsim.pt.transitSchedule.api.TransitSchedule; @@ -53,14 +53,14 @@ /** * Create one TransitStopFacility for each car mode link of the network - * + * * @author aneumann, droeder * */ public final class CreatePStops{ - + private final static Logger log = LogManager.getLogger(CreatePStops.class); - + private final Network net; private final PConfigGroup pConfigGroup; private TransitSchedule transitSchedule; @@ -74,7 +74,7 @@ public final class CreatePStops{ private List topoTypesForStops = null; private NetworkCalcTopoType networkCalcTopoType; - + public static TransitSchedule createPStops(Network network, PConfigGroup pConfigGroup){ return createPStops(network, pConfigGroup, null); } @@ -84,14 +84,14 @@ public static TransitSchedule createPStops(Network network, PConfigGroup pConfig cS.run(); return cS.getTransitSchedule(); } - + /** * Creates PStops in two ways. First, if a serviceAreaFile is defined in the config and this file exists, the file is used. * Second, the (default) min/max-x/y-values are used. - * + * * Following FileTypes are supported: *

    - *
  • Shapefiles with polygons. If one ore more attributes are defined, the last one is parsed + *
  • Shapefiles with polygons. If one ore more attributes are defined, the last one is parsed * to Boolean and used to get include- and exclude-areas.
  • *
  • Textfile, containing a List of x/y-pairs per row, divided by semicolon. The first and the last coordinate should be equal * to get a closed and well defined Geometry.
  • @@ -104,13 +104,13 @@ private CreatePStops(Network net, PConfigGroup pConfigGroup, TransitSchedule rea this.net = net; this.pConfigGroup = pConfigGroup; this.factory = new GeometryFactory(); - + this.linkId2StopFacilityMap = new LinkedHashMap<>(); - + Set> stopsWithoutLinkIds = new TreeSet<>(); int warnCounter = 10; - + if (realTransitSchedule != null) { for (TransitStopFacility stopFacility : realTransitSchedule.getFacilities().values()) { if (stopFacility.getLinkId() != null) { @@ -131,7 +131,7 @@ private CreatePStops(Network net, PConfigGroup pConfigGroup, TransitSchedule rea } } } - + this.exclude = this.factory.buildGeometry(new ArrayList()); if(!new File(pConfigGroup.getServiceAreaFile()).exists()){ log.warn("file " + this.pConfigGroup.getServiceAreaFile() + " not found. Falling back to min/max serviceArea parameters."); @@ -140,7 +140,7 @@ private CreatePStops(Network net, PConfigGroup pConfigGroup, TransitSchedule rea log.warn("using " + this.pConfigGroup.getServiceAreaFile() + " for servicearea. x/y-values defined in the config are not used."); createServiceArea(pConfigGroup.getServiceAreaFile()); } - + if (stopsWithoutLinkIds.size() > 0) { log.warn("There are " + stopsWithoutLinkIds.size() + " stop facilities without a link id, namely: " + stopsWithoutLinkIds.toString()); } @@ -184,7 +184,7 @@ private void createServiceArea(String serviceAreaFile) { * @param serviceAreaFile */ private void createServiceAreaTxt(String serviceAreaFile) { - + List lines = new ArrayList<>(); String line; try { @@ -202,15 +202,15 @@ private void createServiceAreaTxt(String serviceAreaFile) { } catch (IOException e) { e.printStackTrace(); } - + if(lines.size() < 3){ log.warn("an area needs at least 3 points, to be defined. Falling back to simple (default) x/y-values..."); this.createServiceArea(pConfigGroup.getMinX(), pConfigGroup.getMaxX(), pConfigGroup.getMinY(), pConfigGroup.getMaxY()); - return; + return; } - + Coordinate[] c = new Coordinate[lines.size() + 1]; - + double x,y; for(int i = 0; i < lines.size(); i++){ x = Double.parseDouble(lines.get(i).split(";")[0]); @@ -226,10 +226,10 @@ private void createServiceAreaTxt(String serviceAreaFile) { * @param serviceAreaFile */ private void createServiceAreaShp(String serviceAreaFile) { - Collection features = new ShapeFileReader().readFileAndInitialize(serviceAreaFile); + Collection features = new GeoFileReader().readFileAndInitialize(serviceAreaFile); Collection include = new ArrayList<>(); Collection exclude = new ArrayList<>(); - + for(SimpleFeature f: features){ boolean incl = true; Geometry g = null; @@ -240,7 +240,7 @@ private void createServiceAreaShp(String serviceAreaFile) { g = (Geometry) o; } // TODO use a better way to get the attributes, maybe directly per index. - // Now the last attribute is used per default... + // Now the last attribute is used per default... else if (o instanceof String){ incl = Boolean.parseBoolean((String) o); } @@ -253,46 +253,46 @@ else if (o instanceof String){ } } } - this.include = this.factory.createGeometryCollection( + this.include = this.factory.createGeometryCollection( include.toArray(new Geometry[include.size()])).buffer(0); - this.exclude = this.factory.createGeometryCollection( + this.exclude = this.factory.createGeometryCollection( exclude.toArray(new Geometry[exclude.size()])).buffer(0); } private void run(){ this.transitSchedule = new TransitScheduleFactoryImpl().createTransitSchedule(); int stopsAdded = 0; - + for (Link link : this.net.getLinks().values()) { if(link.getAllowedModes().contains(TransportMode.car)){ stopsAdded += addStopOnLink(link); } } - + log.info("Added " + stopsAdded + " additional stops for paratransit services"); } - + private int addStopOnLink(Link link) { if(link == null){ return 0; } - + if(!linkToNodeInServiceArea(link)){ return 0; } - + if (linkHasAlreadyAFormalPTStopFromTheGivenSchedule(link)) { return 0; } - + if(!topoTypeAllowed(link)){ return 0; } - + if (link.getFreespeed() >= this.pConfigGroup.getSpeedLimitForStops()) { return 0; } - + if (link.getCapacity() < this.pConfigGroup.getMinCapacityForStops()) { return 0; } @@ -301,12 +301,12 @@ private int addStopOnLink(Link link) { log.warn("Link " + link.getId() + " has already a stop. This should not happen. Check code."); return 0; } - + Id stopId = Id.create(this.pConfigGroup.getPIdentifier() + link.getId(), TransitStopFacility.class); TransitStopFacility stop = this.transitSchedule.getFactory().createTransitStopFacility(stopId, link.getToNode().getCoord(), false); stop.setLinkId(link.getId()); this.transitSchedule.addStopFacility(stop); - return 1; + return 1; } private boolean topoTypeAllowed(Link link) { @@ -316,7 +316,7 @@ private boolean topoTypeAllowed(Link link) { } Integer topoType = this.networkCalcTopoType.getTopoType(link.getToNode()); return this.topoTypesForStops.contains(topoType); - } + } private boolean linkToNodeInServiceArea(Link link) { Point p = factory.createPoint(MGC.coord2Coordinate(link.getToNode().getCoord())); diff --git a/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStopsOnJunctionApproachesAndBetweenJunctions.java b/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStopsOnJunctionApproachesAndBetweenJunctions.java index c8f6676e835..f2303896615 100644 --- a/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStopsOnJunctionApproachesAndBetweenJunctions.java +++ b/contribs/minibus/src/main/java/org/matsim/contrib/minibus/schedule/CreatePStopsOnJunctionApproachesAndBetweenJunctions.java @@ -55,7 +55,7 @@ import org.matsim.core.network.filter.NetworkFilterManager; import org.matsim.core.network.filter.NetworkLinkFilter; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.matsim.pt.transitSchedule.TransitScheduleFactoryImpl; import org.matsim.pt.transitSchedule.api.TransitSchedule; @@ -64,14 +64,14 @@ /** * Create one TransitStopFacility for each car mode link of the network - * + * * @author aneumann, droeder, gleich * */ public final class CreatePStopsOnJunctionApproachesAndBetweenJunctions{ - + private final static Logger log = LogManager.getLogger(CreatePStopsOnJunctionApproachesAndBetweenJunctions.class); - + private final Network net; private final Network intersectionSimplifiedRoadNetwork; private final PConfigGroup pConfigGroup; @@ -89,7 +89,7 @@ public final class CreatePStopsOnJunctionApproachesAndBetweenJunctions{ private final double stopDistance; private NetworkCalcTopoType networkCalcTopoType; - + public static TransitSchedule createPStops(Network network, PConfigGroup pConfigGroup, NetworkConfigGroup networkConfigGroup) { return createPStops(network, pConfigGroup, null, networkConfigGroup); } @@ -99,14 +99,14 @@ public static TransitSchedule createPStops(Network network, PConfigGroup pConfig cS.run(); return cS.getTransitSchedule(); } - + /** * Creates PStops in two ways. First, if a serviceAreaFile is defined in the config and this file exists, the file is used. * Second, the (default) min/max-x/y-values are used. - * + * * Following FileTypes are supported: *
      - *
    • Shapefiles with polygons. If one ore more attributes are defined, the last one is parsed + *
    • Shapefiles with polygons. If one ore more attributes are defined, the last one is parsed * to Boolean and used to get include- and exclude-areas.
    • *
    • Textfile, containing a List of x/y-pairs per row, divided by semicolon. The first and the last coordinate should be equal * to get a closed and well defined Geometry.
    • @@ -120,13 +120,13 @@ private CreatePStopsOnJunctionApproachesAndBetweenJunctions(Network net, PConfig this.pConfigGroup = pConfigGroup; this.networkConfigGroup = networkConfigGroup; this.factory = new GeometryFactory(); - + this.linkId2StopFacilityMap = new LinkedHashMap<>(); - + Set> stopsWithoutLinkIds = new TreeSet<>(); int warnCounter = 10; - + if (realTransitSchedule != null) { for (TransitStopFacility stopFacility : realTransitSchedule.getFacilities().values()) { if (stopFacility.getLinkId() != null) { @@ -147,7 +147,7 @@ private CreatePStopsOnJunctionApproachesAndBetweenJunctions(Network net, PConfig } } } - + this.exclude = this.factory.buildGeometry(new ArrayList()); if(!new File(pConfigGroup.getServiceAreaFile()).exists()){ log.warn("file " + this.pConfigGroup.getServiceAreaFile() + " not found. Falling back to min/max serviceArea parameters."); @@ -156,14 +156,14 @@ private CreatePStopsOnJunctionApproachesAndBetweenJunctions(Network net, PConfig log.warn("using " + this.pConfigGroup.getServiceAreaFile() + " for servicearea. x/y-values defined in the config are not used."); createServiceArea(pConfigGroup.getServiceAreaFile()); } - + if (stopsWithoutLinkIds.size() > 0) { log.warn("There are " + stopsWithoutLinkIds.size() + " stop facilities without a link id, namely: " + stopsWithoutLinkIds.toString()); } this.topoTypesForStops = this.pConfigGroup.getTopoTypesForStops(); this.networkCalcTopoType = new NetworkCalcTopoType(); this.networkCalcTopoType.run(net); - + // parse StopLocationSelectorParameter from config String[] stopLocationSelectorParameter = pConfigGroup.getStopLocationSelectorParameter().split(","); if (stopLocationSelectorParameter.length != 3) { @@ -178,7 +178,7 @@ private CreatePStopsOnJunctionApproachesAndBetweenJunctions(Network net, PConfig } else { stopDistance = Double.parseDouble(stopLocationSelectorParameter[2]); } - + intersectionSimplifiedRoadNetwork = generateIntersectionSimplifiedNetwork(pmin, epsilon); } @@ -216,7 +216,7 @@ private void createServiceArea(String serviceAreaFile) { * @param serviceAreaFile */ private void createServiceAreaTxt(String serviceAreaFile) { - + List lines = new ArrayList<>(); String line; try { @@ -234,15 +234,15 @@ private void createServiceAreaTxt(String serviceAreaFile) { } catch (IOException e) { e.printStackTrace(); } - + if(lines.size() < 3){ log.warn("an area needs at least 3 points, to be defined. Falling back to simple (default) x/y-values..."); this.createServiceArea(pConfigGroup.getMinX(), pConfigGroup.getMaxX(), pConfigGroup.getMinY(), pConfigGroup.getMaxY()); - return; + return; } - + Coordinate[] c = new Coordinate[lines.size() + 1]; - + double x,y; for(int i = 0; i < lines.size(); i++){ x = Double.parseDouble(lines.get(i).split(";")[0]); @@ -258,10 +258,10 @@ private void createServiceAreaTxt(String serviceAreaFile) { * @param serviceAreaFile */ private void createServiceAreaShp(String serviceAreaFile) { - Collection features = new ShapeFileReader().readFileAndInitialize(serviceAreaFile); + Collection features = new GeoFileReader().readFileAndInitialize(serviceAreaFile); Collection include = new ArrayList<>(); Collection exclude = new ArrayList<>(); - + for(SimpleFeature f: features){ boolean incl = true; Geometry g = null; @@ -272,7 +272,7 @@ private void createServiceAreaShp(String serviceAreaFile) { g = (Geometry) o; } // TODO use a better way to get the attributes, maybe directly per index. - // Now the last attribute is used per default... + // Now the last attribute is used per default... else if (o instanceof String){ incl = Boolean.parseBoolean((String) o); } @@ -285,18 +285,18 @@ else if (o instanceof String){ } } } - this.include = this.factory.createGeometryCollection( + this.include = this.factory.createGeometryCollection( include.toArray(new Geometry[include.size()])).buffer(0); - this.exclude = this.factory.createGeometryCollection( + this.exclude = this.factory.createGeometryCollection( exclude.toArray(new Geometry[exclude.size()])).buffer(0); } - + /* Generate a simplified network to determine stop locations (the simplified network will not be used in simulation) */ private Network generateIntersectionSimplifiedNetwork(double pmin, int epsilon) { // Extract road network NetworkFilterManager nfmCar = new NetworkFilterManager(net, networkConfigGroup); nfmCar.addLinkFilter(new NetworkLinkFilter() { - + @Override public boolean judgeLink(Link l) { if (l.getAllowedModes().contains("car")) return true; @@ -304,11 +304,11 @@ public boolean judgeLink(Link l) { } }); Network roadNetwork = nfmCar.applyFilters(); - + // Remove low capacity links NetworkFilterManager nfm = new NetworkFilterManager(roadNetwork, networkConfigGroup); nfm.addLinkFilter(new NetworkLinkFilter() { - + @Override public boolean judgeLink(Link l) { if (l.getCapacity() >= pConfigGroup.getMinCapacityForStops()) return true; @@ -317,58 +317,58 @@ public boolean judgeLink(Link l) { }); Network newRoadNetwork = nfm.applyFilters(); new NetworkCleaner().run(newRoadNetwork); - + // Run Johan's intersection clustering algorithm IntersectionSimplifier ns = new IntersectionSimplifier(pmin, epsilon); Network newClusteredIntersectionsRoadNetwork = ns.simplify(newRoadNetwork); new NetworkCleaner().run(newClusteredIntersectionsRoadNetwork); - + // intersection clustering leaves some duplicate links (same start and end node), merge them NetworkMergeDoubleLinks mergeDoubleLinks = new NetworkMergeDoubleLinks(MergeType.MAXIMUM, LogInfoLevel.NOINFO); mergeDoubleLinks.run(newClusteredIntersectionsRoadNetwork); - + // Merge all links between two junctions NetworkSimplifier simplifier = new NetworkSimplifier(); // Merge links with different attributes, because we will not use the output network for simulation simplifier.setMergeLinkStats(true); simplifier.run(newClusteredIntersectionsRoadNetwork); new NetworkCleaner().run(newClusteredIntersectionsRoadNetwork); - + return(newClusteredIntersectionsRoadNetwork); } private void run(){ this.transitSchedule = new TransitScheduleFactoryImpl().createTransitSchedule(); int stopsAdded = 0; - + /* handle all (merged) links between junction */ for (Link link : this.intersectionSimplifiedRoadNetwork.getLinks().values()) { if(link.getAllowedModes().contains(TransportMode.car)){ stopsAdded += addStopForSimplifiedNetworkLink(link); } } - + log.info("Added " + stopsAdded + " additional stops for paratransit services"); } - + private int addStopForSimplifiedNetworkLink(Link simplifiedNetworkLink) { int numberOfStopsCreated = 0; String[] originalIdsMergedLink = simplifiedNetworkLink.getId().toString().split("-"); /* Bus stops should be placed on approaches to road junctions, and not inside junction - * areas. - * The Intersection simplifier creates a network where most junction nodes are merged into + * areas. + * The Intersection simplifier creates a network where most junction nodes are merged into * one node per junction. By merging two neighbouring nodes, all links in between are removed. * So, initially no links are modified, but most links located inside a junction area are - * removed. Than the NetworkSimplifier merges short links between intersections, thereby + * removed. Than the NetworkSimplifier merges short links between intersections, thereby * creating a network where between two junctions there is at most one link per direction. * Given that simplified network the corresponding stop locations are looked up on the * original network using the link ids remaining in the simplified network. - * + * * First add stops at links approaching junction areas: - * Start from last part of the merged link and move forward until the first intersection + * Start from last part of the merged link and move forward until the first intersection * (>=2 outlinks) is reached. This should be in most cases the begin of the junction area. * - * It could be that the original network already had "-" in link ids, + * It could be that the original network already had "-" in link ids, * so the link would not be found here. Add previous id parts of the merged link. * Go from the last link id backwards, because we want to find the last link. */ @@ -384,33 +384,33 @@ private int addStopForSimplifiedNetworkLink(Link simplifiedNetworkLink) { reappendedlinkIds = originalIdsMergedLink[indexOfNextLinkIdToBeAppended] + "-" + reappendedlinkIds; lastPartOfMergedLink = net.getLinks().get(Id.createLinkId(reappendedlinkIds)); } - + /* Get node ids of all nodes merged into the clustered node where the merged link ends */ Set> originalNodeIdsBeforeClustering = new HashSet<>(); String[] clusteredNodeIdSplit = simplifiedNetworkLink.getToNode().getId().toString().split("-"); for (String originalNodeId : clusteredNodeIdSplit) { originalNodeIdsBeforeClustering.add(Id.createNodeId(originalNodeId)); } - - while (lastPartOfMergedLink.getToNode().getOutLinks().size() <= 1 && + + while (lastPartOfMergedLink.getToNode().getOutLinks().size() <= 1 && ! originalNodeIdsBeforeClustering.contains(lastPartOfMergedLink.getToNode().getId())) { lastPartOfMergedLink = lastPartOfMergedLink.getToNode().getOutLinks().values().iterator().next(); } - + numberOfStopsCreated += addStopOnLink(lastPartOfMergedLink); - + /* Go backward from junction approach and add infill stops to create a proper bus stop spacing between junctions. */ double distanceFromLastStop = lastPartOfMergedLink.getLength(); if (numberOfStopsCreated == 0) { - /* No stop was created on link approaching the junction (due to link characteristics excluded + /* No stop was created on link approaching the junction (due to link characteristics excluded in pConfigGroup, see {@link addStopOnLink(Link link)}), so make the algorithm add one on the next suitable link */ distanceFromLastStop = stopDistance; } - + Link currentLink = lastPartOfMergedLink; reappendedlinkIds = ""; - + /* Stop if the previous junction area is reached */ while (indexOfNextLinkIdToBeAppended > 0) { indexOfNextLinkIdToBeAppended--; @@ -418,9 +418,9 @@ private int addStopForSimplifiedNetworkLink(Link simplifiedNetworkLink) { reappendedlinkIds = originalIdsMergedLink[indexOfNextLinkIdToBeAppended]; } else { reappendedlinkIds = originalIdsMergedLink[indexOfNextLinkIdToBeAppended] + "-" + reappendedlinkIds; - } + } Link tryLinkId = net.getLinks().get(Id.createLinkId(reappendedlinkIds)); - + if (tryLinkId == null) { /* Next backward link could not be found, because it's id already contains "-", e.g. because */ continue; @@ -428,11 +428,11 @@ private int addStopForSimplifiedNetworkLink(Link simplifiedNetworkLink) { distanceFromLastStop += currentLink.getLength(); currentLink = tryLinkId; reappendedlinkIds = ""; - + if (distanceFromLastStop >= stopDistance) { /* Try to add a new stop on current link */ int stopCreated = addStopOnLink(currentLink); - + // TODO: Add check of distance to next junction further backward if (stopCreated > 0) { /* If stop was added, reset distanceFromLastStop */ @@ -442,31 +442,31 @@ private int addStopForSimplifiedNetworkLink(Link simplifiedNetworkLink) { } } } - + return numberOfStopsCreated; } - + private int addStopOnLink(Link link) { if(link == null){ return 0; } - + if(!linkToNodeInServiceArea(link)){ return 0; } - + if (linkHasAlreadyAFormalPTStopFromTheGivenSchedule(link)) { return 0; } - + if(!topoTypeAllowed(link)){ return 0; } - + if (link.getFreespeed() >= this.pConfigGroup.getSpeedLimitForStops()) { return 0; } - + if (link.getCapacity() < this.pConfigGroup.getMinCapacityForStops()) { return 0; } @@ -475,17 +475,17 @@ private int addStopOnLink(Link link) { log.warn("Link " + link.getId() + " has already a stop in the given (non-paratransit) TransitSchedule. This should not happen. Check code."); return 0; } - + if (this.transitSchedule.getFacilities().get(Id.create(pConfigGroup.getPIdentifier() + link.getId().toString(), TransitStopFacility.class)) != null) { log.warn("Link " + link.getId() + " has already a stop. This should not happen. Check code."); return 0; } - + Id stopId = Id.create(this.pConfigGroup.getPIdentifier() + link.getId(), TransitStopFacility.class); TransitStopFacility stop = this.transitSchedule.getFactory().createTransitStopFacility(stopId, link.getToNode().getCoord(), false); stop.setLinkId(link.getId()); this.transitSchedule.addStopFacility(stop); - return 1; + return 1; } private boolean topoTypeAllowed(Link link) { @@ -495,7 +495,7 @@ private boolean topoTypeAllowed(Link link) { } Integer topoType = this.networkCalcTopoType.getTopoType(link.getToNode()); return this.topoTypesForStops.contains(topoType); - } + } private boolean linkToNodeInServiceArea(Link link) { Point p = factory.createPoint(MGC.coord2Coordinate(link.getToNode().getCoord())); diff --git a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/run/RunAreaAnalysis.java b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/run/RunAreaAnalysis.java index 270cb77fa09..7643b4ea3b4 100644 --- a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/run/RunAreaAnalysis.java +++ b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/run/RunAreaAnalysis.java @@ -28,7 +28,7 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.population.io.StreamingPopulationReader; import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; public class RunAreaAnalysis { @@ -38,8 +38,8 @@ public static void main(String[] args) { Path experiencedPlansPath = Paths.get(args[1]); Path networkFile = Paths.get(args[2]); - Collection features = ShapeFileReader.getAllFeatures(shapefile.toString()); - + Collection features = GeoFileReader.getAllFeatures(shapefile.toString()); + Config conf = ConfigUtils.createConfig(); conf.network().setInputFile(networkFile.toString()); RegionModeshareAnalyzer modeshares = new RegionModeshareAnalyzer(features); diff --git a/contribs/protobuf/pom.xml b/contribs/protobuf/pom.xml index c53160b6208..9e13515761a 100644 --- a/contribs/protobuf/pom.xml +++ b/contribs/protobuf/pom.xml @@ -11,7 +11,7 @@ protobuf - 3.25.2 + 3.25.3 diff --git a/contribs/railsim/pom.xml b/contribs/railsim/pom.xml index 345b4fd0cea..b6172ab206c 100644 --- a/contribs/railsim/pom.xml +++ b/contribs/railsim/pom.xml @@ -12,6 +12,11 @@ railsim + + it.unimi.dsi + fastutil + + org.assertj assertj-core diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java index 6eff2e945f2..297e2c99da3 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java @@ -20,12 +20,14 @@ package ch.sbb.matsim.contrib.railsim; import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.ResourceType; import org.matsim.api.core.v01.network.Link; import org.matsim.vehicles.VehicleType; import java.math.BigDecimal; import java.math.RoundingMode; + /** * Utility class for working with Railsim and its specific attributes. * @@ -40,6 +42,7 @@ public final class RailsimUtils { public static final String LINK_ATTRIBUTE_MINIMUM_TIME = "railsimMinimumTime"; public static final String VEHICLE_ATTRIBUTE_ACCELERATION = "railsimAcceleration"; public static final String VEHICLE_ATTRIBUTE_DECELERATION = "railsimDeceleration"; + public static final String RESOURCE_TYPE = "railsimResourceType"; private RailsimUtils() { } @@ -155,4 +158,19 @@ public static void setTrainAcceleration(VehicleType vehicle, double acceleration vehicle.getAttributes().putAttribute(VEHICLE_ATTRIBUTE_ACCELERATION, acceleration); } + /** + * Return the resource type for a link, if not set, fixed block is assumed. + */ + public static ResourceType getResourceType(Link link) { + Object attr = link.getAttributes().getAttribute(RESOURCE_TYPE); + return attr != null ? ResourceType.valueOf(attr.toString()) : ResourceType.fixedBlock; + } + + /** + * Sets the resource type for the link. + */ + public static void setResourceType(Link link, ResourceType type) { + link.getAttributes().putAttribute(RESOURCE_TYPE, type.toString()); + } + } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java index 61462ddebbd..82beea8bb11 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java @@ -45,7 +45,7 @@ private RailsimCsvWriter() { * Write {@link RailsimLinkStateChangeEvent} to a csv file. */ public static void writeLinkStatesCsv(List events, String filename) throws UncheckedIOException { - String[] header = {"link", "time", "state", "vehicle", "track"}; + String[] header = {"link", "time", "state", "vehicle"}; try (CSVPrinter csv = new CSVPrinter(IOUtils.getBufferedWriter(filename), CSVFormat.DEFAULT.builder().setHeader(header).build())) { for (RailsimLinkStateChangeEvent event : events) { @@ -53,7 +53,6 @@ public static void writeLinkStatesCsv(List events, csv.print(event.getTime()); csv.print(event.getState().toString()); csv.print(event.getVehicleId() != null ? event.getVehicleId().toString() : ""); - csv.print(event.getTrack()); csv.println(); } } catch (IOException e) { diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java index 97488e97e1d..cb1cc71c73b 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java @@ -20,7 +20,7 @@ package ch.sbb.matsim.contrib.railsim.eventmappers; import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; -import ch.sbb.matsim.contrib.railsim.qsimengine.TrackState; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.ResourceState; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.network.Link; @@ -38,8 +38,7 @@ public RailsimLinkStateChangeEvent apply(GenericEvent event) { event.getTime(), asId(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_LINK), Link.class), asId(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_VEHICLE), Vehicle.class), - TrackState.valueOf(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_STATE)), - Integer.parseInt(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_TRACK)) + ResourceState.valueOf(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_STATE)) ); } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java index f5a84b0e8ff..70b2fddd1c1 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java @@ -39,17 +39,17 @@ public class RailsimDetourEvent extends Event implements HasVehicleId { public static final String EVENT_TYPE = "railsimDetourEvent"; private final Id vehicleId; - private final Id entry; - private final Id exit; + private final Id start; + private final Id end; private final List> detour; private final Id newStop; - public RailsimDetourEvent(double time, Id vehicleId, Id entry, Id exit, List> detour, + public RailsimDetourEvent(double time, Id vehicleId, Id start, Id end, List> detour, Id newStop) { super(time); this.vehicleId = vehicleId; - this.entry = entry; - this.exit = exit; + this.start = start; + this.end = end; this.detour = detour; this.newStop = newStop; } @@ -70,8 +70,8 @@ public Map getAttributes() { Map attributes = super.getAttributes(); attributes.put(HasVehicleId.ATTRIBUTE_VEHICLE, vehicleId.toString()); - attributes.put("entry", entry.toString()); - attributes.put("exit", exit.toString()); + attributes.put("start", start.toString()); + attributes.put("end", end.toString()); attributes.put("detour", detour.stream().map(Object::toString).collect(Collectors.joining(","))); attributes.put("newStop", Objects.toString(newStop)); diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java index 55a73fdf6d7..04d2610406f 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java @@ -19,7 +19,7 @@ package ch.sbb.matsim.contrib.railsim.events; -import ch.sbb.matsim.contrib.railsim.qsimengine.TrackState; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.ResourceState; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.events.HasLinkId; @@ -30,26 +30,23 @@ import java.util.Map; /** - * Event thrown when the {@link ch.sbb.matsim.contrib.railsim.qsimengine.TrackState} of a {@link Link} changes. + * Event thrown when the {@link ResourceState} of a {@link Link} changes. */ public final class RailsimLinkStateChangeEvent extends Event implements HasLinkId, HasVehicleId { public static final String EVENT_TYPE = "railsimLinkStateChangeEvent"; public static final String ATTRIBUTE_STATE = "state"; - public static final String ATTRIBUTE_TRACK = "track"; private final Id linkId; private final Id vehicleId; - private final TrackState state; - private final int track; + private final ResourceState state; - public RailsimLinkStateChangeEvent(double time, Id linkId, Id vehicleId, TrackState state, int track) { + public RailsimLinkStateChangeEvent(double time, Id linkId, Id vehicleId, ResourceState state) { super(time); this.linkId = linkId; this.vehicleId = vehicleId; this.state = state; - this.track = track; } @Override @@ -67,21 +64,16 @@ public Id getVehicleId() { return this.vehicleId; } - public TrackState getState() { + public ResourceState getState() { return state; } - public int getTrack() { - return track; - } - @Override public Map getAttributes() { Map attr = super.getAttributes(); attr.put(ATTRIBUTE_LINK, this.linkId.toString()); attr.put(ATTRIBUTE_VEHICLE, this.vehicleId.toString()); attr.put(ATTRIBUTE_STATE, this.state.toString()); - attr.put(ATTRIBUTE_TRACK, String.valueOf(track)); return attr; } } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java index 3e27651c038..df3aaa9fdef 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java @@ -24,7 +24,12 @@ */ final class FuzzyUtils { - private static final double EPSILON = 1E-5; + /** + * The allowed deviation for small numbers to be considered equal. + * Contrary to intuition, this value should not be too large. + * It might happen that trains are moved too earlier over links. + */ + private static final double EPSILON = 1E-6; private FuzzyUtils() { } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java deleted file mode 100644 index ded83867f7d..00000000000 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java +++ /dev/null @@ -1,62 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2023 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package ch.sbb.matsim.contrib.railsim.qsimengine; - -import org.matsim.core.mobsim.framework.MobsimDriverAgent; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - - -/** - * A resource representing multiple {@link RailLink}. - */ -public class RailResource { - - /** - * Links belonging to this resource. - */ - final List links; - - /** - * Agents holding this resource exclusively. - */ - final Set reservations; - - /** - * Maximum number of reservations. - */ - int capacity; - - public RailResource(List links) { - this.links = links; - this.reservations = new HashSet<>(); - this.capacity = links.stream().mapToInt(RailLink::getNumberOfTracks).min().orElseThrow(); - } - - /** - * Whether an agent is able to block this resource. - */ - boolean hasCapacity() { - return reservations.size() < capacity; - } - -} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java deleted file mode 100644 index 6f00f25b9d6..00000000000 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java +++ /dev/null @@ -1,200 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2023 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package ch.sbb.matsim.contrib.railsim.qsimengine; - -import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; -import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; -import jakarta.inject.Inject; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.IdMap; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.mobsim.framework.MobsimDriverAgent; -import org.matsim.core.mobsim.qsim.QSim; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Class responsible for managing and blocking resources and segments of links. - */ -public final class RailResourceManager { - - private final EventsManager eventsManager; - - /** - * Rail links. - */ - private final Map, RailLink> links; - - private final Map, RailResource> resources; - - @Inject - public RailResourceManager(QSim qsim) { - this(qsim.getEventsManager(), ConfigUtils.addOrGetModule(qsim.getScenario().getConfig(), RailsimConfigGroup.class), qsim.getScenario().getNetwork()); - } - - /** - * Construct resources from network. - */ - public RailResourceManager(EventsManager eventsManager, RailsimConfigGroup config, Network network) { - this.eventsManager = eventsManager; - this.links = new IdMap<>(Link.class, network.getLinks().size()); - - Set modes = config.getNetworkModes(); - for (Map.Entry, ? extends Link> e : network.getLinks().entrySet()) { - if (e.getValue().getAllowedModes().stream().anyMatch(modes::contains)) - this.links.put(e.getKey(), new RailLink(e.getValue())); - } - - Map, List> collect = links.values().stream() - .filter(l -> l.resource != null) - .collect(Collectors.groupingBy(l -> l.resource, Collectors.toList()) - ); - - resources = new IdMap<>(RailResource.class, collect.size()); - for (Map.Entry, List> e : collect.entrySet()) { - resources.put(e.getKey(), new RailResource(e.getValue())); - } - } - - /** - * Get single link that belongs to an id. - */ - public RailLink getLink(Id id) { - return links.get(id); - } - - /** - * Return the resource for a given id. - */ - public RailResource getResource(Id id) { - if (id == null) return null; - return resources.get(id); - } - - /** - * Try to block a resource for a specific driver. - * - * @return true if the resource is now blocked or was blocked for this driver already. - */ - private boolean tryBlockResource(RailResource resource, MobsimDriverAgent driver) { - - if (resource.reservations.contains(driver)) - return true; - - if (resource.hasCapacity()) { - resource.reservations.add(driver); - return true; - } - - return false; - } - - /** - * Try to release a resource, but only if none of the links are blocked anymore by this driver. - * - * @return whether driver is still blocking this resource. - */ - private boolean tryReleaseResource(RailResource resource, MobsimDriverAgent driver) { - - if (resource.links.stream().noneMatch(l -> l.isBlockedBy(driver))) { - resource.reservations.remove(driver); - return true; - } - - return false; - } - - /** - * Try to block a track and the underlying resource and return whether it was successful. - */ - public boolean tryBlockTrack(double time, MobsimDriverAgent driver, RailLink link) { - - if (link.isBlockedBy(driver)) - return true; - - Id resourceId = link.getResourceId(); - if (resourceId != null) { - - RailResource resource = getResource(resourceId); - - // resource is required - if (!tryBlockResource(resource, driver)) { - return false; - } - } - - if (link.hasFreeTrack()) { - int track = link.blockTrack(driver); - eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), - driver.getVehicle().getId(), TrackState.BLOCKED, track)); - - return true; - } - - return false; - } - - /** - * Checks whether a link or underlying resource has remaining capacity. - */ - public boolean hasCapacity(Id link) { - - RailLink l = getLink(link); - - if (!l.hasFreeTrack()) - return false; - - RailResource res = getResource(l.getResourceId()); - if (res != null) { - return res.hasCapacity(); - } - - return true; - } - - /** - * Whether a driver already reserved a link or would be able to reserve it. - */ - public boolean isBlockedBy(RailLink link, MobsimDriverAgent driver) { - // If a link is blocked, the resource must be blocked as well - return link.isBlockedBy(driver); - } - - /** - * Release a non-free track to be free again. - */ - public void releaseTrack(double time, MobsimDriverAgent driver, RailLink link) { - int track = link.releaseTrack(driver); - eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), driver.getVehicle().getId(), - TrackState.FREE, track)); - - // Release held resources - if (link.getResourceId() != null) { - tryReleaseResource(getResource(link.getResourceId()), driver); - } - - } -} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java index 62437d0bfa7..117947728d3 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java @@ -19,13 +19,15 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; + import java.util.ArrayList; import java.util.List; /** * Utility class holding static calculation methods related to state (updates). */ -final class RailsimCalc { +public final class RailsimCalc { private RailsimCalc() { } @@ -49,6 +51,7 @@ static double solveTraveledDist(double speed, double dist, double acceleration) /** * Calculate time needed to advance distance {@code dist}. Depending on acceleration and max speed. + * If dist can never be reached, will return time needed to stop. */ static double calcRequiredTime(TrainState state, double dist) { @@ -84,7 +87,7 @@ static double calcRequiredTime(TrainState state, double dist) { } else if (dist <= max) { return solveTraveledDist(state.speed, dist, state.acceleration); } else - return Double.POSITIVE_INFINITY; + return decelTime; } } @@ -100,6 +103,12 @@ static SpeedTarget calcTargetSpeed(double dist, double acceleration, double dece return new SpeedTarget(finalSpeed, Double.POSITIVE_INFINITY); } + // Distance could be zero, speeds must be already equal then + if (FuzzyUtils.equals(dist, 0)) { + assert FuzzyUtils.equals(currentSpeed, finalSpeed) : "Current speed must be equal to allowed speed"; + return new SpeedTarget(finalSpeed, 0); + } + double timeDecel = (allowedSpeed - finalSpeed) / deceleration; double distDecel = calcTraveledDist(allowedSpeed, timeDecel, -deceleration); @@ -162,83 +171,92 @@ static double calcTargetSpeedForStop(double dist, double acceleration, double de } /** - * Calculate when the reservation function should be triggered. - * Should return {@link Double#POSITIVE_INFINITY} if this distance is far in the future and can be checked at later point. - * - * @param state current train state - * @param currentLink the link where the train head is on - * @return travel distance after which reservations should be updated. + * Calculate the minimum distance that needs to be reserved for the train, such that it can stop safely. */ - static double nextLinkReservation(TrainState state, RailLink currentLink) { - - // on way to pt stop, no need to reserve anymore - if (state.isStop(currentLink.getLinkId()) && FuzzyUtils.lessThan(state.headPosition, currentLink.length)) - return Double.POSITIVE_INFINITY; + static double calcReservationDistance(TrainState state, RailLink currentLink) { double assumedSpeed = calcPossibleMaxSpeed(state); - // time needed for full stop - double stopTime = assumedSpeed / state.train.deceleration(); + // stop at end of link + if (state.isStop(currentLink.getLinkId())) + return currentLink.length - state.headPosition; - assert stopTime > 0 : "Stop time can not be negative"; + double stopTime = assumedSpeed / state.train.deceleration(); // safety distance double safety = RailsimCalc.calcTraveledDist(assumedSpeed, stopTime, -state.train.deceleration()); + double distToNextStop = currentLink.length - state.headPosition; int idx = state.routeIdx; - double dist = currentLink.length - state.headPosition; - - RailLink nextLink = null; - // need to check beyond safety distance - while (FuzzyUtils.lessEqualThan(dist, safety * 2) && idx < state.route.size()) { - nextLink = state.route.get(idx++); - if (!nextLink.isBlockedBy(state.driver)) - return dist - safety; + while (idx < state.route.size()) { + RailLink nextLink = state.route.get(idx++); + distToNextStop += nextLink.length; - // No reservation beyond pt stop if (state.isStop(nextLink.getLinkId())) break; - - dist += nextLink.length; } - // No reservation needed after the end - if (idx == state.route.size() || (nextLink != null && state.isStop(nextLink.getLinkId()))) - return Double.POSITIVE_INFINITY; + return Math.min(safety, distToNextStop); + } + + /** + * Calculate the projected driven distance, based on current position and state. + */ + public static double projectedDistance(double time, TrainPosition position) { + + if (!(position instanceof TrainState state)) + throw new IllegalArgumentException("Position must be a TrainState."); + + double elapsed = time - state.timestamp; + + if (elapsed == 0) + return 0; + + double accelTime = (state.targetSpeed - state.speed) / state.acceleration; + + double dist; + if (state.acceleration == 0) { + dist = state.speed * elapsed; + + } else if (accelTime < elapsed) { + // Travelled distance under constant acceleration + dist = RailsimCalc.calcTraveledDist(state.speed, accelTime, state.acceleration); - return dist - safety; + // Remaining time at constant speed + if (state.acceleration > 0) + dist += RailsimCalc.calcTraveledDist(state.targetSpeed, elapsed - accelTime, 0); + + } else { + // Acceleration was constant the whole time + dist = RailsimCalc.calcTraveledDist(state.speed, elapsed, state.acceleration); + } + + return dist; } /** * Links that need to be blocked or otherwise stop needs to be initiated. + * This method is only valid for fixed block resources. */ - static List calcLinksToBlock(TrainState state, RailLink currentLink) { + public static List calcLinksToBlock(TrainPosition position, RailLink currentLink, double reserveDist) { List result = new ArrayList<>(); - // Currently driving to pt stop - if (state.isStop(currentLink.getLinkId()) && FuzzyUtils.lessThan(state.headPosition, currentLink.length)) - return result; + // Assume current distance left on link is already reserved (only for fixed block) + double dist = currentLink.length - position.getHeadPosition(); - double assumedSpeed = calcPossibleMaxSpeed(state); - double stopTime = assumedSpeed / state.train.deceleration(); - - // safety distance - double safety = RailsimCalc.calcTraveledDist(assumedSpeed, stopTime, -state.train.deceleration()); - - int idx = state.routeIdx; + int idx = position.getRouteIndex(); - // dist to next - double dist = currentLink.length - state.headPosition; + // This function always needs to provide more reserve distance than requested (except when it will stop) + while (FuzzyUtils.lessEqualThan(dist, reserveDist) && idx < position.getRouteSize()) { + RailLink nextLink = position.getRoute(idx++); + dist += nextLink.length; - while (FuzzyUtils.lessEqualThan(dist, safety) && idx < state.route.size()) { - RailLink nextLink = state.route.get(idx++); result.add(nextLink); - dist += nextLink.length; - // Beyond pt stop links don't need to be reserved - if (state.isStop(nextLink.getLinkId())) + // Don't block beyond stop + if (position.isStop(nextLink.getLinkId())) break; } @@ -246,18 +264,27 @@ static List calcLinksToBlock(TrainState state, RailLink currentLink) { } /** - * Whether re-routing should be tried. - * - * @param upcoming the upcoming links the train tried to block. + * Calculate distance to the next stop. */ - static boolean considerReRouting(List upcoming, RailLink currentLink) { - return currentLink.isEntryLink() || upcoming.stream().anyMatch(RailLink::isEntryLink); + public static double calcDistToNextStop(TrainPosition position, RailLink currentLink) { + double dist = currentLink.length - position.getHeadPosition(); + + int idx = position.getRouteIndex(); + while (idx < position.getRouteSize()) { + RailLink nextLink = position.getRoute(idx++); + dist += nextLink.length; + + if (position.isStop(nextLink.getLinkId())) + break; + } + + return dist; } /** * Maximum speed of the next upcoming links. */ - private static double calcPossibleMaxSpeed(TrainState state) { + static double calcPossibleMaxSpeed(TrainState state) { double safety = RailsimCalc.calcTraveledDist(state.train.maxVelocity(), state.train.maxVelocity() / state.train.deceleration(), -state.train.deceleration()); double maxSpeed = state.allowedMaxSpeed; diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java index 6573ed1b47e..ff98123051a 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java @@ -26,14 +26,13 @@ import java.util.Queue; import java.util.stream.Collectors; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.DispositionResponse; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.Event; -import org.matsim.api.core.v01.events.LinkEnterEvent; -import org.matsim.api.core.v01.events.LinkLeaveEvent; -import org.matsim.api.core.v01.events.PersonEntersVehicleEvent; -import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent; +import org.matsim.api.core.v01.events.*; import org.matsim.api.core.v01.network.Link; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimDriverAgent; @@ -52,6 +51,11 @@ */ final class RailsimEngine implements Steppable { + /** + * Additional safety distance in meter that is added to the reservation distance. + * Ensure that trains always have enough distance to progress. + */ + private static double SAFETY_DIST = 10; private static final Logger log = LogManager.getLogger(RailsimEngine.class); private final EventsManager eventsManager; private final RailsimConfigGroup config; @@ -210,8 +214,8 @@ private void checkTrackReservation(double time, UpdateEvent event) { boolean allBlocked = blockLinkTracks(time, state); - // Driver can advance if the next link is already free - if (allBlocked || (nextLink != null && nextLink.isBlockedBy(state.driver))) { + // Driver can advance if there is approved distance + if (allBlocked || FuzzyUtils.greaterThan(state.approvedDist, 0)) { if (allBlocked) event.checkReservation = -1; @@ -219,8 +223,11 @@ private void checkTrackReservation(double time, UpdateEvent event) { event.checkReservation = time + config.pollInterval; } - // Train already waits at the end of previous link - if (event.waitingForLink) { + // Train already waits at the end of previous link, next link is already blocked + if (event.waitingForLink && + nextLink != null && + resources.isBlockedBy(nextLink, state) && + FuzzyUtils.equals(resources.getLink(state.headLink).length, state.headPosition)) { enterLink(time, event); event.waitingForLink = false; @@ -238,7 +245,7 @@ private void checkTrackReservation(double time, UpdateEvent event) { event.checkReservation = time + config.pollInterval; // If train is already standing still and waiting, there is no update needed. - if (event.waitingForLink) { + if (event.waitingForLink && FuzzyUtils.equals(state.speed, 0)) { event.plannedTime = time + config.pollInterval; } else { decideNextUpdate(event); @@ -259,7 +266,7 @@ private void updateDeparture(double time, UpdateEvent event) { state.tailPosition = firstLink.length - state.train.length(); // reserve links and start if first one is free - if (blockLinkTracks(time, state) || resources.isBlockedBy(firstLink, state.driver)) { + if (blockLinkTracks(time, state) || resources.isBlockedBy(firstLink, state)) { createEvent(new PersonEntersVehicleEvent(time, state.driver.getId(), state.driver.getVehicle().getId())); createEvent(new VehicleEntersTrafficEvent(time, state.driver.getId(), @@ -296,74 +303,48 @@ private void updateDeparture(double time, UpdateEvent event) { */ private boolean blockLinkTracks(double time, TrainState state) { - List links = RailsimCalc.calcLinksToBlock(state, resources.getLink(state.headLink)); + double reserveDist = RailsimCalc.calcReservationDistance(state, resources.getLink(state.headLink)); - if (links.isEmpty()) - return true; + DispositionResponse response = disposition.requestNextSegment(time, state, reserveDist); - if (state.pt != null && RailsimCalc.considerReRouting(links, resources.getLink(state.headLink))) { + // Disposition assigned a detour + if (state.pt != null && response.detour() != null) { - int start = -1; - int end = -1; - RailLink entry = null; - RailLink exit = null; + List subRoute = state.route.subList(response.detour().startIdx(), response.detour().endIdx()); - for (int i = Math.max(0, state.routeIdx - 1); i < state.route.size(); i++) { - RailLink l = state.route.get(i); - - if (l.isEntryLink()) { - entry = l; - start = i; - } else if (start > -1 && l.isBlockedBy(state.driver)) { - // check if any link beyond entry is already blocked - // if that is the case re-route is not possible anymore - break; - } else if (start > -1 && l.isExitLink()) { - exit = l; - end = i; - break; - } - } + TransitStopFacility newStop = state.pt.addDetour(subRoute, response.detour().newRoute()); - // there might be no exit link if this is the end of the route - // exit will be set to null if re-route is too late - // network could be wrong as well, but hard to verify - if (exit != null) { + subRoute.clear(); + subRoute.addAll(response.detour().newRoute()); - // check if this route is different - List subRoute = state.route.subList(start + 1, end); - - List detour = disposition.requestRoute(time, state.pt, subRoute, entry, exit); - - if (detour != null && !subRoute.equals(detour)) { - - TransitStopFacility newStop = state.pt.addDetour(subRoute, detour); + if (newStop != null) { + state.nextStop = newStop; + } - subRoute.clear(); - subRoute.addAll(detour); + createEvent(new RailsimDetourEvent( + time, state.driver.getVehicle().getId(), + response.detour().startLink(), response.detour().endLink(), + response.detour().newRoute().stream().map(RailLink::getLinkId).toList(), + newStop != null ? newStop.getId() : null + )); - if (newStop != null) { - state.nextStop = newStop; - } + // reserve the assigned links from the detour + response = disposition.requestNextSegment(time, state, reserveDist); + } - createEvent(new RailsimDetourEvent( - time, state.driver.getVehicle().getId(), - entry.getLinkId(), exit.getLinkId(), - detour.stream().map(RailLink::getLinkId).toList(), - newStop != null ? newStop.getId() : null - )); + state.approvedDist = response.approvedDist(); - // Block links again using the updated route - links = RailsimCalc.calcLinksToBlock(state, resources.getLink(state.headLink)); + assert FuzzyUtils.greaterEqualThan(state.approvedDist, 0) : "Approved distance must be positive, but was " + response.approvedDist(); - } - } - } - - List blocked = disposition.blockRailSegment(time, state.driver, links); + // At the moment the approved speed from the disposition is not used + // Stop when the approved distance is not enough + if (FuzzyUtils.lessThan(state.approvedDist, reserveDist + SAFETY_DIST)) + state.approvedSpeed = 0; + else + state.approvedSpeed = Double.POSITIVE_INFINITY; - // Only continue successfully if all requested link have been blocked - return links.size() == blocked.size(); + // Return whether the train has to stop + return state.approvedSpeed != 0; } private void enterLink(double time, UpdateEvent event) { @@ -393,7 +374,7 @@ private void enterLink(double time, UpdateEvent event) { // Free all reservations for (RailLink link : state.route) { - if (link.isBlockedBy(state.driver)) { + if (resources.isBlockedBy(link, state)) { disposition.unblockRailLink(time, state.driver, link); } } @@ -412,7 +393,7 @@ private void enterLink(double time, UpdateEvent event) { RailLink currentLink = state.route.get(state.routeIdx); // If this linked is blocked the driver can continue - if (!currentLink.isBlockedBy(state.driver)) { + if (!resources.isBlockedBy(currentLink, state)) { event.waitingForLink = true; event.type = UpdateEvent.Type.WAIT_FOR_RESERVATION; event.plannedTime = time + config.pollInterval; @@ -424,15 +405,16 @@ private void enterLink(double time, UpdateEvent event) { createEvent(new LinkLeaveEvent(time, state.driver.getVehicle().getId(), state.headLink)); // Get link and increment - state.headPosition = 0; state.headLink = state.route.get(state.routeIdx++).getLinkId(); - state.driver.notifyMoveOverNode(state.headLink); - createEvent(new LinkEnterEvent(time, state.driver.getVehicle().getId(), state.headLink)); + assert resources.isBlockedBy(resources.getLink(state.headLink), state) : "Link has to be blocked by driver when entered"; - RailLink link = resources.getLink(state.headLink); + state.headPosition = 0; + // Reset waiting flag + event.waitingForLink = false; - assert link.isBlockedBy(state.driver) : "Link has to be blocked by driver when entered"; + state.driver.notifyMoveOverNode(state.headLink); + createEvent(new LinkEnterEvent(time, state.driver.getVehicle().getId(), state.headLink)); decideTargetSpeed(event, state); @@ -529,18 +511,20 @@ private void updatePosition(double time, UpdateEvent event) { state.headPosition += dist; state.tailPosition += dist; + state.approvedDist -= dist; if (Double.isFinite(state.targetDecelDist)) { state.targetDecelDist -= dist; } // When trains are put into the network their tail may be longer than the current link - // this assertion may not hold depending on the network, should possibly be removed - assert state.routeIdx <= 2 || FuzzyUtils.greaterEqualThan(state.tailPosition, 0) : "Illegal state update. Tail position should not be negative"; + // this assertion may not hold depending on the network +// assert state.routeIdx <= 2 || FuzzyUtils.greaterEqualThan(state.tailPosition, 0) : "Illegal state update. Tail position should not be negative"; assert FuzzyUtils.lessEqualThan(state.headPosition, resources.getLink(state.headLink).length) : "Illegal state update. Head position must be smaller than link length"; assert FuzzyUtils.greaterEqualThan(state.headPosition, 0) : "Head position must be positive"; assert FuzzyUtils.lessEqualThan(state.speed, state.allowedMaxSpeed) : "Speed must be less equal than the allowed speed"; + assert FuzzyUtils.greaterEqualThan(state.approvedDist, 0) : "Approved distance must be positive"; state.timestamp = time; @@ -587,19 +571,24 @@ private void decideNextUpdate(UpdateEvent event) { // (3) next link needs reservation double reserveDist = Double.POSITIVE_INFINITY; if (!state.isRouteAtEnd() && !event.isAwaitingReservation()) { - reserveDist = RailsimCalc.nextLinkReservation(state, currentLink); + // Check first if more distance than for the next stop is needed + double nextStop = RailsimCalc.calcDistToNextStop(state, currentLink); + + if (!FuzzyUtils.equals(state.approvedDist, nextStop)) { + double requiredDist = RailsimCalc.calcReservationDistance(state, currentLink); + // when the approved distance is equal to the required distance, new reservation needs to be made + reserveDist = state.approvedDist - requiredDist; + } + + // With moving blocks the reserve distance might not be set to 0, but rather the accel or decel distance if (reserveDist < 0) reserveDist = 0; // Outside of block track the reserve distance is always greater 0 // infinite loops would occur otherwise if (!(event.type != UpdateEvent.Type.BLOCK_TRACK || FuzzyUtils.greaterThan(reserveDist, 0))) { - // There are here for debugging - List tmp = RailsimCalc.calcLinksToBlock(state, currentLink); - double r = RailsimCalc.nextLinkReservation(state, currentLink); - - throw new AssertionError("Reserve distance must be positive, but was " + r); + throw new AssertionError("Reserve distance must be positive, but was " + reserveDist); } } @@ -615,7 +604,18 @@ private void decideNextUpdate(UpdateEvent event) { // Find the earliest required update double dist; - if (reserveDist <= accelDist && reserveDist <= decelDist && reserveDist <= tailDist && reserveDist <= headDist) { + if (FuzzyUtils.lessEqualThan(tailDist, decelDist) && + FuzzyUtils.lessEqualThan(tailDist, reserveDist) && + FuzzyUtils.lessEqualThan(tailDist, headDist) && + FuzzyUtils.lessEqualThan(tailDist, accelDist)) { + + // leave link has priority, and is ensured to be executed before others at same time step + // otherwise train might block links of same length as itself + + dist = tailDist; + event.type = UpdateEvent.Type.LEAVE_LINK; + + } else if (reserveDist <= accelDist && reserveDist <= decelDist && reserveDist <= tailDist && reserveDist <= headDist) { dist = reserveDist; event.type = UpdateEvent.Type.BLOCK_TRACK; } else if (accelDist <= decelDist && accelDist <= reserveDist && accelDist <= tailDist && accelDist <= headDist) { @@ -624,9 +624,6 @@ private void decideNextUpdate(UpdateEvent event) { } else if (decelDist <= accelDist && decelDist <= reserveDist && decelDist <= tailDist && decelDist <= headDist) { dist = decelDist; event.type = UpdateEvent.Type.SPEED_CHANGE; - } else if (tailDist <= decelDist && tailDist <= reserveDist && tailDist <= headDist) { - dist = tailDist; - event.type = UpdateEvent.Type.LEAVE_LINK; } else { dist = headDist; event.type = UpdateEvent.Type.ENTER_LINK; @@ -674,7 +671,28 @@ private void decideTargetSpeed(UpdateEvent event, TrainState state) { state.targetSpeed = state.allowedMaxSpeed; state.targetDecelDist = Double.POSITIVE_INFINITY; - for (int i = state.routeIdx; i <= state.route.size(); i++) { + boolean stop = false; + + // Set speed approved by disposition + if (state.approvedSpeed < minAllowed) { + + RailsimCalc.SpeedTarget target = RailsimCalc.calcTargetSpeed(state.approvedDist, + state.train.acceleration(), state.train.deceleration(), + state.speed, state.allowedMaxSpeed, state.approvedSpeed); + + assert FuzzyUtils.greaterEqualThan(target.decelDist(), 0) : "Decel dist is " + target.decelDist() + ", stopping is not possible"; + + if (FuzzyUtils.equals(target.decelDist(), 0)) { + state.targetSpeed = state.approvedSpeed; + state.targetDecelDist = Double.POSITIVE_INFINITY; + stop = true; + } else { + state.targetSpeed = target.targetSpeed(); + state.targetDecelDist = target.decelDist(); + } + } + + for (int i = state.routeIdx; i <= state.route.size() && !stop; i++) { RailLink link; double allowed; @@ -688,8 +706,6 @@ private void decideTargetSpeed(UpdateEvent event, TrainState state) { // train stops at the very end of a link if (i > 0 && state.isStop(state.route.get(i - 1).getLinkId())) allowed = 0; - else if (!resources.isBlockedBy(link, state.driver)) - allowed = 0; else allowed = link.getAllowedFreespeed(state.driver); } @@ -723,7 +739,7 @@ else if (!resources.isBlockedBy(link, state.driver)) if (link != null) dist += link.length; - if (dist >= window) + if (FuzzyUtils.greaterEqualThan(dist, window)) break; minAllowed = allowed; @@ -761,4 +777,16 @@ private double retrieveAllowedMaxSpeed(TrainState state) { return maxSpeed; } + /** + * Remove all trains from simulation and generate events at the end of the day. + * + * @param now end of day time + */ + void clearTrains(double now) { + + for (TrainState train : activeTrains) { + eventsManager.processEvent(new VehicleAbortsEvent(now, train.driver.getVehicle().getId(), train.headLink)); + eventsManager.processEvent(new PersonStuckEvent(now, train.driver.getId(), train.headLink, train.driver.getMode())); + } + } } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java index 88ee0b22cbc..2e65080b4a1 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java @@ -21,6 +21,7 @@ import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.TrainDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; import com.google.inject.Inject; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -79,7 +80,7 @@ public void onPrepareSim() { @Override public void afterSim() { - + engine.clearTrains(qsim.getSimTimer().getTimeOfDay()); } @Override diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java index 77e4e04133b..5add9a20252 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java @@ -19,8 +19,11 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.DeadlockAvoidance; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.SimpleDeadlockAvoidance; import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.TrainDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; import com.google.inject.multibindings.OptionalBinder; import org.matsim.core.mobsim.qsim.AbstractQSimModule; @@ -47,8 +50,9 @@ protected void configureQSim() { bind(TrainRouter.class).asEagerSingleton(); bind(RailResourceManager.class).asEagerSingleton(); - // This Interface might be replaced with other implementations + // These interfaces might be replaced with other implementations bind(TrainDisposition.class).to(SimpleDisposition.class).asEagerSingleton(); + bind(DeadlockAvoidance.class).to(SimpleDeadlockAvoidance.class).asEagerSingleton(); addQSimComponentBinding(COMPONENT_NAME).to(RailsimQSimEngine.class); diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java index be2ebcb6ffb..8768ce90029 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java @@ -19,6 +19,7 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java index 9d395d785d0..e48aebaad18 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java @@ -27,7 +27,7 @@ /** * Non-mutable static information for a single train. */ -record TrainInfo( +public record TrainInfo( Id id, double length, double maxVelocity, @@ -47,7 +47,7 @@ record TrainInfo( ); } - public void checkConsistency() { + void checkConsistency() { if (!Double.isFinite(maxVelocity) || maxVelocity <= 0) throw new IllegalArgumentException("Train of type " + id + " does not have a finite maximumVelocity."); diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainPosition.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainPosition.java new file mode 100644 index 00000000000..2e99d3903bc --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainPosition.java @@ -0,0 +1,86 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Interface allowing to query current train position and information. + */ +public interface TrainPosition { + + /** + * The driver, which can also be used as identifier. + */ + MobsimDriverAgent getDriver(); + + /** + * Get transit driver agent, if this is a pt transit. + */ + @Nullable + RailsimTransitDriverAgent getPt(); + + /** + * The train type. + */ + TrainInfo getTrain(); + + /** + * The link the where the head of the train is on. Can be null if not yet departed. + */ + @Nullable + Id getHeadLink(); + + /** + * The link the where the tail of the train is on. Can be null if not yet departed. + */ + @Nullable + Id getTailLink(); + + /** + * The position of the head of the train on the link, in meters. + */ + double getHeadPosition(); + + /** + * The position of the tail of the train on the link, in meters. + */ + double getTailPosition(); + + /** + * Current route index. + */ + int getRouteIndex(); + + /** + * Total route size. + */ + int getRouteSize(); + + /** + * Get part of the route. + */ + RailLink getRoute(int idx); + + /** + * Get part of the route. + * + * @param from from index, inclusive + * @param to to index, exclusive + */ + List getRoute(int from, int to); + + /** + * Return the route until the next stop based on the current position. + */ + List getRouteUntilNextStop(); + + /** + * Check whether to stop at certain link. + */ + boolean isStop(Id link); +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java index 1c0f66cb969..acd544c74e4 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java @@ -20,6 +20,7 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.core.mobsim.framework.MobsimDriverAgent; @@ -31,7 +32,7 @@ /** * Stores the mutable current state of a train. */ -final class TrainState { +final class TrainState implements TrainPosition { /** * Driver of the train. @@ -103,10 +104,20 @@ final class TrainState { double headPosition; /** - * * Distance in meters away from the {@code tailLink}s {@code fromNode}. + * Distance in meters away from the {@code tailLink}s {@code fromNode}. */ double tailPosition; + /** + * Distance in meters, which are approved by dispatcher. + */ + double approvedDist; + + /** + * Speed in m/s, which is approved after reaching {@link #approvedDist}. + */ + double approvedSpeed; + /** * Speed in m/s. */ @@ -140,6 +151,7 @@ public String toString() { ", allowedMaxSpeed=" + allowedMaxSpeed + ", headPosition=" + headPosition + ", tailPosition=" + tailPosition + + ", approvedDist=" + approvedDist + ", speed=" + speed + ", acceleration=" + acceleration + '}'; @@ -150,10 +162,8 @@ boolean isRouteAtEnd() { return routeIdx >= route.size(); } - /** - * Check whether to stop at certain link. - */ - boolean isStop(Id link) { + @Override + public boolean isStop(Id link) { return nextStop != null && nextStop.getLinkId().equals(link); } @@ -164,4 +174,71 @@ RailsimTrainStateEvent asEvent(double time) { speed, acceleration, targetSpeed); } + @Override + public MobsimDriverAgent getDriver() { + return driver; + } + + @Override + @Nullable + public RailsimTransitDriverAgent getPt() { + return pt; + } + + @Override + public TrainInfo getTrain() { + return train; + } + + @Override + public Id getHeadLink() { + return headLink; + } + + @Override + public Id getTailLink() { + return tailLink; + } + + @Override + public double getHeadPosition() { + return headPosition; + } + + @Override + public double getTailPosition() { + return tailPosition; + } + + @Override + public int getRouteIndex() { + return routeIdx; + } + + @Override + public int getRouteSize() { + return route.size(); + } + + @Override + public RailLink getRoute(int idx) { + return route.get(idx); + } + + @Override + public List getRoute(int from, int to) { + return route.subList(from, to); + } + + @Override + public List getRouteUntilNextStop() { + int from = routeIdx; + + for (int i = 0; i < route.size(); i++) { + if (isStop(route.get(i).getLinkId())) { + return route.subList(from, i + 1); + } + } + return route.subList(from, route.size()); + } } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java index 52776792c9b..a8a2c246ddd 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java @@ -19,6 +19,8 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; + import java.util.Objects; /** diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/DeadlockAvoidance.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/DeadlockAvoidance.java new file mode 100644 index 00000000000..513b55df61a --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/DeadlockAvoidance.java @@ -0,0 +1,55 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResource; +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import java.util.List; +import java.util.Map; + +/** + * Interface for deadlock prevention strategies. + * Strategies should aim to prevent deadlocks, but is not required to guarantee it. + */ +public interface DeadlockAvoidance { + + /** + * Let the strategy known about the resources that are available. + */ + default void initResources(Map, RailResource> resources) { + } + + + /** + * Check if reserving this link may produce a deadlock. + * @return true if the link can be reserved, false otherwise. + */ + boolean checkLink(double time, RailLink link, TrainPosition position); + + /** + * Check if performing this re-route may produce a deadlock. + * @param subRoute the original route to be changed + * @param detour new detour route + * @return true if the rerouting can be performed, false otherwise. + */ + boolean checkReroute(double time, RailLink start, RailLink end, List subRoute, List detour, TrainPosition position); + + /** + * Called when a resource was reserved. + */ + void onReserve(double time, RailResource resource, TrainPosition position); + + /** + * Called when a resource was released. + */ + void onRelease(double time, RailResource resource, MobsimDriverAgent driver); + + /** + * Called when a link was released. + */ + default void onReleaseLink(double time, RailLink link, MobsimDriverAgent driver) { + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/NoDeadlockAvoidance.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/NoDeadlockAvoidance.java new file mode 100644 index 00000000000..d5fe67ca604 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/NoDeadlockAvoidance.java @@ -0,0 +1,36 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResource; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import java.util.List; + +/** + * Does not prevent deadlocks and permits every request. + */ +public class NoDeadlockAvoidance implements DeadlockAvoidance { + + + @Override + public void onReserve(double time, RailResource resource, TrainPosition position) { + } + + + @Override + public boolean checkLink(double time, RailLink link, TrainPosition position) { + return true; + } + + @Override + public boolean checkReroute(double time, RailLink start, RailLink end, List subRoute, List detour, TrainPosition position) { + return true; + } + + @Override + public void onRelease(double time, RailResource resource, MobsimDriverAgent driver) { + } + + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/SimpleDeadlockAvoidance.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/SimpleDeadlockAvoidance.java new file mode 100644 index 00000000000..6ad95f76623 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/deadlocks/SimpleDeadlockAvoidance.java @@ -0,0 +1,117 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResource; +import jakarta.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.*; + +/** + * A simple deadlock avoidance strategy that backtracks conflicting points in the schedule of trains. + * This implementation does not guarantee deadlock freedom, but can avoid many cases. + */ +public class SimpleDeadlockAvoidance implements DeadlockAvoidance { + + /** + * Stores conflict points of trains. + */ + private final Map conflictPoints = new HashMap<>(); + + @Inject + public SimpleDeadlockAvoidance() { + } + + /** + * This strategy only safeguards sections with single capacity. + */ + private static boolean isConflictPoint(RailResource resource) { + return resource.getTotalCapacity() == 1; + } + + @Override + public void onReserve(double time, RailResource resource, TrainPosition position) { + + boolean resourceFound = false; + + int idx = Math.max(0, position.getRouteIndex() - 1); + for (int i = idx; i < position.getRouteSize(); i++) { + + RailLink link = position.getRoute(i); + RailResource r = link.getResource(); +// // Iterate through route until requested resource is present + + if (r == resource) { + resourceFound = true; + } + + if (!resourceFound) + continue; + + if (isConflictPoint(r)) { + + Reservation reservation = conflictPoints.computeIfAbsent(r, k -> new Reservation()); + + // Reserve non conflict points, otherwise stop + if (reservation.direction != null && reservation.direction != link) + break; + + reservation.direction = link; + reservation.trains.add(position.getDriver()); + + } else + break; + + } + } + + @Override + public boolean checkLink(double time, RailLink link, TrainPosition position) { + + // Passing points can be ignored + if (!isConflictPoint(link.getResource())) + return true; + + Reservation other = conflictPoints.get(link.getResource()); + + // not reserved or reserved by same direction + return other == null || other.direction == null || other.direction == link; + } + + @Override + public boolean checkReroute(double time, RailLink start, RailLink end, List subRoute, List detour, TrainPosition position) { + + // rerouting is always allowed, but reservations needs to be removed + for (RailLink link : subRoute) { + onRelease(time, link.getResource(), position.getDriver()); + } + + return true; + } + + @Override + public void onRelease(double time, RailResource resource, MobsimDriverAgent driver) { + + Reservation reservation = conflictPoints.get(resource); + if (reservation != null) { + reservation.trains.remove(driver); + + // this direction is free again + if (reservation.trains.isEmpty()) + reservation.direction = null; + } + } + + /** + * Holds current direction and drivers holding a reservation. + */ + private static final class Reservation { + + private RailLink direction; + private final Set trains = new LinkedHashSet<>(); + + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/Detour.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/Detour.java new file mode 100644 index 00000000000..728324b49d9 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/Detour.java @@ -0,0 +1,13 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; + +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; + +import java.util.List; + +/** + * Instruction to detour from original route. + */ +public record Detour(int startIdx, int endIdx, Id startLink, Id endLink, List newRoute) { +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/DispositionResponse.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/DispositionResponse.java new file mode 100644 index 00000000000..3514a2f66b7 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/DispositionResponse.java @@ -0,0 +1,11 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; + +/** + * Response from {@link TrainDisposition}. + * @param approvedDist distance cleared for the train + * @param approvedSpeed speed cleared after approved distance, if 0 the train needs to stop. + * @param detour detour to be taken, if any. + */ +public record DispositionResponse(double approvedDist, double approvedSpeed, Detour detour) { + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java index 99418a7547b..7bb0642bfad 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java @@ -19,15 +19,15 @@ package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailResourceManager; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimTransitDriverAgent; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResource; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimCalc; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; import jakarta.inject.Inject; import org.matsim.core.mobsim.framework.MobsimDriverAgent; -import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.List; /** @@ -38,6 +38,15 @@ public class SimpleDisposition implements TrainDisposition { private final RailResourceManager resources; private final TrainRouter router; + /** + * Whether re-routing should be tried. + * + * @param upcoming the upcoming links the train tried to block. + */ + static boolean considerReRouting(List upcoming, RailLink currentLink) { + return currentLink.isEntryLink() || upcoming.stream().anyMatch(RailLink::isEntryLink); + } + @Inject public SimpleDisposition(RailResourceManager resources, TrainRouter router) { this.resources = resources; @@ -49,41 +58,132 @@ public void onDeparture(double time, MobsimDriverAgent driver, List ro // Nothing to do. } - @Nullable @Override - public List requestRoute(double time, RailsimTransitDriverAgent driver, List segment, RailLink entry, RailLink exit) { + public DispositionResponse requestNextSegment(double time, TrainPosition position, double dist) { - // Only re-routes if the link segment is occupied - for (RailLink link : segment) { - if (!resources.isBlockedBy(link, driver) && !resources.hasCapacity(link.getLinkId())) - return router.calcRoute(entry, exit); + RailLink currentLink = resources.getLink(position.getHeadLink()); + List segment = RailsimCalc.calcLinksToBlock(position, currentLink, dist); + + // Check for re routing + Detour detour = checkDetour(time, segment, position); + + if (detour != null) { + // train needs to integrate the detour and request a new route + return new DispositionResponse(0, 0, detour); } - return null; - } + double reserveDist = resources.tryBlockLink(time, currentLink, RailResourceManager.ANY_TRACK_NON_BLOCKING, position); - @Override - public List blockRailSegment(double time, MobsimDriverAgent driver, List segment) { + if (reserveDist == RailResource.NO_RESERVATION) + return new DispositionResponse(0, 0, null); + + // current link only partial reserved + if (reserveDist < currentLink.length) { + return new DispositionResponse(reserveDist - position.getHeadPosition(), 0, null); + } - List blocked = new ArrayList<>(); + // remove already used distance + reserveDist -= position.getHeadPosition(); + boolean stop = false; // Iterate all links that need to be blocked for (RailLink link : segment) { - // Check if single link can be reserved - if (resources.tryBlockTrack(time, driver, link)) { - blocked.add(link); - } else + // first link does not need to be blocked again + if (link == currentLink) + continue; + + dist = resources.tryBlockLink(time, link, RailResourceManager.ANY_TRACK_NON_BLOCKING, position); + + if (dist == RailResource.NO_RESERVATION) { + stop = true; + break; + } + + // partial reservation + reserveDist += dist; + + // If the link is not fully reserved then stop + // there might be a better advised speed (speed of train in-front) + if (dist < link.getLength()) { + stop = true; break; + } } - return blocked; + return new DispositionResponse(reserveDist, stop ? 0 : Double.POSITIVE_INFINITY, detour); } + private Detour checkDetour(double time, List segment, TrainPosition position) { + + if (position.getPt() != null && considerReRouting(segment, resources.getLink(position.getHeadLink()))) { + + int start = -1; + int end = -1; + RailLink entry = null; + RailLink exit = null; + + for (int i = Math.max(0, position.getRouteIndex() - 1); i < position.getRouteSize(); i++) { + RailLink l = position.getRoute(i); + + if (l.isEntryLink()) { + entry = l; + start = i; + } else if (start > -1 && resources.isBlockedBy(l, position)) { + // check if any link beyond entry is already blocked + // if that is the case re-route is not possible anymore + break; + } else if (start > -1 && l.isExitLink()) { + exit = l; + end = i; + break; + } + } + + // there might be no exit link if this is the end of the route + // exit will be set to null if re-route is too late + // network could be wrong as well, but hard to verify + if (exit != null) { + + List subRoute = position.getRoute(start + 1, end); + + List newRoute = reroute(time, subRoute, position, entry, exit); + + if (newRoute != null) + return new Detour(start + 1, end, entry.getLinkId(), exit.getLinkId(), newRoute); + } + } + + return null; + } + + private List reroute(double time, List subRoute, TrainPosition position, RailLink entry, RailLink exit) { + + // Only re-routes if the link segment is occupied + for (RailLink link : subRoute) { + if (!resources.isBlockedBy(link, position) && + !resources.hasCapacity(time, link.getLinkId(), RailResourceManager.ANY_TRACK_NON_BLOCKING, position)) { + + List detour = router.calcRoute(position, entry, exit); + + if (subRoute.equals(detour)) + return null; + + if (!resources.checkReroute(time, entry, exit, subRoute, detour, position)) + return null; + + return detour; + } + } + + return null; + } + + @Override public void unblockRailLink(double time, MobsimDriverAgent driver, RailLink link) { // put resource handling into release track - resources.releaseTrack(time, driver, link); + resources.releaseLink(time, link, driver); } } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java index eb178dc663c..3cd2cdeb11a 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java @@ -19,11 +19,10 @@ package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimTransitDriverAgent; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; import org.matsim.core.mobsim.framework.MobsimDriverAgent; -import javax.annotation.Nullable; import java.util.List; /** @@ -37,27 +36,16 @@ public interface TrainDisposition { void onDeparture(double time, MobsimDriverAgent driver, List route); /** - * Called by the driver when an entry link is within stop distance. - * - * @param segment the original link segment between entry and exit - * @return the route change, or null if nothing should be changed + * Request the next segment to be reserved. + * @param time current time + * @param position position information + * @param dist distance in meter the train is requesting */ - @Nullable - default List requestRoute(double time, RailsimTransitDriverAgent driver, List segment, - RailLink entry, RailLink exit) { - return null; - } + DispositionResponse requestNextSegment(double time, TrainPosition position, double dist); - /** - * Train is reaching the given links and is trying to block them. - * - * @return links of the request that are exclusively blocked for the train. - */ - List blockRailSegment(double time, MobsimDriverAgent driver, List segment); /** * Inform the resource manager that the train has passed a link that can now be unblocked. - * This needs to be called after track states have been updated already. */ void unblockRailLink(double time, MobsimDriverAgent driver, RailLink link); diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/FixedBlockResource.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/FixedBlockResource.java new file mode 100644 index 00000000000..eefec6b7a29 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/FixedBlockResource.java @@ -0,0 +1,203 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import java.util.*; + + +/** + * Fixed block where each track is exclusively reserved by one agent. + */ +final class FixedBlockResource implements RailResourceInternal { + + private final Id id; + + /** + * Links belonging to this resource. + */ + private final List links; + + /** + * Reservations per link and per track. + */ + private final Map tracks; + + /** + * Tracks drivers that have at least one reservation on any link os this resource. + * The link a driver used to enter the resource is stored for each. + */ + private final Map reservations; + + /** + * Maximum number of reservations. + */ + private final int capacity; + + FixedBlockResource(Id id, List links) { + this.id = id; + this.links = links; + this.capacity = links.stream().mapToInt(l -> l.tracks).min().orElseThrow(); + this.reservations = new HashMap<>(capacity); + this.tracks = new HashMap<>(links.size()); + + for (RailLink link : links) { + tracks.put(link, new MobsimDriverAgent[link.tracks]); + } + } + + @Override + public Id getId() { + return id; + } + + @Override + public ResourceType getType() { + return ResourceType.fixedBlock; + } + + @Override + public List getLinks() { + return links; + } + + @Override + public int getTotalCapacity() { + return capacity; + } + + @Override + public ResourceState getState(RailLink link) { + if (reservations.isEmpty()) + return ResourceState.EMPTY; + if (reservations.size() < capacity) + return ResourceState.IN_USE; + + return ResourceState.EXHAUSTED; + } + + @Override + public boolean hasCapacity(double time, RailLink link, int track, TrainPosition position) { + + if (track >= 0) + throw new IllegalArgumentException("Fixed block does not support choosing individual tracks."); + + // there is no need to check the individual tracks here + if (reservations.containsKey(position.getDriver())) { + return true; + } + + // Can return any free track + if (track == RailResourceManager.ANY_TRACK) + return reservations.size() < capacity; + + assert track == RailResourceManager.ANY_TRACK_NON_BLOCKING; + + boolean samePresent = false; + for (RailLink enterLink : reservations.values()) { + if (enterLink == link) { + samePresent = true; + break; + } + } + + // if same direction is already used, one extra track needs to be available + return samePresent ? reservations.size() < capacity - 1 : reservations.size() < capacity; + } + + + @Override + public double getReservedDist(RailLink link, TrainPosition position) { + MobsimDriverAgent[] state = tracks.get(link); + for (MobsimDriverAgent reserved : state) { + if (reserved == position.getDriver()) { + return link.length; + } + } + + return RailResourceInternal.NO_RESERVATION; + } + + @Override + public double reserve(double time, RailLink link, int track, TrainPosition position) { + + if (track >= 0) + throw new IllegalArgumentException("Fixed block does not support choosing individual tracks."); + + assert position.getDriver() != null: "Driver must not be null."; + + // store the link the driver used to enter the resource + if (!reservations.containsKey(position.getDriver())) + reservations.put(position.getDriver(), link); + + if (reservations.size() > capacity) { + throw new IllegalStateException("Too many reservations. Capacity needs to be checked before calling reserve."); + } + + MobsimDriverAgent[] state = tracks.get(link); + for (int i = 0; i < state.length; i++) { + if (state[i] == null) { + state[i] = position.getDriver(); + return link.length; + } + } + + throw new IllegalStateException("No track was free."); + } + + @Override + public boolean release(RailLink link, MobsimDriverAgent driver) { + + MobsimDriverAgent[] state = tracks.get(link); + int track = -1; + for (int i = 0; i < state.length; i++) { + if (state[i] == driver) { + state[i] = null; + track = i; + break; + } + } + + if (track == -1) + throw new AssertionError("Driver " + driver + " has not reserved the track."); + + boolean allFree = true; + for (MobsimDriverAgent[] others : tracks.values()) { + for (MobsimDriverAgent other : others) { + if (other == driver) { + allFree = false; + break; + } + } + } + + // if the driver has no more reservations, remove it from the set + if (allFree) { + reservations.remove(driver); + return true; + } + + return false; + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/MovingBlockResource.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/MovingBlockResource.java new file mode 100644 index 00000000000..126c90b1f2f --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/MovingBlockResource.java @@ -0,0 +1,286 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimCalc; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import it.unimi.dsi.fastutil.ints.Int2DoubleArrayMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.*; + +/** + * A moving block, which allows multiple trains and needs to make sure that they don't collide. + */ +final class MovingBlockResource implements RailResourceInternal { + + private final Id id; + private final List links; + private final Track[] tracks; + private final int capacity; + + /** + * Links that are reserved by trains. + */ + private final Map> moving = new HashMap<>(); + + /** + * Map driver on segment to their incoming link. + */ + private final Map reservations; + + MovingBlockResource(Id id, List links) { + this.id = id; + this.links = links; + + this.capacity = links.stream().mapToInt(l -> l.tracks).min().orElseThrow(); + this.tracks = new Track[capacity]; + + for (int i = 0; i < capacity; i++) { + tracks[i] = new Track(); + } + + this.reservations = new HashMap<>(); + + for (RailLink link : links) { + moving.put(link, new HashSet<>()); + } + } + + @Override + public Id getId() { + return id; + } + + @Override + public ResourceType getType() { + return ResourceType.movingBlock; + } + + @Override + public List getLinks() { + return links; + } + + @Override + public int getTotalCapacity() { + return capacity; + } + + @Override + public ResourceState getState(RailLink link) { + // All links have the same state + int used = 0; + for (Track track : tracks) { + if (track.incoming != null) + used++; + } + + if (used == 0) + return ResourceState.EMPTY; + + return ResourceState.IN_USE; + } + + @Override + public boolean hasCapacity(double time, RailLink link, int track, TrainPosition position) { + + TrainEntry entry = reservations.get(position.getDriver()); + + // No entry yet + if (entry == null && track < 0) { + track = chooseTrack(link, track); + + // No track was available + if (track < 0) + return false; + } else if (entry != null && track < 0) { + track = entry.track; + } else if (entry != null && track != entry.track) { + throw new IllegalStateException("Train is already on a different track."); + } + + // entry should store track + double dist = checkReserve(time, link, track, entry, position); + + return dist > 0; + } + + @Override + public double getReservedDist(RailLink link, TrainPosition position) { + + TrainEntry entry = reservations.get(position.getDriver()); + if (entry == null) + return NO_RESERVATION; + + return entry.reservedDistance.getOrDefault(link.getLinkId().index(), NO_RESERVATION); + } + + @Override + public double reserve(double time, RailLink link, int track, TrainPosition position) { + + moving.get(link).add(position.getDriver()); + + // store trains incoming link + if (!reservations.containsKey(position.getDriver())) { + + if (track < 0) + track = chooseTrack(link, track); + + TrainEntry e = new TrainEntry(track, position, links.size()); + Track t = tracks[track]; + + reservations.put(position.getDriver(), e); + + // Mark this as used by this direction + if (t.incoming == null) + t.incoming = link; + + t.queue.add(e); + } + + TrainEntry self = reservations.get(position.getDriver()); + + double dist = checkReserve(time, link, self.track, self, position); + self.reservedDistance.put(link.getLinkId().index(), dist); + + return dist; + } + + /** + * Chooses a track for trains that don't have one yet. + */ + private int chooseTrack(RailLink link, int mode) { + int same = -1; + int available = -1; + int free = 0; + + assert mode < 0: "Can not give a specific track at this point"; + + for (int i = 0; i < tracks.length; i++) { + if (tracks[i].incoming == link) + same = i; + else if (tracks[i].incoming == null) { + available = i; + free++; + } + } + + // Always try to keep a track in reserve in non blocking mode + int newTrack = mode == RailResourceManager.ANY_TRACK_NON_BLOCKING ? 1 : 0; + + // If there is more than one free track, a new one is opened + if (same != -1) // there is a train in the same direction already + return free > newTrack ? available : same; + + return available; + } + + /** + * Compute the distance that can be reserved without actually reserving it. + */ + private double checkReserve(double time, RailLink link, int track, + @Nullable TrainEntry entry, TrainPosition position) { + + List queue = tracks[track].queue; + + // Approve whole link length for the first train in the queue + int idx = entry != null ? queue.indexOf(entry) : queue.size(); + if (idx == 0) { + return link.length; + } + + // The train in front of this one + TrainEntry inFront = queue.get(idx - 1); + + // tail is on the same link + if (Objects.equals(inFront.position.getTailLink(), link.getLinkId())) { + + // available distance can be at most the link length + return Math.min( + link.length, + inFront.position.getTailPosition() + RailsimCalc.projectedDistance(time, inFront.position) + ); + } + + // if no train is on it, whole link is available + if (moving.get(link).isEmpty()) + return link.length; + + // assume link is fully occupied + // this might not be 100% accurate, when states are not up-to-date + // should be fine for most use-cases + return 0; + } + + @Override + public boolean release(RailLink link, MobsimDriverAgent driver) { + + moving.get(link).remove(driver); + + boolean allFree = true; + for (Set others : moving.values()) { + if (others.contains(driver)) { + allFree = false; + break; + } + } + + assert reservations.containsKey(driver) : "Driver does not has a reservation."; + + // Remove this train from the incoming map + if (allFree) { + + TrainEntry entry = reservations.remove(driver); + + Track track = tracks[entry.track]; + track.queue.removeIf(p -> p.position.getDriver() == driver); + + // This track is completely free again + if (track.queue.isEmpty()) + track.incoming = null; + + return true; + } + + return false; + } + + /** + * Class to keep track of train positions and reservations. + */ + private static final class TrainEntry { + + final int track; + final TrainPosition position; + + /** + * Stores the reserved distance per link. (index of link id) + */ + final Int2DoubleMap reservedDistance; + + public TrainEntry(int track, TrainPosition position, int links) { + this.track = track; + this.position = position; + this.reservedDistance = new Int2DoubleArrayMap(links); + } + } + + /** + * One instance for each available track. + */ + private static final class Track { + + /** + * Link used to enter this track, which indicates the direction. + */ + private RailLink incoming; + + /** + * Train currently on this track. + */ + private final List queue = new ArrayList<>(); + + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailLink.java similarity index 56% rename from contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java rename to contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailLink.java index d59ae3801a2..06be3f5556e 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailLink.java @@ -17,7 +17,7 @@ * * * *********************************************************************** */ -package ch.sbb.matsim.contrib.railsim.qsimengine; +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; import ch.sbb.matsim.contrib.railsim.RailsimUtils; import org.matsim.api.core.v01.Id; @@ -25,52 +25,34 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.core.mobsim.framework.MobsimDriverAgent; -import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Objects; /** * Rail links which can has multiple tracks and corresponds to exactly one link. */ public final class RailLink implements HasLinkId { - private final Id id; - - /** - * States per track. - */ - private final TrackState[] state; - private final boolean isEntryLink; private final boolean isExitLink; - /** - * Drivers of each blocked track. - */ - private final MobsimDriverAgent[] blocked; - - final double length; - final double freeSpeed; - final double minimumHeadwayTime; + public final double length; + public final double minimumHeadwayTime; + public final double freeSpeed; + final int tracks; /** - * ID of the resource this link belongs to. + * Resource this link belongs to. */ - @Nullable - final Id resource; + RailResourceInternal resource; public RailLink(Link link) { - id = link.getId(); - state = new TrackState[RailsimUtils.getTrainCapacity(link)]; - Arrays.fill(state, TrackState.FREE); - blocked = new MobsimDriverAgent[state.length]; - length = link.getLength(); - freeSpeed = link.getFreespeed(); - minimumHeadwayTime = RailsimUtils.getMinimumHeadwayTime(link); - String resourceId = RailsimUtils.getResourceId(link); - resource = resourceId != null ? Id.create(resourceId, RailResource.class) : null; - isEntryLink = RailsimUtils.isEntryLink(link); - isExitLink = RailsimUtils.isExitLink(link); + this.id = link.getId(); + this.length = link.getLength(); + this.tracks = RailsimUtils.getTrainCapacity(link); + this.freeSpeed = link.getFreespeed(); + this.minimumHeadwayTime = RailsimUtils.getMinimumHeadwayTime(link); + this.isEntryLink = RailsimUtils.isEntryLink(link); + this.isExitLink = RailsimUtils.isExitLink(link); } @Override @@ -78,16 +60,15 @@ public Id getLinkId() { return id; } - @Nullable - public Id getResourceId() { - return resource; + void setResource(RailResourceInternal resource) { + this.resource = resource; } /** - * Number of tracks on this link. + * Access to the underlying resource. */ - public int getNumberOfTracks() { - return state.length; + public RailResource getResource() { + return resource; } /** @@ -97,57 +78,6 @@ public double getAllowedFreespeed(MobsimDriverAgent driver) { return Math.min(freeSpeed, driver.getVehicle().getVehicle().getType().getMaximumVelocity()); } - /** - * Check if driver has already reserved this link. - */ - public boolean isBlockedBy(MobsimDriverAgent driver) { - for (MobsimDriverAgent reservation : blocked) { - if (reservation == driver) - return true; - } - return false; - } - - /** - * Whether at least one track is free. - */ - boolean hasFreeTrack() { - for (TrackState trackState : state) { - if (trackState == TrackState.FREE) - return true; - } - return false; - } - - /** - * Block a track that was previously reserved. - */ - int blockTrack(MobsimDriverAgent driver) { - for (int i = 0; i < state.length; i++) { - if (state[i] == TrackState.FREE) { - blocked[i] = driver; - state[i] = TrackState.BLOCKED; - return i; - } - } - throw new IllegalStateException("No track was free."); - } - - /** - * Release a non-free track to be free again. - */ - int releaseTrack(MobsimDriverAgent driver) { - for (int i = 0; i < state.length; i++) { - if (blocked[i] == driver) { - state[i] = TrackState.FREE; - blocked[i] = null; - return i; - } - } - throw new AssertionError("Driver " + driver + " has not reserved the track."); - } - - /** * Entry link of a station relevant for re-routing. */ @@ -162,6 +92,13 @@ public boolean isExitLink() { return isExitLink; } + /** + * Length in meter. + */ + public double getLength() { + return length; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResource.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResource.java new file mode 100644 index 00000000000..409b8e39e8a --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResource.java @@ -0,0 +1,37 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import org.matsim.api.core.v01.Identifiable; + +import java.util.List; + +/** + * A resource representing multiple {@link RailLink}. + */ +public interface RailResource extends Identifiable { + + /** + * Constant returned if no reservation is present. + */ + double NO_RESERVATION = -1; + + /** + * Type of resource. + */ + ResourceType getType(); + + /** + * The links that are represented by this resource. + */ + List getLinks(); + + /** + * The total capacity of this resource, i.e number of tracks. + */ + int getTotalCapacity(); + + /** + * State of a specific link. + */ + ResourceState getState(RailLink link); + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceInternal.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceInternal.java new file mode 100644 index 00000000000..96a0e63132a --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceInternal.java @@ -0,0 +1,36 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +/** + * Internal rail resource interface, which allows modifying the state. + * Disposition should only interact with resources via {@link RailResourceManager}. + */ +interface RailResourceInternal extends RailResource { + + /** + * Whether an agent is able to block this resource. + */ + boolean hasCapacity(double time, RailLink link, int track, TrainPosition position); + + /** + * The reserved distance on this link for an agent. Returns 0 if the agent has no reservation. + * @return the reserved distance, -1 if there is no reservation. A reservation with 0 dist could be possible. + */ + double getReservedDist(RailLink link, TrainPosition position); + + /** + * Reserves this resource for the given agent. + * + * @return the reserved distance on this link + */ + double reserve(double time, RailLink link, int track, TrainPosition position); + + /** + * Releases the link on this resource for the given agent. + * @return if the resource was released, i.e. no more links are occupied. + */ + boolean release(RailLink link, MobsimDriverAgent driver); + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceManager.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceManager.java new file mode 100644 index 00000000000..9a04894bf00 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/RailResourceManager.java @@ -0,0 +1,215 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.DeadlockAvoidance; +import jakarta.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.core.mobsim.qsim.QSim; + +import java.util.*; + +/** + * Class responsible for managing and blocking resources and segments of links. + */ +public final class RailResourceManager { + + /** + * Constant that can be used as track number to indicate that any track is allowed. + */ + public static final int ANY_TRACK = -1; + + /** + * Constant to indicate than any track is allowed as long as the opposing direction is not blocked. + */ + public static final int ANY_TRACK_NON_BLOCKING = -2; + + private final EventsManager eventsManager; + + /** + * Rail links. + */ + private final Map, RailLink> links; + + private final Map, RailResource> resources; + + private final DeadlockAvoidance dla; + + /** + * Retrieve source id of a link. + */ + public static Id getResourceId(Link link) { + String id = RailsimUtils.getResourceId(link); + if (id == null) + return Id.create(link.getId().toString(), RailResource.class); + else + return Id.create(id, RailResource.class); + } + + @Inject + public RailResourceManager(QSim qsim, DeadlockAvoidance dla) { + this(qsim.getEventsManager(), + ConfigUtils.addOrGetModule(qsim.getScenario().getConfig(), RailsimConfigGroup.class), + qsim.getScenario().getNetwork(), + dla); + } + + /** + * Construct resources from network. + */ + public RailResourceManager(EventsManager eventsManager, RailsimConfigGroup config, + Network network, DeadlockAvoidance dla) { + this.eventsManager = eventsManager; + this.dla = dla; + this.links = new IdMap<>(Link.class, network.getLinks().size()); + + // Mapping for resources to be created + Map, List> resourceMapping = new HashMap<>(); + + Set modes = config.getNetworkModes(); + for (Map.Entry, ? extends Link> e : network.getLinks().entrySet()) { + if (e.getValue().getAllowedModes().stream().anyMatch(modes::contains)) { + + RailLink link = new RailLink(e.getValue()); + resourceMapping.computeIfAbsent(getResourceId(e.getValue()), k -> new ArrayList<>()).add(link); + this.links.put(e.getKey(), link); + } + } + + resources = new IdMap<>(RailResource.class, resourceMapping.size()); + for (Map.Entry, List> e : resourceMapping.entrySet()) { + + // use type of the first link + RailLink link = e.getValue().get(0); + ResourceType type = RailsimUtils.getResourceType(network.getLinks().get(link.getLinkId())); + + RailResourceInternal r = switch (type) { + case fixedBlock -> new FixedBlockResource(e.getKey(), e.getValue()); + case movingBlock -> new MovingBlockResource(e.getKey(), e.getValue()); + }; + + e.getValue().forEach(l -> l.setResource(r)); + + resources.put(e.getKey(), r); + } + + dla.initResources(resources); + } + + /** + * All available resources. + */ + public Collection getResources() { + return resources.values(); + } + + /** + * Get single link that belongs to an id. + */ + public RailLink getLink(Id id) { + return links.get(id); + } + + + /** + * Try to block a track and the underlying resource and return the allowed distance. + */ + public double tryBlockLink(double time, RailLink link, int track, TrainPosition position) { + + double reservedDist = link.resource.getReservedDist(link, position); + + // return only fully reserved links + if (reservedDist != RailResourceInternal.NO_RESERVATION && reservedDist == link.length) { + return reservedDist; + } + + if (link.resource.hasCapacity(time, link, track, position)) { + + if (!dla.checkLink(time, link, position)) { + assert reservedDist == RailResourceInternal.NO_RESERVATION : "Link should not be reserved already."; + + return RailResourceInternal.NO_RESERVATION; + } + + double dist = link.resource.reserve(time, link, track, position); + eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), + position.getDriver().getVehicle().getId(), link.resource.getState(link))); + + dla.onReserve(time, link.resource, position); + + assert dist >= 0 : "Reserved distance must be equal or larger than 0."; + + return dist; + } + + // may be previously reserved dist, or no reservation + return reservedDist; + } + + /** + * Checks whether a link or underlying resource has remaining capacity. + */ + public boolean hasCapacity(double time, Id link, int track, TrainPosition position) { + RailLink l = getLink(link); + return l.resource.hasCapacity(time, l, track, position); + } + + /** + * Whether a driver already reserved a link. + */ + public boolean isBlockedBy(RailLink link, TrainPosition position) { + return link.resource.getReservedDist(link, position) > RailResourceInternal.NO_RESERVATION; + } + + /** + * Release a non-free track to be free again. + */ + public void releaseLink(double time, RailLink link, MobsimDriverAgent driver) { + + boolean release = link.resource.release(link, driver); + + dla.onReleaseLink(time, link, driver); + + if (release) { + dla.onRelease(time, link.resource, driver); + } + + eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), driver.getVehicle().getId(), + link.resource.getState(link))); + } + + /** + * Check if a re-route is allowed. + * @see DeadlockAvoidance#checkReroute(double, RailLink, RailLink, List, List, TrainPosition) + */ + public boolean checkReroute(double time, RailLink start, RailLink end, List subRoute, List detour, TrainPosition position) { + return dla.checkReroute(time, start, end, subRoute, detour, position); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceState.java similarity index 87% rename from contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java rename to contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceState.java index 976d2d2e9ad..cff904dffbf 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceState.java @@ -17,16 +17,15 @@ * * * *********************************************************************** */ -package ch.sbb.matsim.contrib.railsim.qsimengine; +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; /** - * Current state of a track. + * Current state resource. */ -public enum TrackState { - FREE, +public enum ResourceState { + EMPTY, - /** - * Blocked tracks that are exclusively available for trains. - */ - BLOCKED + IN_USE, + + EXHAUSTED, } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceType.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceType.java new file mode 100644 index 00000000000..409b0514551 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ResourceType.java @@ -0,0 +1,15 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +/** + * Describes the type of resource. + */ +public enum ResourceType { + + fixedBlock, + + /** + * A block that allows driving similar to ETCS Level 2 without signals. + */ + movingBlock + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java index bb25b2ac3b2..889d80d84fb 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java @@ -19,14 +19,16 @@ package ch.sbb.matsim.contrib.railsim.qsimengine.router; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; -import ch.sbb.matsim.contrib.railsim.qsimengine.RailResourceManager; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; import jakarta.inject.Inject; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.Person; import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.router.DijkstraFactory; import org.matsim.core.router.speedy.SpeedyALTFactory; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.TravelDisutility; @@ -44,6 +46,8 @@ public final class TrainRouter { private final RailResourceManager resources; private final LeastCostPathCalculator lpc; + private final DisUtility disutility = new DisUtility(); + @Inject public TrainRouter(QSim qsim, RailResourceManager resources) { this(qsim.getScenario().getNetwork(), resources); @@ -54,26 +58,36 @@ public TrainRouter(Network network, RailResourceManager resources) { this.resources = resources; // uses the full network, which should not be slower than filtered network as long as dijkstra is used - this.lpc = new SpeedyALTFactory().createPathCalculator(network, new DisUtility(), new FreeSpeedTravelTime()); + this.lpc = new DijkstraFactory().createPathCalculator(network, disutility, new FreeSpeedTravelTime()); } /** - * Calculate the shortest path between two links. + * Calculate the shortest path between two links. This method is not thread-safe, because of mutable state in the disutility. */ - public List calcRoute(RailLink from, RailLink to) { + public List calcRoute(TrainPosition position, RailLink from, RailLink to) { Node fromNode = network.getLinks().get(from.getLinkId()).getToNode(); Node toNode = network.getLinks().get(to.getLinkId()).getFromNode(); + disutility.setPosition(position); + LeastCostPathCalculator.Path path = lpc.calcLeastCostPath(fromNode, toNode, 0, null, null); return path.links.stream().map(l -> resources.getLink(l.getId())).toList(); } private final class DisUtility implements TravelDisutility { + + private TrainPosition position; + + public void setPosition(TrainPosition position) { + this.position = position; + } + @Override public double getLinkTravelDisutility(Link link, double time, Person person, Vehicle vehicle) { - return resources.hasCapacity(link.getId()) ? 0 : 1; + // only works with fixed block + return resources.hasCapacity(time, link.getId(), RailResourceManager.ANY_TRACK, position) ? 0 : 1; } @Override diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java index 726b229ba80..a6fb8b7be16 100644 --- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java @@ -45,6 +45,19 @@ public EventsAssert hasTrainState(String veh, double time, double headPosition, ); } + public EventsAssert hasTrainState(String veh, double time, String headLink, double speed) { + return haveAtLeast(1, + new Condition<>(event -> + (event instanceof RailsimTrainStateEvent ev) + && ev.getVehicleId().toString().equals(veh) + && FuzzyUtils.equals(ev.getTime(), time) + && ev.getHeadLink().toString().equals(headLink) + && FuzzyUtils.equals(ev.getSpeed(), speed), + String.format("event with veh %s time %.0f link: %s speed: %.2f", veh, time, headLink, speed)) + ); + } + + @Override protected EventAssert toAssert(Event value, String description) { return null; diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java index bf7b63411e9..32e77a080c2 100644 --- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java @@ -147,4 +147,24 @@ void testCalcTargetSpeedForStop() { .isCloseTo(1000, Offset.offset(0.0001)); } + + @Test + public void testCalcRequiredTime() { + + TrainState state = new TrainState(null, null, 0, null, null); + + state.speed = 0; + state.acceleration = 0; + + assertThat(RailsimCalc.calcRequiredTime(state, 1000)) + .isEqualTo(Double.POSITIVE_INFINITY); + + state.speed = 10; + state.acceleration = -1; + + // Comes to stop after 10s + assertThat(RailsimCalc.calcRequiredTime(state, 10000)) + .isEqualTo(10); + + } } diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDeadlockTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDeadlockTest.java new file mode 100644 index 00000000000..ddd998ee3f1 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDeadlockTest.java @@ -0,0 +1,229 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.DeadlockAvoidance; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.NoDeadlockAvoidance; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.SimpleDeadlockAvoidance; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.ResourceType; +import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.testcases.MatsimTestUtils; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.Set; +import java.util.function.Consumer; + +public class RailsimDeadlockTest { + + @RegisterExtension + public MatsimTestUtils utils = new MatsimTestUtils(); + + private EventsManager eventsManager; + private RailsimTestUtils.EventCollector collector; + + @BeforeEach + public void setUp() { + eventsManager = EventsUtils.createEventsManager(); + collector = new RailsimTestUtils.EventCollector(); + + eventsManager.addHandler(collector); + eventsManager.initProcessing(); + } + + private RailsimTestUtils.Holder getTestEngine(String network, DeadlockAvoidance dla, @Nullable Consumer f) { + Network net = NetworkUtils.readNetwork(new File(utils.getPackageInputDirectory(), network).toString()); + RailsimConfigGroup config = new RailsimConfigGroup(); + + collector.clear(); + + if (f != null) { + for (Link link : net.getLinks().values()) { + f.accept(link); + } + } + + RailResourceManager res = new RailResourceManager(eventsManager, config, net, dla); + TrainRouter router = new TrainRouter(net, res); + + return new RailsimTestUtils.Holder(new RailsimEngine(eventsManager, config, res, new SimpleDisposition(res, router)), net); + } + + @Test + public void deadlock() { + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new NoDeadlockAvoidance(), null); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "HG", "DC"); + + test.doSimStepUntil(250); +// test.debugFiles(collector, "deadLock"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 240, "y1y", 0) + .hasTrainState("regio2", 240, "zy", 0); + + } + + @Test + public void deadLockAvoidancePoint() { + + Set increased = Set.of("y1y", "yy1"); + + // This avoidance point is too small for these trains and will lead to a deadlock + // this test is also an example where the simple deadlock avoidance fails currently + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), l -> { + String id = l.getId().toString(); + if (increased.contains(id)) { + RailsimUtils.setTrainCapacity(l, 2); + } + }); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "HG", "CD"); + + test.doSimStepUntil(250); +// test.debugFiles(collector, "deadLockAvoidancePoint"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 240, "y1y", 0) + .hasTrainState("regio2", 240, "yy1", 0); + + } + + @Test + public void avoidancePoint() { + + // There is an avoidance point which is also large enough for the train + Set increased = Set.of("y1y", "yy1"); + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), l -> { + String id = l.getId().toString(); + if (increased.contains(id)) { + RailsimUtils.setTrainCapacity(l, 2); + l.setLength(500); + } + }); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "HG", "CD"); + + test.doSimStepUntil(800); + test.debugFiles(collector, "avoidancePoint"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 420, "EF", 0) + .hasTrainState("regio2", 410, "CD", 0); + + } + + @Test + public void tooSmall() { + + Set increased = Set.of("xB", "Bx", "yx", "xy", "AB", "BA"); + + // Increase some capacity, but one of the segment is too small for multiple trains + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), l -> { + String id = l.getId().toString(); + if (increased.contains(id)) + RailsimUtils.setTrainCapacity(l, 2); + + l.setFreespeed(5); + }); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1a", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1b", 0, "AB", "EF"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2a", 0, "HG", "CD"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2b", 0, "HG", "CD"); + + test.doSimStepUntil(1500); +// test.debugFiles(collector, "tooSmall"); + + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1a", 1110, "EF", 0) + .hasTrainState("regio1b", 1360, "EF", 0) + .hasTrainState("regio2a", 670, "CD", 0) + .hasTrainState("regio2b", 980, "CD", 0); + + } + + @Test + public void oneWay() { + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), null); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "HG", "CD"); + + test.doSimStepUntil(800); +// test.debugFiles(collector, "oneWay"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 350, "EF", 0) + .hasTrainState("regio2", 520, "CD", 0); + + } + + @Test + public void twoWay() { + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), l -> RailsimUtils.setTrainCapacity(l, 2)); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1a", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1b", 0, "AB", "EF"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2a", 0, "HG", "CD"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2b", 0, "HG", "CD"); + + test.doSimStepUntil(800); +// test.debugFiles(collector, "twoWay"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1a", 350, "EF", 0) + .hasTrainState("regio1b", 510, "EF", 0) + .hasTrainState("regio2a", 350, "CD", 0) + .hasTrainState("regio2b", 510, "CD", 0); + + } + + @Test + public void movingBlock() { + + // Keep start and end as fixed block + Set fixed = Set.of("AB", "BA", "CD", "DC", "EF", "FE", "HG", "GH"); + + RailsimTestUtils.Holder test = getTestEngine("networkDeadlocks.xml", new SimpleDeadlockAvoidance(), l -> { + String id = l.getId().toString(); + if (!fixed.contains(id)) { + RailsimUtils.setResourceType(l, ResourceType.movingBlock); + } + + l.setFreespeed(5); + }); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1a", 0, "AB", "EF"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1b", 0, "AB", "EF"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2a", 0, "HG", "CD"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2b", 0, "HG", "CD"); + + test.doSimStepUntil(1500); +// test.debugFiles(collector, "movingBlock"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1a", 670, "EF", 0) + .hasTrainState("regio1b", 790, "EF", 0) + .hasTrainState("regio2a", 1120, "CD", 0) + .hasTrainState("regio2b", 1243, "CD", 0); + + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineMovingBlockTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineMovingBlockTest.java new file mode 100644 index 00000000000..ae31cac7539 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineMovingBlockTest.java @@ -0,0 +1,187 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.NoDeadlockAvoidance; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; +import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.testcases.MatsimTestUtils; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.function.Consumer; + +/** + * Tests for moving block logic. + */ +public class RailsimEngineMovingBlockTest { + + @RegisterExtension + public MatsimTestUtils utils = new MatsimTestUtils(); + + private EventsManager eventsManager; + private RailsimTestUtils.EventCollector collector; + + @BeforeEach + public void setUp() { + eventsManager = EventsUtils.createEventsManager(); + collector = new RailsimTestUtils.EventCollector(); + + eventsManager.addHandler(collector); + eventsManager.initProcessing(); + } + + private RailsimTestUtils.Holder getTestEngine(String network, @Nullable Consumer f) { + Network net = NetworkUtils.readNetwork(new File(utils.getPackageInputDirectory(), network).toString()); + RailsimConfigGroup config = new RailsimConfigGroup(); + + collector.clear(); + + if (f != null) { + for (Link link : net.getLinks().values()) { + f.accept(link); + } + } + RailResourceManager res = new RailResourceManager(eventsManager, config, net, new NoDeadlockAvoidance()); + TrainRouter router = new TrainRouter(net, res); + + return new RailsimTestUtils.Holder(new RailsimEngine(eventsManager, config, res, new SimpleDisposition(res, router)), net); + } + + private RailsimTestUtils.Holder getTestEngine(String network) { + return getTestEngine(network, null); + } + + @Test + public void multipleTrains() { + + RailsimTestUtils.Holder test = getTestEngine("networkMovingBlocks.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 60, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 120, "l1-2", "l6-7"); + + test.doSimStepUntil(5_000); + +// test.debugFiles(collector, "movingBlock"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 2370, 200, 0) + .hasTrainState("cargo", 3268, 200, 0) + .hasTrainState("sprinter", 3345, 200, 0); + + test = getTestEngine("networkMovingBlocks.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 60, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 120, "l1-2", "l6-7"); + + test.doStateUpdatesUntil(5_000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 2370, 200, 0) + .hasTrainState("cargo", 3268, 200, 0) + .hasTrainState("sprinter", 3345, 200, 0); + + } + + @Test + public void opposite() { + + RailsimTestUtils.Holder test = getTestEngine("networkMovingBlocks.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter2", 400, "l6-5", "l2-1"); + + test.doSimStepUntil(2_000); +// test.debugFiles(collector, "opposite"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("sprinter2", 1368, 200, 0) + .hasTrainState("sprinter", 1559, 200, 0); + + test = getTestEngine("networkMovingBlocks.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter2", 400, "l6-5", "l2-1"); + + test.doStateUpdatesUntil(2_000, 5); +// test.debugFiles(collector, "opposite_detailed"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("sprinter2", 1368, 200, 0) + .hasTrainState("sprinter", 1559, 200, 0); + + } + + @Test + public void multiTrack() { + + // This test increased capacity + RailsimTestUtils.Holder test = getTestEngine("networkMovingBlocks.xml", l -> RailsimUtils.setTrainCapacity(l, 3)); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 60, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 120, "l1-2", "l6-7"); + + test.doSimStepUntil(5_000); +// test.debugFiles(collector, "multiTrack"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 2370, 200, 0) + .hasTrainState("cargo", 3268, 200, 0) + .hasTrainState("sprinter", 1984, 200, 0); + + test = getTestEngine("networkMovingBlocks.xml", l -> RailsimUtils.setTrainCapacity(l, 3)); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 60, "l1-2", "l6-7"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 120, "l1-2", "l6-7"); + + test.doStateUpdatesUntil(5_000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 2370, 200, 0) + .hasTrainState("cargo", 3268, 200, 0) + .hasTrainState("sprinter", 1984, 200, 0); + + } + + + @Test + public void mixed() { + + RailsimTestUtils.Holder test = getTestEngine("networkMixedTypes.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 0, "1-2", "20-21"); + + test.doSimStepUntil(2_000); + test.debugFiles(collector, "mixed"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 1418, 1000, 0) + .hasTrainState("cargo", 1241, 1000, 0) + .hasTrainState("sprinter", 1324, 1000, 0); + + test = getTestEngine("networkMixedTypes.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Sprinter, "sprinter", 0, "1-2", "20-21"); + + test.doStateUpdatesUntil(2_000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 1418, 1000, 0) + .hasTrainState("cargo", 1241, 1000, 0) + .hasTrainState("sprinter", 1324, 1000, 0); + + } + + +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java index 8b5aee8695e..6d0d67ad81b 100644 --- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java @@ -21,7 +21,9 @@ import ch.sbb.matsim.contrib.railsim.RailsimUtils; import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.deadlocks.NoDeadlockAvoidance; import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailResourceManager; import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -65,7 +67,7 @@ private RailsimTestUtils.Holder getTestEngine(String network, @Nullable Consumer f.accept(link); } } - RailResourceManager res = new RailResourceManager(eventsManager, config, net); + RailResourceManager res = new RailResourceManager(eventsManager, config, net, new NoDeadlockAvoidance()); TrainRouter router = new TrainRouter(net, res); return new RailsimTestUtils.Holder(new RailsimEngine(eventsManager, config, res, new SimpleDisposition(res, router)), net); diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java index 63f5249cef6..fa2e7744e69 100644 --- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java @@ -19,9 +19,11 @@ package ch.sbb.matsim.contrib.railsim.qsimengine; +import ch.sbb.matsim.contrib.railsim.RailsimUtils; import ch.sbb.matsim.contrib.railsim.analysis.RailsimCsvWriter; import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.resources.RailLink; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.network.Link; @@ -36,6 +38,8 @@ import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.matsim.utils.objectattributes.attributable.Attributes; +import org.matsim.utils.objectattributes.attributable.AttributesImpl; import org.matsim.vehicles.MatsimVehicleReader; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; @@ -97,10 +101,27 @@ public static void createDeparture(Holder test, TestVehicle type, String veh, do Mockito.when(mobVeh.getVehicle()).thenReturn(vehicle); Mockito.when(mobVeh.getId()).thenReturn(vehicleId); Mockito.when(driver.getVehicle()).thenReturn(mobVeh); + Mockito.when(driver.getId()).thenReturn(Id.createPersonId("driver_" + veh)); test.engine.handleDeparture(time, driver, route.getStartLinkId(), route); } + /** + * Create a RailLink for testing. + */ + public static RailLink createLink(double length, int trainCapacity) { + + Link link = Mockito.mock(Link.class, Answers.RETURNS_MOCKS); + + AttributesImpl attr = new AttributesImpl(); + Mockito.when(link.getAttributes()).thenReturn(attr); + Mockito.when(link.getLength()).thenReturn(length); + + RailsimUtils.setTrainCapacity(link, trainCapacity); + + return new RailLink(link); + } + /** * Collect events during testing */ diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ReserveResourceTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ReserveResourceTest.java new file mode 100644 index 00000000000..e8af037225a --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/resources/ReserveResourceTest.java @@ -0,0 +1,81 @@ +package ch.sbb.matsim.contrib.railsim.qsimengine.resources; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimTestUtils; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrainPosition; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.matsim.api.core.v01.Id; +import org.mockito.Mockito; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ReserveResourceTest { + + private TrainPosition d1 = Mockito.mock(TrainPosition.class, Mockito.RETURNS_DEEP_STUBS); + private TrainPosition d2 = Mockito.mock(TrainPosition.class, Mockito.RETURNS_DEEP_STUBS); + private TrainPosition d3 = Mockito.mock(TrainPosition.class, Mockito.RETURNS_DEEP_STUBS); + + static List params() { + RailLink l1 = RailsimTestUtils.createLink(10, 2); + RailLink l2 = RailsimTestUtils.createLink(10, 2); + return List.of( + Arguments.of(l1, l2, new FixedBlockResource(Id.create("r", RailResource.class), List.of(l1, l2))), + Arguments.of(l1, l2, new MovingBlockResource(Id.create("r", RailResource.class), List.of(l1, l2))) + ); + } + + @ParameterizedTest + @MethodSource("params") + public void anyTrack(RailLink l1, RailLink l2, RailResourceInternal r) { + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK, d1)) + .isTrue(); + + double reserved = r.reserve(0, l1, RailResourceManager.ANY_TRACK, d1); + assertThat(reserved).isEqualTo(10); + + reserved = r.reserve(0, l1, RailResourceManager.ANY_TRACK, d2); + assertThat(reserved).isEqualTo(10); + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK, d3)) + .isFalse(); + + r.release(l1, d1.getDriver()); + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK, d3)) + .isTrue(); + + } + + @ParameterizedTest + @MethodSource("params") + public void anyTrackNonBlocking(RailLink l1, RailLink l2, RailResourceInternal r) { + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK_NON_BLOCKING, d1)) + .isTrue(); + + double reserved = r.reserve(0, l1, RailResourceManager.ANY_TRACK_NON_BLOCKING, d1); + assertThat(reserved).isEqualTo(10); + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK_NON_BLOCKING, d2)) + .isFalse(); + + assertThat(r.hasCapacity(0, l2, RailResourceManager.ANY_TRACK_NON_BLOCKING, d2)) + .isTrue(); + + assertThat(r.reserve(0, l2, RailResourceManager.ANY_TRACK_NON_BLOCKING, d2)) + .isEqualTo(10); + + r.release(l1, d1.getDriver()); + + assertThat(r.hasCapacity(0, l1, RailResourceManager.ANY_TRACK_NON_BLOCKING, d3)) + .isTrue(); + + assertThat(r.hasCapacity(0, l2, RailResourceManager.ANY_TRACK_NON_BLOCKING, d1)) + .isFalse(); + + } +} diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml index 1ebe818b2c5..7125765a18b 100644 --- a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml @@ -61,12 +61,12 @@ - 2 + 3 - 2 + 3 true @@ -81,25 +81,25 @@ - 2 + 3 true - 2 + 3 - 2 + 3 true - 2 + 3 diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkDeadlocks.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkDeadlocks.xml new file mode 100644 index 00000000000..de496846f1b --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkDeadlocks.xml @@ -0,0 +1,171 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AB + + + + + AB + + + + + Bx + + + + + Bx + + + + + + CD + + + + + CD + + + + + Cx + + + + + Cx + + + + + + + xy + + + + + xy + + + + + + yy + + + + + yy + + + + + + yz + + + + + yz + + + + + + zE + + + + + zE + + + + + EF + + + + + EF + + + + + + Gz + + + + + Gz + + + + + GH + + + + + GH + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml index e1296d25fea..22cb9b5fb24 100644 --- a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml @@ -33,7 +33,7 @@ - 2 + 3 @@ -43,7 +43,7 @@ - 5 + 6 diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMixedTypes.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMixedTypes.xml new file mode 100644 index 00000000000..fe3c72e3416 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMixedTypes.xml @@ -0,0 +1,197 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 3 + l23 + movingBlock + + + + + 3 + l23 + movingBlock + + + + + 1 + l45 + movingBlock + + + + + 1 + l45 + movingBlock + + + + + 3 + l67 + + + + + 3 + l67 + + + + + 1 + l89 + movingBlock + + + + + + 1 + l89 + movingBlock + + + + + 1 + l89 + movingBlock + + + + + 1 + l89 + movingBlock + + + + + 1 + movingBlock + + + + + 1 + movingBlock + + + + + 1 + movingBlock + + + + + 1 + movingBlock + + + + + + 1 + + + + + 1 + + + + + 1 + movingBlock + + + + + + 1 + movingBlock + + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMovingBlocks.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMovingBlocks.xml new file mode 100644 index 00000000000..b4b9a8b5dd5 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMovingBlocks.xml @@ -0,0 +1,99 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + l23 + movingBlock + + + + + l23 + movingBlock + + + + + + + + + + + + l45 + movingBlock + + + + + l45 + movingBlock + + + + + + l56 + movingBlock + + + + + l56 + movingBlock + + + + + + + + + + + + diff --git a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/CalculateSkimMatrices.java b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/CalculateSkimMatrices.java index c3ba439a7c7..2684832db0a 100644 --- a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/CalculateSkimMatrices.java +++ b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/CalculateSkimMatrices.java @@ -70,7 +70,7 @@ import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; import org.matsim.core.trafficmonitoring.TravelTimeCalculator; import org.matsim.core.utils.collections.CollectionUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.matsim.core.utils.misc.Counter; import org.matsim.core.utils.misc.StringUtils; @@ -233,7 +233,7 @@ public final void calculateSamplingPointsPerZoneFromNetwork(String networkFilena public final void selectSamplingPoints(List locations, int numberOfPointsPerZone, String zonesShapeFilename, String zonesIdAttributeName, Random r) throws IOException { log.info("loading zones from " + zonesShapeFilename); - Collection zones = new ShapeFileReader().readFileAndInitialize(zonesShapeFilename); + Collection zones = new GeoFileReader().readFileAndInitialize(zonesShapeFilename); SpatialIndex zonesQt = new Quadtree(); for (SimpleFeature zone : zones) { Envelope envelope = ((Geometry) (zone.getDefaultGeometry())).getEnvelopeInternal(); diff --git a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/MatricesToXY.java b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/MatricesToXY.java index 1facd37be1e..405cdb0be00 100644 --- a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/MatricesToXY.java +++ b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/analysis/skims/MatricesToXY.java @@ -29,7 +29,7 @@ import org.apache.logging.log4j.Logger; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.opengis.feature.simple.SimpleFeature; @@ -52,7 +52,7 @@ public static void main(String[] args) throws IOException { String xyCsvOutputFilename = args[3]; // path to the csv-file to be written, e.g. /path/to/skim-data.csv log.info("loading zones from " + zonesShapeFilename); - Collection zones = new ShapeFileReader().readFileAndInitialize(zonesShapeFilename); + Collection zones = new GeoFileReader().readFileAndInitialize(zonesShapeFilename); Map zonesById = new HashMap<>(); for (SimpleFeature zone : zones) { String zoneId = zone.getAttribute(zonesIdAttributeName).toString(); diff --git a/contribs/shared_mobility/src/main/java/org/matsim/contrib/shared_mobility/utils/WriteStationShapefile.java b/contribs/shared_mobility/src/main/java/org/matsim/contrib/shared_mobility/utils/WriteStationShapefile.java index 9a870c93a3b..5edcf65eb29 100644 --- a/contribs/shared_mobility/src/main/java/org/matsim/contrib/shared_mobility/utils/WriteStationShapefile.java +++ b/contribs/shared_mobility/src/main/java/org/matsim/contrib/shared_mobility/utils/WriteStationShapefile.java @@ -16,7 +16,7 @@ import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PointFeatureFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -57,6 +57,6 @@ static public void main(String[] args) throws ConfigurationException { null)); } - ShapeFileWriter.writeGeometries(features, cmd.getOptionStrict("output-path")); + GeoFileWriter.writeGeometries(features, cmd.getOptionStrict("output-path")); } } diff --git a/contribs/simwrapper/pom.xml b/contribs/simwrapper/pom.xml index 26bff793134..b7557529aec 100644 --- a/contribs/simwrapper/pom.xml +++ b/contribs/simwrapper/pom.xml @@ -38,6 +38,18 @@ tech.tablesaw tablesaw-jsplot + + + io.pebbletemplates + pebble + + + + + + io.pebbletemplates + pebble + 3.1.6 diff --git a/contribs/small-scale-traffic-generation/README.md b/contribs/small-scale-traffic-generation/README.md index 19a22421db1..23eda473092 100644 --- a/contribs/small-scale-traffic-generation/README.md +++ b/contribs/small-scale-traffic-generation/README.md @@ -1,3 +1,44 @@ # small scale traffic generation -construction site; will contain small scale traffic generation code +This provides a tool to generate small-scale commercial traffic. + +The tool is based on : +- IVV. Kleinrรคumige Wirtschaftsverkehrsmodelle. Endbericht zum Forschungsprojekt FE-Nr. 70.0689/2002/ im Auftrag des Bundesministeriums fรผr Verkehr, Bau und Wohnungswesen. 2005. Download: +[https://daten.clearingstelle-verkehr.de/194/](https://daten.clearingstelle-verkehr.de/194/). + +The description of the implementation in MATSim is given in the following paper : +- R. Ewert and K. Nagel, โ€œAgentenbasierte Modellierung des kleinrรคumigen Wirtschaftsverkehrs,โ€ 2024. Accepted for publication at HEUREKA Conference 2024. + +## Model types +The tool provides two different model types of small-scale commercial traffic. Therefore, you can select between the following options: +- **commercialPersonTraffic**: This model contains the personal commercial traffic. This traffic has the objective to transport persons from one location to another. +- **goodsTraffic**: This model contains the goods traffic. This traffic has the objective to transport goods from one location to another. + - !! The long distance freight traffic is not included in this model. !! Therefore, see application contrib: [link](https://github.com/matsim-org/matsim-libs/tree/master/contribs/application/src/main/java/org/matsim/application/prepare/freight) +- **completeSmallScaleCommercialTraffic**: This model contains both, the personal commercial traffic and the goods traffic. + + +## Input data +- The generation model uses only open data. + - zone shape file: + - contains the zones for each region + - should contain columns: `areaID`, `region` + - structure data: + - *dataDistributionPerZone.csv* + - contains numbers of inhabitants and employees per sector for each `region` + - this file should be located next to the config file + - OSM data: + - Landuse + - should contain the following columns: `fclass` + - Buildings + - should contain the following columns: `type`, `levels`, `area` +- Remarks: + - The zone shape file and the structure data should have the same regions. + +## Usage +For generating the traffic, the following steps are necessary: +- get necessary input data +- run the tool by calling the main class `GenerateSmallScaleCommercialTrafficDemand` + +## Example +An example is given as a test. See test `RunGenerateSmallScaleCommercialTrafficTest` and the mentioned example input data. + diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java similarity index 100% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java similarity index 99% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java index 46ec4a745b6..b15a3d7d99c 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java +++ b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java @@ -39,6 +39,7 @@ import org.matsim.application.options.ShpOptions; import org.matsim.application.options.ShpOptions.Index; import org.matsim.core.config.consistency.UnmaterializedConfigGroupChecker; +import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.scenario.ProjectionUtils; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; @@ -227,8 +228,6 @@ public Integer call() throws Exception { } Path inputDataDirectory = Path.of(config.getContext().toURI()).getParent(); - ShpOptions shpZones = new ShpOptions(shapeFileZonePath, shapeCRS, StandardCharsets.UTF_8); - indexZones = SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS); indexBuildings = SmallScaleCommercialTrafficUtils.getIndexBuildings(shapeFileBuildingsPath, shapeCRS); indexLanduse = SmallScaleCommercialTrafficUtils.getIndexLanduse(shapeFileLandusePath, shapeCRS); @@ -242,14 +241,14 @@ public Integer call() throws Exception { switch (usedSmallScaleCommercialTrafficType) { case commercialPersonTraffic, goodsTraffic -> - createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, + createCarriersAndDemand(output, scenario, resultingDataPerZone, regionLinksMap, usedSmallScaleCommercialTrafficType.toString(), includeExistingModels); case completeSmallScaleCommercialTraffic -> { - createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "commercialPersonTraffic", + createCarriersAndDemand(output, scenario, resultingDataPerZone, regionLinksMap, "commercialPersonTraffic", includeExistingModels); includeExistingModels = false; // because already included in the step before - createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "goodsTraffic", + createCarriersAndDemand(output, scenario, resultingDataPerZone, regionLinksMap, "goodsTraffic", includeExistingModels); } default -> throw new RuntimeException("No traffic type selected."); @@ -423,7 +422,7 @@ private void solveSeparatedVRPs(Scenario originalScenario, Map> resultingDataPerZone, Map, Link>> regionLinksMap, String smallScaleCommercialTrafficType, boolean includeExistingModels) throws Exception { @@ -450,7 +449,7 @@ else if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); } final TripDistributionMatrix odMatrix = createTripDistribution(trafficVolumePerTypeAndZone_start, - trafficVolumePerTypeAndZone_stop, shpZones, smallScaleCommercialTrafficType, scenario, output, regionLinksMap); + trafficVolumePerTypeAndZone_stop, smallScaleCommercialTrafficType, scenario, output, regionLinksMap); createCarriers(scenario, odMatrix, resultingDataPerZone, smallScaleCommercialTrafficType, regionLinksMap); } @@ -491,8 +490,8 @@ private Config readAndCheckConfig(Path configPath, String modelName, String samp new OutputDirectoryHierarchy(config.controller().getOutputDirectory(), config.controller().getRunId(), config.controller().getOverwriteFileSetting(), ControllerConfigGroup.CompressionType.gzip); new File(Path.of(config.controller().getOutputDirectory()).resolve("calculatedData").toString()).mkdir(); - rnd = new Random(config.global().getRandomSeed()); - + MatsimRandom.getRandom().setSeed(config.global().getRandomSeed()); + rnd = MatsimRandom.getRandom(); if (config.network().getInputFile() == null) throw new Exception("No network file in config"); if (config.global().getCoordinateSystem() == null) @@ -946,17 +945,17 @@ private static void findNearestLinkForZonesWithoutLinks(Network networkToChange, */ private TripDistributionMatrix createTripDistribution( Map> trafficVolume_start, - Map> trafficVolume_stop, ShpOptions shpZones, + Map> trafficVolume_stop, String smallScaleCommercialTrafficType, Scenario scenario, Path output, Map, Link>> regionLinksMap) throws Exception { - final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder - .newInstance(indexZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType).build(); - List listOfZones = new ArrayList<>(); + ArrayList listOfZones = new ArrayList<>(); trafficVolume_start.forEach((k, v) -> { if (!listOfZones.contains(k.getZone())) listOfZones.add(k.getZone()); }); + final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder + .newInstance(indexZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType, listOfZones).build(); Network network = scenario.getNetwork(); int count = 0; @@ -968,7 +967,7 @@ private TripDistributionMatrix createTripDistribution( String startZone = trafficVolumeKey.getZone(); String modeORvehType = trafficVolumeKey.getModeORvehType(); for (Integer purpose : trafficVolume_start.get(trafficVolumeKey).keySet()) { - Collections.shuffle(listOfZones); + Collections.shuffle(listOfZones, rnd); for (String stopZone : listOfZones) { odMatrix.setTripDistributionValue(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType, network, regionLinksMap, resistanceFactor); diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java similarity index 100% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java similarity index 100% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java similarity index 99% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java index 7e9e591eabc..a4633cfaf10 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java +++ b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java @@ -406,7 +406,7 @@ private static void reduceVolumeForOtherArea( Integer purpose, String volumeType, String originalZone) { ArrayList shuffledKeys = new ArrayList<>( trafficVolumePerTypeAndZone.keySet()); - Collections.shuffle(shuffledKeys); + Collections.shuffle(shuffledKeys, MatsimRandom.getRandom()); for (TrafficVolumeKey trafficVolumeKey : shuffledKeys) { if (trafficVolumeKey.getModeORvehType().equals(modeORvehType) && trafficVolumePerTypeAndZone.get(trafficVolumeKey).getDouble(purpose) > 0) { diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java similarity index 97% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java index 7d631a6b96d..ce06aa2b34c 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java +++ b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java @@ -33,6 +33,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions.Index; +import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.utils.io.IOUtils; import org.matsim.freight.carriers.jsprit.NetworkBasedTransportCosts; import org.matsim.smallScaleCommercialTrafficGeneration.TrafficVolumeGeneration.TrafficVolumeKey; @@ -60,7 +61,7 @@ public class TripDistributionMatrix { private static final Logger log = LogManager.getLogger(TripDistributionMatrix.class); private static final Joiner JOIN = Joiner.on("\t"); - private final ArrayList listOfZones = new ArrayList<>(); + private final ArrayList listOfZones; private final ArrayList listOfModesORvehTypes = new ArrayList<>(); private final ArrayList listOfPurposes = new ArrayList<>(); private final List zonesFeatures; @@ -238,23 +239,25 @@ public static class Builder { private final Map> trafficVolume_start; private final Map> trafficVolume_stop; private final String smallScaleCommercialTrafficType; + private final ArrayList listOfZones; public static Builder newInstance(Index indexZones, Map> trafficVolume_start, Map> trafficVolume_stop, - String smallScaleCommercialTrafficType) { - return new Builder(indexZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType); + String smallScaleCommercialTrafficType, ArrayList listOfZones) { + return new Builder(indexZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType, listOfZones); } private Builder(Index indexZones, Map> trafficVolume_start, Map> trafficVolume_stop, - String smallScaleCommercialTrafficType) { + String smallScaleCommercialTrafficType, ArrayList listOfZones) { super(); this.zonesFeatures = indexZones.getAllFeatures(); this.trafficVolume_start = trafficVolume_start; this.trafficVolume_stop = trafficVolume_stop; this.smallScaleCommercialTrafficType = smallScaleCommercialTrafficType; + this.listOfZones = new ArrayList<>(listOfZones); } public TripDistributionMatrix build() { @@ -267,6 +270,7 @@ private TripDistributionMatrix(Builder builder) { trafficVolume_start = builder.trafficVolume_start; trafficVolume_stop = builder.trafficVolume_stop; smallScaleCommercialTrafficType = builder.smallScaleCommercialTrafficType; + listOfZones = builder.listOfZones; } private final ConcurrentHashMap matrixCache = new ConcurrentHashMap<>(); @@ -309,10 +313,6 @@ void setTripDistributionValue(String startZone, String stopZone, String modeORve roundedVolume++; roundingError.get(stopZone).merge((modeORvehType + "_" + purpose), -1, Double::sum); } - if (!listOfZones.contains(startZone)) - listOfZones.add(startZone); - if (!listOfZones.contains(stopZone)) - listOfZones.add(stopZone); } else roundedVolume = 0; TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType); @@ -412,7 +412,7 @@ void clearRoundingError() { smallScaleCommercialTrafficType); } else { ArrayList shuffledZones = new ArrayList<>(getListOfZones()); - Collections.shuffle(shuffledZones); + Collections.shuffle(shuffledZones, MatsimRandom.getRandom()); for (String startZone : shuffledZones) { TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType); @@ -519,16 +519,6 @@ private GravityConstantKey makeGravityKey(String fromZone, String modeOrVehType, * @return listOfZones */ ArrayList getListOfZones() { -// int count = 0; -// if (listOfZones.isEmpty()) -// for (TripDistributionMatrixKey key : matrixCache.keySet()) { -// count++; -// System.out.println(count); -// if (!listOfZones.contains(key.getFromZone())) -// listOfZones.add(key.getFromZone()); -// if (!listOfZones.contains(key.getToZone())) -// listOfZones.add(key.getToZone()); -// } return listOfZones; } diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java similarity index 86% rename from contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java rename to contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java index f0aff20d0e1..8a2d7ef9407 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java +++ b/contribs/small-scale-traffic-generation/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java @@ -45,8 +45,8 @@ private void weightedProbability() { sum += l.getProbability(); cumulativeProbabilities.add(sum); } - //Generate a random number between 0 and 1 - double r = Math.random() * sum; + //Generate a random number between 0 and sum + double r = rnd.nextDouble(0.0, sum); //Select a value based on the cumulative probabilities String selectedLetter = ProbabilityDistribution.stream() //Find the first value whose cumulative probability is greater than the random number @@ -58,14 +58,6 @@ private void weightedProbability() { .filter(a -> a.getValue().equals(selectedLetter)) .findFirst() .ifPresent(l -> l.setExpectedCount(l.getExpectedCount() + 1)); - - //After 'testCount' loops, print out the number of times each value was selected and the percentage it represents -// for (ProbabilityForValue probabilityForValue : ProbabilityDistribution) { -// System.out.println(probabilityForValue.getValue() -// + " -> expected: " + probabilityForValue.getExpectedCount() -// + "(" + String.format("%.2f", (probabilityForValue.getExpectedCount() * Math.pow(anIntAsSum, -// -1)) * 100) + " %); prob: " + ((double)Math.round(probabilityForValue.getProbability() * 1000)/10) + "%"); -// } } public void writeResults(){ for (ProbabilityForValue probabilityForValue : ProbabilityDistribution) { diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java similarity index 97% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java index 6d562e6fbf1..be97112e74c 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java +++ b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java @@ -33,8 +33,6 @@ import java.util.List; import java.util.Map; -import static org.matsim.smallScaleCommercialTrafficGeneration.SCTUtils.*; - /** * @author Ricardo Ewert * @@ -58,7 +56,7 @@ void testReadOfDataDistributionPerZoneAndBuildingAnalysis() throws IOException { Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); Assertions.assertEquals(3, resultingDataPerZone.size(), MatsimTestUtils.EPSILON); @@ -145,10 +143,10 @@ void testReadOfDataDistributionPerZoneAndBuildingAnalysis() throws IOException { } // tests if the reading of the buildings works correctly - List buildingsFeatures = getIndexBuildings(inputDataDirectory).getAllFeatures(); + List buildingsFeatures = SCTUtils.getIndexBuildings(inputDataDirectory).getAllFeatures(); Assertions.assertEquals(31, buildingsFeatures.size(), MatsimTestUtils.EPSILON); LanduseBuildingAnalysis.analyzeBuildingType(buildingsFeatures, buildingsPerZone, - landuseCategoriesAndDataConnection, getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory)); + landuseCategoriesAndDataConnection, SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory)); Assertions.assertEquals(3, buildingsPerZone.size(), MatsimTestUtils.EPSILON); Assertions.assertTrue(buildingsPerZone.containsKey("testArea1_area1")); @@ -245,7 +243,7 @@ void testLanduseDistribution() throws IOException { Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); Assertions.assertEquals(3, resultingDataPerZone.size(), MatsimTestUtils.EPSILON); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java similarity index 78% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java index 62127e7e2f5..d88d2318fd1 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java +++ b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java @@ -25,14 +25,16 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; -import org.matsim.freight.carriers.FreightCarriersConfigGroup; -import org.matsim.freight.carriers.Carrier; -import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; import org.matsim.testcases.MatsimTestUtils; +import org.matsim.utils.eventsfilecomparison.EventsFileComparator; import java.io.File; import java.util.Objects; @@ -59,6 +61,7 @@ void testMainRunAndResults() { String buildingsShapeFileName = utils.getPackageInputDirectory() + "/shp/testBuildings.shp"; String landuseShapeFileName = utils.getPackageInputDirectory() + "/shp/testLanduse.shp"; String shapeCRS = "EPSG:4326"; + String resultPopulation = "testPopulation.xml.gz"; new GenerateSmallScaleCommercialTrafficDemand().execute( inputDataDirectory, @@ -71,36 +74,25 @@ void testMainRunAndResults() { "--zoneShapeFileName", zoneShapeFileName, "--buildingsShapeFileName", buildingsShapeFileName, "--landuseShapeFileName", landuseShapeFileName, - "--shapeCRS", shapeCRS); + "--shapeCRS", shapeCRS, + "--nameOutputPopulation", resultPopulation, + "--pathOutput", output); // test results of complete run before Config config = ConfigUtils.createConfig(); Scenario scenarioWOSolution = ScenarioUtils.createScenario(config); Scenario scenarioWSolution = ScenarioUtils.createScenario(config); - File outputFolder = Objects.requireNonNull(new File(output).listFiles())[0]; - Population population = null; - String carriersWOSolutionFileLocation = null; - String carriersWSolutionFileLocation = null; + Population population = PopulationUtils.readPopulation(utils.getOutputDirectory() + "testPopulation.xml.gz"); + String carriersWOSolutionFileLocation = utils.getOutputDirectory() + "test.output_CarrierDemand.xml"; + String carriersWSolutionFileLocation = utils.getOutputDirectory() + "test.output_CarrierDemandWithPlans.xml"; FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); - - for (File outputFiles : Objects.requireNonNull(Objects.requireNonNull(outputFolder.listFiles())[0].listFiles())) { - - if (outputFiles.getName().contains("pct_plans.xml.gz")) - population = PopulationUtils.readPopulation(outputFiles.getPath()); - if (outputFiles.getName().contains("output_CarrierDemand.xml")) - carriersWOSolutionFileLocation = outputFiles.getPath(); - if (outputFiles.getName().contains("output_CarrierDemandWithPlans.xml")) - carriersWSolutionFileLocation = outputFiles.getPath(); - if (outputFiles.getName().contains("output_carriersVehicleTypes.xml.gz")) - freightCarriersConfigGroup.setCarriersVehicleTypesFile(outputFiles.getPath()); - } + freightCarriersConfigGroup.setCarriersVehicleTypesFile(utils.getOutputDirectory() + "test.output_carriersVehicleTypes.xml.gz"); freightCarriersConfigGroup.setCarriersFile(carriersWOSolutionFileLocation); CarriersUtils.loadCarriersAccordingToFreightConfig(scenarioWOSolution); freightCarriersConfigGroup.setCarriersFile(carriersWSolutionFileLocation); CarriersUtils.loadCarriersAccordingToFreightConfig(scenarioWSolution); - assert population != null; for (Person person : population.getPersons().values()) { Assertions.assertNotNull(person.getSelectedPlan()); Assertions.assertTrue(person.getAttributes().getAsMap().containsKey("tourStartArea")); @@ -116,5 +108,18 @@ void testMainRunAndResults() { countedTours += carrier_withSolution.getSelectedPlan().getScheduledTours().size(); } Assertions.assertEquals(population.getPersons().size(), countedTours, 0); + + for (File caculatedFile : Objects.requireNonNull( + Objects.requireNonNull(new File(utils.getOutputDirectory() + "calculatedData").listFiles()))) { + MatsimTestUtils.assertEqualFilesLineByLine( + utils.getPackageInputDirectory() + "calculatedData/" + caculatedFile.getName(), + caculatedFile.getAbsolutePath()); + } + + // compare events + String expected = utils.getPackageInputDirectory() + "test.output_events.xml.gz" ; + String actual = utils.getOutputDirectory() + "test.output_events.xml.gz" ; + EventsFileComparator.Result result = EventsUtils.compareEventsFiles( expected, actual ); + Assertions.assertEquals( EventsFileComparator.Result.FILES_ARE_EQUAL, result ); } } diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SCTUtils.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SCTUtils.java similarity index 100% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SCTUtils.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SCTUtils.java diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java similarity index 97% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java index 3aedc210cde..61d3f759aac 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java +++ b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java @@ -25,7 +25,6 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; -import org.matsim.application.options.ShpOptions; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; @@ -34,9 +33,7 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java similarity index 98% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java index ef979568774..b1646548777 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java +++ b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java @@ -41,8 +41,6 @@ import java.nio.file.Path; import java.util.*; -import static org.matsim.smallScaleCommercialTrafficGeneration.SCTUtils.*; - /** * @author Ricardo Ewert * @@ -66,7 +64,7 @@ void testTrafficVolumeGenerationCommercialPersonTraffic() throws IOException { Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); String usedTrafficType = "commercialPersonTraffic"; @@ -192,7 +190,7 @@ void testTrafficVolumeGenerationGoodsTraffic() throws IOException { Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); String usedTrafficType = "goodsTraffic"; double sample = 1.; @@ -517,7 +515,7 @@ void testReducingDemandAfterAddingExistingScenarios_goods() throws Exception { Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); Map> trafficVolumePerTypeAndZone_start = TrafficVolumeGeneration .createTrafficVolume_start(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); @@ -525,7 +523,7 @@ void testReducingDemandAfterAddingExistingScenarios_goods() throws Exception { .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); Map, Link>> regionLinksMap = GenerateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, getZoneIndex(inputDataDirectory), buildingsPerZone); + .filterLinksForZones(scenario, SCTUtils.getZoneIndex(inputDataDirectory), buildingsPerZone); SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); @@ -672,7 +670,7 @@ void testReducingDemandAfterAddingExistingScenarios_commercialPersonTraffic() th Map> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration, - getIndexLanduse(inputDataDirectory), getZoneIndex(inputDataDirectory), getIndexBuildings(inputDataDirectory), buildingsPerZone); + SCTUtils.getIndexLanduse(inputDataDirectory), SCTUtils.getZoneIndex(inputDataDirectory), SCTUtils.getIndexBuildings(inputDataDirectory), buildingsPerZone); Map> trafficVolumePerTypeAndZone_start = TrafficVolumeGeneration .createTrafficVolume_start(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); @@ -680,7 +678,7 @@ void testReducingDemandAfterAddingExistingScenarios_commercialPersonTraffic() th .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); Map, Link>> regionLinksMap = GenerateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, getZoneIndex(inputDataDirectory), buildingsPerZone); + .filterLinksForZones(scenario, SCTUtils.getZoneIndex(inputDataDirectory), buildingsPerZone); SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java similarity index 97% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java rename to contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java index adc41f61191..0cf215c836c 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java +++ b/contribs/small-scale-traffic-generation/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java @@ -75,8 +75,10 @@ void testTripDistributionCommercialPersonTrafficTraffic() throws IOException { .createTrafficVolume_start(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); Map> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); + ArrayList listOfZones = new ArrayList<>( List.of("testArea1_area1", "testArea1_area2", "testArea2_area3")); final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder - .newInstance(getZoneIndex(inputDataDirectory), trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop, usedTrafficType).build(); + .newInstance(getZoneIndex(inputDataDirectory), trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop, usedTrafficType, + listOfZones).build(); Map, Link>> regionLinksMap = new HashMap<>(); regionLinksMap.put("testArea1_area1", new HashMap<>()); @@ -155,6 +157,8 @@ void testTripDistributionGoodsTraffic() throws IOException { ArrayList modesORvehTypes = new ArrayList( Arrays.asList("vehTyp1", "vehTyp2", "vehTyp3", "vehTyp4", "vehTyp5")); + ArrayList listOfZones = new ArrayList<>( List.of("testArea1_area1", "testArea1_area2", "testArea2_area3")); + TrafficVolumeGeneration.setInputParameters(usedTrafficType); Map> trafficVolumePerTypeAndZone_start = TrafficVolumeGeneration @@ -162,7 +166,8 @@ void testTripDistributionGoodsTraffic() throws IOException { Map> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder - .newInstance(getZoneIndex(inputDataDirectory), trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop, usedTrafficType).build(); + .newInstance(getZoneIndex(inputDataDirectory), trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop, usedTrafficType, + listOfZones).build(); Map, Link>> regionLinksMap = new HashMap<>(); regionLinksMap.put("testArea1_area1", new HashMap<>()); diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_startPerZone_10pt.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_startPerZone_10pt.csv new file mode 100644 index 00000000000..bfdf8e63f8c --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_startPerZone_10pt.csv @@ -0,0 +1,4 @@ +areaID mode/vehType 1 2 3 4 5 +testArea2_area3 total 1 3 8 6 9 +testArea1_area2 total 3 21 51 44 63 +testArea1_area1 total 3 12 28 18 25 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_stopPerZone_10pt.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_stopPerZone_10pt.csv new file mode 100644 index 00000000000..0c53286d73b --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/TrafficVolume_commercialPersonTraffic_stopPerZone_10pt.csv @@ -0,0 +1,4 @@ +areaID mode/vehType 1 2 3 4 5 +testArea2_area3 total 0 3 13 4 2 +testArea1_area2 total 1 20 86 25 10 +testArea1_area1 total 1 10 43 12 6 diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/dataDistributionPerZone.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/dataDistributionPerZone.csv similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/dataDistributionPerZone.csv rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/dataDistributionPerZone.csv diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose1.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose1.csv new file mode 100644 index 00000000000..38573c7f4c0 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose1.csv @@ -0,0 +1,4 @@ + testArea2_area3 testArea1_area2 testArea1_area1 +testArea2_area3 0 0 0 +testArea1_area2 0 1 0 +testArea1_area1 0 0 1 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose2.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose2.csv new file mode 100644 index 00000000000..30cf57e5fc3 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose2.csv @@ -0,0 +1,4 @@ + testArea2_area3 testArea1_area2 testArea1_area1 +testArea2_area3 0 0 0 +testArea1_area2 0 8 7 +testArea1_area1 1 5 3 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose3.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose3.csv new file mode 100644 index 00000000000..44108374c44 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose3.csv @@ -0,0 +1,4 @@ + testArea2_area3 testArea1_area2 testArea1_area1 +testArea2_area3 2 7 3 +testArea1_area2 7 52 25 +testArea1_area1 4 27 15 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose4.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose4.csv new file mode 100644 index 00000000000..6cebadd2f70 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose4.csv @@ -0,0 +1,4 @@ + testArea2_area3 testArea1_area2 testArea1_area1 +testArea2_area3 0 2 2 +testArea1_area2 2 17 7 +testArea1_area1 2 6 3 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose5.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose5.csv new file mode 100644 index 00000000000..5d7cb865fbf --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/calculatedData/odMatrix_commercialPersonTraffic_total_purpose5.csv @@ -0,0 +1,4 @@ + testArea2_area3 testArea1_area2 testArea1_area1 +testArea2_area3 0 0 0 +testArea1_area2 1 7 4 +testArea1_area1 1 3 2 diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml new file mode 100644 index 00000000000..ef77da93664 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/dataDistributionPerZone.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/dataDistributionPerZone.csv new file mode 100644 index 00000000000..70857ece6c1 --- /dev/null +++ b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/dataDistributionPerZone.csv @@ -0,0 +1,4 @@ +areaID areaName Inhabitants Employee Employee Primary Sector Employee Construction Employee Secondary Sector Rest Employee Retail Employee Traffic/Parcels Employee Tertiary Sector Rest +testArea1_area2 area2 4000 6500 500 1500 500 500 1500 2000 +testArea2_area3 area3 800 1000 50 200 100 150 200 300 +testArea1_area1 area1 4000 3500 0 500 500 1000 500 1000 \ No newline at end of file diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/output_carriers.xml.gz b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/output_carriers.xml.gz similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/output_carriers.xml.gz rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/output_carriers.xml.gz diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/vehicleTypes.xml.gz b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/vehicleTypes.xml.gz similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/vehicleTypes.xml.gz rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleServiceCarrier/vehicleTypes.xml.gz diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/output_carriers.xml.gz b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/output_carriers.xml.gz similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/output_carriers.xml.gz rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/output_carriers.xml.gz diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/vehicleTypes.xml.gz b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/vehicleTypes.xml.gz similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/vehicleTypes.xml.gz rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/exampleShipmentCarrier/vehicleTypes.xml.gz diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/investigationAreaData.csv b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/investigationAreaData.csv similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/investigationAreaData.csv rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/investigationAreaData.csv diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.cpg b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.cpg similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.cpg rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.cpg diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.dbf b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.dbf similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.dbf rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.dbf diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.prj b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.prj similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.prj rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.prj diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.qmd b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.qmd similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.qmd rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.qmd diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shp b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shp similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shp rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shp diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shx b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shx similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shx rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shx diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.cpg b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.cpg similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.cpg rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.cpg diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.dbf b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.dbf similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.dbf rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.dbf diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.prj b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.prj similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.prj rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.prj diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.qmd b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.qmd similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.qmd rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.qmd diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shp b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shp similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shp rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shp diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shx b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shx similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shx rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shx diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.cpg b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.cpg similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.cpg rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.cpg diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.dbf b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.dbf similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.dbf rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.dbf diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.prj b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.prj similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.prj rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.prj diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.qmd b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.qmd similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.qmd rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.qmd diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shp b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shp similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shp rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shp diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shx b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shx similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shx rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shx diff --git a/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/test.output_events.xml.gz b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/test.output_events.xml.gz new file mode 100644 index 00000000000..a3f7231747b Binary files /dev/null and b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/test.output_events.xml.gz differ diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/vehicleTypes.xml b/contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/vehicleTypes.xml similarity index 100% rename from contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/vehicleTypes.xml rename to contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/vehicleTypes.xml diff --git a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java index 145d887c27f..0a806f1546b 100644 --- a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java +++ b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java @@ -18,7 +18,7 @@ import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.matsim.lanes.*; import org.xml.sax.SAXException; @@ -121,7 +121,7 @@ public static void main(String[] args) { */ private static Geometry calculateNetworkArea(Path shapeFile) { // only the first feature is used - return ((Geometry) ShapeFileReader.getAllFeatures(shapeFile.toString()).iterator().next().getDefaultGeometry()); + return ((Geometry) GeoFileReader.getAllFeatures(shapeFile.toString()).iterator().next().getDefaultGeometry()); } /** diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_events.xml.gz index be4e7c20597..625f9d0073a 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_plans.xml.gz index 09363b05fb8..d0b3f49d5b5 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testAssignment/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_events.xml.gz index 41fd49194bf..0afe8ff3430 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_plans.xml.gz index 03014fe947f..a423bf11d2c 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testOneTaxi/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_events.xml.gz index be4e7c20597..625f9d0073a 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_plans.xml.gz index 09363b05fb8..d0b3f49d5b5 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/etaxi/run/RunETaxiScenarioIT/testRuleBased/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_events.xml.gz index 1607c967ce8..19b00860f2b 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_plans.xml.gz index 3643b13c0cc..7f3683fe733 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_arrivalTime/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_events.xml.gz index 3305da22ddb..5d175bd18a1 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_plans.xml.gz index 4a8bc8d0ff3..c5960ee0794 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_dse/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_events.xml.gz index c32da9baf0b..3e864de13a2 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_plans.xml.gz index 9b4282f8573..ad3aea06944 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_pickupTime/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_events.xml.gz index f8068a4f3fd..79c4783b2aa 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_plans.xml.gz index 1b26da84281..eabe3b28dd2 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/assignment/AssignmentTaxiOptimizerIT/testAssignment_totalWaitTime/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_events.xml.gz index 26bb2f449dc..60e5b2237a1 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_plans.xml.gz index 41143a894b0..4c532baa5c5 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/fifo/FifoTaxiOptimizerIT/testFifo/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_events.xml.gz index 4a779638fde..236b8d57ddd 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_plans.xml.gz index aa424831a99..ed20dc628fc 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_dse/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_events.xml.gz index a0312998c04..dc301f3138d 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_plans.xml.gz index 510a917b087..35560847305 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minPickupTime/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_events.xml.gz index cea982bfcea..d99a059ff9d 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_plans.xml.gz index 8878604ffc4..e09d65480a9 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/rules/RuleBasedTaxiOptimizerIT/testRuleBased_minWaitTime/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_events.xml.gz index ea41623c65f..104bfcba196 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_plans.xml.gz index eb0d178bd21..fc1ffd599e5 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_dse/output_plans.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_events.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_events.xml.gz index 04e6928409b..c8863d97633 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_events.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_events.xml.gz differ diff --git a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_plans.xml.gz b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_plans.xml.gz index 16a5992728a..acddf468ac1 100644 Binary files a/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_plans.xml.gz and b/contribs/taxi/test/input/org/matsim/contrib/taxi/optimizer/zonal/ZonalTaxiOptimizerIT/testZonal_minWaitTime/output_plans.xml.gz differ diff --git a/contribs/vsp/src/main/java/org/matsim/contrib/vsp/scenario/SnzActivities.java b/contribs/vsp/src/main/java/org/matsim/contrib/vsp/scenario/SnzActivities.java index 5687f8b2116..5e1705a677d 100644 --- a/contribs/vsp/src/main/java/org/matsim/contrib/vsp/scenario/SnzActivities.java +++ b/contribs/vsp/src/main/java/org/matsim/contrib/vsp/scenario/SnzActivities.java @@ -74,10 +74,6 @@ public static void addScoringParams(Config config) { } } - config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("car interaction").setTypicalDuration(60)); - config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("ride interaction").setTypicalDuration(60)); - config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("bike interaction").setTypicalDuration(60)); - config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("other").setTypicalDuration(600 * 3)); config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("freight_start").setTypicalDuration(60 * 15)); diff --git a/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/FreightTimeAndDistanceAnalysisEventsHandler.java b/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/FreightTimeAndDistanceAnalysisEventsHandler.java index 42c95fb70ad..3037716368c 100644 --- a/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/FreightTimeAndDistanceAnalysisEventsHandler.java +++ b/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/FreightTimeAndDistanceAnalysisEventsHandler.java @@ -27,6 +27,9 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.events.LinkEnterEvent; +import org.matsim.api.core.v01.events.LinkLeaveEvent; +import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent; +import org.matsim.api.core.v01.events.VehicleLeavesTrafficEvent; import org.matsim.freight.carriers.Carrier; import org.matsim.freight.carriers.CarriersUtils; import org.matsim.freight.carriers.Tour; @@ -55,15 +58,21 @@ public class FreightTimeAndDistanceAnalysisEventsHandler implements BasicEventHa private final Map, Double> vehicleId2TourDuration = new LinkedHashMap<>(); private final Map, Double> vehicleId2TourLength = new LinkedHashMap<>(); + private final Map, Double> vehicleId2TravelTime = new LinkedHashMap<>(); + private final Map, Id> vehicleId2CarrierId = new LinkedHashMap<>(); private final Map, Id> vehicleId2TourId = new LinkedHashMap<>(); - private final Map, Double> vehicleTypeId2SumOfDuration = new LinkedHashMap<>(); + private final Map, Double> vehicleTypeId2SumOfTourDuration = new LinkedHashMap<>(); private final Map, Double> vehicleTypeId2Mileage = new LinkedHashMap<>(); + private final Map, Double> vehicleTypeId2TravelTime = new LinkedHashMap<>(); + private final Map, VehicleType> vehicleId2VehicleType = new TreeMap<>(); private final Map tourStartTime = new LinkedHashMap<>(); + private final Map, Double> vehicleEnteredLinkTime = new LinkedHashMap<>(); + public FreightTimeAndDistanceAnalysisEventsHandler(Scenario scenario) { this.scenario = scenario; @@ -81,7 +90,7 @@ private void handleEvent(CarrierTourEndEvent event) { double tourDuration = event.getTime() - tourStartTime.get(key); vehicleId2TourDuration.put(event.getVehicleId(), tourDuration); VehicleType vehType = VehicleUtils.findVehicle(event.getVehicleId(), scenario).getType(); - vehicleTypeId2SumOfDuration.merge(vehType.getId(), tourDuration, Double::sum); + vehicleTypeId2SumOfTourDuration.merge(vehType.getId(), tourDuration, Double::sum); //Some general information for this vehicle vehicleId2CarrierId.putIfAbsent(event.getVehicleId(), event.getCarrierId()); @@ -93,23 +102,61 @@ private void handleEvent(CarrierTourEndEvent event) { private void handleEvent(LinkEnterEvent event) { final double distance = scenario.getNetwork().getLinks().get(event.getLinkId()).getLength(); vehicleId2TourLength.merge(event.getVehicleId(), distance, Double::sum); + vehicleEnteredLinkTime.put(event.getVehicleId(), event.getTime()); //Safe time when entering the link. final Id vehTypeId = VehicleUtils.findVehicle(event.getVehicleId(), scenario).getType().getId(); vehicleTypeId2Mileage.merge(vehTypeId, distance, Double::sum); } - @Override public void handleEvent(Event event) { + //If the vehicle leaves a link at the end, the travelTime is calculated and stored. + private void handleEvent(LinkLeaveEvent event){ + final Id vehicleId = event.getVehicleId(); + if (vehicleEnteredLinkTime.containsKey(vehicleId)){ + double tt = event.getTime() - vehicleEnteredLinkTime.get(vehicleId); + vehicleId2TravelTime.merge(vehicleId, tt, Double::sum); //per vehicle + + final Id vehTypeId = VehicleUtils.findVehicle(event.getVehicleId(), scenario).getType().getId(); + vehicleTypeId2TravelTime.merge(vehTypeId, tt, Double::sum); // per VehType + + vehicleEnteredLinkTime.remove(vehicleId); //remove from that list. + } + } + + //If the vehicle leaves a link because it reached its destination, the travelTime is calculated and stored. + private void handleEvent(VehicleLeavesTrafficEvent event){ + final Id vehicleId = event.getVehicleId(); + if (vehicleEnteredLinkTime.containsKey(vehicleId)){ + double tt = event.getTime() - vehicleEnteredLinkTime.get(vehicleId); + vehicleId2TravelTime.merge(vehicleId, tt, Double::sum);//per vehicle + + final Id vehTypeId = VehicleUtils.findVehicle(event.getVehicleId(), scenario).getType().getId(); + vehicleTypeId2TravelTime.merge(vehTypeId, tt, Double::sum); // per VehType + + vehicleEnteredLinkTime.remove(vehicleId); //remove from that list. + } + } + + private void handleEvent(VehicleEntersTrafficEvent event){ + vehicleEnteredLinkTime.put(event.getVehicleId(), event.getTime()); + } + @Override public void handleEvent(Event event) { if (event instanceof CarrierTourStartEvent carrierTourStartEvent) { handleEvent(carrierTourStartEvent); } else if (event instanceof CarrierTourEndEvent carrierTourEndEvent) { handleEvent(carrierTourEndEvent); } else if (event instanceof LinkEnterEvent linkEnterEvent) { handleEvent(linkEnterEvent); + } else if (event instanceof LinkLeaveEvent linkLeaveEvent) { + handleEvent(linkLeaveEvent); + } else if (event instanceof VehicleLeavesTrafficEvent vehicleLeavesTrafficEvent) { + handleEvent(vehicleLeavesTrafficEvent); + } else if (event instanceof VehicleEntersTrafficEvent vehicleEntersTrafficEvent) { + handleEvent(vehicleEntersTrafficEvent); } } - void writeTravelTimeAndDistance(String analysisOutputDirectory, Scenario scenario) throws IOException { + void writeTravelTimeAndDistancePerVehicle(String analysisOutputDirectory, Scenario scenario) throws IOException { log.info("Writing out Time & Distance & Costs ... perVehicle"); //Travel time and distance per vehicle String fileName = analysisOutputDirectory + "TimeDistance_perVehicle.tsv"; @@ -117,14 +164,19 @@ void writeTravelTimeAndDistance(String analysisOutputDirectory, Scenario scenari BufferedWriter bw1 = new BufferedWriter(new FileWriter(fileName)); //Write headline: - bw1.write("vehicleId \t carrierId \t vehicleTypeId \t tourId \t tourDuration[s] \t travelDistance[m] \t " + - "costPerSecond[EUR/s] \t costPerMeter[EUR/m] \t fixedCosts[EUR] \t varCostsTime[EUR] \t varCostsDist[EUR] \t totalCosts[EUR]"); + bw1.write("vehicleId \t carrierId \t vehicleTypeId \t tourId \t " + + "tourDuration[s] \t tourDuration[h] \t" + + "travelDistance[m] \t travelDistance[km] \t " + + "travelTime[s] \t travelTime[h] \t" + + "costPerSecond[EUR/s] \t costPerMeter[EUR/m] \t fixedCosts[EUR] \t varCostsTime[EUR] \t varCostsDist[EUR] \t totalCosts[EUR]"); bw1.newLine(); for (Id vehicleId : vehicleId2VehicleType.keySet()) { final Double durationInSeconds = vehicleId2TourDuration.get(vehicleId); final Double distanceInMeters = vehicleId2TourLength.get(vehicleId); + final Double travelTimeInSeconds = vehicleId2TravelTime.get(vehicleId); + final VehicleType vehicleType = VehicleUtils.findVehicle(vehicleId, scenario).getType(); final Double costsPerSecond = vehicleType.getCostInformation().getCostsPerSecond(); @@ -141,7 +193,14 @@ void writeTravelTimeAndDistance(String analysisOutputDirectory, Scenario scenari bw1.write("\t" + vehicleId2TourId.get(vehicleId)); bw1.write("\t" + durationInSeconds); + bw1.write("\t" + durationInSeconds /3600); + bw1.write("\t" + distanceInMeters); + bw1.write("\t" + distanceInMeters/1000); + + bw1.write("\t" + travelTimeInSeconds); + bw1.write("\t" + travelTimeInSeconds /3600); + bw1.write("\t" + costsPerSecond); bw1.write("\t" + costsPerMeter); bw1.write("\t" + fixedCost); @@ -161,7 +220,6 @@ void writeTravelTimeAndDistancePerVehicleType(String analysisOutputDirectory, Sc log.info("Writing out Time & Distance & Costs ... perVehicleType"); //----- All VehicleTypes in CarriervehicleTypes container. Used so that even unused vehTypes appear in the output - TreeMap, VehicleType> vehicleTypesMap = new TreeMap<>(CarriersUtils.getCarrierVehicleTypes(scenario).getVehicleTypes()); //For the case that there are additional vehicle types found in the events. for (VehicleType vehicleType : vehicleId2VehicleType.values()) { @@ -171,34 +229,39 @@ void writeTravelTimeAndDistancePerVehicleType(String analysisOutputDirectory, Sc String fileName = analysisOutputDirectory + "TimeDistance_perVehicleType.tsv"; BufferedWriter bw1 = new BufferedWriter(new FileWriter(fileName)); - //Write headline: - bw1.write("vehicleTypeId \t nuOfVehicles \t SumOfTourDuration[s] \t SumOfTravelDistances[m] \t " + + bw1.write("vehicleTypeId \t nuOfVehicles \t " + + "SumOfTourDuration[s] \t SumOfTourDuration[h] \t" + + "SumOfTravelDistances[m] \t SumOfTravelDistances[km] \t " + + "SumOfTravelTime[s] \t SumOfTravelTime[h] \t" + "costPerSecond[EUR/s] \t costPerMeter[EUR/m] \t fixedCosts[EUR/veh] \t" + "varCostsTime[EUR] \t varCostsDist[EUR] \t fixedCosts[EUR] \t totalCosts[EUR]"); bw1.newLine(); - for (VehicleType vehicleType : vehicleTypesMap.values()) { - long nuOfVehicles = vehicleId2VehicleType.values().stream().filter(vehType -> vehType.getId() == vehicleType.getId()).count(); final Double costRatePerSecond = vehicleType.getCostInformation().getCostsPerSecond(); final Double costRatePerMeter = vehicleType.getCostInformation().getCostsPerMeter(); final Double fixedCostPerVeh = vehicleType.getCostInformation().getFixedCosts(); - final Double sumOfDurationInSeconds = vehicleTypeId2SumOfDuration.getOrDefault(vehicleType.getId(), 0.); + final Double sumOfTourDurationInSeconds = vehicleTypeId2SumOfTourDuration.getOrDefault(vehicleType.getId(), 0.); final Double sumOfDistanceInMeters = vehicleTypeId2Mileage.getOrDefault(vehicleType.getId(), 0.); + final Double sumOfTravelTimeInSeconds = vehicleTypeId2TravelTime.getOrDefault(vehicleType.getId(), 0.); - final double sumOfVarCostsTime = sumOfDurationInSeconds * costRatePerSecond; + final double sumOfVarCostsTime = sumOfTourDurationInSeconds * costRatePerSecond; final double sumOfVarCostsDistance = sumOfDistanceInMeters * costRatePerMeter; final double sumOfFixCosts = nuOfVehicles * fixedCostPerVeh; bw1.write(vehicleType.getId().toString()); bw1.write("\t" + nuOfVehicles); - bw1.write("\t" + sumOfDurationInSeconds); + bw1.write("\t" + sumOfTourDurationInSeconds); + bw1.write("\t" + sumOfTourDurationInSeconds / 3600); bw1.write("\t" + sumOfDistanceInMeters); + bw1.write("\t" + sumOfDistanceInMeters / 1000); + bw1.write("\t" + sumOfTravelTimeInSeconds); + bw1.write("\t" + sumOfTravelTimeInSeconds / 3600); bw1.write("\t" + costRatePerSecond); bw1.write("\t" + costRatePerMeter); bw1.write("\t" + fixedCostPerVeh); diff --git a/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/RunFreightAnalysisEventBased.java b/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/RunFreightAnalysisEventBased.java index 86249801bd0..44374d18dd7 100644 --- a/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/RunFreightAnalysisEventBased.java +++ b/contribs/vsp/src/main/java/org/matsim/freight/carriers/analysis/RunFreightAnalysisEventBased.java @@ -120,7 +120,7 @@ public void runAnalysis() throws IOException { log.info("Analysis completed."); log.info("Writing output..."); - freightTimeAndDistanceAnalysisEventsHandler.writeTravelTimeAndDistance(analysisOutputDirectory, scenario); + freightTimeAndDistanceAnalysisEventsHandler.writeTravelTimeAndDistancePerVehicle(analysisOutputDirectory, scenario); freightTimeAndDistanceAnalysisEventsHandler.writeTravelTimeAndDistancePerVehicleType(analysisOutputDirectory, scenario); carrierLoadAnalysis.writeLoadPerVehicle(analysisOutputDirectory, scenario); } diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/act2mode/ActivityToModeAnalysis.java b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/act2mode/ActivityToModeAnalysis.java index 3c0e2d1c2d6..12055885daa 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/act2mode/ActivityToModeAnalysis.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/act2mode/ActivityToModeAnalysis.java @@ -35,14 +35,14 @@ import org.matsim.core.events.handler.EventHandler; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PointFeatureFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.analysis.modules.AbstractAnalysisModule; /** * @author droeder - * + * * writeResultsPlanCoords() added by gleich * */ @@ -65,7 +65,7 @@ public class ActivityToModeAnalysis extends AbstractAnalysisModule { * @param sc, the scenario (it has to contain facilities!!!) * @param personsOfInterest, might be null, than all persons are processed * @param slotSize, timeSlotSize in seconds - * + * * output using Activity Coordinates from the plans instead of departure * and arrival links with writeResultsPlanCoords() */ @@ -100,24 +100,24 @@ public void postProcessData() { addAttribute("ActType", String.class). addAttribute("Mode", String.class). create(); - + //create features for departure this.departureSlotFeatures = new HashMap>(); for(ActivityToMode atm : this.handler.getDepartures()){ createFeatureAndAdd(atm, this.departureSlotFeatures, factory); } - + this.departureSlotFeaturesPlanCoord = new HashMap>(); for(ActivityToMode atm : this.handlerPlanCoord.getDepartures()){ createFeatureAndAdd(atm, this.departureSlotFeaturesPlanCoord, factory); } - + //create features for arrivals this.arrivalSlotFeatures = new HashMap>(); for(ActivityToMode atm : this.handler.getArrivals()){ createFeatureAndAdd(atm, this.arrivalSlotFeatures, factory); } - + this.arrivalSlotFeaturesPlanCoord = new HashMap>(); for(ActivityToMode atm : this.handlerPlanCoord.getArrivals()){ createFeatureAndAdd(atm, this.arrivalSlotFeaturesPlanCoord, factory); @@ -128,9 +128,9 @@ public void postProcessData() { /** * @param atm * @param departureSlotFeatures2 - * @param featureType + * @param featureType */ - private void createFeatureAndAdd(ActivityToMode atm, + private void createFeatureAndAdd(ActivityToMode atm, HashMap> slotFeatures, PointFeatureFactory factory) { Integer slice = (int) (atm.getTime() / this.slotSize); Set temp = slotFeatures.get(slice); @@ -149,22 +149,22 @@ private void createFeatureAndAdd(ActivityToMode atm, @Override public void writeResults(String outputFolder) { for(Entry> e: this.departureSlotFeatures.entrySet()){ - ShapeFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); + GeoFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); } for(Entry> e: this.departureSlotFeaturesPlanCoord.entrySet()){ - ShapeFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); + GeoFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); } for(Entry> e: this.arrivalSlotFeatures.entrySet()){ - ShapeFileWriter.writeGeometries(e.getValue(), outputFolder + "arrival_" + e.getKey().toString() + ".shp"); + GeoFileWriter.writeGeometries(e.getValue(), outputFolder + "arrival_" + e.getKey().toString() + ".shp"); } } - + public void writeResultsPlanCoords(String outputFolder) { for(Entry> e: this.departureSlotFeaturesPlanCoord.entrySet()){ - ShapeFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); + GeoFileWriter.writeGeometries(e.getValue(), outputFolder + "departure_" + e.getKey().toString() + ".shp"); } for(Entry> e: this.arrivalSlotFeaturesPlanCoord.entrySet()){ - ShapeFileWriter.writeGeometries(e.getValue(), outputFolder + "arrival_" + e.getKey().toString() + ".shp"); + GeoFileWriter.writeGeometries(e.getValue(), outputFolder + "arrival_" + e.getKey().toString() + ".shp"); } } } diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/networkAnalysis/NetworkAnalyzer.java b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/networkAnalysis/NetworkAnalyzer.java index 7f036c0b858..4e41f3ae95f 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/networkAnalysis/NetworkAnalyzer.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/networkAnalysis/NetworkAnalyzer.java @@ -43,7 +43,7 @@ import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.analysis.modules.AbstractAnalysisModule; @@ -58,66 +58,66 @@ * (ii) types (redundant, dead end, exit road) * (2) link statistics * (3) accessibility computation (depending on walk travel time to the network) - * + * * @author dhosse - * + * */ public class NetworkAnalyzer extends AbstractAnalysisModule{ private Logger log = LogManager.getLogger(NetworkAnalyzer.class); - + private Scenario scenario; private Network network; - + private Map> nodeTypes; private Map geometricLengths; - + private List nodesWithHighDegrees; private List lengthBelowStorageCapacity; private Map smallClusterLinks; - + private Map> filesForExportInQGisProject; - + private double totalLength = 0; private double totalGLength = 0; - + private SimpleFeatureBuilder builder; - + private Geometry envelope; - + private final String deadEnd = "deadEnd"; private final String exit = "exit"; private final String redundant = "redundant"; - + private final String TXTfile = ".txt"; private final String QGSfile = ".qgs"; private final String SHPfile = ".shp"; - + private String targetCoordinateSystem; - + private boolean nodesChecked = false; private boolean linksChecked = false; - + private SpatialGrid freeSpeedGrid = null; - + /** * Creates a new analyzer for properties of a given network file. - * + * * @param networkInputFile The MATSim-Network file to analyse. * @param targetCoordinateSystem The coordinate system your network data is transformed to. */ public NetworkAnalyzer(String networkInputFile, String targetCoordinateSystem) { - + super(NetworkAnalyzer.class.getSimpleName()); this.scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); this.targetCoordinateSystem = targetCoordinateSystem; - + if(networkInputFile!=null){ new MatsimNetworkReader(this.scenario.getNetwork()).readFile(networkInputFile); this.network = this.scenario.getNetwork(); } - + } @Override @@ -132,29 +132,29 @@ public void preProcessData() { @Override public void postProcessData() { - + this.nodeTypes = new TreeMap>(); this.geometricLengths = new HashMap(); this.nodesWithHighDegrees = new ArrayList(); this.filesForExportInQGisProject = new HashMap>(); this.lengthBelowStorageCapacity = new ArrayList(); this.smallClusterLinks = new TreeMap(); - + this.envelope = new BoundingPolygon(network, 200).returnPolygon(); this.filesForExportInQGisProject.put("envelope", Polygon.class); - + if(isRoutable()){ continueWithRoutableNetwork(); - + log.info("total length of all network links: " + this.totalLength + " m"); log.info("total geometric length of all network links: " + this.totalGLength+ " m"); } - + } @Override public void writeResults(String outputFolder) { - + try { exportEnvelopeToShape(outputFolder); writeNodesFiles(outputFolder); @@ -164,28 +164,28 @@ public void writeResults(String outputFolder) { } catch (IOException e) { e.printStackTrace(); } - + } - + private void exportEnvelopeToShape(String outputFolder) { SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder(); typeBuilder.setName("shape"); typeBuilder.add("envelope",Polygon.class); typeBuilder.add("area",Double.class); - + this.builder = new SimpleFeatureBuilder(typeBuilder.buildFeatureType()); - + ArrayList features = new ArrayList(); - + SimpleFeature feature = this.builder.buildFeature(null, new Object[]{ this.envelope, this.envelope.getArea() }); - + features.add(feature); - ShapeFileWriter.writeGeometries(features, outputFolder+"envelope"+this.SHPfile); + GeoFileWriter.writeGeometries(features, outputFolder+"envelope"+this.SHPfile); } @@ -194,18 +194,18 @@ private void exportEnvelopeToShape(String outputFolder) { * @return True if the given network contains of only one cluster and false, if there is more than one. */ private boolean isRoutable(){ - + //the nodes that have already been inspected final Map visitedNodes = new TreeMap(); //a map to store the biggest cluster of the network Map biggestCluster = new TreeMap(); - + boolean stillSearching = true; Iterator iter = this.network.getNodes().values().iterator(); - + log.info(" checking " + this.network.getNodes().size() + " nodes and " + this.network.getLinks().size() + " links for dead-ends..."); - + //run as long as there are still unvisited nodes while (iter.hasNext() && stillSearching) { Node startNode = iter.next(); @@ -225,9 +225,9 @@ private boolean isRoutable(){ } } } - + for(Id nodeId : this.network.getNodes().keySet()){ - + if(!biggestCluster.containsKey(nodeId)){ Node node = this.network.getNodes().get(nodeId); for(Link l : node.getOutLinks().values()){ @@ -236,69 +236,69 @@ private boolean isRoutable(){ } } } - + } - + return false; - + } - + /** * If the network is routable, the analysis methods are running over the network. */ private void continueWithRoutableNetwork() { - + checkNodeAttributes(); checkLinkAttributes(); - + //create a bounding box for accessibility computation BoundingBox bbox = BoundingBox.createBoundingBox(this.scenario.getNetwork()); - + double resolution = 100; - + //set measuring points from which the accessibility is measured ZoneLayer> measuringPoints = NetworkAnalyzer.createGridLayerByGridSizeByNetwork(resolution, bbox.getBoundingBox()); //initialize grid that stores the accessibility values this.freeSpeedGrid = new SpatialGrid(bbox.getXMin(),bbox.getYMin(),bbox.getXMax(),bbox.getYMax(), resolution, Double.NaN); MutableScenario sc = (MutableScenario) this.scenario; - + //run accessibility computation new AccessibilityCalc(measuringPoints, freeSpeedGrid, sc,envelope).runAccessibilityComputation(); - + } - + /** * Parses all network nodes and classifies them into either exit road, dead end or redundant nodes. * Currently only for nodes with degree = 1. */ private void checkNodeAttributes() { - + log.info("analysing network nodes..."); - + int exit=0,red=0,de=0; - + this.nodeTypes.put(this.exit, new ArrayList()); this.nodeTypes.put(this.deadEnd, new ArrayList()); this.nodeTypes.put(this.redundant, new ArrayList()); - + //iterate over all network nodes for(Node node : this.network.getNodes().values()){ //if the node has any ingoing and outgoing links (at least one of a kind) investigate the node if(node.getInLinks().size()>0&&node.getOutLinks().size()>0){ - + //catches nodes with high degrees (here: more than five in- or outgoing links) if(node.getInLinks().size()>5||node.getOutLinks().size()>5) this.nodesWithHighDegrees.add(node.getId()); - + //if the node's degree equals 1, then classify it if(node.getInLinks().size()==1&&node.getOutLinks().size()==1){ //get the in- and the outgoing link of the node Link inLink = node.getInLinks().values().iterator().next(); Link outLink = node.getOutLinks().values().iterator().next(); - + //if the only possible moving direction from this node is backwards //(to the node you came from) and it lies outside the bounding polygon //then it's a node of an exit road @@ -312,7 +312,7 @@ private void checkNodeAttributes() { //if it's inside the polygon, it's a node of a dead end road this.nodeTypes.get(this.deadEnd).add(node.getId()); de++; - + } else{ //if all attributes of the ingoing link are equal to the //attributes of the outgoing link, then the node is redundant @@ -327,37 +327,37 @@ private void checkNodeAttributes() { } } } - + log.info("found " + exit + " exit road nodes, " + de + " dead end nodes and " + red + " redundant nodes..."); log.info("...done"); - + this.nodesChecked = true; - + } - + /** *Parses all network links to compute their geometric length (fromNode to toNode) and check if their storage capacity is sufficient for - *storing at least one vehicle (link length >= effektiveCellSize). + *storing at least one vehicle (link length >= effektiveCellSize). */ private void checkLinkAttributes() { - + log.info("analyzing network links..."); - + //the space needed to store a vehicle (+ additional space for the gaps to the vehicle ahead and the one following double cellWidth = ((Network)this.network).getEffectiveCellSize(); int writerIndex = 0; - + //iterate over all links for(Link link : this.network.getLinks().values()){ //compute geometric length (distance between from and to node) - double geometricLength = + double geometricLength = Math.sqrt(Math.pow(link.getToNode().getCoord().getX() - link.getFromNode().getCoord().getX(),2) + Math.pow(link.getToNode().getCoord().getY() - link.getFromNode().getCoord().getY(), 2)); - + this.geometricLengths.put(link.getId(), geometricLength); - + this.totalLength += link.getLength(); this.totalGLength += geometricLength; //if the length of the link is less than the effectiveCellSize then store the link and write a warning about that later @@ -370,7 +370,7 @@ private void checkLinkAttributes() { log.info("...done"); this.linksChecked = true; } - + private Map findCluster(final Node startNode, final Network network) { final Map nodeRoles = new HashMap(network.getNodes().size()); @@ -418,7 +418,7 @@ private Map findCluster(final Node startNode, final Network network) { return clusterNodes; } - + private static DoubleFlagRole getDoubleFlag(final Node n, final Map nodeRoles) { DoubleFlagRole r = nodeRoles.get(n); if (null == r) { @@ -427,56 +427,56 @@ private static DoubleFlagRole getDoubleFlag(final Node n, final Map> createGridLayerByGridSizeByNetwork(double gridSize, double [] boundingBox) { - + // log.info("Setting starting points for accessibility measure ..."); int skippedPoints = 0; int setPoints = 0; - + GeometryFactory factory = new GeometryFactory(); - + Set>> zones = new HashSet<>(); double xmin = boundingBox[0]; double ymin = boundingBox[1]; double xmax = boundingBox[2]; double ymax = boundingBox[3]; - - + + ProgressBar bar = new ProgressBar( (xmax-xmin)/gridSize ); - + // goes step by step from the min x and y coordinate to max x and y coordinate for(double x = xmin; x = xmin && + if (center_X <= xmax && center_X >= xmin && center_Y <= ymax && center_Y >= ymin) { - + Point point = factory.createPoint(new Coordinate(x, y)); - + Coordinate[] coords = new Coordinate[5]; coords[0] = point.getCoordinate(); coords[1] = new Coordinate(x, y + gridSize); @@ -487,11 +487,11 @@ public static ZoneLayer> createGridLayerByGridSizeByNetwork(double grid LinearRing linearRing = factory.createLinearRing(coords); Polygon polygon = factory.createPolygon(linearRing, null); // polygon.setSRID( srid ); // tnicolai: this is not needed to match the grid layer with locations / facilities from UrbanSim - + Zone> zone = new Zone>(polygon); zone.setAttribute( Id.create( setPoints, Zone.class ) ); zones.add(zone); - + setPoints++; } else skippedPoints++; @@ -500,24 +500,24 @@ public static ZoneLayer> createGridLayerByGridSizeByNetwork(double grid // log.info("Having " + setPoints + " inside the shape file boundary (and " + skippedPoints + " outside)."); // log.info("Done with setting starting points!"); - + ZoneLayer> layer = new ZoneLayer<>(zones); return layer; } - + /** * Calls the writing methods for node statistics after the analysis and classification - * - * @param outputFolder + * + * @param outputFolder * @throws IOException */ private void writeNodesFiles(String outputFolder) throws IOException { - + if(this.nodesChecked){ writeNodeStatisticsFile(outputFolder); exportNodesToShape(outputFolder); } - + } /** @@ -528,25 +528,25 @@ private void writeNodesFiles(String outputFolder) throws IOException { private void writeNodeStatisticsFile(String outputFolder) throws IOException { File file = new File(outputFolder+"nodeStatistics"+this.TXTfile); FileWriter writer = new FileWriter(file);; - + writer.write("ID\tinDegree\toutDegree"); - + for(Node n : this.network.getNodes().values()){ - + writer.write("\n"+n.getId()+"\t"+n.getInLinks().size()+"\t"+n.getOutLinks().size()); - + } writer.flush(); writer.close(); } - + /** * Writes the classified nodes to a shape file for visualisation in QGis. - * + * * @param outputFolder */ private void exportNodesToShape(String outputFolder){ - + SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder(); typeBuilder.setName("node"); typeBuilder.add("location",Point.class); @@ -554,32 +554,32 @@ private void exportNodesToShape(String outputFolder){ typeBuilder.add("type",String.class); typeBuilder.setCRS(MGC.getCRS(this.targetCoordinateSystem)); this.builder = new SimpleFeatureBuilder(typeBuilder.buildFeatureType()); - + Collection features = new ArrayList(); - + for(String nodeType : this.nodeTypes.keySet()){ for(Id nodeId : this.nodeTypes.get(nodeType)){ Point p = MGC.coord2Point(this.network.getNodes().get(nodeId).getCoord()); features.add(this.builder.buildFeature(null, new Object[]{p,nodeId.toString(),nodeType})); - + } - + } - + String destination = ""; - + if(features.size() > 0){ destination = "nodeTypes"; - + this.filesForExportInQGisProject.put(destination,Point.class); - - ShapeFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); + + GeoFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); } - + if(!(this.nodesWithHighDegrees.size() < 1)){ features.clear(); - + typeBuilder.setName("node"); typeBuilder.add("location",Point.class); typeBuilder.add("ID",String.class); @@ -587,42 +587,42 @@ private void exportNodesToShape(String outputFolder){ typeBuilder.add("out-degree",Integer.class); typeBuilder.setCRS(MGC.getCRS(this.targetCoordinateSystem)); this.builder = new SimpleFeatureBuilder(typeBuilder.buildFeatureType()); - + for(Id nodeId : this.nodesWithHighDegrees){ - + Point p = MGC.coord2Point(this.network.getNodes().get(nodeId).getCoord()); features.add(this.builder.buildFeature(null, new Object[]{p,nodeId.toString(), this.network.getNodes().get(nodeId).getInLinks().size(), this.network.getNodes().get(nodeId).getOutLinks().size()})); } - + destination = "highDegreeNodes"; - + this.filesForExportInQGisProject.put(destination, Point.class); - ShapeFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); + GeoFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); } - + } /** * Calls the writing methods for link statistics after analysis. - * + * * @param outputFolder * @throws IOException */ private void writeLinkFiles(String outputFolder) throws IOException { - + if(this.linksChecked){ writeLinkStatisticsFile(outputFolder); writeStorageCapacityWarningFile(outputFolder); } writeClustersAndNetwork2ESRIShape(outputFolder); - + } /** - * Writes a logfile with all warnings about links with insufficient storage capacity (link length < effectiveCellSize). - * + * Writes a logfile with all warnings about links with insufficient storage capacity (link length < effectiveCellSize). + * * @param outputFolder * @throws IOException */ @@ -631,7 +631,7 @@ private void writeStorageCapacityWarningFile(String outputFolder) throws IOExcep FileWriter writer; double cellWidth = ((Network)this.network).getEffectiveCellSize(); - + writer = new FileWriter(file); for(Link link : this.lengthBelowStorageCapacity){ writer.write("length of link " + link.getId() + " below min length for storage capacity of one vehicle (" + @@ -640,42 +640,42 @@ private void writeStorageCapacityWarningFile(String outputFolder) throws IOExcep writer.flush(); writer.close(); } - + /** * Writes the link statistics (length, geometric length, number of lanes, capacity) to a plain text file. - * + * * @param outputFolder * @throws IOException */ private void writeLinkStatisticsFile(String outputFolder) throws IOException{ - + File file = new File(outputFolder+"linkStatistics"+this.TXTfile); - - + + FileWriter writer = new FileWriter(file); - + writer.write("ID \t length \t geometricLength \t nlanes \t capacity"); - + for(Link l : this.network.getLinks().values()){ - + writer.write("\n"+ l.getId() + "\t"+l.getLength() + "\t" + this.geometricLengths.get(l.getId()) + "\t" + l.getNumberOfLanes() + "\t" + l.getCapacity()); - + } writer.flush(); writer.close(); - + } - + /** * Exports the network links and (if existing) the links contained in small clusters to an ESRI shapefile. * @param outputFolder */ private void writeClustersAndNetwork2ESRIShape(String outputFolder) { - + SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder(); typeBuilder.setName("shape"); typeBuilder.add("link",LineString.class); @@ -686,11 +686,11 @@ private void writeClustersAndNetwork2ESRIShape(String outputFolder) { typeBuilder.add("nlanes", String.class); typeBuilder.setCRS(MGC.getCRS(this.targetCoordinateSystem)); this.builder = new SimpleFeatureBuilder(typeBuilder.buildFeatureType()); - + ArrayList features = new ArrayList(); - + for(Link link : this.network.getLinks().values()){ - + SimpleFeature feature = this.builder.buildFeature(null, new Object[]{ new GeometryFactory().createLineString(new Coordinate[]{ new Coordinate(link.getFromNode().getCoord().getX(),link.getFromNode().getCoord().getY()), @@ -701,25 +701,25 @@ private void writeClustersAndNetwork2ESRIShape(String outputFolder) { link.getFreespeed(), link.getCapacity(), link.getNumberOfLanes() - + }); - + features.add(feature); - + } - + String destination = "network"; - + this.filesForExportInQGisProject.put(destination,LineString.class); - - ShapeFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); - + + GeoFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); + if(this.smallClusterLinks.size()>0){ - + features.clear(); - + for(Link link : this.smallClusterLinks.values()){ - + SimpleFeature feature = this.builder.buildFeature(null, new Object[]{ new GeometryFactory().createLineString(new Coordinate[]{ new Coordinate(link.getFromNode().getCoord().getX(),link.getFromNode().getCoord().getY()), @@ -731,31 +731,31 @@ private void writeClustersAndNetwork2ESRIShape(String outputFolder) { link.getCapacity(), link.getNumberOfLanes() }); - + features.add(feature); - + } - + destination = "smallClusters"; - + this.filesForExportInQGisProject.put(destination,LineString.class); - - ShapeFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); - + + GeoFileWriter.writeGeometries(features, outputFolder+destination+this.SHPfile); + } } - + private void writeAccessibilityMap(String outputFolder){ - + try{ BufferedWriter writer = new BufferedWriter(new FileWriter(outputFolder+"/freeSpeedAccessibility.txt")); - + for(double x = this.freeSpeedGrid.getXmin(); x <= this.freeSpeedGrid.getXmax(); x += this.freeSpeedGrid.getResolution()) { writer.write(SpatialGridTableWriter.separator); writer.write(String.valueOf(x)); } writer.newLine(); - + for(double y = this.freeSpeedGrid.getYmin(); y <= this.freeSpeedGrid.getYmax() ; y += this.freeSpeedGrid.getResolution()) { writer.write(String.valueOf(y)); for(double x = this.freeSpeedGrid.getXmin(); x <= this.freeSpeedGrid.getXmax(); x += this.freeSpeedGrid.getResolution()) { @@ -774,7 +774,7 @@ private void writeAccessibilityMap(String outputFolder){ catch(IOException e){ e.printStackTrace(); } - + } - + } diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccesShapeWriter.java b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccesShapeWriter.java index e4c6db96362..f21e092ba84 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccesShapeWriter.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccesShapeWriter.java @@ -34,7 +34,7 @@ import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PointFeatureFactory; import org.matsim.core.utils.gis.PolygonFeatureFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.analysis.modules.ptAccessibility.activity.ActivityLocation; @@ -44,7 +44,7 @@ /** * @author droeder, aneumann * - * just a helper-class + * just a helper-class */ public class PtAccesShapeWriter { @@ -60,16 +60,16 @@ public static void writeMultiPolygons(Map mps, String file addAttribute("name", String.class). create(); Collection features = new ArrayList(); - + Object[] featureAttribs; for(Entry e: mps.entrySet()){ featureAttribs = new Object[1]; featureAttribs[0] = e.getKey(); features.add(factory.createPolygon(e.getValue(), featureAttribs, null)); } - ShapeFileWriter.writeGeometries(features, filename); + GeoFileWriter.writeGeometries(features, filename); } - + public static void writeActivityLocations(LocationMap locationMap, String outputFolder, String name, String targetCoordinateSystem, double gridSize){ PointFeatureFactory featureFactory = new PointFeatureFactory.Builder(). setCrs(MGC.getCRS(targetCoordinateSystem)). @@ -77,13 +77,13 @@ public static void writeActivityLocations(LocationMap locationMap, String output addAttribute("name", String.class). addAttribute("type", String.class). create(); - + PointFeatureFactory clusterFeatureFactory = new PointFeatureFactory.Builder(). setCrs(MGC.getCRS(targetCoordinateSystem)). setName(name). addAttribute("count", Integer.class). create(); - + for(Entry> type2LocationEntry: locationMap.getType2Locations().entrySet()){ if (!type2LocationEntry.getValue().isEmpty()) { try { @@ -94,44 +94,44 @@ public static void writeActivityLocations(LocationMap locationMap, String output type2LocationEntry.getKey() }, null)); } - - ShapeFileWriter.writeGeometries(features, outputFolder + "activityLocations_" + type2LocationEntry.getKey() + ".shp"); + + GeoFileWriter.writeGeometries(features, outputFolder + "activityLocations_" + type2LocationEntry.getKey() + ".shp"); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch(ServiceConfigurationError e){ e.printStackTrace(); } - + // cluster first write then - + try { Collection features = new ArrayList(); - + HashMap gridNodeId2GridNode = new HashMap(); - + // go through all acts of this type for(int i = 0; i < type2LocationEntry.getValue().size(); i++){ Coord coord = new Coord(type2LocationEntry.getValue().get(i).getCoord().x, type2LocationEntry.getValue().get(i).getCoord().y); String gridNodeId = GridNode.getGridNodeIdForCoord(coord, gridSize); - + if (gridNodeId2GridNode.get(gridNodeId) == null) { gridNodeId2GridNode.put(gridNodeId, new GridNode(gridNodeId)); } - + gridNodeId2GridNode.get(gridNodeId).addPoint(type2LocationEntry.getKey(), coord); } - + for (GridNode gridNode : gridNodeId2GridNode.values()) { features.add(clusterFeatureFactory.createPoint(new Coordinate(gridNode.getX(), gridNode.getY()), new Object[] {gridNode.getCountForType(type2LocationEntry.getKey())}, null)); } - - ShapeFileWriter.writeGeometries(features, outputFolder + "activityLocations_clustered_" + type2LocationEntry.getKey() + ".shp"); + + GeoFileWriter.writeGeometries(features, outputFolder + "activityLocations_clustered_" + type2LocationEntry.getKey() + ".shp"); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch(ServiceConfigurationError e){ e.printStackTrace(); } - + } else { log.info("No activities found for cluster " + type2LocationEntry.getKey()); } diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccessMapShapeWriter.java b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccessMapShapeWriter.java index 6605af703cc..615512e0fe6 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccessMapShapeWriter.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/ptAccessibility/utils/PtAccessMapShapeWriter.java @@ -31,20 +31,20 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.MultiPolygon; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.analysis.modules.ptAccessibility.stops.PtStopMap; /** * Calculates and writes buffers to shapes - * + * * @author aneumann */ public class PtAccessMapShapeWriter { private PtAccessMapShapeWriter() { - + } public static void writeAccessMap(Map> cluster2mode2area, int quadrantSegments, String outputFolder, String targetCoordinateSystem) { @@ -56,11 +56,11 @@ public static void writeAccessMap(Map> cluster Collections.sort(distancesSmallestFirst); HashMap> distance2mode2buffer = new HashMap>(); - + // Calculate buffer for all Multipolygons HashMap mode2buffer = null; int lastDistance = 0; - + for (Integer distance : distancesSmallestFirst) { if (mode2buffer == null) { // it's the frist and smallest one @@ -77,29 +77,29 @@ public static void writeAccessMap(Map> cluster mode2buffer = tempBuffers; lastDistance = distance.intValue(); } - + distance2mode2buffer.put(distance, mode2buffer); } - + writeGeometries(outputFolder + PtStopMap.FILESUFFIX + "_buffer", distance2mode2buffer, targetCoordinateSystem); - - - + + + // resort distances - largest first ArrayList distancesLargestFirst = new ArrayList(); for (Integer distance : distancesSmallestFirst) { distancesLargestFirst.add(0, distance); } - + HashMap> distance2mode2diffBuffer = new HashMap>(); HashMap lastMode2Buffer = null; Integer lastDist = null; - + // calculate Diff for all buffers for (Integer distance : distancesLargestFirst) { distance2mode2diffBuffer.put(distance, new HashMap()); - + if (lastMode2Buffer == null) { lastMode2Buffer = distance2mode2buffer.get(distance); lastDist = distance; @@ -113,12 +113,12 @@ public static void writeAccessMap(Map> cluster lastDist = distance; } } - + // add last (smallest) one as well for (Entry mode2BufferEntry : lastMode2Buffer.entrySet()) { distance2mode2diffBuffer.get(lastDist).put(mode2BufferEntry.getKey(), mode2BufferEntry.getValue()); } - + writeGeometries(outputFolder + PtStopMap.FILESUFFIX + "_diffBuffer", distance2mode2diffBuffer, targetCoordinateSystem); } @@ -130,10 +130,10 @@ private static void writeGeometries(String outputFolderAndFileName, HashMap bufferFeatures; Object[] bufferFeatureAttribs; - + for (Entry> distance2mode2bufferEntry : distance2mode2buffer.entrySet()) { bufferFeatures = new ArrayList(); HashMap mode2buffer = distance2mode2bufferEntry.getValue(); @@ -148,13 +148,13 @@ private static void writeGeometries(String outputFolderAndFileName, HashMap getAll(String targetCoordinateSystem) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setCRS(MGC.getCRS(targetCoordinateSystem)); @@ -93,9 +93,9 @@ private Collection getAll(String targetCoordinateSystem) { b.add("pax_" + String.valueOf(i), Double.class); } SimpleFeatureBuilder builder = new SimpleFeatureBuilder(b.buildFeatureType()); - + Collection features = new ArrayList(); - + Object[] featureAttribs; for(Link link: this.sc.getNetwork().getLinks().values()){ featureAttribs = getFeatureAll(link,new Object[4 + this.handler.getMaxInterval()]); @@ -116,7 +116,7 @@ private Object[] getFeatureAll(Link link, Object[] objects) { Coordinate[] coord = new Coordinate[2]; coord[0] = new Coordinate(link.getFromNode().getCoord().getX(), link.getFromNode().getCoord().getY(), 0.); coord[1] = new Coordinate(link.getToNode().getCoord().getX(), link.getToNode().getCoord().getY(), 0.); - + objects[0] = new GeometryFactory().createLineString(coord); objects[1] = "all"; objects[2] = link.getId().toString(); @@ -138,11 +138,11 @@ private Collection getTransitLineFeatures(TransitLine l, String t for (int i = 0; i < this.handler.getMaxInterval(); i++){ b.add("pax_" + String.valueOf(i), Double.class); } - + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(b.buildFeatureType()); - + Collection features = new ArrayList(); - + Object[] featureAttribs; for(Link link: this.sc.getNetwork().getLinks().values()){ featureAttribs = getLinkFeatureAttribs(link,l.getId(), new Object[4 + this.handler.getMaxInterval()]); @@ -169,7 +169,7 @@ private Object[] getLinkFeatureAttribs(Link link, Id lineId, Object Coordinate[] coord = new Coordinate[2]; coord[0] = new Coordinate(link.getFromNode().getCoord().getX(), link.getFromNode().getCoord().getY(), 0.); coord[1] = new Coordinate(link.getToNode().getCoord().getX(), link.getToNode().getCoord().getY(), 0.); - + objects[0] = new GeometryFactory().createLineString(coord); objects[1] = lineId.toString(); objects[2] = link.getId().toString(); @@ -185,7 +185,7 @@ public void writeResults(String outputFolder) { for(Entry, Collection> ee: this.lineId2features.entrySet()){ try{ if(ee.getValue().size() <= 0) continue; - ShapeFileWriter.writeGeometries(ee.getValue(), outputFolder + ee.getKey().toString()+ ".shp"); + GeoFileWriter.writeGeometries(ee.getValue(), outputFolder + ee.getKey().toString()+ ".shp"); }catch(ServiceConfigurationError e){ e.printStackTrace(); } diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/transitSchedule2Shp/TransitSchedule2Shp.java b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/transitSchedule2Shp/TransitSchedule2Shp.java index 94f8a049b8a..8423b07bc67 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/modules/transitSchedule2Shp/TransitSchedule2Shp.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/modules/transitSchedule2Shp/TransitSchedule2Shp.java @@ -38,7 +38,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.core.events.handler.EventHandler; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.core.utils.misc.Time; import org.matsim.pt.transitSchedule.api.TransitLine; import org.matsim.pt.transitSchedule.api.TransitRoute; @@ -51,9 +51,9 @@ * @author aneumann, droeder */ public class TransitSchedule2Shp extends AbstractAnalysisModule{ - + private static final Logger log = LogManager.getLogger(TransitSchedule2Shp.class); - + private Network network; private TransitSchedule schedule; private final String targetCoordinateSystem; @@ -72,12 +72,12 @@ public List getEventHandler() { @Override public void preProcessData() { - + } @Override public void postProcessData() { - + } @Override @@ -92,23 +92,23 @@ public void writeResults(String outputFolder) { Collection temp = getTransitLineFeatures(transitLine, this.targetCoordinateSystem); features.addAll(temp); try{ - ShapeFileWriter.writeGeometries(temp, outputFolder + transitLine.getId().toString() + ".shp"); + GeoFileWriter.writeGeometries(temp, outputFolder + transitLine.getId().toString() + ".shp"); }catch(ServiceConfigurationError e){ e.printStackTrace(); } } - // write a complete shape + // write a complete shape if(features.isEmpty()){ log.error("the transitschedule seems to be empty. No features are created, thus no shapefile will be written..."); return; } try{ - ShapeFileWriter.writeGeometries(features, outputFolder + "allLines.shp"); + GeoFileWriter.writeGeometries(features, outputFolder + "allLines.shp"); }catch(ServiceConfigurationError e){ e.printStackTrace(); } } - + private Collection getTransitLineFeatures(TransitLine transitLine, String targetCoordinateSystem) { SimpleFeatureTypeBuilder simpleFeatureBuilder = new SimpleFeatureTypeBuilder(); simpleFeatureBuilder.setCRS(MGC.getCRS(targetCoordinateSystem)); @@ -131,9 +131,9 @@ private Collection getTransitLineFeatures(TransitLine transitLine simpleFeatureBuilder.add("lastDep", String.class); simpleFeatureBuilder.add("timeOper", String.class); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(simpleFeatureBuilder.buildFeatureType()); - + Collection features = new ArrayList(); - + Object[] routeFeatureAttributes; for(TransitRoute transitRoute: transitLine.getRoutes().values()){ routeFeatureAttributes = getRouteFeatureAttribs(transitRoute, transitLine.getId(), new Object[17]); @@ -147,25 +147,25 @@ private Collection getTransitLineFeatures(TransitLine transitLine } private Object[] getRouteFeatureAttribs(TransitRoute transitRoute, Id lineId, Object[] routeFeatureAttributes ) { - + List coords = new ArrayList(); // Create the polyline (lineString) - + // add the startLink Coord toNode = this.network.getLinks().get(transitRoute.getRoute().getStartLinkId()).getToNode().getCoord(); coords.add(new Coordinate(toNode.getX(), toNode.getY(), 0.)); - + // add the routeLinks for(Id linkId : transitRoute.getRoute().getLinkIds()){ toNode = this.network.getLinks().get(linkId).getToNode().getCoord(); coords.add(new Coordinate(toNode.getX(), toNode.getY(), 0.)); } - + //add the endlink toNode = this.network.getLinks().get(transitRoute.getRoute().getEndLinkId()).getToNode().getCoord(); coords.add(new Coordinate(toNode.getX(), toNode.getY(), 0.)); - + // create an array Coordinate[] coord = new Coordinate[coords.size()]; coord = coords.toArray(coord); @@ -173,7 +173,7 @@ private Object[] getRouteFeatureAttribs(TransitRoute transitRoute, Id counts, Map mo } } try{ - ShapeFileWriter.writeGeometries(features, file); + GeoFileWriter.writeGeometries(features, file); }catch(ServiceConfigurationError e){ e.printStackTrace(); } catch (UncheckedIOException e) { diff --git a/contribs/vsp/src/main/java/playground/vsp/analysis/utils/heatMap/HeatMap.java b/contribs/vsp/src/main/java/playground/vsp/analysis/utils/heatMap/HeatMap.java index 6bbf5bbb7ce..f8441de40be 100644 --- a/contribs/vsp/src/main/java/playground/vsp/analysis/utils/heatMap/HeatMap.java +++ b/contribs/vsp/src/main/java/playground/vsp/analysis/utils/heatMap/HeatMap.java @@ -35,7 +35,7 @@ import org.matsim.core.utils.collections.QuadTree; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; /** @@ -52,9 +52,9 @@ public class HeatMap { private double maxY = -Double.MAX_VALUE; private QuadTree tiles; private Integer gridSize; - + private class Tile{ - + private Polygon area; private Double value; @@ -78,34 +78,34 @@ public Tile(double minX, double maxX, double minY, double maxY) { this.area = factory.createPolygon(factory.createLinearRing(c), null); this.value = 0.; } - + public Coord getCentroid(){ return MGC.point2Coord(this.area.getCentroid()); } - + public Polygon getGeometry(){ return this.area; } - + public void add(Double d){ this.value += d; } - + public double getValue(){ return this.value; } } - + /** * a class to create a simple HeatMap from coordinates and corresponding values. - * + * * @param gridSize, the number of tiles of the HeatMap */ public HeatMap(Integer gridSize) { this.gridSize = gridSize; this.values = new ArrayList>(); } - + public void addValue(Coord coord, Double value){ this.values.add(new Tuple(coord, value)); findMinMax(coord); @@ -128,7 +128,7 @@ private void findMinMax(Coord coord) { this.minY = coord.getY(); } } - + public void createHeatMap(){ this.tiles = new QuadTree(minX, minY, maxX, maxY); Double dx, dy, inc; @@ -171,7 +171,7 @@ public void createHeatMap(){ t.add(v.getSecond()); } } - + private Collection getTiles() { return this.tiles.values(); } @@ -184,9 +184,9 @@ public static void writeHeatMapShape(String name, HeatMap heatmap, String file, b.add("name", String.class); b.add("count", Double.class); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(b.buildFeatureType()); - + Collection features = new ArrayList(); - + Object[] featureAttribs; int i = 0; for(Tile t: heatmap.getTiles()){ @@ -205,7 +205,7 @@ public static void writeHeatMapShape(String name, HeatMap heatmap, String file, log.info("There are no tiles for " + name); } else { try{ - ShapeFileWriter.writeGeometries(features, file); + GeoFileWriter.writeGeometries(features, file); }catch(ServiceConfigurationError e){ e.printStackTrace(); } diff --git a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/RouteAllModesAsCar.java b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/RouteAllModesAsCar.java index e003b97fc9f..28ddbf7ab00 100644 --- a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/RouteAllModesAsCar.java +++ b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/RouteAllModesAsCar.java @@ -54,19 +54,19 @@ import org.matsim.core.router.util.LeastCostPathCalculator.Path; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.pt.PtConstants; import org.opengis.feature.simple.SimpleFeature; /** * Routes all modes of transport (except for transit_walk) as car modes and counts the number of trips per link. * Output as Shape - * + * * @author aneumann * */ public class RouteAllModesAsCar extends AbstractPersonFilter { - + private final static Logger log = LogManager.getLogger(RouteAllModesAsCar.class); private final Scenario sc; @@ -74,16 +74,16 @@ public class RouteAllModesAsCar extends AbstractPersonFilter { private HashMap link2totals = new HashMap(); private HashMap> mode2link2totals = new HashMap>(); - + public RouteAllModesAsCar(String networkFilename) { Gbl.startMeasurement(); Gbl.printMemoryUsage(); - + // read input data this.sc = ScenarioUtils.createScenario(ConfigUtils.createConfig()); this.sc.getConfig().network().setInputFile(networkFilename); ScenarioUtils.loadScenario(this.sc); - + FreespeedTravelTimeAndDisutility tC = new FreespeedTravelTimeAndDisutility(-6.0, 0.0, 0.0); this.routingAlgo = new DijkstraFactory().createPathCalculator(this.sc.getNetwork(), tC, tC); @SuppressWarnings("serial") @@ -99,7 +99,7 @@ public static void main(String[] args) { String networkFilename = args[1]; String popFilename = args[2]; String targetCoordinateSystem = args[3]; - + RouteAllModesAsCar routeAllModesAsCar = new RouteAllModesAsCar(networkFilename); routeAllModesAsCar.run(popFilename); routeAllModesAsCar.writeAsShape(outputDir, targetCoordinateSystem); @@ -115,15 +115,15 @@ private void writeAsShape(String outputDir, String targetCoordinateSystem) { for (String mode : this.mode2link2totals.keySet()) { typeBuilder.add("count " + mode, Double.class); } - + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(typeBuilder.buildFeatureType()); - + Collection features = new ArrayList(); - + Object[] featureAttribs; - + for(Link link: this.sc.getNetwork().getLinks().values()){ - + featureAttribs = createLinkFeatureAttribs(link, new Object[3 + this.mode2link2totals.keySet().size()]); // skip links without count @@ -137,25 +137,25 @@ private void writeAsShape(String outputDir, String targetCoordinateSystem) { e1.printStackTrace(); } } - - ShapeFileWriter.writeGeometries(features, outputDir + "routeAllModesAsCar.shp"); + + GeoFileWriter.writeGeometries(features, outputDir + "routeAllModesAsCar.shp"); } private Object[] createLinkFeatureAttribs(Link link, Object[] objects) { if(this.link2totals.get(link) == null) { return null; } - + int total = this.link2totals.get(link).intValue(); - + Coordinate[] coord = new Coordinate[2]; coord[0] = new Coordinate(link.getFromNode().getCoord().getX(), link.getFromNode().getCoord().getY(), 0.); coord[1] = new Coordinate(link.getToNode().getCoord().getX(), link.getToNode().getCoord().getY(), 0.); - + objects[0] = new GeometryFactory().createLineString(coord); objects[1] = link.getId().toString(); objects[2] = total; - + int index = 3; for(String mode : this.mode2link2totals.keySet()){ if (this.mode2link2totals.get(mode).get(link) == null) { @@ -195,27 +195,27 @@ public void run(Person person) { count(); Activity lastAct = null; String currentMode = null; - + for (PlanElement planElement : person.getSelectedPlan().getPlanElements()) { if (planElement instanceof Activity) { Activity act = (Activity) planElement; - + if (act.getType().equalsIgnoreCase(PtConstants.TRANSIT_ACTIVITY_TYPE)) { continue; } - + if (lastAct == null) { lastAct = act; } else { Link lastLink = this.sc.getNetwork().getLinks().get(lastAct.getLinkId()); Link currentLink = this.sc.getNetwork().getLinks().get(act.getLinkId()); - + Path path = this.routingAlgo.calcLeastCostPath(lastLink.getToNode(), currentLink.getFromNode(), 0.0, null, null); this.storeLinks(currentMode, path.links); lastAct = act; } } - + if (planElement instanceof Leg) { Leg leg = (Leg) planElement; if (!leg.getMode().equalsIgnoreCase(TransportMode.transit_walk)) { @@ -228,11 +228,11 @@ public void run(Person person) { private void storeLinks(String currentMode, List links) { for (Link link : links) { this.addOne(this.link2totals, link); - + if (this.mode2link2totals.get(currentMode) == null) { this.mode2link2totals.put(currentMode, new HashMap()); } - + this.addOne(this.mode2link2totals.get(currentMode), link); } } diff --git a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/filterActsPerShape/WorkHomeShapeCounter.java b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/filterActsPerShape/WorkHomeShapeCounter.java index e32cc258ad3..c7489566966 100644 --- a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/filterActsPerShape/WorkHomeShapeCounter.java +++ b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/ana/filterActsPerShape/WorkHomeShapeCounter.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.util.HashMap; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.geotools.data.simple.SimpleFeatureIterator; @@ -40,74 +39,74 @@ import org.matsim.core.population.PersonUtils; import org.matsim.core.population.algorithms.AbstractPersonAlgorithm; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; public class WorkHomeShapeCounter extends AbstractPersonAlgorithm{ - + private static final Logger log = LogManager.getLogger(WorkHomeShapeCounter.class); private final Coord minXY; private final Coord maxXY; private String actTypeOne; private String actTypeTwo; - + private boolean initDone = false; private String shapeFile; private SimpleFeatureSource featureSource; private HashMap polygonCountMap = new HashMap(); private HashMap polyNameMap = new HashMap(); - + @Override public void run(Person person) { - + if(!this.initDone){ init(); } - + int nofPlans = person.getPlans().size(); - + for (int planId = 0; planId < nofPlans; planId++) { Plan plan = person.getPlans().get(planId); - + // search selected plan if (PersonUtils.isSelected(plan)) { - - // do something + + // do something for (PlanElement pEOne : plan.getPlanElements()) { if(pEOne instanceof Activity){ - - // check - actTypeOne in search area? - Activity actOne = (Activity) pEOne; + + // check - actTypeOne in search area? + Activity actOne = (Activity) pEOne; if(actOne.getType().equalsIgnoreCase(this.actTypeOne)){ // it is of type actTypeOne -> check coords - + if(actIsSituatedInGivenSearchArea(actOne)){ // act one ok, check type two; - + for (PlanElement pETwo : plan.getPlanElements()) { if(pETwo instanceof Activity){ - - Activity actTwo = (Activity) pETwo; + + Activity actTwo = (Activity) pETwo; if(actTwo.getType().equalsIgnoreCase(this.actTypeTwo)){ - - // act two ok - search corresponding shape + + // act two ok - search corresponding shape searchCorrespondingShape(actTwo); break; } - - } - } + + } + } break; - } - - } - + } + + } + } } - + } - } + } } public WorkHomeShapeCounter(Coord minXY, Coord maxXY, String actTypeOne, String actTypeTwo, String shapeFile){ @@ -130,14 +129,14 @@ public String toString() { } return strB.toString(); } - + public void toFile(String filename){ - + int numberOfActs = 0; for (Polygon poly : this.polygonCountMap.keySet()) { numberOfActs += this.polygonCountMap.get(poly).intValue(); } - + try { BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename))); writer.write("#Results with " + this.actTypeOne + " act in minXY " + this.minXY + " maxXY " + this.maxXY + " and " + this.actTypeTwo + " act located in"); writer.newLine(); @@ -159,10 +158,10 @@ public void toFile(String filename){ * Put all polygon in an iterable list */ private void init() { - + try { - this.featureSource = ShapeFileReader.readDataFile(this.shapeFile); - + this.featureSource = GeoFileReader.readDataFile(this.shapeFile); + SimpleFeatureIterator fIt = this.featureSource.getFeatures().features(); while (fIt.hasNext()) { SimpleFeature ft = fIt.next(); @@ -174,13 +173,13 @@ private void init() { this.polyNameMap.put(poly, (String) ft.getAttribute("Bezirk")); } } - } - + } + } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } - + this.initDone = true; } @@ -189,8 +188,8 @@ private boolean actIsSituatedInGivenSearchArea(Activity act) { if(this.minXY.getY() > act.getCoord().getY()){ return false;} if(this.maxXY.getX() < act.getCoord().getX()){ return false;} if(this.maxXY.getY() < act.getCoord().getY()){ return false;} - - return true; + + return true; } private void searchCorrespondingShape(Activity actTwo) { diff --git a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pop/FilterPopulationByShape.java b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pop/FilterPopulationByShape.java index 5b4ada222e5..63fede8cee6 100644 --- a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pop/FilterPopulationByShape.java +++ b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pop/FilterPopulationByShape.java @@ -52,53 +52,53 @@ import org.matsim.core.population.io.StreamingPopulationWriter; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; /** * Filters a given Population by a given shape - includes all routed modes - * + * * @author aneumann * */ public class FilterPopulationByShape implements LinkEnterEventHandler, PersonEntersVehicleEventHandler, PersonLeavesVehicleEventHandler{ - + private final static Logger log = LogManager.getLogger(FilterPopulationByShape.class); private GeometryFactory factory; private Geometry areaToExclude; private Geometry areaToInclude; private TreeSet includeLinks; private TreeSet agentsToKeep; - + private HashMap> vehId2AgentIdsMap; - + public FilterPopulationByShape(String netFile, String popInFile, String eventsFile, String areaShapeFile, String popOutFile){ Gbl.startMeasurement(); Gbl.printMemoryUsage(); - + log.info("Network: " + netFile); log.info("Population: " + popInFile); log.info("Events: " + eventsFile); log.info("Shape: " + areaShapeFile); log.info("Population out: " + popOutFile); - + Scenario sc = ScenarioUtils.createScenario(ConfigUtils.createConfig()); sc.getConfig().network().setInputFile(netFile); ScenarioUtils.loadScenario(sc); - + this.factory = new GeometryFactory(); this.areaToExclude = this.factory.buildGeometry(new ArrayList()); this.createServiceAreaShp(areaShapeFile); this.includeLinks = new TreeSet(); - - log.info(sc.getNetwork().getLinks().values().size() + " links in given network"); + + log.info(sc.getNetwork().getLinks().values().size() + " links in given network"); for (Link link : sc.getNetwork().getLinks().values()) { if (this.linkToNodeInServiceArea(link)) { this.includeLinks.add(link.getId().toString()); } - } + } log.info(this.includeLinks.size() + " links in area of interest"); - + this.agentsToKeep = new TreeSet(); this.vehId2AgentIdsMap = new HashMap>(); EventsManager eventsManager = EventsUtils.createEventsManager(); @@ -108,17 +108,17 @@ public FilterPopulationByShape(String netFile, String popInFile, String eventsFi log.info("Found " + this.agentsToKeep.size() + " agent ids to keep."); Gbl.printMemoryUsage(); Gbl.printElapsedTime(); - + sc.getConfig().plans().setInputFile(popInFile); Population pop = (Population) sc.getPopulation(); StreamingDeprecated.setIsStreaming(pop, true); PopulationReader popReader = new PopulationReader(sc); StreamingPopulationWriter popWriter = new StreamingPopulationWriter(); popWriter.startStreaming(popOutFile); - + StreamingDeprecated.addAlgorithm(pop, new PersonIdFilter(this.agentsToKeep, popWriter)); Gbl.printMemoryUsage(); - + popReader.readFile(popInFile); PopulationUtils.printPlansCount(pop) ; popWriter.closeStreaming(); @@ -129,7 +129,7 @@ public FilterPopulationByShape(String netFile, String popInFile, String eventsFi @Override public void reset(int iteration) { // TODO Auto-generated method stub - + } @Override @@ -138,7 +138,7 @@ public void handleEvent(LinkEnterEvent event) { // link not of interest return; } - + if (this.vehId2AgentIdsMap.get(event.getVehicleId()) == null) { // it's a private car this.agentsToKeep.add(event.getDriverId()); @@ -150,7 +150,7 @@ public void handleEvent(LinkEnterEvent event) { @Override public void handleEvent(PersonLeavesVehicleEvent event) { - this.vehId2AgentIdsMap.get(event.getVehicleId()).remove(event.getPersonId()); + this.vehId2AgentIdsMap.get(event.getVehicleId()).remove(event.getPersonId()); } @Override @@ -158,8 +158,8 @@ public void handleEvent(PersonEntersVehicleEvent event) { if (this.vehId2AgentIdsMap.get(event.getVehicleId()) == null) { this.vehId2AgentIdsMap.put(event.getVehicleId(), new TreeSet()); } - - this.vehId2AgentIdsMap.get(event.getVehicleId()).add(event.getPersonId()); + + this.vehId2AgentIdsMap.get(event.getVehicleId()).add(event.getPersonId()); } private boolean linkToNodeInServiceArea(Link link) { @@ -172,12 +172,12 @@ private boolean linkToNodeInServiceArea(Link link) { } return false; } - + private void createServiceAreaShp(String serviceAreaFile) { - Collection features = ShapeFileReader.getAllFeatures(serviceAreaFile); + Collection features = GeoFileReader.getAllFeatures(serviceAreaFile); Collection include = new ArrayList(); Collection exclude = new ArrayList(); - + for (SimpleFeature f: features) { boolean incl = true; Geometry g = null; @@ -188,7 +188,7 @@ private void createServiceAreaShp(String serviceAreaFile) { g = (Geometry) o; } // TODO use a better way to get the attributes, maybe directly per index. - // Now the last attribute is used per default... + // Now the last attribute is used per default... else if (o instanceof String){ incl = Boolean.parseBoolean((String) o); } @@ -201,12 +201,12 @@ else if (o instanceof String){ } } } - this.areaToInclude = this.factory.createGeometryCollection( + this.areaToInclude = this.factory.createGeometryCollection( include.toArray(new Geometry[include.size()])).buffer(0); - this.areaToExclude = this.factory.createGeometryCollection( + this.areaToExclude = this.factory.createGeometryCollection( exclude.toArray(new Geometry[exclude.size()])).buffer(0); } - + public static void main(String[] args) { // Charlottenburg // String netFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/network.final.xml.gz"; @@ -214,22 +214,22 @@ public static void main(String[] args) { // String eventsFile = "e:/berlin-bvg09_runs/bvg.run189.10pct/ITERS/it.100/bvg.run189.10pct.100.events.xml.gz"; // String areaShapeFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/area/simulated_area.shp"; // String popOutFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/plans/bvg.run189.10pct.100.plans.selected_movedToTXL_diluted.xml.gz"; - + // Spandau // String netFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/network.final.xml.gz"; // String popInFile = "e:/berlin-bvg09_runs/bvg.run192.100pct/ITERS/it.100/bvg.run192.100pct.100.plans.selected.xml.gz"; // String eventsFile = "e:/berlin-bvg09_runs/bvg.run192.100pct/ITERS/it.100/bvg.run192.100pct.100.events.xml.gz"; // String areaShapeFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/area/spandau_area.shp"; // String popOutFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/plans/bvg.run192.100pct.100.plans.selected_spandau.xml.gz"; - + // Spandau neu String netFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/network.final.xml.gz"; String popInFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/plans/bvg.run189.10pct.100.plans.selected_movedToTXL.xml.gz"; String eventsFile = "e:/berlin-bvg09_runs/bvg.run189.10pct/ITERS/it.100/bvg.run189.10pct.100.events.xml.gz"; String areaShapeFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/area/spandau_area.shp"; String popOutFile = "e:/_shared-svn/andreas/paratransit/input/trb_2012/plans/bvg.run189.10pct.100.plans.selected_movedToTXL_spandau.xml.gz"; - + FilterPopulationByShape filter = new FilterPopulationByShape(netFile, popInFile, eventsFile, areaShapeFile, popOutFile); } - + } diff --git a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pt/transitSchedule2shape/DaShapeWriter.java b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pt/transitSchedule2shape/DaShapeWriter.java index 0a8aa167350..600ff620232 100644 --- a/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pt/transitSchedule2shape/DaShapeWriter.java +++ b/contribs/vsp/src/main/java/playground/vsp/andreas/utils/pt/transitSchedule2shape/DaShapeWriter.java @@ -43,7 +43,7 @@ import org.matsim.api.core.v01.network.Node; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.pt.transitSchedule.api.TransitLine; import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitRouteStop; @@ -52,7 +52,7 @@ import org.opengis.feature.simple.SimpleFeature; /** - * + * * @author droeder * */ @@ -60,9 +60,9 @@ public class DaShapeWriter { private static final Logger log = LogManager.getLogger(DaShapeWriter.class); private static SimpleFeatureBuilder builder; - + private static GeometryFactory geometryFactory = new GeometryFactory(); - + public static void writeLinks2Shape(String fileName, Map links, Map> attributes, String targetCoordinateSystem){ if(attributes == null){ initLineFeatureType("links", null, true, targetCoordinateSystem); @@ -75,12 +75,12 @@ public static void writeLinks2Shape(String fileName, Map lin write(createLinkFeatures(links, attributes), fileName); } - + public static void writeNodes2Shape(String fileName, Map nodes, String targetCoordinateSystem){ initPointFeatureType("nodes", null, targetCoordinateSystem); write(createNodeFeatures(nodes), fileName); } - + public static void writeDefaultPoints2Shape(String fileName, String name, Map points, Map> attributes, String targetCoordinateSystem){ if(attributes == null){ initPointFeatureType(name, null, targetCoordinateSystem); @@ -92,10 +92,10 @@ public static void writeDefaultPoints2Shape(String fileName, String name, Map> points, Map> attributes, String targetCoordinateSystem){ for (SortedMap m : attributes.values()){ initLineFeatureType("distance", m, targetCoordinateSystem); break; } - + write(createPointDistanceFeatures(points,attributes), fileName); } /** @@ -140,7 +140,7 @@ public static void writePointDist2Shape (String fileName, Map> lineStrings, Map> attributes, String targetCoordinateSystem){ - + if(attributes == null){ initLineFeatureType(name, null, targetCoordinateSystem); }else{ @@ -149,10 +149,10 @@ public static void writeDefaultLineString2Shape(String fileName, String name, M break; } } - + write(createDefaultLineStringFeature(lineStrings, attributes), fileName); } - + public static void writeDefaultLineStrings2Shape(String fileName, String name, Map> lineStrings, String targetCoordinateSystem){ Map> map = new HashMap>(); SortedMap ls; @@ -165,20 +165,20 @@ public static void writeDefaultLineStrings2Shape(String fileName, String name, M } writeDefaultLineString2Shape(fileName, name, map, null, targetCoordinateSystem); } - + private static void write(Collection features, String shapeFileOutName){ if(features.isEmpty()){ log.error("can not write " + shapeFileOutName + ", because featurelist is empty..."); }else{ - ShapeFileWriter.writeGeometries(features, shapeFileOutName); - log.info(shapeFileOutName + " written!"); + GeoFileWriter.writeGeometries(features, shapeFileOutName); + log.info(shapeFileOutName + " written!"); } } - + private static void initLineFeatureType(String name, SortedMap attributes, String targetCoordinateSystem){ initLineFeatureType(name, attributes, false, targetCoordinateSystem); } - + private static void initLineFeatureType(String name, SortedMap attributes, boolean links, String targetCoordinateSystem) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName(name); @@ -198,7 +198,7 @@ private static void initLineFeatureType(String name, SortedMap a builder = new SimpleFeatureBuilder(b.buildFeatureType()); } - + private static void initPointFeatureType(String name, SortedMap attributes, String targetCoordinateSystem){ SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName(name); @@ -210,15 +210,15 @@ private static void initPointFeatureType(String name, SortedMap b.add(e.getKey(), e.getValue().getClass()); } } - + builder = new SimpleFeatureBuilder(b.buildFeatureType()); } - + private static Collection createRouteFeatures(TransitSchedule schedule, Collection lines2write, Map> attributes){ Collection features = new ArrayList(); SimpleFeature feature; Coordinate[] coord; - + for (TransitLine line : schedule.getTransitLines().values()){ if( (lines2write == null) || lines2write.contains(line.getId())){ for(TransitRoute route : line.getRoutes().values()){ @@ -239,7 +239,7 @@ private static Collection createRouteFeatures(TransitSchedule sch } return features; } - + private static Collection createLinkFeatures(Map links, Map> attributes) { Collection features = new ArrayList(); SimpleFeature feature; @@ -261,12 +261,12 @@ private static Collection createLinkFeatures(Map createPointDistanceFeatures(Map> points, Map> attributes){ Collection features = new ArrayList(); SimpleFeature feature; Coordinate[] coord; - + for(Entry> e : points.entrySet()) { coord = new Coordinate[2]; coord[0] = MGC.coord2Coordinate(e.getValue().getFirst()); @@ -274,14 +274,14 @@ private static Collection createPointDistanceFeatures(Map createStopFeatures(Map stops, Collection stops2write){ Collection features = new ArrayList(); SimpleFeature feature; - + for(TransitStopFacility stop : stops.values()){ if((stops2write == null) || stops2write.contains(stop.getId())){ feature = getPointFeature(stop.getCoord(), stop.getId().toString(), null); @@ -290,22 +290,22 @@ private static Collection createStopFeatures(Map createNodeFeatures(Map nodes){ Collection features = new ArrayList(); SimpleFeature feature; - + for(Node n : nodes.values()){ feature = getPointFeature(n.getCoord(), n.getId().toString(), null); features.add(feature); } return features; } - + private static Collection createDefaultPointFeature(Map points, Map> attributes){ Collection features = new ArrayList(); SimpleFeature feature; - + for(Entry e: points.entrySet()){ if(attributes == null){ feature = getPointFeature(e.getValue(), e.getKey(), null); @@ -314,15 +314,15 @@ private static Collection createDefaultPointFeature(Map createDefaultLineStringFeature(Map> lineStrings, Map> attributes) { Collection features = new ArrayList(); SimpleFeature feature; Coordinate[] coord; - + for(Entry> points : lineStrings.entrySet()){ if (points.getValue().size()<2){ log.error(points.getKey() + ": not enough points for a lineString. Need at least 2 points!"); @@ -336,20 +336,20 @@ private static Collection createDefaultLineStringFeature(Map attributes) { LineString s = geometryFactory.createLineString(c); Object[] attribs ; @@ -361,21 +361,21 @@ private static SimpleFeature getLineStringFeature(CoordinateArraySequence c, Str attribs[0] = s; attribs[1] = name; Integer count = 2; - + if(!(attributes == null)){ for(Object o : attributes.values()){ attribs[count] = o; count++; } } - + try { return builder.buildFeature(name, attribs); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } } - + private static SimpleFeature getPointFeature(Coord coord, String id, SortedMap attributes) { Point p = geometryFactory.createPoint(MGC.coord2Coordinate(coord)); Object [] attribs ; @@ -387,7 +387,7 @@ private static SimpleFeature getPointFeature(Coord coord, String id, SortedMap sha for (String shapeFile : shapeFileToFeatureKey.keySet()) { String key = shapeFileToFeatureKey.get(shapeFile); LOG.info("Processing zone file " + shapeFile + " with feature key " + key); - Collection features = ShapeFileReader.getAllFeatures(shapeFile); + Collection features = GeoFileReader.getAllFeatures(shapeFile); for (SimpleFeature feature : features) { Geometry geometry = (Geometry) feature.getDefaultGeometry(); String shapeId = Cemdap2MatsimUtils.removeLeadingZeroFromString((String) feature.getAttribute(key)); @@ -105,7 +105,7 @@ public CORINELandCoverCoordsModifier(String matsimPlans, Map sha this.sameHomeActivity = sameHomeActivity; if (this.sameHomeActivity) LOG.info("Home activities for a person will be at the same location."); - + this.homeActivityPrefix = homeActivityPrefix; } @@ -157,7 +157,7 @@ public static void main(String[] args) { public void process() { LOG.info("Start processing, this may take a while ... "); - + int personCounter = 0; for (Person person : population.getPersons().values()) { Coord homeLocationCoord = null; @@ -166,7 +166,7 @@ public void process() { if (personCounter%1000 == 0) { LOG.info("Person #" + personCounter); } - + for (Plan plan : person.getPlans()) { for (PlanElement planElement : plan.getPlanElements()) { if (planElement instanceof Activity) { diff --git a/contribs/vsp/src/main/java/playground/vsp/corineLandcover/CorineLandCoverData.java b/contribs/vsp/src/main/java/playground/vsp/corineLandcover/CorineLandCoverData.java index f9f10f9bcae..d8b0584a210 100644 --- a/contribs/vsp/src/main/java/playground/vsp/corineLandcover/CorineLandCoverData.java +++ b/contribs/vsp/src/main/java/playground/vsp/corineLandcover/CorineLandCoverData.java @@ -31,7 +31,7 @@ import org.locationtech.jts.geom.Point; import org.matsim.api.core.v01.Coord; import org.matsim.core.gbl.Gbl; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; /** @@ -57,7 +57,7 @@ public class CorineLandCoverData { public CorineLandCoverData( String corineLandCoverShapeFile, boolean simplifyGeometries, boolean combiningGeom) { LOGGER.info("Reading CORINE landcover shape file . . ."); - Collection landCoverFeatures = ShapeFileReader.getAllFeatures(corineLandCoverShapeFile); + Collection landCoverFeatures = GeoFileReader.getAllFeatures(corineLandCoverShapeFile); this.simplifyGeometries = simplifyGeometries; if (this.simplifyGeometries) LOGGER.warn("Geometries will be simplified such that number of vertices in each geometry is less than 1000. " + diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/CommuterGenerator.java b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/CommuterGenerator.java index 675e4edaf88..41c2466c84d 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/CommuterGenerator.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/CommuterGenerator.java @@ -33,7 +33,7 @@ import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -42,11 +42,11 @@ * @author dgrether */ public class CommuterGenerator { - + private static final Logger log = LogManager.getLogger(CommuterGenerator.class); - + public static void main(String[] args) throws Exception { - + String baseDir = "../../../shared-svn/projects/cottbus/data/scenarios/cottbus_scenario/"; String networkFile = baseDir + "network_wgs84_utm33n.xml.gz"; boolean useLanduse = true; @@ -54,7 +54,7 @@ public static void main(String[] args) throws Exception { // String shapesOutputDirectory = populationOutputDirectory + "shapes_all_modes/"; String shapesOutputDirectory = populationOutputDirectory + "shapes/"; - + // String populationOutputFile = populationOutputDirectory + "commuter_population_wgs84_utm33n.xml.gz"; String populationOutputFile = populationOutputDirectory + "commuter_population_wgs84_utm33n_car_only.xml.gz"; OutputDirectoryLogging.initLoggingWithOutputDirectory(populationOutputDirectory); @@ -63,24 +63,24 @@ public static void main(String[] args) throws Exception { if (! shapes.exists() ) { shapes.mkdir(); } - + Config config1 = ConfigUtils.createConfig(); config1.network().setInputFile(networkFile); Scenario sc = ScenarioUtils.loadScenario(config1); - + CommuterDataReader cdr = new CommuterDataReader(); cdr.addFilterRange(12071000); cdr.addFilter("12052000"); //12052000 == cottbus stadt cdr.readFile("../../../shared-svn/studies/countries/de/pendler_nach_gemeinden/brandenburg_einpendler.csv"); // cdr.getCommuterRelations().add(new CommuterDataElement("12052000", "12052000", 1000)); - + String gemeindenBrandenburgShapeFile = "../../../shared-svn/studies/countries/de/brandenburg_gemeinde_kreisgrenzen/gemeinden/dlm_gemeinden.shp"; - ShapeFileReader gemeindenReader = new ShapeFileReader(); + GeoFileReader gemeindenReader = new GeoFileReader(); Collection gemeindenFeatures = gemeindenReader.readFileAndInitialize(gemeindenBrandenburgShapeFile); - - - - CommuterDemandWriter cdw = new CommuterDemandWriter(gemeindenFeatures, gemeindenReader.getCoordinateSystem(), + + + + CommuterDemandWriter cdw = new CommuterDemandWriter(gemeindenFeatures, gemeindenReader.getCoordinateSystem(), cdr.getCommuterRelations(), MGC.getCRS(TransformationFactory.WGS84_UTM33N)); //landuse if (useLanduse){ @@ -90,31 +90,31 @@ public static void main(String[] args) throws Exception { cdw.addLanduse("home", homeLanduse); cdw.addLanduse("work", workLanduse); } -// +// // cdw.setScalefactor(1.0); // all modes cdw.setScalefactor(0.55); //car mode share // cdw.setScalefactor(0.1); //testing - + cdw.computeDemand(sc); PopulationWriter populationWriter = new PopulationWriter(sc.getPopulation(), sc.getNetwork()); populationWriter.write(populationOutputFile); log.info("population written to " + populationOutputFile); - + //write some test output Config config = ConfigUtils.createConfig(); config.network().setInputFile(networkFile); config.plans().setInputFile(populationOutputFile); Scenario baseScenario = ScenarioUtils.loadScenario(config); - + String shapeFilename = shapesOutputDirectory + "commuter_population_home.shp"; new DgPopulation2ShapeWriter(baseScenario.getPopulation(), MGC.getCRS(TransformationFactory.WGS84_UTM33N)) .write("home", shapeFilename, MGC.getCRS(TransformationFactory.WGS84_UTM33N)); shapeFilename = shapesOutputDirectory + "commuter_population_work.shp"; new DgPopulation2ShapeWriter(baseScenario.getPopulation(), MGC.getCRS(TransformationFactory.WGS84_UTM33N)) .write("work", shapeFilename, MGC.getCRS(TransformationFactory.WGS84_UTM33N)); - + log.info("done!"); OutputDirectoryLogging.closeOutputDirLogging(); } diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgLanduseReader.java b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgLanduseReader.java index c500ee81af4..6a7b47969d3 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgLanduseReader.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgLanduseReader.java @@ -23,7 +23,7 @@ import java.util.HashSet; import org.matsim.core.utils.collections.Tuple; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -33,9 +33,9 @@ * */ public class DgLanduseReader { - + public static final String BASE_DIR = "../../../shared-svn/studies/countries/de/berlin_brandenburg_corine_landcover/shapefiles/"; - + private static final String STADTGEFUEGE_KONTINUIERLICH = BASE_DIR + "clc06_c111.shp"; private static final String STADTGEFUEGE_NICHT_KONTINUIERLICH = BASE_DIR + "clc06_c112.shp"; private static final String INDUSTRIE_GEWERBEGEBIETE = BASE_DIR + "clc06_c121.shp"; @@ -45,33 +45,33 @@ public class DgLanduseReader { // private static final String MINERALE_ABBAUSTAETTEN = BASE_DIR + "clc06_c131.shp"; private static final String DEPONIEN = BASE_DIR + "clc06_c132.shp"; private static final String BAUSTELLEN = BASE_DIR + "clc06_c133.shp"; - - + + private static final String[] landuse_files_home = {STADTGEFUEGE_KONTINUIERLICH, STADTGEFUEGE_NICHT_KONTINUIERLICH}; // private static final String[] landuse_files_home = {STADTGEFUEGE_NICHT_KONTINUIERLICH}; - - private static final String[] landuse_files_work = {STADTGEFUEGE_KONTINUIERLICH, + + private static final String[] landuse_files_work = {STADTGEFUEGE_KONTINUIERLICH, STADTGEFUEGE_NICHT_KONTINUIERLICH, INDUSTRIE_GEWERBEGEBIETE, STRASSEN_SCHIENENNETZE_GEBAUEDE, HAFENGEBIET, FLUGHAEFEN, DEPONIEN, BAUSTELLEN - /* skip mines because they cause disproportional many work places and, thus, to much traffic in their region */ + /* skip mines because they cause disproportional many work places and, thus, to much traffic in their region */ // , MINERALE_ABBAUSTAETTEN }; // private static final String[] landuse_files_work = {INDUSTRIE_GEWERBEGEBIETE}; - + public Tuple,CoordinateReferenceSystem> readLanduseDataHome(){ return this.readLanduseData(landuse_files_home); } - + public Tuple,CoordinateReferenceSystem> readLanduseDataWork(){ return this.readLanduseData(landuse_files_work); } - + private Tuple, CoordinateReferenceSystem> readLanduseData(String[] shapefiles){ - ShapeFileReader shapeReader = new ShapeFileReader(); + GeoFileReader shapeReader = new GeoFileReader(); Collection allFeatures = new HashSet(); Collection currentFeatures = null; for (String filename : shapefiles){ diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgPopulation2ShapeWriter.java b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgPopulation2ShapeWriter.java index 9c8609b2540..943f51c177f 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgPopulation2ShapeWriter.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/commuterDemandCottbus/DgPopulation2ShapeWriter.java @@ -32,7 +32,7 @@ import org.matsim.api.core.v01.population.Population; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PointFeatureFactory; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; @@ -44,7 +44,7 @@ */ public class DgPopulation2ShapeWriter { - + private Population pop; private CoordinateReferenceSystem popCrs; @@ -53,7 +53,7 @@ public DgPopulation2ShapeWriter(Population pop, CoordinateReferenceSystem crs){ this.pop = pop; this.popCrs = crs; } - + public void write(String activityType, String filename, CoordinateReferenceSystem targetCrs){ try { MathTransform transformation = CRS.findMathTransform(this.popCrs, targetCrs, true); @@ -66,7 +66,7 @@ public void write(String activityType, String filename, CoordinateReferenceSyste addAttribute("start_time", Double.class). addAttribute("end_time", Double.class). create(); - + List features = new ArrayList(); SimpleFeature f = null; for (Person p : this.pop.getPersons().values()){ @@ -75,7 +75,7 @@ public void write(String activityType, String filename, CoordinateReferenceSyste if (pe instanceof Activity){ Activity activity = (Activity) pe; if (activity.getType().compareTo(activityType) == 0){ - + String id = p.getId().toString(); String type = activity.getType(); Double startTime = activity.getStartTime().seconds(); @@ -83,20 +83,20 @@ public void write(String activityType, String filename, CoordinateReferenceSyste Coordinate actCoordinate = MGC.coord2Coordinate(activity.getCoord()); actCoordinate = JTS.transform(actCoordinate, actCoordinate, transformation); - + f = factory.createPoint(actCoordinate, new Object[] {id, type, startTime, endTime}, null); features.add(f); } } } } - - ShapeFileWriter.writeGeometries(features, filename); + + GeoFileWriter.writeGeometries(features, filename); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } - + } - + } diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/GVPlanReader.java b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/GVPlanReader.java index 9f162db4bd3..113c347994a 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/GVPlanReader.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/GVPlanReader.java @@ -32,25 +32,25 @@ import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.pipeline.PopulationReaderTask; import playground.vsp.pipeline.PopulationWriterTask; public class GVPlanReader { - - + + private static final String GV_NETWORK_FILENAME = "/Users/michaelzilske/workspace/prognose_2025/demand/network_cleaned_wgs84.xml.gz"; - + private static final String NETWORK_FILENAME = "/Users/michaelzilske/osm/motorway_germany.xml"; - + private static final String GV_PLANS = "/Users/michaelzilske/workspace/run1061/1061.output_plans.xml.gz"; - + private static final String FILTER_FILENAME = "/Users/michaelzilske/workspace/prognose_2025/demand/filter.shp"; - + private static final String LANDKREISE = "/Users/michaelzilske/workspace/prognose_2025/osm_zellen/landkreise.shp"; - + private static boolean isCoordInShape(Coord linkCoord, Collection features, GeometryFactory factory) { boolean found = false; Geometry geo = factory.createPoint(new Coordinate(linkCoord.getX(), linkCoord.getY())); @@ -62,21 +62,21 @@ private static boolean isCoordInShape(Coord linkCoord, Collection } return found; } - + public static void main(String[] args) { Scenario gvNetwork = (MutableScenario) ScenarioUtils.createScenario(ConfigUtils.createConfig()); new MatsimNetworkReader(gvNetwork.getNetwork()).readFile(GV_NETWORK_FILENAME); Scenario osmNetwork = (MutableScenario) ScenarioUtils.createScenario(ConfigUtils.createConfig()); new MatsimNetworkReader(osmNetwork.getNetwork()).readFile(NETWORK_FILENAME); Collection featuresInShape; - featuresInShape = new ShapeFileReader().readFileAndInitialize(FILTER_FILENAME); - + featuresInShape = new GeoFileReader().readFileAndInitialize(FILTER_FILENAME); + PopulationReaderTask populationReaderTask = new PopulationReaderTask(GV_PLANS, gvNetwork.getNetwork()); - + PersonDereferencerTask personDereferencerTask = new PersonDereferencerTask(); - + PersonGeoTransformatorTask personGeoTransformatorTask = new PersonGeoTransformatorTask(TransformationFactory.WGS84, TransformationFactory.DHDN_GK4); - + PersonRouterFilter personRouterFilter = new PersonRouterFilter(osmNetwork.getNetwork()); GeometryFactory factory = new GeometryFactory(); for (Node node : osmNetwork.getNetwork().getNodes().values()) { @@ -84,18 +84,18 @@ public static void main(String[] args) { personRouterFilter.getInterestingNodeIds().add(node.getId()); } } - + PersonVerschmiererTask personVerschmiererTask = new PersonVerschmiererTask(LANDKREISE); - + PopulationWriterTask populationWriterTask = new PopulationWriterTask("/Users/michaelzilske/workspace/prognose_2025/demand/naechster_versuch_gv.xml", gvNetwork.getNetwork()); - + populationReaderTask.setSink(personDereferencerTask); personDereferencerTask.setSink(personGeoTransformatorTask); personGeoTransformatorTask.setSink(personRouterFilter); personRouterFilter.setSink(personVerschmiererTask); personVerschmiererTask.setSink(populationWriterTask); - + populationReaderTask.run(); } - + } diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/PendlerMatrixReader.java b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/PendlerMatrixReader.java index 20126b4a8ed..1d53d1a7aa4 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/PendlerMatrixReader.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/PendlerMatrixReader.java @@ -37,7 +37,7 @@ import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.tabularFileParser.TabularFileHandler; import org.matsim.core.utils.io.tabularFileParser.TabularFileParser; import org.matsim.core.utils.io.tabularFileParser.TabularFileParserConfig; @@ -80,7 +80,7 @@ public void run() { } private void readShape() { - Collection landkreise = ShapeFileReader.getAllFeatures(this.shapeFile); + Collection landkreise = GeoFileReader.getAllFeatures(this.shapeFile); ActivityFacilitiesFactory factory = ((MutableScenario)this.sc).getActivityFacilities().getFactory(); for (SimpleFeature landkreis : landkreise) { Integer gemeindeschluessel = Integer.parseInt((String) landkreis.getAttribute("gemeindesc")); diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/Verschmierer.java b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/Verschmierer.java index 183e69f3e48..4ccba3644f7 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/Verschmierer.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/pendlermatrix/Verschmierer.java @@ -29,25 +29,25 @@ import org.locationtech.jts.geom.Point; import org.matsim.api.core.v01.Coord; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; public class Verschmierer { - + private String filename; - + private Map zones = new HashMap(); private Random random = new Random(); - + public Verschmierer(String filename) { this.filename = filename; readShape(); } private void readShape() { - for (SimpleFeature landkreis : ShapeFileReader.getAllFeatures(filename)) { + for (SimpleFeature landkreis : GeoFileReader.getAllFeatures(filename)) { Integer gemeindeschluessel = Integer.parseInt((String) landkreis.getAttribute("gemeindesc")); zones.put(gemeindeschluessel, (Geometry) landkreis.getDefaultGeometry()); } @@ -72,7 +72,7 @@ public Coord shootIntoSameZoneOrLeaveInPlace(Coord coord) { return coord; } } - + private static Point getRandomPointInFeature(Random rnd, Geometry g) { Point p = null; double x, y; @@ -83,13 +83,13 @@ private static Point getRandomPointInFeature(Random rnd, Geometry g) { } while (!g.contains(p)); return p; } - + private Coord doShoot(Geometry zone) { Coord coord; Point point = getRandomPointInFeature(this.random , zone); coord = new Coord(point.getX(), point.getY()); return coord; } - - + + } diff --git a/contribs/vsp/src/main/java/playground/vsp/demandde/prognose2025/DemandMatrixReader.java b/contribs/vsp/src/main/java/playground/vsp/demandde/prognose2025/DemandMatrixReader.java index a59404b6d23..ba6cd54148d 100644 --- a/contribs/vsp/src/main/java/playground/vsp/demandde/prognose2025/DemandMatrixReader.java +++ b/contribs/vsp/src/main/java/playground/vsp/demandde/prognose2025/DemandMatrixReader.java @@ -41,7 +41,7 @@ import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.tabularFileParser.TabularFileHandler; import org.matsim.core.utils.io.tabularFileParser.TabularFileParser; import org.matsim.core.utils.io.tabularFileParser.TabularFileParserConfig; @@ -157,7 +157,7 @@ public void startRow(String[] row) { } private void readShape() { - Collection landkreise = ShapeFileReader.getAllFeatures(this.shapeFile); + Collection landkreise = GeoFileReader.getAllFeatures(this.shapeFile); final ActivityFacilitiesFactory factory = ((MutableScenario)this.sc).getActivityFacilities().getFactory(); for (SimpleFeature landkreis : landkreise) { Integer gemeindeschluessel = Integer.parseInt((String) landkreis.getAttribute("gemeindesc")); diff --git a/contribs/vsp/src/main/java/playground/vsp/ev/README.md b/contribs/vsp/src/main/java/playground/vsp/ev/README.md index f5bd6a45c42..3bde9544245 100644 --- a/contribs/vsp/src/main/java/playground/vsp/ev/README.md +++ b/contribs/vsp/src/main/java/playground/vsp/ev/README.md @@ -19,6 +19,8 @@ and if there is a charger on the home link, it does not search for a suitable ac The final SoC at the end of the iteration is maintained and transferred as the initial SoC to the next iteration. For this to work, vehicles that represent an EV need to be attached to a vehicle type that is tagged as EV by providing a specific attribute (see `MATSimVehicleWrappingEVSpecificationProvider.class`). +By default, _all_ of these (electric) vehicles are planned by to potentially get charged according to the above described logic. +However, one can prevent this on the vehicle level by `UrbanEVUtils.setChargingDuringActivities(vehicle, false)` when defining the input. ## A few notes to the current state of this package (and it's issues and TODOs) diff --git a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVModule.java b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVModule.java index 84e59275d7b..cf03625932d 100644 --- a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVModule.java +++ b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVModule.java @@ -20,6 +20,8 @@ package playground.vsp.ev; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.matsim.contrib.ev.EvModule; import org.matsim.contrib.ev.charging.ChargingModule; import org.matsim.contrib.ev.discharging.DischargingModule; @@ -31,9 +33,6 @@ import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.mobsim.qsim.AbstractQSimModule; - -import com.google.inject.Inject; -import com.google.inject.Singleton; import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigGroup; public class UrbanEVModule extends AbstractModule { @@ -47,13 +46,6 @@ public class UrbanEVModule extends AbstractModule { QSimComponentsConfigGroup qsimComponentsConfig = ConfigUtils.addOrGetModule( config, QSimComponentsConfigGroup.class ); qsimComponentsConfig.addActiveComponent( EvModule.EV_COMPONENT ); -// UrbanEVConfigGroup urbanEVConfig = ConfigUtils.addOrGetModule( config, UrbanEVConfigGroup.class ); - -// if (urbanEVConfig == null) -// throw new IllegalArgumentException( -// "no config group of type " + UrbanEVConfigGroup.GROUP_NAME + " was specified in the config"); - // was this meaningful? I.e. do we want the code to fail if there is no such config group? kai, apr'23 - //standard EV stuff install(new ChargingInfrastructureModule()); install(new ChargingModule()); @@ -62,7 +54,6 @@ public class UrbanEVModule extends AbstractModule { install(new ElectricFleetModule()); //bind custom EVFleet stuff -// bind(ElectricFleetUpdater.class).in(Singleton.class); addControlerListenerBinding().to(ElectricFleetUpdater.class).in( Singleton.class ); // (this takes the matsim modal vehicles for each leg and gives them to the ElectricFleetSpecification. Don't know why it has to be in // this ad-hoc way. kai, apr'23) @@ -74,6 +65,7 @@ protected void configureQSim() { // bind(UrbanVehicleChargingHandler.class); addMobsimScopeEventHandlerBinding().to(UrbanVehicleChargingHandler.class); // (I think that this takes the plugin/plugout activities, and actually initiates the charging. kai, apr'23) + // yes it does, just like the regular VehicleChargingHandler in the ev contrib. schlenther, feb'24. } }); @@ -81,14 +73,6 @@ protected void configureQSim() { addMobsimListenerBinding().to(UrbanEVTripsPlanner.class); // (I think that this inserts the charging activities just before the mobsim starts (i.e. it is not in the plans). kai, apr'23) - //TODO find a better solution for this yyyy yes. We do not want automagic. kai, apr'23 done. kai, apr'23 -// Collection whileChargingActTypes = urbanEVConfig.getWhileChargingActivityTypes().isEmpty() ? -// config.planCalcScore().getActivityTypes() : -// urbanEVConfig.getWhileChargingActivityTypes(); - -// bind(ActivityWhileChargingFinder.class).toInstance(new ActivityWhileChargingFinder(whileChargingActTypes, -// urbanEVConfig.getMinWhileChargingActivityDuration_s())); - bind( ActivityWhileChargingFinder.class ).in( Singleton.class ); //bind custom analysis: @@ -98,16 +82,4 @@ protected void configureQSim() { addControlerListenerBinding().to(ActsWhileChargingAnalyzer.class); } -// private Set getOpenBerlinActivityTypes() { -// Set activityTypes = new HashSet<>(); -// for (long ii = 600; ii <= 97200; ii += 600) { -// activityTypes.add("home_" + ii + ".0"); -// activityTypes.add("work_" + ii + ".0"); -// activityTypes.add("leisure_" + ii + ".0"); -// activityTypes.add("shopping_" + ii + ".0"); -// activityTypes.add("other_" + ii + ".0"); -// } -// return activityTypes; -// } - } diff --git a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVTripsPlanner.java b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVTripsPlanner.java index b68f4d5bcff..2b9b7dafc6d 100644 --- a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVTripsPlanner.java +++ b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVTripsPlanner.java @@ -20,20 +20,12 @@ package playground.vsp.ev; -import static java.util.stream.Collectors.*; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; +import com.sun.istack.Nullable; import jakarta.inject.Provider; - +import one.util.streamex.StreamEx; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.logging.log4j.LogManager; @@ -43,18 +35,16 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.api.core.v01.population.*; import org.matsim.contrib.common.util.StraightLineKnnFinder; import org.matsim.contrib.ev.charging.ChargingLogic; import org.matsim.contrib.ev.charging.ChargingPower; import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; -import org.matsim.contrib.ev.fleet.*; +import org.matsim.contrib.ev.fleet.ElectricFleetSpecification; +import org.matsim.contrib.ev.fleet.ElectricFleetUtils; +import org.matsim.contrib.ev.fleet.ElectricVehicle; +import org.matsim.contrib.ev.fleet.ElectricVehicleSpecification; import org.matsim.contrib.ev.infrastructure.ChargerSpecification; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructureSpecification; import org.matsim.core.config.Config; @@ -69,11 +59,7 @@ import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.routes.NetworkRoute; -import org.matsim.core.router.LinkWrapperFacility; -import org.matsim.core.router.SingleModeNetworksCache; -import org.matsim.core.router.StageActivityTypeIdentifier; -import org.matsim.core.router.TripRouter; -import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.router.*; import org.matsim.core.router.util.TravelTime; import org.matsim.core.utils.misc.OptionalTime; import org.matsim.core.utils.timing.TimeInterpretation; @@ -82,15 +68,15 @@ import org.matsim.utils.objectattributes.attributable.AttributesImpl; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleUtils; -import org.matsim.vehicles.Vehicles; import org.matsim.withinday.utils.EditPlans; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.inject.Inject; -import com.sun.istack.Nullable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; -import one.util.streamex.StreamEx; +import static java.util.stream.Collectors.*; class UrbanEVTripsPlanner implements MobsimInitializedListener { @@ -100,9 +86,6 @@ class UrbanEVTripsPlanner implements MobsimInitializedListener { @Inject Scenario scenario; - @Inject - Vehicles vehicles; - @Inject private SingleModeNetworksCache singleModeNetworksCache; @@ -151,9 +134,10 @@ public void notifyMobsimInitialized(MobsimInitializedEvent e) { if (!(e.getQueueSimulation() instanceof QSim)) { throw new IllegalStateException(UrbanEVTripsPlanner.class + " only works with a mobsim of type " + QSim.class); } + UrbanEVConfigGroup configGroup = (UrbanEVConfigGroup)config.getModules().get(UrbanEVConfigGroup.GROUP_NAME); //collect all selected plans that contain ev legs and map them to the set of ev used Map>> selectedEVPlans = StreamEx.of(scenario.getPopulation().getPersons().values()) - .mapToEntry(p -> p.getSelectedPlan(), p -> getUsedEV(p.getSelectedPlan())) + .mapToEntry(p -> p.getSelectedPlan(), p -> getUsedEVToPreplan(p.getSelectedPlan())) .filterValues(evSet -> !evSet.isEmpty()) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -169,16 +153,17 @@ public void notifyMobsimInitialized(MobsimInitializedEvent e) { * @param plan * @return */ - private Set> getUsedEV(Plan plan) { + private Set> getUsedEVToPreplan(Plan plan) { return TripStructureUtils.getLegs(plan) .stream() .map(leg -> VehicleUtils.getVehicleId(plan.getPerson(), leg.getMode())) - .filter(vehicleId -> isEV(vehicleId)) + .filter(vehicleId -> isEVAndToBeChargedWhileActivities(vehicleId)) .collect(toSet()); } - private boolean isEV(Id vehicleId) { - return this.electricFleetSpecification.getVehicleSpecifications().containsKey(Id.create(vehicleId, Vehicle.class)); + private boolean isEVAndToBeChargedWhileActivities(Id vehicleId) { + ElectricVehicleSpecification ev = this.electricFleetSpecification.getVehicleSpecifications().getOrDefault(Id.create(vehicleId, Vehicle.class), null); + return (ev != null && UrbanEVUtils.isChargingDuringActivities(ev)); } private void processPlans(Map>> selectedEVPlans) { diff --git a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVUtils.java b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVUtils.java new file mode 100644 index 00000000000..4bf58d29f09 --- /dev/null +++ b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanEVUtils.java @@ -0,0 +1,67 @@ +/* *********************************************************************** * + * project: org.matsim.* + * Controler.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2007 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package playground.vsp.ev; + +import org.matsim.contrib.ev.fleet.ElectricVehicleSpecification; +import org.matsim.vehicles.Vehicle; + +public final class UrbanEVUtils { + + public static final String PLAN_CHARGING_DURING_ACTS_ATTRIBUTE_NAME = "planChargingDuringActivities"; + + /** + * Checks whether {@code electricVehicleSpecification} should be charged during the drivers' activities.
      + * If this is the case, [@code {@link UrbanEVTripsPlanner} will pre-plan charging according to the driver's selected plan.
      + *

      + * Checks for the corresponding attribute with key={@code PLAN_CHARGING_DURING_ACTS_ATTRIBUTE_NAME} in the MATSim input vehicle. + * If the attribute was not provided, true is returned! + * This means that by default, all electricVehicleSpecifications are planned to get charged during activities and not during trips + * (planning for the latter is done by {@code org.matsim.contrib.ev.routing.EVNetworkRoutingModule}. + *

      + * @param electricVehicleSpecification + * @return + */ + public static boolean isChargingDuringActivities(ElectricVehicleSpecification electricVehicleSpecification) { + Object attribute = electricVehicleSpecification.getMatsimVehicle().getAttributes().getAttribute(PLAN_CHARGING_DURING_ACTS_ATTRIBUTE_NAME); + return attribute == null ? true : (Boolean) attribute; + } + + /** + * see description for {@code setChargingDuringActivities(Vehicle vehicle, boolean value)} + * @param electricVehicleSpecification + * @param value + */ + public static void setChargingDuringActivities(ElectricVehicleSpecification electricVehicleSpecification, boolean value) { + setChargingDuringActivities(electricVehicleSpecification.getMatsimVehicle(), value); + } + + /** + * defines whether the vehicle shall be included for planning recharging during the driver's activities (with {@code UrbanEVTripsPlanner}, or not.
      + * In the latter case, recharging may be planned to take place during a trip, if the vehicle is taken for a trip with a mode that is + * routed by {@code org.matsim.contrib.ev.routing.EVNetworkRoutingModule}. + * @param vehicle + * @param value + */ + public static void setChargingDuringActivities(Vehicle vehicle, boolean value) { + vehicle.getAttributes().putAttribute(PLAN_CHARGING_DURING_ACTS_ATTRIBUTE_NAME, value); + } + +} diff --git a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreator.java b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreator.java index bc8819bbdc9..3ec676d187d 100644 --- a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreator.java +++ b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreator.java @@ -39,7 +39,7 @@ import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.population.PopulationUtils; import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.matsim.households.Household; import org.matsim.households.HouseholdImpl; @@ -53,12 +53,12 @@ /** * This class creates a full population of a study region (in Germany) based on the Zensus and the Pendlerstatistik. People are assigned * places or residence and with demographic attributes based on the Zensus and with commuter relations based on the Pendlerstatistik. - * + * * @author dziemke */ public class SynPopCreator { private static final Logger LOG = LogManager.getLogger(SynPopCreator.class); - + private static final Random random = MatsimRandom.getLocalInstance(); // Make sure that stream of random variables is reproducible. // Storage objects @@ -71,7 +71,7 @@ public class SynPopCreator { // Optional (links municipality ID to LOR/PLZ IDs in that municipality) private final Map> spatialRefinementZoneIds = new HashMap<>(); private List idsOfMunicipalitiesForSpatialRefinement; - + // Parameters private String outputBase; private List idsOfFederalStatesIncluded; @@ -93,7 +93,7 @@ public class SynPopCreator { private int allEmployees = 0; private int allPersons = 0; private int allStudents = 0; - + public static void main(String[] args) { // Input and output files @@ -104,11 +104,11 @@ public static void main(String[] args) { String[] commuterFilesOutgoing = {commuterFileOutgoing1, commuterFileOutgoing2, commuterFileOutgoing3, commuterFileOutgoing4}; String censusFile = "../../shared-svn/studies/countries/de/open_berlin_scenario/input/zensus_2011/bevoelkerung/csv_Bevoelkerung/Zensus11_Datensatz_Bevoelkerung_BE_BB.csv"; String outputBase = "../../shared-svn/studies/countries/de/open_berlin_scenario/be_5/cemdap_input/505/"; - + // Parameters int numberOfPlansPerPerson = 10; // Note: Set this higher to a value higher than 1 if spatial refinement is used. List idsOfFederalStatesIncluded = Arrays.asList("11", "12"); // 11=Berlin, 12=Brandenburg - + // Default ratios are used for cases where information is missing, which is the case for smaller municipalities. double defaultAdultsToEmployeesRatio = 1.23; // Calibrated based on sum value from Zensus 2011. double defaultCensusEmployeesToCommutersRatio = 2.5; // This is an assumption, oriented on observed values, deliberately chosen slightly too high. @@ -126,14 +126,14 @@ public static void main(String[] args) { demandGeneratorCensus.generateDemand(); } - - public SynPopCreator(String[] commuterFilesOutgoing, String censusFile, String outputBase, int numberOfPlansPerPerson, + + public SynPopCreator(String[] commuterFilesOutgoing, String censusFile, String outputBase, int numberOfPlansPerPerson, List idsOfFederalStatesIncluded, double defaultAdultsToEmployeesRatio, double defaultEmployeesToCommutersRatio) { LogToOutputSaver.setOutputDirectory(outputBase); - + this.outputBase = outputBase; this.numberOfPlansPerPerson = numberOfPlansPerPerson; - + this.idsOfFederalStatesIncluded = idsOfFederalStatesIncluded; this.idsOfFederalStatesIncluded.stream().forEach(e -> { if (e.length()!=2) throw new IllegalArgumentException("Length of the id for each federal state must be equal to 2. This is not the case for "+ e); @@ -141,7 +141,7 @@ public SynPopCreator(String[] commuterFilesOutgoing, String censusFile, String o this.defaultAdultsToEmployeesRatio = defaultAdultsToEmployeesRatio; this.defaultEmployeesToCommutersRatio = defaultEmployeesToCommutersRatio; - + this.population = ScenarioUtils.createScenario(ConfigUtils.createConfig()).getPopulation(); this.households = new HashMap<>(); @@ -158,7 +158,7 @@ public SynPopCreator(String[] commuterFilesOutgoing, String censusFile, String o } } - + public void generateDemand() { if (this.shapeFileForSpatialRefinement != null && this.refinementFeatureKeyInShapefile != null ) { this.idsOfMunicipalitiesForSpatialRefinement.stream().forEach(e->spatialRefinementZoneIds.put(e, new ArrayList<>())); @@ -173,7 +173,7 @@ public void generateDemand() { } int counter = 1; - + for (String munId : relationsMap.keySet()) { // Loop over municipalities from commuter file Map relationsFromMunicipality = relationsMap.get(munId); @@ -300,7 +300,7 @@ public void generateDemand() { LOG.warn("Total number of employees: " + this.allEmployees); LOG.warn("Total population: " + this.allPersons); LOG.warn("Total number of students: " + this.allStudents); - + // Write output files if (this.writeCemdapInputFiles) { writeCemdapHouseholdsFile(this.households, this.outputBase + "households.dat.gz"); @@ -318,8 +318,8 @@ public void generateDemand() { } } } - - + + private Population clonePopulationAndAdjustLocations(Population inputPopulation){ Population clonedPopulation = ScenarioUtils.createScenario(ConfigUtils.createConfig()).getPopulation(); for (Person person : inputPopulation.getPersons().values()) { @@ -334,7 +334,7 @@ private Population clonePopulationAndAdjustLocations(Population inputPopulation) for(CEMDAPPersonAttributes attributeKey : CEMDAPPersonAttributes.values()){ clonedPerson.getAttributes().putAttribute(attributeKey.toString(), person.getAttributes().getAttribute(attributeKey.toString())); } - + // change locations or use spatially refined location if ((boolean) person.getAttributes().getAttribute(CEMDAPPersonAttributes.employed.toString())) { String locationOfWork = (String) person.getAttributes().getAttribute(CEMDAPPersonAttributes.locationOfWork.toString()); @@ -363,7 +363,7 @@ private Population clonePopulationAndAdjustLocations(Population inputPopulation) } - private void createHouseholdsAndPersons(int counter, String municipalityId, int numberOfPersons, Gender gender, int lowerAgeBound, int upperAgeBound, + private void createHouseholdsAndPersons(int counter, String municipalityId, int numberOfPersons, Gender gender, int lowerAgeBound, int upperAgeBound, double adultsToEmployeesRatio, List commuterRelationList) { for (int i = 0; i < numberOfPersons; i++) { this.allPersons++; @@ -375,7 +375,7 @@ private void createHouseholdsAndPersons(int counter, String municipalityId, int household.getAttributes().putAttribute(CEMDAPHouseholdAttributes.homeTSZLocation.toString(), getExactLocation(municipalityId)); household.getAttributes().putAttribute(CEMDAPHouseholdAttributes.numberOfChildren.toString(), 0); // None, ignore them in this version household.getAttributes().putAttribute(CEMDAPHouseholdAttributes.householdStructure.toString(), 1); // 1 = single, no children - + Id personId = Id.create(householdId + "01", Person.class); // TODO Currently only singel-person households Person person = this.population.getFactory().createPerson(personId); @@ -385,14 +385,14 @@ private void createHouseholdsAndPersons(int counter, String municipalityId, int employed = getEmployed(adultsToEmployeesRatio); } person.getAttributes().putAttribute(CEMDAPPersonAttributes.employed.toString(), employed); - + boolean student = false; if (lowerAgeBound < 30 && upperAgeBound > 17 && !employed) { // Younger and older people are never a student, employed people neither student = true; // TODO quite simplistic assumption, which may be improved later allStudents++; - } + } person.getAttributes().putAttribute(CEMDAPPersonAttributes.student.toString(), student); - + if (employed) { allEmployees++; if (commuterRelationList.size() == 0) { // No relations left in list, which employee could choose from @@ -419,22 +419,22 @@ private void createHouseholdsAndPersons(int counter, String municipalityId, int } else { person.getAttributes().putAttribute(CEMDAPPersonAttributes.locationOfSchool.toString(), "-99"); } - + person.getAttributes().putAttribute(CEMDAPPersonAttributes.hasLicense.toString(), true); // for CEMDAP's "driversLicence" variable person.getAttributes().putAttribute(CEMDAPPersonAttributes.gender.toString(), gender.name()); // for CEMDAP's "female" variable person.getAttributes().putAttribute(CEMDAPPersonAttributes.age.toString(), getAgeInBounds(lowerAgeBound, upperAgeBound)); person.getAttributes().putAttribute(CEMDAPPersonAttributes.parent.toString(), false); - + this.population.addPerson(person); - + List> personIds = new ArrayList<>(); // Does in current implementation (only 1 p/hh) not make much sense personIds.add(personId); household.setMemberIds(personIds); this.households.put(householdId, household); } - } - - + } + + private static void scaleRelations(Map relationsFromMunicipality, int employeesMale, int employeesFemale, double defaultEmployeesToCommutersRatio) { // Count all commuters starting in the given municipality @@ -460,7 +460,7 @@ private static void scaleRelations(Map relationsFrom } commutersFemale += relation.getTripsFemale(); } - + // Compute ratios double employeesToCommutersMaleRatio; double employeesToCommutersFemaleRatio; @@ -474,7 +474,7 @@ private static void scaleRelations(Map relationsFrom } else { employeesToCommutersFemaleRatio = defaultEmployeesToCommutersRatio; } - + // Scale for (CommuterRelationV2 relation : relationsFromMunicipality.values()) { relation.setTripsMale((int) Math.ceil(relation.getTripsMale() * employeesToCommutersMaleRatio)); @@ -532,15 +532,15 @@ private String getSpatiallyRefinedZone(String municipalityId) { private static boolean getEmployed(double adultsToEmployeesRatio) { return random.nextDouble() * adultsToEmployeesRatio < 1; } - - + + private static int getAgeInBounds(int lowerBound, int upperBound) { return (int) (lowerBound + random.nextDouble() * (upperBound - lowerBound + 1)); } private Map> readShapeForSpatialRefinement() { - Collection features = ShapeFileReader.getAllFeatures(this.shapeFileForSpatialRefinement); + Collection features = GeoFileReader.getAllFeatures(this.shapeFileForSpatialRefinement); for (SimpleFeature feature : features) { String municipality; @@ -554,14 +554,14 @@ private Map> readShapeForSpatialRefinement() { } return spatialRefinementZoneIds; } - - + + private void writeCemdapHouseholdsFile(Map, Household> households, String fileName) { BufferedWriter bufferedWriterHouseholds = null; try { bufferedWriterHouseholds = IOUtils.getBufferedWriter(fileName); - + for (Household household : households.values()) { int householdId = Integer.parseInt(household.getId().toString()); int numberOfAdults = (Integer) household.getAttributes().getAttribute(CEMDAPHouseholdAttributes.numberOfAdults.toString()); @@ -579,7 +579,7 @@ private void writeCemdapHouseholdsFile(Map, Household> households, + "\t" + 0); bufferedWriterHouseholds.newLine(); } - + } catch (IOException ex) { ex.printStackTrace(); } finally { @@ -594,43 +594,43 @@ private void writeCemdapHouseholdsFile(Map, Household> households, } LOG.info("Households file " + fileName + " written."); } - - + + private void writeCemdapPersonsFile(Population population, String fileName) { BufferedWriter bufferedWriterPersons = null; try { bufferedWriterPersons = IOUtils.getBufferedWriter(fileName); - + for (Person person : population.getPersons().values()) { Attributes attr = person.getAttributes(); int householdId = Integer.parseInt(attr.getAttribute(CEMDAPPersonAttributes.householdId.toString()).toString()); int personId = Integer.parseInt(person.getId().toString()); - + int employed; if ((boolean) attr.getAttribute(CEMDAPPersonAttributes.employed.toString())) { employed = 1; } else { employed = 0; } - + int student; if ((boolean) attr.getAttribute(CEMDAPPersonAttributes.student.toString())) { student = 1; } else { student = 0; } - + int driversLicence; if ((boolean) attr.getAttribute(CEMDAPPersonAttributes.hasLicense.toString())) { driversLicence = 1; } else { driversLicence = 0; } - + int locationOfWork = Integer.parseInt(attr.getAttribute(CEMDAPPersonAttributes.locationOfWork.toString()).toString()); int locationOfSchool = Integer.parseInt(attr.getAttribute(CEMDAPPersonAttributes.locationOfSchool.toString()).toString()); - + int female; if (Gender.valueOf((String) attr.getAttribute(CEMDAPPersonAttributes.gender.toString())) == Gender.male) { female = 0; @@ -639,25 +639,25 @@ private void writeCemdapPersonsFile(Population population, String fileName) { } else { throw new IllegalArgumentException("Gender must either be male or female."); } - + int age = (Integer) attr.getAttribute(CEMDAPPersonAttributes.age.toString()); - + int parent; if ((boolean) attr.getAttribute(CEMDAPPersonAttributes.parent.toString())) { parent = 1; } else { parent = 0; } - + // Altogether this creates 59 columns = number in query file bufferedWriterPersons.write(householdId + "\t" + personId + "\t" + employed + "\t" + student + "\t" + driversLicence + "\t" + locationOfWork + "\t" + locationOfSchool - + "\t" + female + "\t" + age + "\t" + parent + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + female + "\t" + age + "\t" + parent + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 ); bufferedWriterPersons.newLine(); } @@ -676,26 +676,26 @@ private void writeCemdapPersonsFile(Population population, String fileName) { LOG.info("Persons file " + fileName + " written."); } - + private static void writeMatsimPlansFile(Population population, String fileName) { PopulationWriter popWriter = new PopulationWriter(population); popWriter.write(fileName); } - - + + // Getters and setters public Population getPopulation() { return this.population; } - + public void setShapeFileForSpatialRefinement(String shapeFileForSpatialRefinement) { this.shapeFileForSpatialRefinement = shapeFileForSpatialRefinement; } - + public void setIdsOfMunicipalitiesForSpatialRefinement(List idsOfMunicipalitiesForSpatialRefinement) { this.idsOfMunicipalitiesForSpatialRefinement = idsOfMunicipalitiesForSpatialRefinement; } - + public void setRefinementFeatureKeyInShapefile(String refinementFeatureKeyInShapefile) { this.refinementFeatureKeyInShapefile = refinementFeatureKeyInShapefile; } @@ -703,7 +703,7 @@ public void setRefinementFeatureKeyInShapefile(String refinementFeatureKeyInShap public void setMunicipalityFeatureKeyInShapefile(String municipalityFeatureKeyInShapefile) { this.municipalityFeatureKeyInShapefile = municipalityFeatureKeyInShapefile; } - + public void setWriteCemdapInputFiles(boolean writeCemdapInputFiles) { this.writeCemdapInputFiles = writeCemdapInputFiles; } @@ -715,15 +715,15 @@ public void setWriteMatsimPlanFiles(boolean writeMatsimPlanFiles) { public void setIncludeChildren(boolean includeChildren) { this.includeChildren = includeChildren; } - + public List getAllPopulations() { if (!memorizeAllPopulations) { throw new RuntimeException("The corresponding container object has not been filles. 'memorizeAllPopulations' needs to be activated."); } return this.allPopulations; } - + public void setMemorizeAllPopulations(boolean memorizeAllPopulations) { this.memorizeAllPopulations = memorizeAllPopulations; } -} \ No newline at end of file +} diff --git a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreatorChildren.java b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreatorChildren.java index 9e4d748ddf8..71cec77ea8a 100644 --- a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreatorChildren.java +++ b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/SynPopCreatorChildren.java @@ -29,7 +29,7 @@ import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.population.PopulationUtils; import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.utils.objectattributes.ObjectAttributes; import org.opengis.feature.simple.SimpleFeature; import playground.vsp.openberlinscenario.Gender; @@ -40,7 +40,7 @@ /** * This class creates a children-only population of a study region (in Germany) based on the Zensus and the Pendlerstatistik. * Only meant as as supplement ot an already existing adults-only population. - * + * * @author dziemke */ public class SynPopCreatorChildren { @@ -91,14 +91,14 @@ public static void main(String[] args) { public SynPopCreatorChildren(String censusFile, String outputBase, List idsOfFederalStatesIncluded) { LogToOutputSaver.setOutputDirectory(outputBase); - + this.outputBase = outputBase; this.idsOfFederalStatesIncluded = idsOfFederalStatesIncluded; this.idsOfFederalStatesIncluded.stream().forEach(e -> { if (e.length()!=2) throw new IllegalArgumentException("Length of the id for each federal state must be equal to 2. This is not the case for "+ e); }); - + this.population = ScenarioUtils.createScenario(ConfigUtils.createConfig()).getPopulation(); // Read census @@ -107,7 +107,7 @@ public SynPopCreatorChildren(String censusFile, String outputBase, List this.municipalityList = censusReader.getMunicipalityList(); } - + public void generateDemand() { if (this.shapeFileForSpatialRefinement != null && this.refinementFeatureKeyInShapefile != null ) { this.idsOfMunicipalitiesForSpatialRefinement.stream().forEach(e->spatialRefinementZoneIds.put(e, new ArrayList<>())); @@ -122,7 +122,7 @@ public void generateDemand() { } int counter = counterInit; - + for (String munId : municipalityList) { int pop0_2Male = (int) this.municipalities.getAttribute(munId, CensusAttributes.pop0_2Male.toString()); int pop3_5Male = (int) this.municipalities.getAttribute(munId, CensusAttributes.pop3_5Male.toString()); @@ -159,8 +159,8 @@ public void generateDemand() { Population adjustedPopulation = clonePopulationAndAdjustLocations(this.population); writeMatsimPlansFile(adjustedPopulation, this.outputBase + "plans_children.xml.gz"); } - - + + private Population clonePopulationAndAdjustLocations(Population inputPopulation){ Population clonedPopulation = ScenarioUtils.createScenario(ConfigUtils.createConfig()).getPopulation(); for (Person person : inputPopulation.getPersons().values()) { @@ -217,15 +217,15 @@ private String getSpatiallyRefinedZone(String municipalityId) { List spatiallyRefinedZones = this.spatialRefinementZoneIds.get(municipalityId); return spatiallyRefinedZones.get(random.nextInt(spatiallyRefinedZones.size())); } - - + + private static int getAgeInBounds(int lowerBound, int upperBound) { return (int) (lowerBound + random.nextDouble() * (upperBound - lowerBound + 1)); } private Map> readShapeForSpatialRefinement() { - Collection features = ShapeFileReader.getAllFeatures(this.shapeFileForSpatialRefinement); + Collection features = GeoFileReader.getAllFeatures(this.shapeFileForSpatialRefinement); for (SimpleFeature feature : features) { String municipality; @@ -245,21 +245,21 @@ private static void writeMatsimPlansFile(Population population, String fileName) PopulationWriter popWriter = new PopulationWriter(population); popWriter.write(fileName); } - - + + // Getters and setters public Population getPopulation() { return this.population; } - + public void setShapeFileForSpatialRefinement(String shapeFileForSpatialRefinement) { this.shapeFileForSpatialRefinement = shapeFileForSpatialRefinement; } - + public void setIdsOfMunicipalitiesForSpatialRefinement(List idsOfMunicipalitiesForSpatialRefinement) { this.idsOfMunicipalitiesForSpatialRefinement = idsOfMunicipalitiesForSpatialRefinement; } - + public void setRefinementFeatureKeyInShapefile(String refinementFeatureKeyInShapefile) { this.refinementFeatureKeyInShapefile = refinementFeatureKeyInShapefile; } @@ -271,4 +271,4 @@ public void setMunicipalityFeatureKeyInShapefile(String municipalityFeatureKeyIn public void setWriteMatsimPlanFiles(boolean writeMatsimPlanFiles) { this.writeMatsimPlanFiles = writeMatsimPlanFiles; } -} \ No newline at end of file +} diff --git a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/ZoneAndLOSGeneratorV2.java b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/ZoneAndLOSGeneratorV2.java index 3949dadde8f..c97a9b2cbf6 100644 --- a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/ZoneAndLOSGeneratorV2.java +++ b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/input/ZoneAndLOSGeneratorV2.java @@ -35,7 +35,7 @@ import org.apache.logging.log4j.Logger; import org.locationtech.jts.geom.Geometry; import org.matsim.api.core.v01.Coord; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.core.utils.io.IOUtils; import org.opengis.feature.simple.SimpleFeature; @@ -56,7 +56,7 @@ public class ZoneAndLOSGeneratorV2 { private final Map> zone2ZoneDistanceMap = new HashMap<>(); private final Map> zone2ZoneAdjacencyMap = new HashMap<>(); private final String outputBase; - + // Parameters // private double defaultIntraZoneDistance = 1.72; // in miles; equals 2.76km. private double defaultIntraZoneDistance = 1.; // new, lower value @@ -72,7 +72,7 @@ public class ZoneAndLOSGeneratorV2 { // spatial refinement. Amit Nov'17 private List zoneIdsForSpatialRefinement; // this is filled if shape file for spatial refinement is provided. private double defaultIntraZoneDistanceForSpatialRefinement = Double.NaN; - + public static void main(String[] args) { // Input and output String commuterFileBase = "../../shared-svn/studies/countries/de/open_berlin_scenario/input/pendlerstatistik_2009/"; @@ -86,7 +86,7 @@ public static void main(String[] args) { // Parameters String featureKeyInShapefile = "ID"; - + ZoneAndLOSGeneratorV2 zoneAndLOSGeneratorV2 = new ZoneAndLOSGeneratorV2(commuterFilesOutgoing, shapeFile, outputBase, featureKeyInShapefile); zoneAndLOSGeneratorV2.generateSupply(); } @@ -99,7 +99,7 @@ public ZoneAndLOSGeneratorV2(String[] commuterFilesOutgoing, String shapeFile, S readMunicipalities(commuterFilesOutgoing); readShape(shapeFile, featureKeyInShapefile); } - + public void generateSupply() { compareIdsInShapefileAndCommuterFiles(); computeAndStoreZone2ZoneDistances(); @@ -108,7 +108,7 @@ public void generateSupply() { writeLOSFile("losoffpkam", false); writeLOSFile("lospeakam", true); } - + private void readMunicipalities(String[] commuterFilesOutgoing) { for (String commuterFileOutgoing : commuterFilesOutgoing) { CommuterFileReaderV2 commuterFileReader = new CommuterFileReaderV2(commuterFileOutgoing, "\t"); @@ -118,7 +118,7 @@ private void readMunicipalities(String[] commuterFilesOutgoing) { } private void readShape(String shapeFile, String featureKeyInShapeFile) { - Collection features = ShapeFileReader.getAllFeatures(shapeFile); + Collection features = GeoFileReader.getAllFeatures(shapeFile); for (SimpleFeature feature : features) { String id = (String) feature.getAttribute(featureKeyInShapeFile); Geometry geometry = (Geometry) feature.getDefaultGeometry(); @@ -126,7 +126,7 @@ private void readShape(String shapeFile, String featureKeyInShapeFile) { this.zoneMap.put(id, geometry); } } - + private void compareIdsInShapefileAndCommuterFiles() { LOG.info("Municipality set has " + municipalities.size() + " elements."); LOG.info("Zones set has " + zones.size() + " elements."); @@ -141,7 +141,7 @@ private void compareIdsInShapefileAndCommuterFiles() { } } } - + private void computeAndStoreZone2ZoneDistances() { LOG.info("Start distance and adjacency computations."); LOG.info(this.zones.size() * this.zones.size() + " computations will be performed."); @@ -159,23 +159,23 @@ private void computeAndStoreZone2ZoneDistances() { int adjacent; double distance_mi; double temp = 0.; - + if (originId.equals(destinationId)) { // internal traffic inside zone distance_mi = getIntraZonalDistance(originId) * beelineDistanceFactor; adjacent = 0; } else { Geometry originGeometry = this.zoneMap.get(originId); Coord originCoord = new Coord(originGeometry.getCentroid().getCoordinate().x, originGeometry.getCentroid().getCoordinate().y); - + Geometry destinationGeometry = this.zoneMap.get(destinationId); Coord destinationCoord = new Coord(destinationGeometry.getCentroid().getCoordinate().x, destinationGeometry.getCentroid().getCoordinate().y); - + double distanceX_m = Math.abs(originCoord.getX() - destinationCoord.getX()); double distanceY_m = Math.abs(originCoord.getY() - destinationCoord.getY()); double distance_m = Math.sqrt(distanceX_m * distanceX_m + distanceY_m * distanceY_m); - + distance_mi = distance_m / 1609.344 * beelineDistanceFactor; // Convert from meters to miles - + if (originGeometry.touches(destinationGeometry)) { adjacent = 1; } else { @@ -192,7 +192,7 @@ private void computeAndStoreZone2ZoneDistances() { } LOG.info("Finised distance and adjacency computations."); } - + private void writeZone2ZoneFile() { BufferedWriter bufferedWriterZone2Zone = null; try { @@ -201,7 +201,7 @@ private void writeZone2ZoneFile() { for (String destinationId : this.zones) { double distance_mi = this.zone2ZoneDistanceMap.get(originId).get(destinationId); int adjacent = this.zone2ZoneAdjacencyMap.get(originId).get(destinationId); - + // 4 columns bufferedWriterZone2Zone.write(originId + "\t" + destinationId + "\t" + adjacent + "\t" + distance_mi); bufferedWriterZone2Zone.newLine(); @@ -223,7 +223,7 @@ private void writeZone2ZoneFile() { } System.out.println("Zone2Zone file written."); } - + private void writeZonesFile() { BufferedWriter bufferedWriterZones = null; try { @@ -232,7 +232,7 @@ private void writeZonesFile() { // 45 columns bufferedWriterZones.write(Integer.parseInt(zoneId) + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 - + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0); @@ -254,7 +254,7 @@ private void writeZonesFile() { } System.out.println("Zones file written."); } - + private void writeLOSFile(String filename, boolean isPeak) { BufferedWriter bufferedWriterLos = null; try { @@ -262,15 +262,15 @@ private void writeLOSFile(String filename, boolean isPeak) { double temp = 0.0; for (String originId : this.zones) { for (String destinationId : this.zones) { - + int inSameZone = 0; if (originId.equals(destinationId)) { inSameZone = 1; } - + double distance_mi = this.zone2ZoneDistanceMap.get(originId).get(destinationId); int adjacent = this.zone2ZoneAdjacencyMap.get(originId).get(destinationId); - + double driveAloneIVTT_min; if (isPeak) { driveAloneIVTT_min = distance_mi * durantionDistancePeakRatio_min_mile; @@ -279,13 +279,13 @@ private void writeLOSFile(String filename, boolean isPeak) { } temp = Math.round(driveAloneIVTT_min * 100); // Round to two decimal places driveAloneIVTT_min = temp / 100; - + double driveAloneCost_USD = distance_mi * costDistanceRatio_USD_mile; temp = Math.round(driveAloneCost_USD * 100); // Round to two decimal places driveAloneCost_USD = temp / 100; - + // 14 columns - bufferedWriterLos.write(Integer.parseInt(originId) + "\t" + Integer.parseInt(destinationId) + + bufferedWriterLos.write(Integer.parseInt(originId) + "\t" + Integer.parseInt(destinationId) + "\t" + inSameZone + "\t" + adjacent + "\t" + distance_mi + "\t" + driveAloneIVTT_min + "\t" + 3.1 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + 0 + "\t" + driveAloneCost_USD + "\t" + driveAloneIVTT_min + "\t" + driveAloneCost_USD); @@ -334,22 +334,22 @@ public void setDefaultIntraZoneDistanceForSpatialRefinement(double defaultIntraZ public void setBeelineDistanceFactor(double beelineDistanceFactor) { this.beelineDistanceFactor = beelineDistanceFactor; } - + public void setDurantionDistanceOffPeakRatio_min_mile(double durantionDistanceOffPeakRatio_min_mile) { this.durantionDistanceOffPeakRatio_min_mile = durantionDistanceOffPeakRatio_min_mile; } - + public void setDurantionDistancePeakRatio_min_mile(double durantionDistancePeakRatio_min_mile) { this.durantionDistancePeakRatio_min_mile = durantionDistancePeakRatio_min_mile; } - + public void setCostDistanceRatio_USD_mile(double costDistanceRatio_USD_mile) { this.costDistanceRatio_USD_mile = costDistanceRatio_USD_mile; } public void setShapeFileForRefinement(String shapeFileForSpatialRefinement, String featureKeyInShapeFileForRefinement){ LOG.info("Using spatial refinement..."); - this.zoneIdsForSpatialRefinement = ShapeFileReader + this.zoneIdsForSpatialRefinement = GeoFileReader .getAllFeatures(shapeFileForSpatialRefinement) .stream() .map(feature -> feature.getAttribute(featureKeyInShapeFileForRefinement).toString()) diff --git a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/output/CemdapOutput2MatsimPlansConverter.java b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/output/CemdapOutput2MatsimPlansConverter.java index 52ebcdd4f33..b8be4c3a3a8 100644 --- a/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/output/CemdapOutput2MatsimPlansConverter.java +++ b/contribs/vsp/src/main/java/playground/vsp/openberlinscenario/cemdap/output/CemdapOutput2MatsimPlansConverter.java @@ -40,7 +40,7 @@ import org.matsim.api.core.v01.population.PopulationWriter; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.utils.objectattributes.ObjectAttributes; import org.opengis.feature.simple.SimpleFeature; @@ -55,7 +55,7 @@ public class CemdapOutput2MatsimPlansConverter { public static final String activityZoneId_attributeKey = "zoneId"; private static final Logger LOG = LogManager.getLogger(CemdapOutput2MatsimPlansConverter.class); - + public static void main(String[] args) throws IOException { // Local use @@ -84,7 +84,7 @@ public static void main(String[] args) throws IOException { boolean assignCoordinatesToActivities = true; // if set to false, the zone id will be attached to activity types and a fake coordinate will be given. boolean combiningGeoms = true; int activityDurationThreshold_s = Integer.MIN_VALUE; - + // Server use if (args.length != 0) { numberOfFirstCemdapOutputFile = Integer.parseInt(args[0]); @@ -106,14 +106,14 @@ public static void main(String[] args) throws IOException { Map shapeFileToFeatureKey = new HashMap<>(); shapeFileToFeatureKey.put(zonalShapeFile, zoneIdTag); - + convert(cemdapDataRoot, numberOfFirstCemdapOutputFile, numberOfPlans, outputDirectory, shapeFileToFeatureKey, allowVariousWorkAndEducationLocations, addStayHomePlan, useLandCoverData, landCoverFile, stopFile, activityFile, simplifyGeometries, combiningGeoms, assignCoordinatesToActivities, activityDurationThreshold_s); } - + public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputFile, int numberOfPlans, String outputDirectory, Map shapeFileToFeatureKey, // ensures, unique shape files with same or different featureKey. Amit Nov'17 boolean allowVariousWorkAndEducationLocations, boolean addStayHomePlan, @@ -127,9 +127,9 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF } LogToOutputSaver.setOutputDirectory(outputDirectory); - + Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); - + // Find respective stops file Map cemdapStopsFilesMap = new HashMap<>(); for (int planNumber = 0; planNumber < numberOfPlans; planNumber++) { @@ -137,20 +137,20 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF String cemdapStopsFile = cemdapDataRoot + numberOfCurrentInputFile + "/" + stopFile; cemdapStopsFilesMap.put(planNumber, cemdapStopsFile); } - + // Create ObjectAttributes for each agent and each plan Map personZoneAttributesMap = new HashMap<>(); for (int planNumber = 0; planNumber < numberOfPlans; planNumber++) { ObjectAttributes personZoneAttributes = new ObjectAttributes(); personZoneAttributesMap.put(planNumber, personZoneAttributes); } - + Map, Coord> homeZones = new HashMap<>(); - + // Write all (geographic) features of planning area to a map Map zones = new HashMap<>(); for (Map.Entry entry : shapeFileToFeatureKey.entrySet()) { - for (SimpleFeature feature : ShapeFileReader.getAllFeatures(entry.getKey())) { + for (SimpleFeature feature : GeoFileReader.getAllFeatures(entry.getKey())) { Geometry geometry = (Geometry)feature.getDefaultGeometry(); String shapeId = Cemdap2MatsimUtils.removeLeadingZeroFromString((String) feature.getAttribute(entry.getValue())); // TODO check if removal of leading zero is always valid @@ -164,7 +164,7 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF } } } - + // Get all persons from activity file // List> personsIds = new LinkedList<>(); Map, String> personHomeMap = new HashMap<>(); @@ -175,14 +175,14 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF // cemdapPersonParser.parse(cemdapDataRoot + numberOfFirstCemdapOutputFile + "/" + cemdapChildrenFilename, personsIds); CemdapActivityParser cemdapActivityParser = new CemdapActivityParser(); cemdapActivityParser.parse(cemdapDataRoot + numberOfFirstCemdapOutputFile + "/" + activityFile, personHomeMap); - + Population population = scenario.getPopulation(); - + for (int planNumber = 0; planNumber < numberOfPlans; planNumber++) { CemdapStopsParser cemdapStopsParser = new CemdapStopsParser(); cemdapStopsParser.setActivityDurationThreshold_s(activityDurationThreshold_s); cemdapStopsParser.parse(cemdapStopsFilesMap.get(planNumber), planNumber, population, personZoneAttributesMap.get(planNumber), outputDirectory); - + // Commenting this for the time being; it does not do anything if the activity file is not considered on top of the stops file, dz,aa, sep'17 // Add a stay-home plan for those people who have no stops (i.e. no travel) in current stop file // Following is required to add persons who just stays at home. Such persons does not appear in the Stops.out file. dz, aa Oct'17 @@ -247,7 +247,7 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF // If applicable, add a stay-home plan for everybody if (addStayHomePlan) { numberOfPlans++; - + for (Person person : population.getPersons().values()) { Plan firstPlan = person.getPlans().get(0); // Get first (i.e. presumably "home") activity from agent's first plan @@ -261,7 +261,7 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF person.addPlan(stayHomePlan); } } - + // Check if number of plans that each agent has is correct for (Person person : scenario.getPopulation().getPersons().values()) { if (person.getPlans().size() < numberOfPlans) { @@ -271,7 +271,7 @@ public static void convert(String cemdapDataRoot, int numberOfFirstCemdapOutputF LOG.warn("Person with ID " + person.getId() + " has more than " + numberOfPlans + " plans"); } } - + // Write population file new File(outputDirectory).mkdir(); new PopulationWriter(scenario.getPopulation(), null).write(outputDirectory + "plans.xml.gz"); diff --git a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java index 9f470684027..823fb89bf83 100644 --- a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java +++ b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVIT.java @@ -8,13 +8,9 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.utils.io.IOUtils; -import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; import org.matsim.utils.eventsfilecomparison.EventsFileComparator; -import java.net.URL; - public class UrbanEVIT { @RegisterExtension private MatsimTestUtils utils = new MatsimTestUtils(); diff --git a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVTests.java b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVTests.java index a71bfc6ce8a..fcbff65ded3 100644 --- a/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVTests.java +++ b/contribs/vsp/src/test/java/playground/vsp/ev/UrbanEVTests.java @@ -1,11 +1,5 @@ package playground.vsp.ev; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -30,6 +24,13 @@ import org.matsim.testcases.MatsimTestUtils; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; // TODO translate and complete @@ -88,6 +89,12 @@ public static void run() { // EVUtils.setInitialEnergy(scenario.getVehicles().getVehicleTypes().get(Id.create("Not enough time so charging early", VehicleType.class)).getEngineInformation(), // 5.0); + //this guy shall not be pre-plan charging during activities + Id carNotToCharge = VehicleUtils.getVehicleIds( + scenario.getPopulation().getPersons().get(Id.createPersonId("Do not charge during activities"))) + .get(TransportMode.car); + UrbanEVUtils.setChargingDuringActivities(scenario.getVehicles().getVehicles().get(carNotToCharge), false); + ///controler with Urban EV module Controler controler = RunUrbanEVExample.prepareControler(scenario); handler = new UrbanEVTestHandler(); @@ -119,7 +126,7 @@ void testAgentsExecuteSameNumberOfActsAsPlanned() { } } Assertions.assertFalse(fail, - "the following persons do not execute the same amount of activities as they plan to:" + personsWithDifferingActCount); + "the following persons do not execute the same number of activities as they plan to:" + personsWithDifferingActCount); } @Test @@ -174,8 +181,8 @@ void testChargerSelectionShopping() { Assertions.assertEquals(1, plugins.size(), 0); ActivityStartEvent pluginActStart = plugins.get(0); - //starts at 10am at work and travels 8 links ร  99s - Assertions.assertEquals(10 * 3600 + 8 * 99, pluginActStart.getTime(), MatsimTestUtils.EPSILON, "wrong charging start time"); + //starts at 10am at work and travels 8 links ร  99s + 3s waiting time to enter traffic + Assertions.assertEquals(10 * 3600 + 8 * 99 + 3, pluginActStart.getTime(), MatsimTestUtils.EPSILON, "wrong charging start time"); Assertions.assertEquals("172", pluginActStart.getLinkId().toString(), "wrong charging start location"); List plugouts = this.handler.plugOutCntPerPerson.getOrDefault(Id.createPersonId("Charging during shopping"), List.of()); @@ -186,6 +193,15 @@ void testChargerSelectionShopping() { } + @Test + void testDoNotChargeDuringActivities() { + List plugins = this.handler.plugInCntPerPerson.getOrDefault(Id.createPersonId("Do not charge during activities"), List.of()); + Assertions.assertEquals(0, plugins.size(), 0); + + List plugouts = this.handler.plugOutCntPerPerson.getOrDefault(Id.createPersonId("Do not charge during activities"), List.of()); + Assertions.assertEquals(0, plugouts.size(), 0); + } + @Test void testLongDistance() { List plugins = this.handler.plugInCntPerPerson.getOrDefault(Id.createPersonId("Charger Selection long distance leg"), @@ -193,8 +209,8 @@ void testLongDistance() { Assertions.assertEquals(1, plugins.size(), 0); ActivityStartEvent pluginActStart = plugins.get(0); - //starts at 8 am and travels 19 links ร  99s + 3s waiting time to enter traffic - Assertions.assertEquals(8 * 3600 + 19 * 99 + 3, pluginActStart.getTime(), MatsimTestUtils.EPSILON, "wrong charging start time"); + //starts at 8 am and travels 19 links ร  99s + 7s waiting time to enter traffic + Assertions.assertEquals(8 * 3600 + 19 * 99 + 7, pluginActStart.getTime(), MatsimTestUtils.EPSILON, "wrong charging start time"); Assertions.assertEquals("89", pluginActStart.getLinkId().toString(), "wrong charging start location"); List plugouts = this.handler.plugOutCntPerPerson.getOrDefault(Id.createPersonId("Charger Selection long distance leg"), @@ -421,6 +437,50 @@ private static void overridePopulation(Scenario scenario) { scenario.getPopulation().addPerson(person2); } + { + Person person2a = factory.createPerson(Id.createPersonId("Do not charge during activities")); + + Plan plan2a = factory.createPlan(); + + Activity home21 = factory.createActivityFromLinkId("home", Id.createLinkId("1")); + home21.setEndTime(8 * 3600); + plan2a.addActivity(home21); + plan2a.addLeg(factory.createLeg(TransportMode.car)); + + Activity work21 = factory.createActivityFromLinkId("work", Id.createLinkId("176")); + work21.setEndTime(10 * 3600); + plan2a.addActivity(work21); + + plan2a.addLeg(factory.createLeg(TransportMode.car)); + + // Activity work22 = factory.createActivityFromLinkId("work", Id.createLinkId("60")); + // work22.setEndTime(12 * 3600); + // plan2a.addActivity(work22); + // + // plan2a.addLeg(factory.createLeg(TransportMode.car)); + + Activity shopping21 = factory.createActivityFromLinkId("shopping", Id.createLinkId("9")); + shopping21.setMaximumDuration(1200); + + plan2a.addActivity(shopping21); + + plan2a.addLeg(factory.createLeg(TransportMode.car)); + + Activity work23 = factory.createActivityFromLinkId("work", Id.createLinkId("5")); + work23.setEndTime(13 * 3600); + plan2a.addActivity(work23); + + plan2a.addLeg(factory.createLeg(TransportMode.car)); + + Activity home22 = factory.createActivityFromLinkId("home", Id.createLinkId("1")); + home22.setEndTime(15 * 3600); + plan2a.addActivity(home22); + person2a.addPlan(plan2a); + person2a.setSelectedPlan(plan2a); + + scenario.getPopulation().addPerson(person2a); + } + { Person person3 = factory.createPerson(Id.createPersonId("Charger Selection long distance leg")); diff --git a/contribs/vsp/src/test/java/playground/vsp/pt/fare/PtTripFareEstimatorTest.java b/contribs/vsp/src/test/java/playground/vsp/pt/fare/PtTripFareEstimatorTest.java index 7c3527d08b4..6ddfef69d44 100644 --- a/contribs/vsp/src/test/java/playground/vsp/pt/fare/PtTripFareEstimatorTest.java +++ b/contribs/vsp/src/test/java/playground/vsp/pt/fare/PtTripFareEstimatorTest.java @@ -44,7 +44,7 @@ public class PtTripFareEstimatorTest { @Inject private ScoringParametersForPerson params; @Inject - private Map> tripEstimator; + private Map tripEstimator; private PtTripWithDistanceBasedFareEstimator estimator; @BeforeEach diff --git a/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicle.tsv b/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicle.tsv index a3716b50cdc..d39ab8f1eb1 100644 --- a/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicle.tsv +++ b/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicle.tsv @@ -1 +1 @@ -vehicleId carrierId vehicleTypeId tourId tourDuration[s] travelDistance[m] costPerSecond[EUR/s] costPerMeter[EUR/m] fixedCosts[EUR] varCostsTime[EUR] varCostsDist[EUR] totalCosts[EUR] +vehicleId carrierId vehicleTypeId tourId tourDuration[s] tourDuration[h] travelDistance[m] travelDistance[km] travelTime[s] travelTime[h] costPerSecond[EUR/s] costPerMeter[EUR/m] fixedCosts[EUR] varCostsTime[EUR] varCostsDist[EUR] totalCosts[EUR] diff --git a/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicleType.tsv b/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicleType.tsv index dacd1779828..2125ae69db1 100644 --- a/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicleType.tsv +++ b/contribs/vsp/test/input/org/matsim/freight/carriers/analysis/FreightAnalysisEventBasedTest/runFreightAnalysisEventBasedTest/TimeDistance_perVehicleType.tsv @@ -1,2 +1,2 @@ -vehicleTypeId nuOfVehicles SumOfTourDuration[s] SumOfTravelDistances[m] costPerSecond[EUR/s] costPerMeter[EUR/m] fixedCosts[EUR/veh] varCostsTime[EUR] varCostsDist[EUR] fixedCosts[EUR] totalCosts[EUR] -light 0 0.0 60000.0 0.008 4.7E-4 84.0 0.0 28.2 0.0 28.2 +vehicleTypeId nuOfVehicles SumOfTourDuration[s] SumOfTourDuration[h] SumOfTravelDistances[m] SumOfTravelDistances[km] SumOfTravelTime[s] SumOfTravelTime[h] costPerSecond[EUR/s] costPerMeter[EUR/m] fixedCosts[EUR/veh] varCostsTime[EUR] varCostsDist[EUR] fixedCosts[EUR] totalCosts[EUR] +light 0 0.0 0.0 60000.0 60.0 8040.0 2.2333333333333334 0.008 4.7E-4 84.0 0.0 28.2 0.0 28.2 diff --git a/contribs/vsp/test/input/playground/vsp/ev/UrbanEVIT/run/output_events.xml.gz b/contribs/vsp/test/input/playground/vsp/ev/UrbanEVIT/run/output_events.xml.gz index 7c2e5641672..66a3835bc83 100644 Binary files a/contribs/vsp/test/input/playground/vsp/ev/UrbanEVIT/run/output_events.xml.gz and b/contribs/vsp/test/input/playground/vsp/ev/UrbanEVIT/run/output_events.xml.gz differ diff --git a/matsim/pom.xml b/matsim/pom.xml index dd83fd128b5..ba989fc0dff 100644 --- a/matsim/pom.xml +++ b/matsim/pom.xml @@ -127,7 +127,7 @@ it.unimi.dsi fastutil-core - 8.5.12 + 8.5.13 org.geotools @@ -144,6 +144,11 @@ gt-shapefile ${geotools.version} + + org.geotools + gt-geopkg + ${geotools.version} + org.geotools gt-epsg-hsql @@ -339,7 +344,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.6.0 + 3.7.0 src/main/assembly/assembly-release.xml diff --git a/matsim/src/main/java/org/matsim/analysis/pt/stop2stop/PtStop2StopAnalysis2Shp.java b/matsim/src/main/java/org/matsim/analysis/pt/stop2stop/PtStop2StopAnalysis2Shp.java index 6c5516a6a42..99eeb4c0b20 100644 --- a/matsim/src/main/java/org/matsim/analysis/pt/stop2stop/PtStop2StopAnalysis2Shp.java +++ b/matsim/src/main/java/org/matsim/analysis/pt/stop2stop/PtStop2StopAnalysis2Shp.java @@ -32,7 +32,7 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.matsim.core.utils.io.IOUtils; import org.matsim.pt.transitSchedule.api.TransitLine; import org.opengis.feature.simple.SimpleFeature; @@ -104,7 +104,7 @@ public static void writePtStop2StopAnalysisByTransitLine2ShpFile( } // TODO: add stops? - ShapeFileWriter.writeGeometries(features, shpFileName); + GeoFileWriter.writeGeometries(features, shpFileName); } public static void writePtStop2StopAnalysisByTransitLine2CsvFile( diff --git a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java index 3a00441d761..55da3166090 100644 --- a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java @@ -256,7 +256,10 @@ private static boolean checkType(Type type) { var rawType = pType.getRawType(); if (rawType.equals(List.class) || rawType.equals(Set.class)) { var typeArgument = pType.getActualTypeArguments()[0]; - return typeArgument.equals(String.class) || (typeArgument instanceof Class && ((Class) typeArgument).isEnum()); + return typeArgument.equals(String.class) || + typeArgument.equals(Double.class) || + typeArgument.equals(Integer.class) || + (typeArgument instanceof Class && ((Class) typeArgument).isEnum()); } if (rawType.equals(Class.class)) @@ -412,6 +415,11 @@ private Object fromString(String value, Class type, @Nullable Field paramFiel List> enumConstants = getEnumConstants(paramField); return stream.map(s -> stringToEnumValue(s, enumConstants)).collect(toImmutableSet()); } + if (paramField != null && isCollectionOfDoubleType(paramField)) + return stream.map(Double::parseDouble).collect(toImmutableSet()); + if (paramField != null && isCollectionOfIntegerType(paramField)) + return stream.map(Integer::parseInt).collect(toImmutableSet()); + return stream.collect(toImmutableSet()); } else if (type.equals(List.class)) { if (value.isBlank()) { @@ -422,6 +430,11 @@ private Object fromString(String value, Class type, @Nullable Field paramFiel List> enumConstants = getEnumConstants(paramField); return stream.map(s -> stringToEnumValue(s, enumConstants)).toList(); } + if (paramField != null && isCollectionOfDoubleType(paramField)) + return stream.map(Double::parseDouble).toList(); + if (paramField != null && isCollectionOfIntegerType(paramField)) + return stream.map(Integer::parseInt).toList(); + return stream.toList(); } else if (type.equals(Class.class)) { try { @@ -488,7 +501,9 @@ private String getParamField(Field paramField) { boolean accessible = enforceAccessible(paramField); try { var result = paramField.get(this); - if (result != null && isCollectionOfEnumsWithUniqueStringValues(paramField)) { + if (result != null && (isCollectionOfEnumsWithUniqueStringValues(paramField) || + isCollectionOfDoubleType(paramField) || + isCollectionOfIntegerType(paramField))) { result = ((Collection) result).stream() .map(Object::toString) // map enum values to string .collect(Collectors.toList()); @@ -674,6 +689,30 @@ private static boolean isCollectionOfEnumsWithUniqueStringValues(Field paramFiel return false; } + private static boolean isCollectionOfIntegerType(Field paramField) { + var type = paramField.getGenericType(); + if (type instanceof ParameterizedType pType) { + var rawType = pType.getRawType(); + if (rawType.equals(List.class) || rawType.equals(Set.class)) { + var typeArgument = pType.getActualTypeArguments()[0]; + return typeArgument.equals(Integer.class) || typeArgument.equals(Integer.TYPE); + } + } + return false; + } + + private static boolean isCollectionOfDoubleType(Field paramField) { + var type = paramField.getGenericType(); + if (type instanceof ParameterizedType pType) { + var rawType = pType.getRawType(); + if (rawType.equals(List.class) || rawType.equals(Set.class)) { + var typeArgument = pType.getActualTypeArguments()[0]; + return typeArgument.equals(Double.class) || typeArgument.equals(Double.TYPE); + } + } + return false; + } + private static boolean enumStringsAreUnique(Class enumClass) { T[] enumConstants = enumClass.getEnumConstants(); long uniqueStringValues = Arrays.stream(enumConstants) diff --git a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java index 9cbde96f1e3..c84d9eee16c 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java @@ -79,6 +79,8 @@ public enum CleanIterations { private static final String COMPRESSION_TYPE = "compressionType"; private static final String EVENT_TYPE_TO_CREATE_SCORING_FUNCTIONS = "createScoringFunctionType"; + private static final String MEMORY_OBSERVER_INTERVAL = "memoryObserverInterval"; + /*package*/ static final String MOBSIM = "mobsim"; public enum MobsimType {qsim, JDEQSim, hermes} @@ -109,6 +111,8 @@ public enum MobsimType {qsim, JDEQSim, hermes} private CleanIterations cleanItersAtEnd = CleanIterations.keep; + private int memoryObserverInterval = 60; + public ControllerConfigGroup() { super(GROUP_NAME); } @@ -151,6 +155,7 @@ public final Map getComments() { "to a file. `0' disables snapshots writing completely"); map.put(DUMP_DATA_AT_END, "true if at the end of a run, plans, network, config etc should be dumped to a file"); map.put(CLEAN_ITERS_AT_END, "Defines what should be done with the ITERS directory when a simulation finished successfully"); + map.put(MEMORY_OBSERVER_INTERVAL, "Defines the interval for printing memory usage to the log in [seconds]. Must be positive. Defaults to 60."); return map; } @@ -427,6 +432,17 @@ public EventTypeToCreateScoringFunctions getEventTypeToCreateScoringFunctions() public void setEventTypeToCreateScoringFunctions(EventTypeToCreateScoringFunctions eventTypeToCreateScoringFunctions) { this.eventTypeToCreateScoringFunctions = eventTypeToCreateScoringFunctions; } + + @StringGetter(MEMORY_OBSERVER_INTERVAL) + public int getMemoryObserverInterval() { + return memoryObserverInterval; + } + + @StringSetter(MEMORY_OBSERVER_INTERVAL) + public void setMemoryObserverInterval(int memoryObserverInterval) { + this.memoryObserverInterval = memoryObserverInterval; + } + // --- int writePlansUntilIteration = 1 ; public int getWritePlansUntilIteration() { @@ -450,5 +466,8 @@ protected void checkConsistency(Config config) { log.warn( "this is not recommended, as it might result in a directory containing output from several model runs" ); log.warn( "prefer the options "+OverwriteFileSetting.deleteDirectoryIfExists+" or "+OverwriteFileSetting.failIfDirectoryExists ); } + if(config.controller().getMemoryObserverInterval() < 0) { + log.warn("Memory observer interval is negative. Simulation will most likely crash."); + } } } diff --git a/matsim/src/main/java/org/matsim/core/controler/AbstractController.java b/matsim/src/main/java/org/matsim/core/controler/AbstractController.java index 93c1680a231..a1c008e31ba 100644 --- a/matsim/src/main/java/org/matsim/core/controler/AbstractController.java +++ b/matsim/src/main/java/org/matsim/core/controler/AbstractController.java @@ -73,7 +73,7 @@ final void setupOutputDirectory(OutputDirectoryHierarchy controlerIO) { } protected final void run(final Config config) { - MemoryObserver.start(60); + MemoryObserver.start(config.controller().getMemoryObserverInterval()); MatsimRuntimeModifications.MyRunnable runnable = new MatsimRuntimeModifications.MyRunnable() { @Override public void run() throws MatsimRuntimeModifications.UnexpectedShutdownException { diff --git a/matsim/src/main/java/org/matsim/core/network/LinkImpl.java b/matsim/src/main/java/org/matsim/core/network/LinkImpl.java index eab36be649f..1f9b660f9e2 100644 --- a/matsim/src/main/java/org/matsim/core/network/LinkImpl.java +++ b/matsim/src/main/java/org/matsim/core/network/LinkImpl.java @@ -36,6 +36,8 @@ import org.matsim.utils.objectattributes.attributable.Attributes; import org.matsim.utils.objectattributes.attributable.AttributesImpl; +import com.google.common.collect.ImmutableSortedSet; + /*deliberately package*/ class LinkImpl implements Link { private final static Logger log = LogManager.getLogger(Link.class); @@ -131,7 +133,7 @@ private void checkLengthSemantics(){ } } - + ////////////////////////////////////////////////////////////////////// // get methods @@ -184,7 +186,7 @@ public double getCapacityPeriod() { } // --- - + @Override public double getFreespeed() { return this.freespeed; @@ -281,7 +283,7 @@ public static Set get(final Set set) { if (set == null) { return null; } - return cache.computeIfAbsent(set.hashCode(), key -> Set.copyOf(set)); + return cache.computeIfAbsent(set.hashCode(), key -> ImmutableSortedSet.copyOf(set)); } } } diff --git a/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java b/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java index 239f189da42..9e98fbe2269 100644 --- a/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java +++ b/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java @@ -1018,4 +1018,8 @@ public static boolean addDisallowedNextLinks(Link link, String mode, List>> mSequences = new TreeMap<>(); - + try (FileInputStream fis = new FileInputStream(this.mpDbfFileName)) { DbaseFileReader r = new DbaseFileReader(fis.getChannel(), true, IOUtils.CHARSET_WINDOWS_ISO88591); // get header indices @@ -190,7 +190,7 @@ private void run2(final Network network) throws Exception { log.trace(" "+MP_ID_NAME+"-->"+mpIdNameIndex); log.trace(" "+MP_SEQNR_NAME+"-->"+mpSeqNrNameIndex); log.trace(" "+MP_TRPELID_NAME+"-->"+mpTrpelIDNameIndex); - + // create mp data structure // TreeMap> log.info(" parsing meneuver paths dbf file..."); @@ -218,7 +218,7 @@ private void run2(final Network network) throws Exception { // TreeMap>> log.info(" parsing meneuver shape file..."); TreeMap, ArrayList>> maneuvers = new TreeMap<>(); - SimpleFeatureSource fs = ShapeFileReader.readDataFile(this.mnShpFileName); + SimpleFeatureSource fs = GeoFileReader.readDataFile(this.mnShpFileName); SimpleFeatureIterator fIt = fs.getFeatures().features(); while (fIt.hasNext()) { SimpleFeature f = fIt.next(); @@ -256,8 +256,8 @@ else if ((featType == 9401) || (featType == 2104)) { int virtualLinksCnt = 0; for (Map.Entry, ArrayList>> entry : maneuvers.entrySet()) { Id nodeId = entry.getKey(); - if (network.getNodes().get(nodeId) == null) { - log.trace(" nodeid="+nodeId+": maneuvers exist for that node but node is missing. Ignoring and proceeding anyway..."); + if (network.getNodes().get(nodeId) == null) { + log.trace(" nodeid="+nodeId+": maneuvers exist for that node but node is missing. Ignoring and proceeding anyway..."); nodesIgnoredCnt++; } else { // node found @@ -282,7 +282,7 @@ else if ((featType == 9401) || (featType == 2104)) { inLink = n.getInLinks().get(Id.create(firstLinkid+"TF", Link.class)); } Link outLink = n.getOutLinks().get(Id.create(otherLinkId+"FT", Link.class)); - if (outLink == null) { + if (outLink == null) { outLink = n.getOutLinks().get(Id.create(otherLinkId+"TF", Link.class)); } if ((inLink != null) && (outLink != null)) { diff --git a/matsim/src/main/java/org/matsim/core/network/io/NetworkReaderTeleatlas.java b/matsim/src/main/java/org/matsim/core/network/io/NetworkReaderTeleatlas.java index 92d71b014bb..c4416bb5531 100644 --- a/matsim/src/main/java/org/matsim/core/network/io/NetworkReaderTeleatlas.java +++ b/matsim/src/main/java/org/matsim/core/network/io/NetworkReaderTeleatlas.java @@ -33,7 +33,7 @@ import org.matsim.api.core.v01.network.Node; import org.matsim.core.api.internal.MatsimSomeReader; import org.matsim.core.network.NetworkUtils; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.opengis.feature.simple.SimpleFeature; import org.opengis.geometry.BoundingBox; @@ -47,7 +47,7 @@ *
    • nw.shp: junction shape file (typically called xyz________nw.shp) *
    • * - * + * * @author balmermi */ public final class NetworkReaderTeleatlas implements MatsimSomeReader { @@ -154,7 +154,7 @@ public final class NetworkReaderTeleatlas implements MatsimSomeReader { /** * Instantiate a new Tele Atlas MultiNet Shapefile reader based on the * junction and the network shape file. - * + * * @param network * MATSim network database in which the reader stores the data * @param jcShpFileName @@ -189,7 +189,7 @@ public void read() throws IOException { /** * Reads Tele Atlas MultiNet junction Shapefile given in * {@link #jcShpFileName}. - * + * *

      * It uses the following feature attributes: *

        @@ -212,7 +212,7 @@ public void read() throws IOException { *
      *

      * The MATSim {@link Node#type} is set as - * + * *
       	 * {@link Node#type} = {@link #NODE_FEATTYP_NAME}+"-"+{@link #NODE_JNCTTYP_NAME}
       	 * 
      @@ -220,7 +220,7 @@ public void read() throws IOException { */ private void readNodesFromJCshp() throws IOException { int nCnt = network.getNodes().size(); - SimpleFeatureSource fs = ShapeFileReader.readDataFile(jcShpFileName); + SimpleFeatureSource fs = GeoFileReader.readDataFile(jcShpFileName); SimpleFeatureIterator fIt = fs.getFeatures().features(); while (fIt.hasNext()) { SimpleFeature f = fIt.next(); @@ -258,7 +258,7 @@ private void readNodesFromJCshp() throws IOException { /** * Reads Tele Atlas MultiNet network Shapefile given in * {@link #nwShpFileName}. - * + * *

      * It uses the following feature attributes: *

        @@ -307,7 +307,7 @@ private void readNodesFromJCshp() throws IOException { * *
      • {@link #LINK_LANES_NAME} (Number of Lanes)
      • *
      - * + * * Conversion rules: *
        *
      • Links that refer to not existing from- or to-link will be ignored @@ -330,21 +330,21 @@ private void readNodesFromJCshp() throws IOException { * {@link #LINK_ID_NAME}+"TF", * {@link #LINK_ID_NAME}+"FT" resp.
      • *
      • The {@link Link#type} is set as: - * + * *
         	 * {@link Link#type} = {@link #LINK_FRCTYP_NAME}+"-"+{@link #LINK_FEATTYP_NAME}+"-"+{@link #LINK_FERRYTYP_NAME}
         	 * 
        - * + * *
      • *
      *

      - * + * * @throws IOException */ private void readLinksFromNWshp() throws IOException { int lCnt = network.getLinks().size(); int ignoreCnt = 0; - SimpleFeatureSource fs = ShapeFileReader.readDataFile(this.nwShpFileName); + SimpleFeatureSource fs = GeoFileReader.readDataFile(this.nwShpFileName); SimpleFeatureIterator fIt = fs.getFeatures().features(); while (fIt.hasNext()) { SimpleFeature f = fIt.next(); @@ -474,7 +474,7 @@ private void readLinksFromNWshp() throws IOException { /** * prints the variable settings to the STDOUT - * + * * @param prefix * a prefix for each line of the STDOUT */ diff --git a/matsim/src/main/java/org/matsim/core/population/PersonUtils.java b/matsim/src/main/java/org/matsim/core/population/PersonUtils.java index 9c64e247d87..8959899a9b5 100644 --- a/matsim/src/main/java/org/matsim/core/population/PersonUtils.java +++ b/matsim/src/main/java/org/matsim/core/population/PersonUtils.java @@ -27,8 +27,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; public final class PersonUtils { private PersonUtils() { @@ -225,4 +229,23 @@ public static TreeSet getTravelcards(Person person) { public static boolean isSelected(Plan plan) { return plan.getPerson().getSelectedPlan() == plan; } + + /** + * Attaches vehicle types to a person, so that the router knows which vehicle to use for which mode and person. + * @param modeToVehicleType mode string mapped to vehicle type ids. The provided map is copied and stored as unmodifiable map. + */ + public static void insertVehicleTypesIntoPersonAttributes(Person person, Map> modeToVehicleType ) { + VehicleUtils.insertVehicleTypesIntoPersonAttributes( person, modeToVehicleType ); + } + /** + * Attaches vehicle ids to a person, so that the router knows which vehicle to use for which mode and person. + * + * @param modeToVehicle mode string mapped to vehicle ids. The provided map is copied and stored as unmodifiable map. + * If a mode key already exists in the persons's attributes it is overridden. Otherwise, existing + * and provided values are merged into one map + * We use PersonVehicle Class in order to have a dedicated PersonVehicleAttributeConverter to/from XML + */ + public static void insertVehicleIdsIntoPersonAttributes(Person person, Map> modeToVehicle ) { + VehicleUtils.insertVehicleIdsIntoPersonAttributes( person, modeToVehicle ); + } } diff --git a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java index 574f2f76e93..cae206c534b 100644 --- a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java +++ b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java @@ -77,6 +77,9 @@ import org.matsim.facilities.ActivityFacility; import org.matsim.utils.objectattributes.attributable.Attributes; import org.matsim.utils.objectattributes.attributable.AttributesUtils; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; /** * @author nagel, ikaddoura @@ -1266,5 +1269,23 @@ public static Person findPerson( Id personId, Scenario scenario ) { return person ; } + /** + * Attaches vehicle types to a person, so that the router knows which vehicle to use for which mode and person. + * @param modeToVehicleType mode string mapped to vehicle type ids. The provided map is copied and stored as unmodifiable map. + */ + public static void insertVehicleTypesIntoPersonAttributes(Person person, Map> modeToVehicleType ) { + VehicleUtils.insertVehicleTypesIntoPersonAttributes( person, modeToVehicleType ); + } + /** + * Attaches vehicle ids to a person, so that the router knows which vehicle to use for which mode and person. + * + * @param modeToVehicle mode string mapped to vehicle ids. The provided map is copied and stored as unmodifiable map. + * If a mode key already exists in the persons's attributes it is overridden. Otherwise, existing + * and provided values are merged into one map + * We use PersonVehicle Class in order to have a dedicated PersonVehicleAttributeConverter to/from XML + */ + public static void insertVehicleIdsIntoPersonAttributes(Person person, Map> modeToVehicle ) { + VehicleUtils.insertVehicleIdsIntoPersonAttributes( person, modeToVehicle ); + } } diff --git a/matsim/src/main/java/org/matsim/core/router/NetworkRoutingInclAccessEgressModule.java b/matsim/src/main/java/org/matsim/core/router/NetworkRoutingInclAccessEgressModule.java index 49da68730c1..1a24567b1dd 100644 --- a/matsim/src/main/java/org/matsim/core/router/NetworkRoutingInclAccessEgressModule.java +++ b/matsim/src/main/java/org/matsim/core/router/NetworkRoutingInclAccessEgressModule.java @@ -82,7 +82,7 @@ public final class NetworkRoutingInclAccessEgressModule implements RoutingModule private final Config config; private static boolean hasWarnedAccessEgress = false; - private RoutingConfigGroup.AccessEgressType accessEgressType; + private final RoutingConfigGroup.AccessEgressType accessEgressType; private final TimeInterpretation timeInterpretation; private final MultimodalLinkChooser multimodalLinkChooser; @@ -102,7 +102,7 @@ public final class NetworkRoutingInclAccessEgressModule implements RoutingModule final MultimodalLinkChooser multimodalLinkChooser) { this.multimodalLinkChooser = multimodalLinkChooser; Gbl.assertNotNull(scenario.getNetwork()); - Gbl.assertIf(scenario.getNetwork().getLinks().size() > 0); // otherwise network for mode probably not defined + Gbl.assertIf(!scenario.getNetwork().getLinks().isEmpty()); // otherwise network for mode probably not defined this.filteredNetwork = filteredNetwork; this.invertedNetwork = invertedNetwork; this.routeAlgo = routeAlgo; @@ -314,9 +314,7 @@ private List computeAccessTripFromFacilityToLinkIfNecessa } private static Activity createInteractionActivity(final Coord interactionCoord, final Id interactionLink, final String mode) { - Activity act = PopulationUtils.createStageActivityFromCoordLinkIdAndModePrefix(interactionCoord, interactionLink, mode); -// act.setMaximumDuration(0.0); // obsolete since this is hard-coded in InteractionActivity - return act; + return PopulationUtils.createStageActivityFromCoordLinkIdAndModePrefix(interactionCoord, interactionLink, mode); } private static void routeBushwhackingLeg(Person person, Leg leg, Coord fromCoord, Coord toCoord, double depTime, @@ -410,9 +408,16 @@ public String toString() { NetworkRoute route = this.populationFactory.getRouteFactories().createRoute(NetworkRoute.class, fromLink.getId(), toLink.getId()); route.setLinkIds(fromLink.getId(), NetworkUtils.getLinkIds(path.links), toLink.getId()); - route.setTravelTime((int) path.travelTime); + + double relPosOnDepartureLink = 1.0; + double relPosOnArrivalLink = 1.0; + + double maxSpeedOnToLink = Math.min(vehicle.getType().getMaximumVelocity(),toLink.getFreespeed()); + double travelTimeEstimateOnToLink = (toLink.getLength() / maxSpeedOnToLink) * relPosOnArrivalLink; + route.setTravelTime((int) (path.travelTime+travelTimeEstimateOnToLink)); + route.setTravelCost(path.travelCost); - route.setDistance(RouteUtils.calcDistance(route, 1.0, 1.0, this.filteredNetwork)); + route.setDistance(RouteUtils.calcDistance(route, relPosOnDepartureLink, relPosOnArrivalLink, this.filteredNetwork)); route.setVehicleId(vehicleId); leg.setRoute(route); travTime = (int) path.travelTime; diff --git a/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileReader.java b/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileReader.java new file mode 100644 index 00000000000..9938a3e1548 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileReader.java @@ -0,0 +1,285 @@ +/* *********************************************************************** * + * project: org.matsim.* + * ShapeFileReader.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2008 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.core.utils.gis; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.geotools.data.DataStore; +import org.geotools.data.DataStoreFinder; +import org.geotools.data.FileDataStore; +import org.geotools.data.FileDataStoreFinder; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.data.simple.SimpleFeatureIterator; +import org.geotools.data.simple.SimpleFeatureSource; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.geopkg.GeoPkgDataStoreFactory; +import org.geotools.jdbc.JDBCDataStore; +import org.matsim.core.api.internal.MatsimSomeReader; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.utils.misc.Counter; +import org.opengis.feature.Feature; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.feature.type.Name; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URL; +import java.util.*; + +/** + * @author glaemmel + * @author dgrether + * @author mrieser // switch to GeoTools 2.7.3 + * @author nkuehnel / MOIA // add gpkg suuport + */ +public class GeoFileReader implements MatsimSomeReader { + private static final Logger log = LogManager.getLogger(GeoFileReader.class); + + private SimpleFeatureSource featureSource = null; + + private ReferencedEnvelope bounds = null; + + private DataStore dataStore = null; + + private SimpleFeatureCollection featureCollection = null; + + private SimpleFeatureType schema = null; + + private Collection featureSet = null; + + private CoordinateReferenceSystem crs; + + public static Collection getAllFeatures(final String filename) { + return getAllFeatures(filename, null); + } + + + public static Collection getAllFeatures(final String filename, Name layerName) { + try { + if(filename.endsWith(".shp")) { + File dataFile = new File(filename); + log.info("will try to read from " + dataFile.getAbsolutePath()); + Gbl.assertIf(dataFile.exists()); + FileDataStore dataStore = FileDataStoreFinder.getDataStore(dataFile); + return getSimpleFeatures(dataStore); + } else if(filename.endsWith(".gpkg")){ + Gbl.assertNotNull(layerName); + Map params = new HashMap<>(); + params.put(GeoPkgDataStoreFactory.DBTYPE.key, "geopkg"); + params.put(GeoPkgDataStoreFactory.DATABASE.key, filename); + params.put("read-only", true); + DataStore dataStore = DataStoreFinder.getDataStore(params); + return getSimpleFeatures(dataStore, layerName); + } else { + throw new RuntimeException("Unsupported file type."); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + + + public static Collection getAllFeatures(final URL url) { + try { + log.info( "will try to read from " + url.getPath() ) ; + return getSimpleFeatures(FileDataStoreFinder.getDataStore(url)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Read all simple features from a data store. This method makes sure the store is closed afterwards. + * @return list of contained features. + */ + public static List getSimpleFeatures(FileDataStore dataStore) throws IOException { + SimpleFeatureSource featureSource = dataStore.getFeatureSource(); + List featureSet = getSimpleFeatures(featureSource); + dataStore.dispose(); + return featureSet; + } + + /** + * Read all simple features from a data store. This method makes sure the store is closed afterwards. + * @return list of contained features. + */ + public static List getSimpleFeatures(DataStore dataStore, Name layerName) throws IOException { + SimpleFeatureSource featureSource = dataStore.getFeatureSource(layerName); + Gbl.assertNotNull(featureSource); + List featureSet = getSimpleFeatures(featureSource); + dataStore.dispose(); + return featureSet; + } + + private static List getSimpleFeatures(SimpleFeatureSource featureSource) throws IOException { + SimpleFeatureIterator it = featureSource.getFeatures().features(); + List featureSet = new ArrayList<>(); + while (it.hasNext()) { + SimpleFeature ft = it.next(); + featureSet.add(ft); + } + it.close(); + return featureSet; + } + + public Collection readFileAndInitialize(final String filename) { + return readFileAndInitialize(filename, null); + } + + /** + * Reads all Features in the file into the returned Set and initializes the instance of this class. + */ + public Collection readFileAndInitialize(final String filename, Name layerName) throws UncheckedIOException { + try { + this.featureSource = GeoFileReader.readDataFile(filename, layerName); + this.init(); + SimpleFeature ft = null; + SimpleFeatureIterator it = this.featureSource.getFeatures().features(); + this.featureSet = new ArrayList(); + log.info("features to read #" + this.featureSource.getFeatures().size()); + Counter cnt = new Counter("features read #"); + while (it.hasNext()) { + ft = it.next(); + this.featureSet.add(ft); + cnt.incCounter(); + } + cnt.printCounter(); + it.close(); + return this.featureSet; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static SimpleFeatureSource readDataFile(final String filename) { + return readDataFile(filename, null); + } + + /** + * VERY IMPORTANT NOTE
      + *

      + * There are many ways to use that class in a wrong way. The safe way is the following: + *

      + *
       GeoFileReader geoFileReader = new GeoFileReader();
      +		 * geoFileReader.readFileAndInitialize(geoFile); 
      + *

      + * Then, get the features by + *

      + *
       Set<{@link Feature}> features = geoFileReader.getFeatureSet(); 
      + *

      + * If you need metadata you can use + *

      + *
       FeatureSource fs = geoFileReader.getFeatureSource(); 
      + *

      + * to get access to the feature source.
      + * BUT NEVER CALL fs.getFeatures(); !!! It can happen that you will read from disk again!!! + *

      + *

      + * Actually, the whole class must be fixed. But since it is anyway necessary to move to a more recent version of the geotools only this javadoc is added instead. + *

      + *

      + *

      + * The following old doc is kept here: + *

      + *

      + * Provides access to a shape or geopackage file and returns a FeatureSource containing all features. + * Take care access means on disk access, i.e. the FeatureSource is only a pointer to the information + * stored in the file. This can be horribly slow if invoked many times and throw exceptions if two many read + * operations to the same file are performed. In those cases it is recommended to use the method readDataFileToMemory + * of this class. + * + * @param filename File name of a shape or geopackage file (ending in *.shp or *.gpkg) + * @return FeatureSource containing all features. + * @throws UncheckedIOException if the file cannot be found or another error happens during reading + */ + public static SimpleFeatureSource readDataFile(final String filename, Name layerName) throws UncheckedIOException { + try { + log.warn("Unsafe method! store.dispose() is not called from within this method"); + SimpleFeatureSource featureSource; + if(filename.endsWith(".shp")) { + File dataFile = new File(filename); + FileDataStore store = FileDataStoreFinder.getDataStore(dataFile); + featureSource = store.getFeatureSource(); + } else if(filename.endsWith(".gpkg")) { + Gbl.assertNotNull(layerName); + Map params = new HashMap<>(); + params.put(GeoPkgDataStoreFactory.DBTYPE.key, "geopkg"); + params.put(GeoPkgDataStoreFactory.DATABASE.key, filename); + params.put("read-only", true); + + DataStore datastore = DataStoreFinder.getDataStore(params); + featureSource = datastore.getFeatureSource(layerName); + Gbl.assertNotNull(featureSource); + } else { + throw new RuntimeException("Unsupported file type."); + } + return featureSource; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void init() { + try { + this.bounds = this.featureSource.getBounds(); + this.dataStore = (DataStore) this.featureSource.getDataStore(); + this.featureCollection = this.featureSource.getFeatures(); + this.schema = this.featureSource.getSchema(); + this.crs = this.featureSource.getSchema().getCoordinateReferenceSystem(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public SimpleFeatureSource getFeatureSource() { + return featureSource; + } + + public ReferencedEnvelope getBounds() { + return bounds; + } + + public DataStore getDataStore() { + return dataStore; + } + + public SimpleFeatureCollection getFeatureCollection() { + return featureCollection; + } + + public SimpleFeatureType getSchema() { + return schema; + } + + public Collection getFeatureSet() { + return featureSet; + } + + public CoordinateReferenceSystem getCoordinateSystem(){ + return this.crs; + } + + +} diff --git a/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileWriter.java b/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileWriter.java new file mode 100644 index 00000000000..26296281c56 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/utils/gis/GeoFileWriter.java @@ -0,0 +1,101 @@ +/* *********************************************************************** * + * project: org.matsim.* + * ShapeFileWriter.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2007 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.core.utils.gis; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.geotools.data.DataStore; +import org.geotools.data.DataStoreFinder; +import org.geotools.data.FileDataStore; +import org.geotools.data.shapefile.ShapefileDataStore; +import org.geotools.data.simple.SimpleFeatureStore; +import org.geotools.feature.DefaultFeatureCollection; +import org.geotools.feature.NameImpl; +import org.geotools.geopkg.GeoPkgDataStoreFactory; +import org.geotools.jdbc.JDBCDataStoreFactory; +import org.matsim.core.api.internal.MatsimSomeWriter; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.feature.type.Name; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * This is a simple utility class that provides methods to write Feature instances + * of the geotools framework to an ESRI shape or geopackage file. + * + * @author glaemmel + * @author nkuehnel / MOIA // add gpkg support + */ +public class GeoFileWriter implements MatsimSomeWriter { + + private static final Logger log = LogManager.getLogger(GeoFileWriter.class); + + public static void writeGeometries(final Collection features, final String filename) { + writeGeometries(features, filename, null); + } + + + public static void writeGeometries(final Collection features, final String filename, Name layerName) { + if (features.isEmpty()) { + throw new UncheckedIOException(new IOException("Cannot write empty collection")); + } + + try { + SimpleFeatureStore featureSource; + SimpleFeatureType featureType = features.iterator().next().getFeatureType(); + + if(filename.endsWith(".shp")) { + log.info("Writing shapefile to " + filename); + URL fileURL = (new File(filename)).toURI().toURL(); + FileDataStore datastore = new ShapefileDataStore(fileURL); + datastore.createSchema(featureType); + featureSource = (SimpleFeatureStore) datastore.getFeatureSource(); + } else if(filename.endsWith(".gpkg")){ + Map map = new HashMap<>(); + map.put(GeoPkgDataStoreFactory.DBTYPE.key, GeoPkgDataStoreFactory.DBTYPE.sample); + map.put(GeoPkgDataStoreFactory.DATABASE.key, filename); + map.put(JDBCDataStoreFactory.BATCH_INSERT_SIZE.key, 50); + DataStore datastore = DataStoreFinder.getDataStore(map); + datastore.createSchema(featureType); + if(layerName == null) { + layerName = new NameImpl(featureType.getTypeName()); + } + featureSource = (SimpleFeatureStore) datastore.getFeatureSource(layerName); + } else { + throw new RuntimeException("Unsupported file type."); + } + + DefaultFeatureCollection coll = new DefaultFeatureCollection(); + coll.addAll(features); + + featureSource.addFeatures(coll); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileReader.java b/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileReader.java index 33bb84d6982..d3ddd7a6c8c 100644 --- a/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileReader.java +++ b/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileReader.java @@ -20,28 +20,20 @@ package org.matsim.core.utils.gis; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.geotools.data.DataStore; import org.geotools.data.FileDataStore; -import org.geotools.data.FileDataStoreFinder; import org.geotools.data.simple.SimpleFeatureCollection; -import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.geometry.jts.ReferencedEnvelope; import org.matsim.core.api.internal.MatsimSomeReader; -import org.matsim.core.gbl.Gbl; -import org.matsim.core.utils.misc.Counter; import org.opengis.feature.Feature; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URL; -import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -50,41 +42,17 @@ * @author dgrether * @author mrieser // switch to GeoTools 2.7.3 */ +@Deprecated public class ShapeFileReader implements MatsimSomeReader { - private static final Logger log = LogManager.getLogger(ShapeFileReader.class); - private SimpleFeatureSource featureSource = null; - - private ReferencedEnvelope bounds = null; - - private DataStore dataStore = null; - - private SimpleFeatureCollection featureCollection = null; - - private SimpleFeatureType schema = null; - - private Collection featureSet = null; - - private CoordinateReferenceSystem crs; + private final GeoFileReader geoFileReader = new GeoFileReader(); public static Collection getAllFeatures(final String filename) { - try { - File dataFile = new File(filename); - log.info( "will try to read from " + dataFile.getAbsolutePath() ) ; - Gbl.assertIf( dataFile.exists() ); - return getSimpleFeatures(FileDataStoreFinder.getDataStore(dataFile)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return GeoFileReader.getAllFeatures(filename, null); } public static Collection getAllFeatures(final URL url) { - try { - log.info( "will try to read from " + url.getPath() ) ; - return getSimpleFeatures(FileDataStoreFinder.getDataStore(url)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return GeoFileReader.getAllFeatures(url); } /** @@ -92,42 +60,14 @@ public static Collection getAllFeatures(final URL url) { * @return list of contained features. */ public static List getSimpleFeatures(FileDataStore dataStore) throws IOException { - SimpleFeatureSource featureSource = dataStore.getFeatureSource(); - - SimpleFeatureIterator it = featureSource.getFeatures().features(); - List featureSet = new ArrayList<>(); - while (it.hasNext()) { - SimpleFeature ft = it.next(); - featureSet.add(ft); - } - it.close(); - dataStore.dispose(); - return featureSet; + return GeoFileReader.getSimpleFeatures(dataStore); } /** * Reads all Features in the file into the returned Set and initializes the instance of this class. */ public Collection readFileAndInitialize(final String filename) throws UncheckedIOException { - try { - this.featureSource = ShapeFileReader.readDataFile(filename); - this.init(); - SimpleFeature ft = null; - SimpleFeatureIterator it = this.featureSource.getFeatures().features(); - this.featureSet = new ArrayList(); - log.info("features to read #" + this.featureSource.getFeatures().size()); - Counter cnt = new Counter("features read #"); - while (it.hasNext()) { - ft = it.next(); - this.featureSet.add(ft); - cnt.incCounter(); - } - cnt.printCounter(); - it.close(); - return this.featureSet; - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return geoFileReader.readFileAndInitialize(filename); } /** @@ -168,54 +108,35 @@ public Collection readFileAndInitialize(final String filename) th * @throws UncheckedIOException if the file cannot be found or another error happens during reading */ public static SimpleFeatureSource readDataFile(final String filename) throws UncheckedIOException { - try { - log.warn("Unsafe method! store.dispose() is not called from within this method"); - File dataFile = new File(filename); - FileDataStore store = FileDataStoreFinder.getDataStore(dataFile); - return store.getFeatureSource(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private void init() { - try { - this.bounds = this.featureSource.getBounds(); - this.dataStore = (DataStore) this.featureSource.getDataStore(); - this.featureCollection = this.featureSource.getFeatures(); - this.schema = this.featureSource.getSchema(); - this.crs = this.featureSource.getSchema().getCoordinateReferenceSystem(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return GeoFileReader.readDataFile(filename); } public SimpleFeatureSource getFeatureSource() { - return featureSource; + return geoFileReader.getFeatureSource(); } public ReferencedEnvelope getBounds() { - return bounds; + return geoFileReader.getBounds(); } public DataStore getDataStore() { - return dataStore; + return geoFileReader.getDataStore(); } public SimpleFeatureCollection getFeatureCollection() { - return featureCollection; + return geoFileReader.getFeatureCollection(); } public SimpleFeatureType getSchema() { - return schema; + return geoFileReader.getSchema(); } public Collection getFeatureSet() { - return featureSet; + return geoFileReader.getFeatureSet(); } public CoordinateReferenceSystem getCoordinateSystem(){ - return this.crs; + return geoFileReader.getCoordinateSystem(); } diff --git a/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileWriter.java b/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileWriter.java index 2bb2650f814..c0ea83390e9 100644 --- a/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileWriter.java +++ b/matsim/src/main/java/org/matsim/core/utils/gis/ShapeFileWriter.java @@ -20,19 +20,9 @@ package org.matsim.core.utils.gis; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.geotools.data.shapefile.ShapefileDataStore; -import org.geotools.data.simple.SimpleFeatureStore; -import org.geotools.feature.DefaultFeatureCollection; import org.matsim.core.api.internal.MatsimSomeWriter; import org.opengis.feature.simple.SimpleFeature; -import org.opengis.feature.simple.SimpleFeatureType; -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URL; import java.util.Collection; /** * This is a simple utility class that provides methods to write Feature instances @@ -40,31 +30,10 @@ * * @author glaemmel */ +@Deprecated public class ShapeFileWriter implements MatsimSomeWriter { - private static final Logger log = LogManager.getLogger(ShapeFileWriter.class); - public static void writeGeometries(final Collection features, final String filename) { - if (features.isEmpty()) { - throw new UncheckedIOException(new IOException("Cannot write empty collection")); - } - log.info("Writing shapefile to " + filename); - try { - URL fileURL = (new File(filename)).toURI().toURL(); - - ShapefileDataStore datastore = new ShapefileDataStore(fileURL); - SimpleFeature feature = features.iterator().next(); - datastore.createSchema(feature.getFeatureType()); - - DefaultFeatureCollection coll = new DefaultFeatureCollection(); - coll.addAll(features); - - SimpleFeatureType featureType = features.iterator().next().getFeatureType(); - datastore.createSchema(featureType); - SimpleFeatureStore featureSource = (SimpleFeatureStore) datastore.getFeatureSource(); - featureSource.addFeatures(coll); - } catch (IOException e) { - throw new RuntimeException(e); - } + GeoFileWriter.writeGeometries(features, filename); } } diff --git a/matsim/src/main/java/org/matsim/run/gui/ExeRunner.java b/matsim/src/main/java/org/matsim/run/gui/ExeRunner.java index 8e2f94c3109..fac8a5df38d 100644 --- a/matsim/src/main/java/org/matsim/run/gui/ExeRunner.java +++ b/matsim/src/main/java/org/matsim/run/gui/ExeRunner.java @@ -97,6 +97,13 @@ public void killProcess() { public void run() { var processBuilder = new ProcessBuilder(); processBuilder.environment().put("MATSIM_GUI", "true"); // add "MATSIM_GUI" to the inherited vars + + // Copy the MATSIM_GUI_ARGS environment variable to the process environment + // these arguments may be used internally by the matsim scenario + if (System.getProperty("MATSIM_GUI_ARGS") != null) { + processBuilder.environment().put("MATSIM_GUI_ARGS", System.getProperty("MATSIM_GUI_ARGS")); + } + if (workingDirectory != null) { processBuilder.directory(new File(workingDirectory)); } diff --git a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Links2ESRIShape.java b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Links2ESRIShape.java index f27a0195ba1..d8c8d452eb4 100755 --- a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Links2ESRIShape.java +++ b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Links2ESRIShape.java @@ -33,7 +33,7 @@ import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -74,7 +74,7 @@ public void write() { features.add(this.featureGenerator.getFeature(link)); } log.info("writing features to shape file... " + this.filename); - ShapeFileWriter.writeGeometries(features, this.filename); + GeoFileWriter.writeGeometries(features, this.filename); log.info("done writing shape file."); } diff --git a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Nodes2ESRIShape.java b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Nodes2ESRIShape.java index 90b48753450..003c08d7a34 100644 --- a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Nodes2ESRIShape.java +++ b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/network/Nodes2ESRIShape.java @@ -35,7 +35,7 @@ import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -56,11 +56,11 @@ public class Nodes2ESRIShape { private final String filename; private SimpleFeatureBuilder builder; - + public Nodes2ESRIShape(final Network network, final String filename, final String coordinateSystem) { this(network, filename, MGC.getCRS(coordinateSystem)); } - + public Nodes2ESRIShape(Network network, String filename, CoordinateReferenceSystem crs) { this.network = network; this.filename = filename; @@ -72,7 +72,7 @@ public void write() { for (Node node : NetworkUtils.getSortedNodes(this.network)) { features.add(getFeature(node)); } - ShapeFileWriter.writeGeometries(features, this.filename); + GeoFileWriter.writeGeometries(features, this.filename); } diff --git a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShape.java b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShape.java index dfaee197ecc..f40dc653e51 100755 --- a/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShape.java +++ b/matsim/src/main/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShape.java @@ -51,7 +51,7 @@ import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileWriter; +import org.matsim.core.utils.gis.GeoFileWriter; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -112,7 +112,7 @@ public void write() { drawOutputSample(); if (this.writeActs) { writeActs(); - } + } if (this.writeLegs) { writeLegs(); } @@ -143,7 +143,7 @@ private void writeActs() throws IOException { } } - ShapeFileWriter.writeGeometries(fts, outputFile); + GeoFileWriter.writeGeometries(fts, outputFile); } private void writeLegs() throws IOException { @@ -164,7 +164,7 @@ private void writeLegs() throws IOException { } } } - ShapeFileWriter.writeGeometries(fts, outputFile); + GeoFileWriter.writeGeometries(fts, outputFile); } private SimpleFeature getActFeature(final String id, final Activity act) { @@ -176,7 +176,7 @@ private SimpleFeature getActFeature(final String id, final Activity act) { double ry = MatsimRandom.getRandom().nextDouble() * this.actBlurFactor; Coord cc = this.network.getLinks().get(act.getLinkId()).getCoord(); Coord c = new Coord(cc.getX() + rx, cc.getY() + ry); - + try { return this.actBuilder.buildFeature(null, new Object [] {MGC.coord2Point(c), id, type, linkId, startTime, endTime}); } catch (IllegalArgumentException e) { @@ -235,7 +235,7 @@ private void initFeatureType() { actBuilder.add("LINK_ID", String.class); actBuilder.add("START_TIME", Double.class); actBuilder.add("END_TIME", Double.class); - + SimpleFeatureTypeBuilder legBuilder = new SimpleFeatureTypeBuilder(); legBuilder.setName("leg"); legBuilder.setCRS(this.crs); diff --git a/matsim/src/main/java/org/matsim/utils/gis/shp2matsim/ShpGeometryUtils.java b/matsim/src/main/java/org/matsim/utils/gis/shp2matsim/ShpGeometryUtils.java index bee189e3e24..4c5d56e9f98 100644 --- a/matsim/src/main/java/org/matsim/utils/gis/shp2matsim/ShpGeometryUtils.java +++ b/matsim/src/main/java/org/matsim/utils/gis/shp2matsim/ShpGeometryUtils.java @@ -29,11 +29,11 @@ import org.locationtech.jts.geom.prep.PreparedGeometryFactory; import org.matsim.api.core.v01.Coord; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; public class ShpGeometryUtils { public static List loadGeometries(URL url) { - return ShapeFileReader.getAllFeatures(url) + return GeoFileReader.getAllFeatures(url) .stream() .map(sf -> (Geometry)sf.getDefaultGeometry()) .collect(Collectors.toList()); @@ -41,7 +41,7 @@ public static List loadGeometries(URL url) { public static List loadPreparedGeometries(URL url) { PreparedGeometryFactory factory = new PreparedGeometryFactory(); - return ShapeFileReader.getAllFeatures(url) + return GeoFileReader.getAllFeatures(url) .stream() .map(sf -> factory.create((Geometry)sf.getDefaultGeometry())) .collect(Collectors.toList()); diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java index d8d8f8e6f3d..52a342140d0 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java @@ -64,6 +64,7 @@ public ObjectAttributesConverter() { this.converters.put(PersonVehicles.class.getName(), new PersonVehiclesAttributeConverter()); this.converters.put(DisallowedNextLinks.class.getName(), new DisallowedNextLinksAttributeConverter()); this.converters.put(PersonVehicleTypes.class.getName(), new PersonVehicleTypesAttributeConverter()); + this.converters.put(StringDoubleMap.class.getName(), new StringDoubleMapConverter()); } //this is for reading diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/StringDoubleMap.java b/matsim/src/main/java/org/matsim/utils/objectattributes/StringDoubleMap.java new file mode 100644 index 00000000000..4125ea0e1dc --- /dev/null +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/StringDoubleMap.java @@ -0,0 +1,14 @@ +package org.matsim.utils.objectattributes; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class StringDoubleMap extends LinkedHashMap { + public StringDoubleMap() { + super(); + } + + public StringDoubleMap(Map map) { + super(map); + } +} diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringConverter.java index 07ede6e8bd5..bf9a209818a 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringConverter.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.matsim.utils.objectattributes.AttributeConverter; @@ -29,19 +30,15 @@ * @author mrieser */ public class StringConverter implements AttributeConverter { - private final Map stringCache = new HashMap(1000); + private final Map stringCache = new ConcurrentHashMap<>(1000); + @Override public String convert(String value) { - String s = this.stringCache.get(value); - if (s == null) { - s = value; - this.stringCache.put(s, s); - } - return s; + return stringCache.computeIfAbsent(value, k -> k); } @Override public String convertToString(Object o) { return (String) o; } -} \ No newline at end of file +} diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverter.java new file mode 100644 index 00000000000..06578bdc186 --- /dev/null +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverter.java @@ -0,0 +1,38 @@ +package org.matsim.utils.objectattributes.attributeconverters; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.utils.objectattributes.AttributeConverter; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.MapType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.matsim.utils.objectattributes.StringDoubleMap; + +public class StringDoubleMapConverter implements AttributeConverter { + private static final Logger LOG = LogManager.getLogger(StringDoubleMapConverter.class); + private static final ObjectMapper mapper = new ObjectMapper(); + private static final MapType mapType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, + Double.class); + + @Override + public StringDoubleMap convert(String value) { + try { + return new StringDoubleMap(mapper.readValue(value, mapType)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + public String convertToString(Object o) { + if (!(o instanceof StringDoubleMap)) { + LOG.error("Object is not of type StringDoubleMap: {}", o.getClass()); + return null; + } + + return new StringStringMapConverter().convertToString(o); + } +} diff --git a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java index 0141e814600..6c0a9b289ba 100644 --- a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java +++ b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java @@ -141,9 +141,9 @@ public static Map> getVehicleIds(Person person) { var personVehicles = (PersonVehicles) person.getAttributes().getAttribute(VehicleUtils.VEHICLE_ATTRIBUTE_KEY); if (personVehicles == null) { throw new RuntimeException("Could not retrieve vehicle id from person: " + person.getId().toString() + - ". \nIf you are not using config.qsim().getVehicleSource() with 'defaultVehicle' or 'modeVehicleTypesFromVehiclesData' you have to provide " + - "a vehicle for each mode for each person. Attach a PersonVehicles instance (containing a map of mode:String -> id:Id) with key 'vehicles' as person attribute to each person." + - "\n VehicleUtils.insertVehicleIdIntoAttributes does this for you."); + ". \nIf you are not using config.qsim().getVehicleSource() with 'defaultVehicle' or 'modeVehicleTypesFromVehiclesData' you have to provide " + + "a vehicle for each mode for each person. Attach a PersonVehicles instance (containing a map of mode:String -> id:Id) with key 'vehicles' as person attribute to each person." + + "\n VehicleUtils.insertVehicleIdIntoAttributes does this for you."); } return personVehicles.getModeVehicles(); } @@ -167,23 +167,37 @@ public static Id getVehicleId(Person person, String mode) { Map> vehicleIds = getVehicleIds(person); if (!vehicleIds.containsKey(mode)) { throw new RuntimeException("Could not retrieve vehicle id from person: " + person.getId().toString() + " for mode: " + mode + - ". \nIf you are not using config.qsim().getVehicleSource() with 'defaultVehicle' or 'modeVehicleTypesFromVehiclesData' you have to provide " + - "a vehicle for each mode for each person. Attach a PersonVehicles instance (containing a map of mode:String -> id:Id) with key 'vehicles' as person attribute to each person." + - "\n VehicleUtils.insertVehicleIdIntoAttributes does this for you." + ". \nIf you are not using config.qsim().getVehicleSource() with 'defaultVehicle' or 'modeVehicleTypesFromVehiclesData' you have to provide " + + "a vehicle for each mode for each person. Attach a PersonVehicles instance (containing a map of mode:String -> id:Id) with key 'vehicles' as person attribute to each person." + + "\n VehicleUtils.insertVehicleIdIntoAttributes does this for you." ); } return vehicleIds.get(mode); } - /** + /** * Attaches vehicle ids to a person, so that the router knows which vehicle to use for which mode and person. * * @param modeToVehicle mode string mapped to vehicle ids. The provided map is copied and stored as unmodifiable map. * If a mode key already exists in the persons's attributes it is overridden. Otherwise, existing * and provided values are merged into one map * We use PersonVehicle Class in order to have a dedicated PersonVehicleAttributeConverter to/from XML + * + * @deprecated inline to more expressive method */ - public static void insertVehicleIdsIntoAttributes(Person person, Map> modeToVehicle) { + @Deprecated + public static void insertVehicleIdsIntoAttributes(Person person, Map> modeToVehicle){ + insertVehicleIdsIntoPersonAttributes( person, modeToVehicle ); + } + /** + * Attaches vehicle ids to a person, so that the router knows which vehicle to use for which mode and person. + * + * @param modeToVehicle mode string mapped to vehicle ids. The provided map is copied and stored as unmodifiable map. + * If a mode key already exists in the persons's attributes it is overridden. Otherwise, existing + * and provided values are merged into one map + * We use PersonVehicle Class in order to have a dedicated PersonVehicleAttributeConverter to/from XML + */ + public static void insertVehicleIdsIntoPersonAttributes(Person person, Map> modeToVehicle) { Object attr = person.getAttributes().getAttribute(VEHICLE_ATTRIBUTE_KEY); // copy in case it's a UnmodifiableMap Map> modeToVehicleCopy = new HashMap<>(modeToVehicle); @@ -200,8 +214,17 @@ public static void insertVehicleIdsIntoAttributes(Person person, Map> modeToVehicleType) { + insertVehicleTypesIntoPersonAttributes( person, modeToVehicleType ); + } + /** + * Attaches vehicle types to a person, so that the router knows which vehicle to use for which mode and person. + * @param modeToVehicleType mode string mapped to vehicle type ids. The provided map is copied and stored as unmodifiable map. + */ + public static void insertVehicleTypesIntoPersonAttributes(Person person, Map> modeToVehicleType) { Object attr = person.getAttributes().getAttribute(VEHICLE_TYPES_ATTRIBUTE_KEY); Map> modeToTypesCopy = new HashMap<>(modeToVehicleType); @@ -273,7 +296,7 @@ public static Double getFuelConsumption(VehicleType vehicleType) { } public static void setFuelConsumption(VehicleType vehicleType, double literPerMeter) { - setFuelConsumption(vehicleType.getEngineInformation(), literPerMeter); + setFuelConsumption(vehicleType.getEngineInformation(), literPerMeter); } //******** EngineInformation attributes ************ @@ -307,11 +330,11 @@ public static void setHbefaEmissionsConcept( EngineInformation engineInformation } public static Double getEnergyConsumptionKWhPerMeter(EngineInformation engineInformation) { - return (Double) engineInformation.getAttributes().getAttribute(ENERGYCONSUMPTION); - } + return (Double) engineInformation.getAttributes().getAttribute(ENERGYCONSUMPTION); + } public static void setEnergyConsumptionKWhPerMeter(EngineInformation engineInformation, double energyConsumptionKWhPerMeter) { - engineInformation.getAttributes().putAttribute(ENERGYCONSUMPTION, energyConsumptionKWhPerMeter); + engineInformation.getAttributes().putAttribute(ENERGYCONSUMPTION, energyConsumptionKWhPerMeter); } public static Double getEnergyCapacity(EngineInformation engineInformation) { diff --git a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java index 17b8278da9e..9e0f1e55fb1 100644 --- a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java @@ -37,6 +37,7 @@ import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Person; import org.matsim.core.config.ReflectiveConfigGroup.InconsistentModuleException; import org.matsim.testcases.MatsimTestUtils; @@ -70,6 +71,7 @@ void testDumpAndRead() { dumpedModule.enumSetField = Set.of(MyEnum.VALUE2); dumpedModule.setField = ImmutableSet.of("a", "b", "c"); dumpedModule.listField = List.of("1", "2", "3"); + dumpedModule.ints = List.of(1, 2, 3); assertEqualAfterDumpAndRead(dumpedModule); } @@ -166,6 +168,7 @@ void testComments() { expectedComments.put("enumListField", "list of enum"); expectedComments.put("enumSetField", "set of enum"); expectedComments.put("setField", "set"); + expectedComments.put("ints", "list of ints"); assertThat(new MyModule().getComments()).isEqualTo(expectedComments); } @@ -324,7 +327,7 @@ void testFailUnsupportedType_StringCollections() { void testFailUnsupportedType_NonStringList() { assertThatThrownBy(() -> new ReflectiveConfigGroup("name") { @Parameter("field") - private List stuff; + private List stuff; }).isInstanceOf(InconsistentModuleException.class); } @@ -472,6 +475,10 @@ private static class MyModule extends ReflectiveConfigGroup { @Parameter private Set enumSetField; + @Comment("list of ints") + @Parameter + private List ints; + // Object fields: // Id: string representation is toString private Id idField; diff --git a/matsim/src/test/java/org/matsim/core/network/AbstractNetworkWriterReaderTest.java b/matsim/src/test/java/org/matsim/core/network/AbstractNetworkWriterReaderTest.java index b3c47aecbf7..c479667c573 100644 --- a/matsim/src/test/java/org/matsim/core/network/AbstractNetworkWriterReaderTest.java +++ b/matsim/src/test/java/org/matsim/core/network/AbstractNetworkWriterReaderTest.java @@ -93,6 +93,12 @@ public abstract class AbstractNetworkWriterReaderTest { */ protected abstract void readNetwork(final Scenario scenario, final InputStream stream); + @Test + void testAllowedModes_manyUnsortedModes() { + doTestAllowedModes(createHashSet("train", "drt", "car", "bus", "walk", "freight"), + utils.getOutputDirectory() + "network.xml"); + } + @Test void testAllowedModes_multipleModes() { doTestAllowedModes(createHashSet("bus", "train"), utils.getOutputDirectory() + "network.xml"); @@ -245,6 +251,19 @@ private void doTestAllowedModes(final Set modes, final String filename) Set modes2 = link1.getAllowedModes(); assertEquals(modes.size(), modes2.size(), "wrong number of allowed modes."); assertTrue(modes2.containsAll(modes), "wrong mode."); + + assertModesSorted(modes2); + } + + private static void assertModesSorted(Set modes) { + List originalModes = new ArrayList<>(modes); + List sortedModes = new ArrayList<>(modes); + Collections.sort(sortedModes); + + for (int i = 0; i < originalModes.size(); i++) { + assertEquals(sortedModes.get(i), originalModes.get(i), + "modes are not sorted. expected '" + sortedModes + "'' but got '" + originalModes + "'"); + } } private static Set createHashSet(T... mode) { diff --git a/matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileReaderTest.java b/matsim/src/test/java/org/matsim/core/utils/gis/GeoFileReaderTest.java similarity index 78% rename from matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileReaderTest.java rename to matsim/src/test/java/org/matsim/core/utils/gis/GeoFileReaderTest.java index 1a07451aa7b..d74ae57c395 100644 --- a/matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileReaderTest.java +++ b/matsim/src/test/java/org/matsim/core/utils/gis/GeoFileReaderTest.java @@ -24,6 +24,7 @@ import java.io.IOException; import org.geotools.data.FeatureSource; +import org.geotools.feature.NameImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -31,8 +32,9 @@ /** * @author mrieser / senozon + * @author nkuehnel / MOIA // add gpkg test */ -public class ShapeFileReaderTest { +public class GeoFileReaderTest { @RegisterExtension private MatsimTestUtils utils = new MatsimTestUtils(); @@ -41,9 +43,19 @@ public class ShapeFileReaderTest { * @throws IOException */ @Test - void testPlusInFilename() throws IOException { + void testShp() throws IOException { String filename = "src/test/resources/" + utils.getInputDirectory() + "test+test.shp"; - FeatureSource fs = ShapeFileReader.readDataFile(filename); + FeatureSource fs = GeoFileReader.readDataFile(filename); + Assertions.assertEquals(3, fs.getFeatures().size()); + } + + /** + * @throws IOException + */ + @Test + void testGpkg() throws IOException { + String filename = "src/test/resources/" + utils.getInputDirectory() + "test+test.gpkg"; + FeatureSource fs = GeoFileReader.readDataFile(filename, new NameImpl("testtest")); Assertions.assertEquals(3, fs.getFeatures().size()); } } diff --git a/matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileWriterTest.java b/matsim/src/test/java/org/matsim/core/utils/gis/GeoFileWriterTest.java similarity index 70% rename from matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileWriterTest.java rename to matsim/src/test/java/org/matsim/core/utils/gis/GeoFileWriterTest.java index 70c19d22d88..3c4d1de3994 100644 --- a/matsim/src/test/java/org/matsim/core/utils/gis/ShapeFileWriterTest.java +++ b/matsim/src/test/java/org/matsim/core/utils/gis/GeoFileWriterTest.java @@ -29,6 +29,7 @@ import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; +import org.geotools.feature.NameImpl; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.referencing.crs.DefaultGeographicCRS; @@ -45,7 +46,7 @@ import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; -public class ShapeFileWriterTest { +public class GeoFileWriterTest { @RegisterExtension private MatsimTestUtils utils = new MatsimTestUtils(); @@ -55,16 +56,16 @@ void testShapeFileWriter() throws IOException{ String inFile = "src/test/resources/" + utils.getInputDirectory() + "test.shp"; String outFile = utils.getOutputDirectory() + "/test.shp"; - SimpleFeatureSource s = ShapeFileReader.readDataFile(inFile); + SimpleFeatureSource s = GeoFileReader.readDataFile(inFile); SimpleFeatureCollection fts = s.getFeatures(); SimpleFeatureIterator it = fts.features(); SimpleFeature ft = it.next(); Geometry g = (Geometry) ft.getDefaultGeometry(); List fc = new ArrayList<>(); fc.add(ft); - ShapeFileWriter.writeGeometries(fc, outFile); + GeoFileWriter.writeGeometries(fc, outFile); - SimpleFeatureSource s1 = ShapeFileReader.readDataFile(outFile); + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile); SimpleFeatureCollection fts1 = s1.getFeatures(); SimpleFeatureIterator it1 = fts1.features(); SimpleFeature ft1 = it1.next(); @@ -74,6 +75,31 @@ void testShapeFileWriter() throws IOException{ } + @Test + void testGeopackageFileWriter() throws IOException{ + + String inFile = "src/test/resources/" + utils.getInputDirectory() + "test.gpkg"; + + String outFile = utils.getOutputDirectory() + "/test.gpkg"; + SimpleFeatureSource s = GeoFileReader.readDataFile(inFile, new NameImpl("test")); + SimpleFeatureCollection fts = s.getFeatures(); + SimpleFeatureIterator it = fts.features(); + SimpleFeature ft = it.next(); + Geometry g = (Geometry) ft.getDefaultGeometry(); + List fc = new ArrayList<>(); + fc.add(ft); + GeoFileWriter.writeGeometries(fc, outFile); + + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile, new NameImpl("test")); + SimpleFeatureCollection fts1 = s1.getFeatures(); + SimpleFeatureIterator it1 = fts1.features(); + SimpleFeature ft1 = it1.next(); + Geometry g1 = (Geometry) ft1.getDefaultGeometry(); + + Assertions.assertEquals(g.getCoordinates().length, g1.getCoordinates().length); + + } + @Test void testShapeFileWriterWithSelfCreatedContent() throws IOException { String outFile = utils.getOutputDirectory() + "/test.shp"; @@ -94,9 +120,42 @@ void testShapeFileWriterWithSelfCreatedContent() throws IOException { Geometry g0 = (Geometry) features.iterator().next().getDefaultGeometry(); - ShapeFileWriter.writeGeometries(features, outFile); + GeoFileWriter.writeGeometries(features, outFile); + + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile); + SimpleFeatureCollection fts1 = s1.getFeatures(); + SimpleFeatureIterator it1 = fts1.features(); + SimpleFeature ft1 = it1.next(); + Geometry g1 = (Geometry) ft1.getDefaultGeometry(); + + Assertions.assertEquals(g0.getCoordinates().length, g1.getCoordinates().length); + + + } + + @Test + void testGeopackageFileWriterWithSelfCreatedContent() throws IOException { + String outFile = utils.getOutputDirectory() + "/test2.gpkg"; + SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); + b.setName("EvacuationArea"); + b.setCRS(DefaultGeographicCRS.WGS84); + b.add("the_geom", MultiPolygon.class); + b.add("name", String.class); + SimpleFeatureType ft = b.buildFeatureType(); + + GeometryFactory geofac = new GeometryFactory(); + LinearRing lr = geofac.createLinearRing(new Coordinate[]{new Coordinate(0,0),new Coordinate(0,1),new Coordinate(1,1),new Coordinate(0,0)}); + Polygon p = geofac.createPolygon(lr,null); + MultiPolygon mp = geofac.createMultiPolygon(new Polygon[]{p}); + Collection features = new ArrayList(); + features.add(SimpleFeatureBuilder.build(ft, new Object[]{mp,"test_name"},"fid")); + + + Geometry g0 = (Geometry) features.iterator().next().getDefaultGeometry(); + + GeoFileWriter.writeGeometries(features, outFile); - SimpleFeatureSource s1 = ShapeFileReader.readDataFile(outFile); + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile, new NameImpl("EvacuationArea")); SimpleFeatureCollection fts1 = s1.getFeatures(); SimpleFeatureIterator it1 = fts1.features(); SimpleFeature ft1 = it1.next(); @@ -125,9 +184,9 @@ void testShapeFileWriterWithSelfCreatedContent_withMatsimFactory_Polygon() throw Geometry g0 = (Geometry) f.getDefaultGeometry(); - ShapeFileWriter.writeGeometries(features, outFile); + GeoFileWriter.writeGeometries(features, outFile); - SimpleFeatureSource s1 = ShapeFileReader.readDataFile(outFile); + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile); SimpleFeatureCollection fts1 = s1.getFeatures(); SimpleFeatureIterator it1 = fts1.features(); SimpleFeature ft1 = it1.next(); @@ -154,9 +213,9 @@ void testShapeFileWriterWithSelfCreatedContent_withMatsimFactory_Polyline() thro Geometry g0 = (Geometry) f.getDefaultGeometry(); - ShapeFileWriter.writeGeometries(features, outFile); + GeoFileWriter.writeGeometries(features, outFile); - SimpleFeatureSource s1 = ShapeFileReader.readDataFile(outFile); + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile); SimpleFeatureCollection fts1 = s1.getFeatures(); SimpleFeatureIterator it1 = fts1.features(); SimpleFeature ft1 = it1.next(); @@ -182,9 +241,9 @@ void testShapeFileWriterWithSelfCreatedContent_withMatsimFactory_Point() throws Geometry g0 = (Geometry) f.getDefaultGeometry(); - ShapeFileWriter.writeGeometries(features, outFile); + GeoFileWriter.writeGeometries(features, outFile); - SimpleFeatureSource s1 = ShapeFileReader.readDataFile(outFile); + SimpleFeatureSource s1 = GeoFileReader.readDataFile(outFile); SimpleFeatureCollection fts1 = s1.getFeatures(); SimpleFeatureIterator it1 = fts1.features(); SimpleFeature ft1 = it1.next(); diff --git a/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/network/Network2ESRIShapeTest.java b/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/network/Network2ESRIShapeTest.java index 6700340ee8f..ad21f1e38cd 100755 --- a/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/network/Network2ESRIShapeTest.java +++ b/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/network/Network2ESRIShapeTest.java @@ -31,7 +31,7 @@ import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.testcases.MatsimTestUtils; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -60,7 +60,7 @@ void testPolygonCapacityShape() { builder.setCoordinateReferenceSystem(crs); new Links2ESRIShape(network,outputFileP, builder).write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outputFileP); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outputFileP); Assertions.assertEquals(network.getLinks().size(), writtenFeatures.size()); } @@ -83,7 +83,7 @@ void testPolygonLanesShape() { builder.setCoordinateReferenceSystem(crs); new Links2ESRIShape(network,outputFileP, builder).write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outputFileP); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outputFileP); Assertions.assertEquals(network.getLinks().size(), writtenFeatures.size()); } @@ -106,7 +106,7 @@ void testPolygonFreespeedShape() { builder.setCoordinateReferenceSystem(crs); new Links2ESRIShape(network,outputFileP, builder).write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outputFileP); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outputFileP); Assertions.assertEquals(network.getLinks().size(), writtenFeatures.size()); } @@ -129,7 +129,7 @@ void testLineStringShape() { builder.setCoordinateReferenceSystem(crs); new Links2ESRIShape(network,outputFileShp, builder).write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outputFileShp); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outputFileShp); Assertions.assertEquals(network.getLinks().size(), writtenFeatures.size()); } @@ -145,7 +145,7 @@ void testNodesShape() { new Nodes2ESRIShape(network,outputFileShp, "DHDN_GK4").write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outputFileShp); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outputFileShp); Assertions.assertEquals(network.getNodes().size(), writtenFeatures.size()); } } diff --git a/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShapeTest.java b/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShapeTest.java index f5ec5a13be3..c619704622b 100755 --- a/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShapeTest.java +++ b/matsim/src/test/java/org/matsim/utils/gis/matsim2esri/plans/SelectedPlans2ESRIShapeTest.java @@ -34,7 +34,7 @@ import org.matsim.core.population.io.PopulationReader; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.core.utils.gis.GeoFileReader; import org.matsim.testcases.MatsimTestUtils; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -66,7 +66,7 @@ void testSelectedPlansActsShape() throws IOException { sp.setWriteLegs(false); sp.write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outShp); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outShp); Assertions.assertEquals(2235, writtenFeatures.size()); } @@ -91,7 +91,7 @@ void testSelectedPlansLegsShape() throws IOException { sp.setWriteLegs(true); sp.write(); - Collection writtenFeatures = ShapeFileReader.getAllFeatures(outShp); + Collection writtenFeatures = GeoFileReader.getAllFeatures(outShp); Assertions.assertEquals(1431, writtenFeatures.size()); } diff --git a/matsim/src/test/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverterTest.java b/matsim/src/test/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverterTest.java new file mode 100644 index 00000000000..e749b41e22e --- /dev/null +++ b/matsim/src/test/java/org/matsim/utils/objectattributes/attributeconverters/StringDoubleMapConverterTest.java @@ -0,0 +1,16 @@ +package org.matsim.utils.objectattributes.attributeconverters; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class StringDoubleMapConverterTest { + + @Test + void test() { + var expectedString = "{\"a\":0.1,\"b\":0.2}"; + var converter = new StringDoubleMapConverter(); + var serializedString = converter.convertToString(converter.convert(expectedString)); + assertEquals(expectedString, serializedString); + } +} \ No newline at end of file diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testGpkg/test+test.gpkg b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testGpkg/test+test.gpkg new file mode 100644 index 00000000000..3b7e9a7b3fa Binary files /dev/null and b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testGpkg/test+test.gpkg differ diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.dbf b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.dbf similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.dbf rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.dbf diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.prj b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.prj similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.prj rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.prj diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.qpj b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.qpj similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.qpj rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.qpj diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.shp b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.shp similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.shp rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.shp diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.shx b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.shx similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileReaderTest/testPlusInFilename/test+test.shx rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileReaderTest/testShp/test+test.shx diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testGeopackageFileWriter/test.gpkg b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testGeopackageFileWriter/test.gpkg new file mode 100644 index 00000000000..f3221c7739c Binary files /dev/null and b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testGeopackageFileWriter/test.gpkg differ diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.dbf b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.dbf similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.dbf rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.dbf diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.prj b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.prj similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.prj rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.prj diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.qix b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.qix similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.qix rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.qix diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.qpj b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.qpj similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.qpj rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.qpj diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.shp b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.shp similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.shp rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.shp diff --git a/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.shx b/matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.shx similarity index 100% rename from matsim/src/test/resources/test/input/org/matsim/core/utils/gis/ShapeFileWriterTest/testShapeFileWriter/test.shx rename to matsim/src/test/resources/test/input/org/matsim/core/utils/gis/GeoFileWriterTest/testShapeFileWriter/test.shx diff --git a/pom.xml b/pom.xml index b585deb5450..8ab3815ad40 100644 --- a/pom.xml +++ b/pom.xml @@ -30,14 +30,14 @@ 17 - 2.22.1 - 29.2 + 2.23.1 + 29.5 0.49.2 1.19.0 7.0.0 - 2.16.1 + 2.16.2 2.5.0 - 5.10.1 + 5.10.2 @@ -118,7 +118,7 @@ org.apache.commons commons-compress - 1.25.0 + 1.26.1 commons-logging @@ -128,13 +128,18 @@ commons-codec commons-codec - 1.16.0 + 1.16.1 org.apache.commons commons-text 1.11.0 + + commons-io + commons-io + 2.15.1 + com.opencsv opencsv @@ -187,7 +192,7 @@ org.glassfish.jaxb jaxb-bom - 4.0.4 + 4.0.5 pom import @@ -195,7 +200,7 @@ com.google.errorprone error_prone_annotations - 2.24.1 + 2.26.1 @@ -207,7 +212,7 @@ com.fasterxml.woodstox woodstox-core - 6.5.1 + 6.6.1 @@ -232,7 +237,7 @@ org.slf4j slf4j-api - 2.0.11 + 2.0.12 @@ -298,26 +303,26 @@ it.unimi.dsi fastutil - 8.5.12 + 8.5.13 org.assertj assertj-core - 3.25.1 + 3.25.3 test org.mockito mockito-core - 5.9.0 + 5.11.0 test org.mockito mockito-junit-jupiter - 5.9.0 + 5.11.0 test @@ -342,7 +347,7 @@ net.bytebuddy byte-buddy - 1.14.11 + 1.14.12 test