20 package org.matsim.pt.utils;
22 import java.io.IOException;
25 import javax.xml.parsers.ParserConfigurationException;
27 import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
28 import it.unimi.dsi.fastutil.doubles.DoubleList;
40 import org.xml.sax.SAXException;
62 if (network == null || network.
getLinks().size() == 0) {
63 result.
addWarning(
"Cannot validate network routes: No network given!");
70 if (netRoute == null) {
78 " contains a link that is not part of the network: " + linkId,
ValidationResult.
Type.
OTHER, Collections.singleton(route.getId())));
81 " has inconsistent network route, e.g. between link " + prevLink.
getId() +
" and " + linkId,
ValidationResult.
Type.
OTHER, Collections.singleton(route.getId())));
100 if (network == null || network.
getLinks().size() == 0) {
101 result.
addWarning(
"Cannot validate stops on network route: No network given!");
108 if (netRoute == null) {
111 List<Id<Link>> linkIds =
new ArrayList<>();
115 Iterator<Id<Link>> linkIdIterator = linkIds.iterator();
116 Id<Link> nextLinkId = linkIdIterator.next();
117 boolean error =
false;
119 Id<Link> linkRefId = stop.getStopFacility().getLinkId();
121 while (!linkRefId.
equals(nextLinkId)) {
122 if (linkIdIterator.hasNext()) {
123 nextLinkId = linkIdIterator.next();
146 Id<Link> linkId = stop.getStopFacility().getLinkId();
147 if (linkId == null) {
161 if (stop.getStopFacility() == null) {
163 }
else if (schedule.
getFacilities().get(stop.getStopFacility().getId()) == null) {
177 ArrayList<TransitRouteStop> stops =
new ArrayList<TransitRouteStop>(route.getStops());
178 int stopCount = stops.size();
183 result.
addError(
"Transit line " + line.getId() +
", route " + route.getId() +
": The first stop does not contain any departure offset.");
186 for (
int i = 1; i < stopCount - 1; i++) {
189 result.
addError(
"Transit line " + line.getId() +
", route " + route.getId() +
": Stop " + i +
" does not contain any departure offset.");
193 stop = stops.get(stopCount - 1);
195 result.
addError(
"Transit line " + line.getId() +
", route " + route.getId() +
": The last stop does not contain any arrival offset.");
198 result.
addWarning(
"Transit line " + line.getId() +
", route " + route.getId() +
": The route has not stops assigned, looks suspicious.");
212 Set<Id> missingFromStops =
new HashSet<>();
213 Set<Id> missingToStops =
new HashSet<>();
215 while (iter.hasNext()) {
219 double transferTime = iter.getSeconds();
221 if (fromStopId == null && toStopId == null) {
222 result.
addError(
"Minimal Transfer Times: both fromStop and toStop are null.");
223 }
else if (fromStopId == null) {
224 result.
addError(
"Minimal Transfer Times: fromStop = null, toStop " + toStopId +
".");
225 }
else if (toStopId == null) {
226 result.
addError(
"Minimal Transfer Times: fromStop " + fromStopId +
", toStop = null.");
228 if (transferTime <= 0) {
229 result.
addWarning(
"Minimal Transfer Times: fromStop " + fromStopId +
" toStop " + toStopId +
" with transferTime = " + transferTime);
231 if (schedule.
getFacilities().get(fromStopId) == null && missingFromStops.add(fromStopId)) {
232 result.
addError(
"Minimal Transfer Times: fromStop " + fromStopId +
" does not exist in schedule.");
234 if (schedule.
getFacilities().get(toStopId) == null && missingToStops.add(toStopId)) {
235 result.
addError(
"Minimal Transfer Times: toStop " + toStopId +
" does not exist in schedule.");
246 if (route.getDepartures().isEmpty())
247 result.
addError(
"No departures defined for line %s, route %s".formatted(line.getId(), route.getId()));
269 List<TransitRouteStop> routeStops = route.getStops();
272 if (routeStops.size() <= 4)
275 double lastDepartureOffset = routeStops.getFirst().getDepartureOffset().or(routeStops.getFirst().getArrivalOffset()).seconds();
277 DoubleList speeds =
new DoubleArrayList();
278 DoubleList dists =
new DoubleArrayList();
280 for (
int i = 1; i < routeStops.size(); i++) {
287 double travelTime = departureOffset - lastDepartureOffset;
289 routeStops.get(i - 1).getStopFacility().getCoord());
299 if (travelTime == 0) {
300 speeds.add(Double.POSITIVE_INFINITY);
304 double speed = length / travelTime;
306 lastDepartureOffset = departureOffset;
310 if (speeds.size() == routeStops.size() - 1) {
314 for (
int i = 0; i < speeds.size() - 1; i++) {
316 double toStop = speeds.getDouble(i);
317 double fromStop = speeds.getDouble(i + 1);
319 double both = (toStop + fromStop) / 2;
321 double dist = (dists.getDouble(i) + dists.getDouble(i + 1)) / 2;
328 DoubleList copy =
new DoubleArrayList(speeds);
329 copy.removeDouble(i);
330 copy.removeDouble(i);
331 copy.removeIf(s -> s == -1 || s == Double.POSITIVE_INFINITY);
333 double mean = copy.doubleStream().average().orElse(-1);
341 if (((toStop > 3 * mean && both > 50) || toStop > 120) && (((fromStop > 3 * mean && both > 50) || fromStop > 120))) {
342 DoubleList suspiciousSpeeds = suspiciousStops.computeIfAbsent(stop.
getStopFacility(), (k) ->
new DoubleArrayList());
343 suspiciousSpeeds.add(toStop);
344 suspiciousSpeeds.add(fromStop);
349 for (
int i = 0; i < speeds.size(); i++) {
350 double speed = speeds.getDouble(i);
354 result.
addWarning(
"Suspicious high speed from stop %s (%s) to %s (%s) on line %s, route %s, index: %d: %.2f m/s, %.2fm" 355 .formatted(from.
getName(), from.
getId(), to.
getName(), to.
getId(), line.getId(), route.getId(), i, speed, dists.getDouble(i)));
364 double speed = e.getValue().doubleStream().average().orElse(-1);
376 }
catch (NullPointerException e) {
377 v.
addError(
"Exception during 'validateStopsOnNetworkRoute'. Most likely something is wrong in the file, but it cannot be specified in more detail." + Arrays.toString(e.getStackTrace()));
389 System.out.println(
"Schedule appears valid!");
391 System.out.println(
"Schedule is NOT valid!");
394 System.out.println(
"Validation errors:");
396 System.out.println(e);
400 System.out.println(
"Validation warnings:");
402 System.out.println(w);
413 public static void main(String[] args)
throws IOException, SAXException, ParserConfigurationException {
414 if (args.length > 2 || args.length < 1) {
415 System.err.println(
"Usage: TransitScheduleValidator transitSchedule.xml [network.xml]");
424 if (args.length > 1) {
475 private final List<ValidationIssue>
issues =
new ArrayList<>();
482 List<String> result =
new ArrayList<>();
485 result.add(issue.getMessage());
488 return Collections.unmodifiableList(result);
492 List<String> result =
new ArrayList<>();
495 result.add(issue.getMessage());
498 return Collections.unmodifiableList(result);
502 return Collections.unmodifiableList(this.issues);
511 this.isValid =
false;
515 this.issues.add(issue);
517 this.isValid =
false;
522 this.issues.addAll(otherResult.
getIssues());
523 this.isValid = this.isValid && otherResult.
isValid;
void readFile(final String filename)
void addWarning(final String warning)
List< ValidationIssue > getIssues()
Id< Link > getStartLinkId()
final TransitSchedule getTransitSchedule()
void setUseTransit(boolean val)
Map< Id< TransitStopFacility >, TransitStopFacility > getFacilities()
ValidationIssue(Severity severity, String message, Type errorCode, Collection< Id< T >> entities)
static double calcEuclideanDistance(Coord coord, Coord other)
static ValidationResult validateDepartures(TransitSchedule schedule)
final List< ValidationIssue > issues
TransitScheduleValidator()
List< String > getErrors()
HAS_MISSING_STOP_FACILITY
List< Id< Link > > getLinkIds()
List< String > getWarnings()
abstract OptionalTime getArrivalOffset()
void addError(final String error)
ROUTE_HAS_UNREACHABLE_STOP
static ValidationResult validateAllStopsExist(final TransitSchedule schedule)
static ValidationResult validateOffsets(final TransitSchedule schedule)
Id< TransitStopArea > getStopAreaId()
TransitConfigGroup transit()
static void printResult(final ValidationResult result)
static void main(String[] args)
void addIssue(final ValidationIssue issue)
static ValidationResult validateAll(final TransitSchedule schedule, final Network network)
MinimalTransferTimesIterator iterator()
static ValidationResult validateUsedStopsHaveLinkId(final TransitSchedule schedule)
static ValidationResult validateNetworkRoutes(final TransitSchedule schedule, final Network network)
Map< Id< Link >, ? extends Link > getLinks()
abstract TransitStopFacility getStopFacility()
final Network getNetwork()
void add(final ValidationResult otherResult)
Collection< Id< T > > getEntities()
final Collection< Id< T > > entities
boolean equals(Object obj)
Id< Link > getEndLinkId()
Map< Id< TransitLine >, TransitLine > getTransitLines()
abstract OptionalTime getDepartureOffset()
static ValidationResult validateTransfers(final TransitSchedule schedule)
static Scenario createScenario(final Config config)
MinimalTransferTimes getMinimalTransferTimes()
static ValidationResult validateStopCoordinates(final TransitSchedule schedule)
static Config createConfig(final String context)
static ValidationResult validateStopsOnNetworkRoute(final TransitSchedule schedule, final Network network)
OptionalTime or(OptionalTime optionalTime)