From f2687ffaad964465a70b80ca9a4f7686a1d736bc Mon Sep 17 00:00:00 2001 From: Markus Straub Date: Thu, 28 Nov 2024 13:14:35 +0100 Subject: [PATCH] fix #6 --- .gitignore | 2 +- README.md | 11 +- data/floridsdorf/config_drs.xml | 2 +- .../at/ac/ait/matsim/drs/engine/DrsData.java | 29 +++- .../matsim/drs/optimizer/DrsOptimizer.java | 4 +- .../ait/matsim/drs/optimizer/DrsRequest.java | 9 + .../ait/matsim/drs/optimizer/MatchMaker.java | 2 +- .../optimizer/PotentialRequestsFinder.java | 10 +- .../drs/optimizer/RequestZoneRegistry.java | 59 +++++-- .../ac/ait/matsim/drs/run/DrsConfigGroup.java | 40 +++-- .../ac/ait/matsim/drs/run/H3Experiments.java | 7 +- .../drs/run/RunPerfectMatchExample.java | 2 +- .../drs/run/RunPredefinedDrsLegsExample.java | 2 +- .../ac/ait/matsim/drs/engine/DrsDataTest.java | 16 ++ .../optimizer/RequestZoneRegistryTest.java | 156 +++++++++++------- 15 files changed, 240 insertions(+), 111 deletions(-) create mode 100644 src/test/java/at/ac/ait/matsim/drs/engine/DrsDataTest.java diff --git a/.gitignore b/.gitignore index 62212db..786e362 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .vscode .idea -/TODO +/TODO.md /data/floridsdorf/network.geojsonl /data/vienna diff --git a/README.md b/README.md index 1e782ae..2e9674d 100644 --- a/README.md +++ b/README.md @@ -136,17 +136,18 @@ List of all parameters: **Matching Algorithm** -- `cellSize`: The side length of square zones in meters used in zone registers of riders requests. - The default value is good for urban areas. For large areas with sparsely distributed population and low drs share, - you may consider using a bigger cell size. On the other hand, if neighbourhoodSize is very low, a smaller cell size may work better. +- `maxMatchingDistanceMeters`: Maximum euclidean distance between requests to be considered by the matching algorithm, + i.e. if both the distance between two requests' origin and destination location are closer than the given value they are potential matches. + The default value is good for urban areas. + For large areas with sparsely distributed population and low drs share, you may consider using a larger value. - `maxPossibleCandidates`: Limits the number of possible riders requests considered for a driver during the matching process. Used to speed up computations, values 20 to 40 make a good trade-off between computational speed and quality of results. To turn off this feature specify a sufficiently big number (not recommended). - `minDriverLegMeters`: minimum length of legs (routed with the drsDriver mode) to be considered for the drs driver mode. 0 means no minimum. - `minRiderLegMeters` minimum length of legs (routed with the drsDriver mode) to be considered for the drs ride mode. 0 means no minimum. - `timeSegmentLengthSeconds`: The duration of the time segments used in time segment registers of riders requests. - To avoid scenarios where a driver and a rider departure time are close but cross a segment boundary - candidate requests are token not only from the current segment but also from the one before and after. + To avoid scenarios where a driver and a rider departure time are close, but cross a segment boundary, + candidate requests are taken not only from the current segment but also from the one before and after. **Simulation / Plan Adjustment** diff --git a/data/floridsdorf/config_drs.xml b/data/floridsdorf/config_drs.xml index 05a85bf..e05f47b 100644 --- a/data/floridsdorf/config_drs.xml +++ b/data/floridsdorf/config_drs.xml @@ -136,7 +136,7 @@ - + diff --git a/src/main/java/at/ac/ait/matsim/drs/engine/DrsData.java b/src/main/java/at/ac/ait/matsim/drs/engine/DrsData.java index 7c98570..4fc182e 100644 --- a/src/main/java/at/ac/ait/matsim/drs/engine/DrsData.java +++ b/src/main/java/at/ac/ait/matsim/drs/engine/DrsData.java @@ -4,12 +4,15 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Network; -import org.matsim.contrib.common.zones.ZoneSystem; -import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystem; +import org.matsim.contrib.common.zones.systems.grid.h3.H3Utils; +import org.matsim.contrib.common.zones.systems.grid.h3.H3ZoneSystem; import org.matsim.pt2matsim.tools.NetworkTools; +import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; +import com.uber.h3core.H3Core; +import com.uber.h3core.LengthUnit; import at.ac.ait.matsim.drs.run.Drs; import at.ac.ait.matsim.drs.run.DrsConfigGroup; @@ -20,9 +23,10 @@ public class DrsData { private static final Logger LOGGER = LogManager.getLogger(); + private static final int H3_MAX_LEVEL = 15; private Network drsNetwork; - private ZoneSystem zoneSystem; + private H3ZoneSystem zoneSystem; @Inject public DrsData(Scenario scenario, DrsConfigGroup drsConfig) { @@ -31,8 +35,21 @@ public DrsData(Scenario scenario, DrsConfigGroup drsConfig) { LOGGER.info("Filtered {} drs driver links from network with {} links", drsNetwork.getLinks().size(), scenario.getNetwork().getLinks().size()); - this.zoneSystem = new SquareGridZoneSystem(scenario.getNetwork(), drsConfig.getCellSize()); - LOGGER.info("Initialized zone system."); + int resolution = findH3ResolutionForDistance(drsConfig.getMaxMatchingDistanceMeters()); + this.zoneSystem = new H3ZoneSystem(scenario.getConfig().global().getCoordinateSystem(), resolution, + scenario.getNetwork(), Predicates.alwaysTrue()); + LOGGER.info("Initialized H3 zone system with resolution {}.", resolution); + } + + public static int findH3ResolutionForDistance(int meters) { + H3Core h3core = H3Utils.getInstance(); + for (int res = H3_MAX_LEVEL; res >= 0; res--) { + double edgeLength = h3core.getHexagonEdgeLengthAvg(res, LengthUnit.m); + if (edgeLength > meters) { + return res; + } + } + return 0; } public static Logger getLogger() { @@ -43,7 +60,7 @@ public Network getDrsNetwork() { return drsNetwork; } - public ZoneSystem getZoneSystem() { + public H3ZoneSystem getH3ZoneSystem() { return zoneSystem; } } diff --git a/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsOptimizer.java b/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsOptimizer.java index 20bbead..09a83dc 100644 --- a/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsOptimizer.java +++ b/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsOptimizer.java @@ -27,9 +27,9 @@ public DrsOptimizer(DrsData drsData, public MatchingResult optimize() { LOGGER.info("Matching process started!"); - RequestZoneRegistry originZoneRegistry = RequestZoneRegistry.forOrigins(drsData.getZoneSystem()); + RequestZoneRegistry originZoneRegistry = RequestZoneRegistry.forOrigins(drsData.getH3ZoneSystem()); RequestZoneRegistry destinationZoneRegistry = RequestZoneRegistry - .forDestinations(drsData.getZoneSystem()); + .forDestinations(drsData.getH3ZoneSystem()); RequestTimeSegmentRegistry timeSegmentRegistry = new RequestTimeSegmentRegistry(drsConfig); RequestsCollector requestsCollector = new RequestsCollector(drsConfig, population, drsData.getDrsNetwork(), driverRouter); diff --git a/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsRequest.java b/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsRequest.java index 127b7e1..9d17cf9 100644 --- a/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsRequest.java +++ b/src/main/java/at/ac/ait/matsim/drs/optimizer/DrsRequest.java @@ -10,6 +10,7 @@ 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.network.Node; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; @@ -171,6 +172,14 @@ public Link getToLink() { return toLink; } + public Node getFromNode() { + return fromLink.getFromNode(); + } + + public Node getToNode() { + return toLink.getToNode(); + } + @Override public double getSubmissionTime() { return 0; diff --git a/src/main/java/at/ac/ait/matsim/drs/optimizer/MatchMaker.java b/src/main/java/at/ac/ait/matsim/drs/optimizer/MatchMaker.java index 5fad53f..fdfbdf9 100644 --- a/src/main/java/at/ac/ait/matsim/drs/optimizer/MatchMaker.java +++ b/src/main/java/at/ac/ait/matsim/drs/optimizer/MatchMaker.java @@ -69,7 +69,7 @@ public void match() { for (Iterator iterator = driverRequests.iterator(); iterator.hasNext();) { DrsRequest driverRequest = iterator.next(); List potentialRequests = potentialRequestsFinder.findRegistryIntersections( - driverRequest.getFromLink().getFromNode(), driverRequest.getToLink().getFromNode(), + driverRequest.getFromNode(), driverRequest.getToNode(), driverRequest.getDepartureTime()); List filteredMatches = requestsFilter.filterRequests(driverRequest, potentialRequests); DrsMatch bestMatch = bestRequestFinder.findBestRequest(filteredMatches); diff --git a/src/main/java/at/ac/ait/matsim/drs/optimizer/PotentialRequestsFinder.java b/src/main/java/at/ac/ait/matsim/drs/optimizer/PotentialRequestsFinder.java index 58b8904..8a3dafd 100644 --- a/src/main/java/at/ac/ait/matsim/drs/optimizer/PotentialRequestsFinder.java +++ b/src/main/java/at/ac/ait/matsim/drs/optimizer/PotentialRequestsFinder.java @@ -19,8 +19,11 @@ public PotentialRequestsFinder(DrsConfigGroup drsConfig, RequestsRegister reques } public List findRegistryIntersections(Node origin, Node destination, double departureTime) { - return getIntersection(drsConfig, requestsRegister.getOriginZoneRegistry().findNearestRequests(origin), - requestsRegister.getDestinationZoneRegistry().findNearestRequests(destination), + return getIntersection(drsConfig, + requestsRegister.getOriginZoneRegistry().findRequestsWithinDistance(origin, + drsConfig.getMaxMatchingDistanceMeters()), + requestsRegister.getDestinationZoneRegistry().findRequestsWithinDistance(destination, + drsConfig.getMaxMatchingDistanceMeters()), requestsRegister.getTimeSegmentRegistry().findNearestRequests(departureTime)); } @@ -30,6 +33,7 @@ static List getIntersection(DrsConfigGroup drsConfig, Stream zoneRegistryIntersection = originNearRequests .filter(destinationNearRequests.collect(Collectors.toList())::contains); return zoneRegistryIntersection.filter(temporalNearRequests.collect(Collectors.toList())::contains) - .limit(drsConfig.getMaxPossibleCandidates()).collect(Collectors.toCollection(ArrayList::new)); + .limit(drsConfig.getMaxPossibleCandidates()) + .collect(Collectors.toCollection(ArrayList::new)); } } diff --git a/src/main/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistry.java b/src/main/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistry.java index f5b810d..56472a6 100644 --- a/src/main/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistry.java +++ b/src/main/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistry.java @@ -1,40 +1,48 @@ package at.ac.ait.matsim.drs.optimizer; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Node; import org.matsim.contrib.common.zones.Zone; -import org.matsim.contrib.common.zones.ZoneSystem; +import org.matsim.contrib.common.zones.systems.grid.h3.H3Utils; +import org.matsim.contrib.common.zones.systems.grid.h3.H3ZoneSystem; import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.core.network.NetworkUtils; + +import com.uber.h3core.H3Core; /** - * Heavily inspired by - * org.matsim.contrib.taxi.optimizer.rules.UnplannedRequestZonalRegistry + * Geographically matches each request to a H3 cell (zone) */ public class RequestZoneRegistry { - private final ZoneSystem zoneSystem; + private final H3ZoneSystem zoneSystem; private final boolean isOriginZoneRegistry; private final Map, Map, DrsRequest>> requestsInZones; + private final H3Core h3; - private RequestZoneRegistry(ZoneSystem zoneSystem, boolean isOriginZoneRegistry) { + private RequestZoneRegistry(H3ZoneSystem zoneSystem, boolean isOriginZoneRegistry) { this.zoneSystem = zoneSystem; this.isOriginZoneRegistry = isOriginZoneRegistry; requestsInZones = new HashMap<>(zoneSystem.getZones().size()); for (Id id : zoneSystem.getZones().keySet()) { requestsInZones.put(id, new LinkedHashMap<>()); } + h3 = H3Utils.getInstance(); } - public static RequestZoneRegistry forOrigins(ZoneSystem zoneSystem) { + public static RequestZoneRegistry forOrigins(H3ZoneSystem zoneSystem) { return new RequestZoneRegistry(zoneSystem, true); } - public static RequestZoneRegistry forDestinations(ZoneSystem zoneSystem) { + public static RequestZoneRegistry forDestinations(H3ZoneSystem zoneSystem) { return new RequestZoneRegistry(zoneSystem, false); } @@ -52,20 +60,45 @@ public void removeRequest(DrsRequest request) { } } - public Stream findNearestRequests(Node node) { - return requestsInZones.get(getZoneId(node)).values().stream(); + /** + * Uses neighbouring H3 cells to guarantee finding all requests within the given + * max distance (this guarantee only holds if the given distance does not exceed + * the value used for calculating the H3 resolution) + */ + public Stream findRequestsWithinDistance(Node node, int maxEuclideanDistance) { + Id centerZoneId = getZoneId(node); + Long cellId = Long.parseLong(centerZoneId.toString()); + + List disk = h3.gridDisk(cellId, 1); + List> diskZones = disk.stream().map(id -> Id.create(id, Zone.class)).collect(Collectors.toList()); + + List potentialRequests = new ArrayList<>(); + for (var zoneId : diskZones) { + potentialRequests.addAll(requestsInZones.getOrDefault(zoneId, Map.of()).values()); + } + + return potentialRequests.stream() + .filter(r -> NetworkUtils.getEuclideanDistance(node.getCoord(), + getRequestNode(r).getCoord()) < maxEuclideanDistance); } public Map, Map, DrsRequest>> getRequestsInZones() { return requestsInZones; } - public Id getZoneId(DrsRequest request) { - Node node = isOriginZoneRegistry ? request.getFromLink().getFromNode() : request.getToLink().getToNode(); - return getZoneId(node); + public long requestCount() { + return requestsInZones.values().stream().flatMap(m -> m.values().stream()).count(); + } + + Node getRequestNode(DrsRequest request) { + return isOriginZoneRegistry ? request.getFromNode() : request.getToNode(); + } + + Id getZoneId(DrsRequest request) { + return getZoneId(getRequestNode(request)); } - public Id getZoneId(Node node) { + Id getZoneId(Node node) { Optional zone = zoneSystem.getZoneForNodeId(node.getId()); if (!zone.isPresent()) { throw new IllegalArgumentException("no zone found for node " + node.getId()); diff --git a/src/main/java/at/ac/ait/matsim/drs/run/DrsConfigGroup.java b/src/main/java/at/ac/ait/matsim/drs/run/DrsConfigGroup.java index 50803e8..a036d6d 100644 --- a/src/main/java/at/ac/ait/matsim/drs/run/DrsConfigGroup.java +++ b/src/main/java/at/ac/ait/matsim/drs/run/DrsConfigGroup.java @@ -12,8 +12,8 @@ public class DrsConfigGroup extends ReflectiveConfigGroupWithConfigurableParamet public static final String GROUP_NAME = "drs"; - public static final String CELL_SIZE = "cellSize"; - private int cellSize = 4000; + public static final String MAX_MATCHING_DISTANCE_METERS = "maxMatchingDistanceMeters"; + private int maxMatchingDistanceMeters = 2000; public static final String TIME_SEGMENT_LENGTH_SECONDS = "timeSegmentLengthSeconds"; private int timeSegmentLengthSeconds = 2 * 60 * 60; @@ -54,20 +54,32 @@ public DrsConfigGroup() { @Override public Map getComments() { Map map = super.getComments(); - map.put(CELL_SIZE, - "The side length of square zones in meters used in zone registers of riders requests. The default value is good for urban areas. For large areas with sparsely distributed population and low drs share, you may consider using a bigger cell size. On the other hand, if neighbourhoodSize is very low, a smaller cell size may work better. (inspired by taxi contrib)"); + map.put(MAX_MATCHING_DISTANCE_METERS, + "Maximum euclidean distance between requests to be considered by the matching algorithm, " + + "i.e. if both the distance between two requests' origin and destination location are closer than the given value they are potential matches. " + + "The default value is good for urban areas. " + + "For large areas with sparsely distributed population and low drs share, you may consider using a larger value."); map.put(MAX_POSSIBLE_CANDIDATES, - "Limits the number of possible riders requests considered for a driver during the matching process. Used to speed up computations, values 20 to 40 make a good trade-off between computational speed and quality of results. To turn off this feature specify a sufficiently big number (not recommended). (inspired by taxi contrib)"); + "Limits the number of possible riders requests considered for a driver during the matching process. " + + "Used to speed up computations, values 20 to 40 make a good trade-off between computational speed and quality of results. " + + "To turn off this feature specify a sufficiently big number (not recommended). (inspired by taxi contrib)"); map.put(RIDER_DEPARTURE_TIME_ADJUSTMENT_SECONDS, - "The amount of time the riders are willing to adjust their departure times. During the matching process, the arrival of driver to pick-up point is checked whether it is within the rider departure time +- the riderDepartureTimeAdjustment."); + "The amount of time the riders are willing to adjust their departure times. " + + "During the matching process, the arrival of driver to pick-up point is checked whether it is within " + + "the rider departure time +- the riderDepartureTimeAdjustment."); map.put(TIME_SEGMENT_LENGTH_SECONDS, - "The duration of the time segments used in time segment registers of riders requests. To avoid scenarios where a driver and a rider departure time are close but cross a segment boundary candidate requests are token not only from the current segment but also from the one before and after."); + "The duration of the time segments used in time segment registers of riders requests. Must be larger than " + + RIDER_DEPARTURE_TIME_ADJUSTMENT_SECONDS + + ". To avoid scenarios where a driver and a rider departure time are close, but cross a segment boundary, " + + "candidate requests are taken not only from the current segment but also from the one before and after."); map.put(PICKUP_WAITING_SECONDS, "The amount of time the driver is expected to wait until the rider enters the vehicle."); map.put(DRIVER_PROFIT_PER_KM, "The amount of money per kilometre the driver gains for a rider (typically positive)"); map.put(CAR_AND_DRS_DAILY_MONETARY_CONSTANT, - "Daily price for car usage including when using the private car as drsDriver. If specified here do not additionaly specify it in planCalcScore.scoringParameters.modeParams.dailyMonetaryConstant - otherwise it will be counted twice (typically negative)"); + "Daily price for car usage including when using the private car as drsDriver. " + + "If specified here do not additionaly specify it in planCalcScore.scoringParameters.modeParams.dailyMonetaryConstant - " + + "otherwise it will be counted twice (typically negative)"); map.put(SUBTOUR_MODE_CHOICE_MODES, "Defines all modes available for the '" + SubtourModeChoiceForDrs.STRATEGY_NAME + "' strategy, including chain-based modes, separated by commas"); @@ -81,14 +93,14 @@ public Map getComments() { return map; } - @StringGetter(CELL_SIZE) - public int getCellSize() { - return cellSize; + @StringGetter(MAX_MATCHING_DISTANCE_METERS) + public int getMaxMatchingDistanceMeters() { + return maxMatchingDistanceMeters; } - @StringSetter(CELL_SIZE) - public void setCellSize(int cellSize) { - this.cellSize = cellSize; + @StringSetter(MAX_MATCHING_DISTANCE_METERS) + public void setMaxMatchingDistanceMeters(int maxMatchingDistanceMeters) { + this.maxMatchingDistanceMeters = maxMatchingDistanceMeters; } @StringGetter(MAX_POSSIBLE_CANDIDATES) diff --git a/src/main/java/at/ac/ait/matsim/drs/run/H3Experiments.java b/src/main/java/at/ac/ait/matsim/drs/run/H3Experiments.java index d00dc4e..82fd04f 100644 --- a/src/main/java/at/ac/ait/matsim/drs/run/H3Experiments.java +++ b/src/main/java/at/ac/ait/matsim/drs/run/H3Experiments.java @@ -24,7 +24,7 @@ public class H3Experiments { public static void main(String[] args) { playground(); - // writeCellsForTestNetwork(); + writeCellsForTestNetwork(); } public static void playground() { @@ -59,10 +59,11 @@ public static void writeCellsForTestNetwork() { Collection features = new ArrayList<>(); for (Entry, Zone> entry : h3.getZones().entrySet()) { - String id = entry.getKey().toString(); + long id = Long.parseLong(entry.getKey().toString()); + String address = Long.toHexString(id); features.add( factory.createPolygon(entry.getValue().getPreparedGeometry().getGeometry().getCoordinates(), - new Object[] { id }, id)); + new Object[] { address }, address)); } GeoFileWriter.writeGeometries(features, "/tmp/floridsdorf-h3grid.gpkg"); } diff --git a/src/main/java/at/ac/ait/matsim/drs/run/RunPerfectMatchExample.java b/src/main/java/at/ac/ait/matsim/drs/run/RunPerfectMatchExample.java index 6d8d90c..4177d2e 100644 --- a/src/main/java/at/ac/ait/matsim/drs/run/RunPerfectMatchExample.java +++ b/src/main/java/at/ac/ait/matsim/drs/run/RunPerfectMatchExample.java @@ -21,7 +21,7 @@ public void adjustConfig(Config config) { config.global().setNumberOfThreads(10); DrsConfigGroup drs = (DrsConfigGroup) config.getModules().get("drs"); - drs.setCellSize(500); + drs.setMaxMatchingDistanceMeters(500); } } \ No newline at end of file diff --git a/src/main/java/at/ac/ait/matsim/drs/run/RunPredefinedDrsLegsExample.java b/src/main/java/at/ac/ait/matsim/drs/run/RunPredefinedDrsLegsExample.java index c0b51ab..7d35e5d 100644 --- a/src/main/java/at/ac/ait/matsim/drs/run/RunPredefinedDrsLegsExample.java +++ b/src/main/java/at/ac/ait/matsim/drs/run/RunPredefinedDrsLegsExample.java @@ -18,7 +18,7 @@ public void adjustConfig(Config config) { config.plans().setInputFile("population_drs.xml"); DrsConfigGroup drs = (DrsConfigGroup) config.getModules().get("drs"); - drs.setCellSize(500); + drs.setMaxMatchingDistanceMeters(500); } } diff --git a/src/test/java/at/ac/ait/matsim/drs/engine/DrsDataTest.java b/src/test/java/at/ac/ait/matsim/drs/engine/DrsDataTest.java new file mode 100644 index 0000000..b2e3630 --- /dev/null +++ b/src/test/java/at/ac/ait/matsim/drs/engine/DrsDataTest.java @@ -0,0 +1,16 @@ +package at.ac.ait.matsim.drs.engine; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class DrsDataTest { + + @Test + public void testFindH3Resolution() { + assertEquals(9, DrsData.findH3ResolutionForDistance(150)); + assertEquals(8, DrsData.findH3ResolutionForDistance(400)); + assertEquals(7, DrsData.findH3ResolutionForDistance(500)); + assertEquals(7, DrsData.findH3ResolutionForDistance(1200)); + } +} diff --git a/src/test/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistryTest.java b/src/test/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistryTest.java index 12a9ef9..3bddb23 100644 --- a/src/test/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistryTest.java +++ b/src/test/java/at/ac/ait/matsim/drs/optimizer/RequestZoneRegistryTest.java @@ -1,9 +1,12 @@ package at.ac.ait.matsim.drs.optimizer; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -11,10 +14,11 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.common.zones.Zone; -import org.matsim.contrib.common.zones.ZoneSystem; -import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystem; +import org.matsim.contrib.common.zones.systems.grid.h3.H3ZoneSystem; import org.matsim.core.network.NetworkUtils; +import com.google.common.base.Predicates; + import at.ac.ait.matsim.drs.DrsTestUtil; import at.ac.ait.matsim.drs.optimizer.DrsRequest.DrsRiderRequest; import at.ac.ait.matsim.drs.run.Drs; @@ -22,104 +26,136 @@ class RequestZoneRegistryTest { - static Network network; - static ZoneSystem zoneSystem; - static DrsRequest request1, request2, request3; - RequestZoneRegistry zoneRegistry; + private static Network network; + private static H3ZoneSystem zoneSystem; + private static DrsRequest req1084, req1085, req1086, req1087, req1159, req1160, req1161; + private static List allReqs; + + private RequestZoneRegistry zoneRegistry; @BeforeAll public static void setup() { network = NetworkUtils.readNetwork("data/floridsdorf/network.xml"); DrsUtil.addNewAllowedModeToCarLinks(network, Drs.DRIVER_MODE); + // consecutive links on Kugelfanggasse (~900m) + req1084 = req(1084); + req1085 = req(1085); + req1086 = req(1086); + req1087 = req(1087); + req1159 = req(1159); + req1160 = req(1160); + req1161 = req(1161); + allReqs = List.of(req1084, req1085, req1086, req1087, req1159, req1160, req1161); } @BeforeEach public void beforeEach() { - zoneSystem = new SquareGridZoneSystem(network, 800); + zoneSystem = new H3ZoneSystem("EPSG:31256", 9, network, Predicates.alwaysTrue()); zoneRegistry = RequestZoneRegistry.forOrigins(zoneSystem); - request1 = req(1, 1540); - request2 = req(2, 1037); - request3 = req(3, 1674); } - // @Test - // public void testAllRequestsWithinDistance() { - // List requests = List.of(req(1084), req(1085), req(1086), - // req(1159), req(1160), req(1161)); - // } - @Test - void testGetZoneIdForNeighboringRequests() { - assertEquals(zoneRegistry.getZoneId(request1), zoneRegistry.getZoneId(request2)); - } + public void testGetZoneId() { + assertEquals("891e15b600fffff", hex(zoneRegistry.getZoneId(req1084))); - @Test - void testGetZoneIdForNotNeighboringRequests() { - assertNotEquals(zoneRegistry.getZoneId(request1), zoneRegistry.getZoneId(request3)); + assertEquals("891e15b6077ffff", hex(zoneRegistry.getZoneId(req1085))); + assertEquals("891e15b6077ffff", hex(zoneRegistry.getZoneId(req1086))); + assertEquals("891e15b6077ffff", hex(zoneRegistry.getZoneId(req1087))); + + assertEquals("891e15b602bffff", hex(zoneRegistry.getZoneId(req1159))); + assertEquals("891e15b602bffff", hex(zoneRegistry.getZoneId(req1160))); + + assertEquals("891e15b6393ffff", hex(zoneRegistry.getZoneId(req1161))); } @Test - void testAddRequestsToZones() { - zoneRegistry.addRequest(request1); - assertEquals(1, zoneRegistry.getRequestsInZones() - .get(zoneRegistry.getZoneId(request1)).size()); - zoneRegistry.addRequest(request2); - assertEquals(2, zoneRegistry.getRequestsInZones() - .get(zoneRegistry.getZoneId(request1)).size()); - zoneRegistry.addRequest(request3); - assertEquals(2, zoneRegistry.getRequestsInZones() - .get(zoneRegistry.getZoneId(request1)).size()); + public void testAddRequestsToZones() { + assertEquals(0, zoneRegistry.requestCount()); + + zoneRegistry.addRequest(req1084); + assertEquals(1, zoneRegistry.requestCount()); + assertEquals(1, zoneRegistry.getRequestsInZones().get(zoneRegistry.getZoneId(req1084)).size()); + assertEquals(0, zoneRegistry.getRequestsInZones().get(zoneRegistry.getZoneId(req1085)).size()); + + zoneRegistry.addRequest(req1085); + zoneRegistry.addRequest(req1086); + zoneRegistry.addRequest(req1087); + assertEquals(4, zoneRegistry.requestCount()); + assertEquals(1, zoneRegistry.getRequestsInZones().get(zoneRegistry.getZoneId(req1084)).size()); + assertEquals(3, zoneRegistry.getRequestsInZones().get(zoneRegistry.getZoneId(req1085)).size()); } @Test - void testAddSameRequestTwice() { - zoneRegistry.addRequest(request1); + public void testAddSameRequestTwice() { + zoneRegistry.addRequest(req1084); assertThrows(RuntimeException.class, () -> { - zoneRegistry.addRequest(request1); + zoneRegistry.addRequest(req1084); }); } @Test - void testRemoveRequestInRegistry() { - zoneRegistry.addRequest(request1); - zoneRegistry.addRequest(request2); - Id zoneId = zoneRegistry.getZoneId(request1); + public void testRemoveRequestInRegistry() { + zoneRegistry.addRequest(req1085); + zoneRegistry.addRequest(req1086); + Id zoneId = zoneRegistry.getZoneId(req1085); assertEquals(2, zoneRegistry.getRequestsInZones().get(zoneId).size()); - zoneRegistry.removeRequest(request1); + zoneRegistry.removeRequest(req1085); assertEquals(1, zoneRegistry.getRequestsInZones().get(zoneId).size()); - zoneRegistry.removeRequest(request2); + zoneRegistry.removeRequest(req1086); assertEquals(0, zoneRegistry.getRequestsInZones().get(zoneId).size()); } @Test - void testRemoveRequestNotInRegistry() { + public void testRemoveRequestNotInRegistry() { assertThrows(RuntimeException.class, () -> { - zoneRegistry.removeRequest(request1); + zoneRegistry.removeRequest(req1084); }); } @Test - void testFindNearestRequests() { - zoneRegistry.addRequest(request1); - zoneRegistry.addRequest(request2); - zoneRegistry.addRequest(request3); - assertEquals(0, - zoneRegistry.findNearestRequests(link(857).getFromNode()).count()); - assertEquals(1, - zoneRegistry.findNearestRequests(link(1674).getFromNode()).count()); - assertEquals(2, - zoneRegistry.findNearestRequests(link(1541).getFromNode()).count()); + public void findRequestsWithinDistance_1_withinCell() { + allReqs.forEach(zoneRegistry::addRequest); + + Set reqs = find(req1084, 1); + assertEquals(Set.of(req1084), reqs); + } + + @Test + public void findRequestsWithinDistance_125_withinCell() { + allReqs.forEach(zoneRegistry::addRequest); + + Set reqs = find(req1085, 125); + assertEquals(Set.of(req1085, req1086), reqs); } - public DrsRiderRequest req(int linkId) { - return DrsTestUtil.mockRiderRequest(linkId, 8 * 60 * 60, link(linkId), null); + @Test + public void findRequestsWithinDistance_170_acrossCells() { + allReqs.forEach(zoneRegistry::addRequest); + + Set reqs = find(req1085, 170); + assertEquals(Set.of(req1084, req1085, req1086), reqs); } - public DrsRiderRequest req(int id, int linkId) { - return DrsTestUtil.mockRiderRequest(id, 8 * 60 * 60, link(linkId), null); + @Test + public void findRequestsWithinDistance_200_acrossCells() { + allReqs.forEach(zoneRegistry::addRequest); + + Set reqs = find(req1084, 200); + assertEquals(Set.of(req1084, req1085), reqs); } - public Link link(int linkId) { - return network.getLinks().get(Id.createLinkId(linkId)); + private Set find(DrsRequest reference, int distance) { + return zoneRegistry.findRequestsWithinDistance(reference.getFromLink().getFromNode(), distance) + .collect(Collectors.toSet()); } + + public static DrsRiderRequest req(int linkId) { + Link link = network.getLinks().get(Id.createLinkId(linkId)); + return DrsTestUtil.mockRiderRequest(linkId, 0, link, null); + } + + public static String hex(Id id) { + return Long.toHexString(Long.parseLong(id.toString())); + } + } \ No newline at end of file