diff --git a/RULES.md b/RULES.md index 98a399b00c..b8d35a0230 100644 --- a/RULES.md +++ b/RULES.md @@ -71,13 +71,14 @@ Additional details regarding the notices' context is provided in [`NOTICES.md`]( |-----------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | [`BlockTripsWithOverlappingStopTimesNotice`](#BlockTripsWithOverlappingStopTimesNotice) | Block trips with overlapping stop times. | | [`CsvParsingFailedNotice`](#CsvParsingFailedNotice) | Parsing of a CSV file failed. | -| [`DecreasingOrEqualShapeDistanceNotice`](#DecreasingOrEqualShapeDistanceNotice) | Decreasing or equal `shape_dist_traveled` in `shapes.txt`. | +| [`DecreasingShapeDistanceNotice`](#DecreasingShapeDistanceNotice) | Decreasing `shape_dist_traveled` in `shapes.txt`. | | [`DecreasingOrEqualStopTimeDistanceNotice`](#DecreasingOrEqualStopTimeDistanceNotice) | Decreasing or equal `shape_dist_traveled` in `stop_times.txt`. | | [`DuplicatedColumnNotice`](#DuplicatedColumnNotice) | Duplicated column in CSV. | | [`DuplicateFareRuleZoneIdFieldsNotice`](#DuplicateFareRuleZoneIdFieldsNotice) | Duplicate rows from `fare_rules.txt` based on `fare_rules.route_id`, `fare_rules.origin_id`, `fare_rules.contains_id` and `fare_rules.destination_id`. | | [`DuplicateKeyNotice`](#DuplicateKeyNotice) | Duplicated entity. | | [`EmptyColumnNameNotice`](#EmptyColumnNameNotice) | A column name is empty. | | [`EmptyFileNotice`](#EmptyFileNotice) | A CSV file is empty. | +| [`EqualShapeDistanceDiffCoordinatesNotice`](#EqualShapeDistanceDiffCoordinatesNotice) | Two consecutive points have equal `shape_dist_traveled` and different lat/lon coordinates in `shapes.txt`. | | [`ForeignKeyViolationNotice`](#ForeignKeyViolationNotice) | Wrong foreign key. | | [`InconsistentAgencyTimezoneNotice`](#InconsistentAgencyTimezoneNotice) | Inconsistent Timezone among agencies. | | [`InvalidColorNotice`](#InvalidColorNotice) | A field contains an invalid color value. | @@ -128,6 +129,7 @@ Additional details regarding the notices' context is provided in [`NOTICES.md`]( | [`AttributionWithoutRoleNotice`](#AttributionWithoutRoleNotice) | Attribution with no role. | | [`DuplicateRouteNameNotice`](#DuplicateRouteNameNotice) | Two distinct routes have either the same `route_short_name`, the same `route_long_name`, or the same combination of `route_short_name` and `route_long_name`. | | [`EmptyRowNotice`](#EmptyRowNotice) | A row in the input file has only spaces. | +| [`EqualShapeDistanceSameCoordinatesNotice`](#EqualShapeDistanceSameCoordinatesNotice) | Two consecutive points have equal `shape_dist_traveled` and the same lat/lon coordinates in `shapes.txt`. | | [`FastTravelBetweenConsecutiveStopsNotice`](#FastTravelBetweenConsecutiveStopsNotice) | A transit vehicle moves too fast between two consecutive stops. | | [`FastTravelBetweenFarStopsNotice`](#FastTravelBetweenFarStopsNotice) | A transit vehicle moves too fast between two far stops. | | [`FeedExpirationDateNotice`](#FeedExpirationDateNotice) | Dataset should be valid for at least the next 7 days. Dataset should cover at least the next 30 days of service. | @@ -199,11 +201,11 @@ Trips with the same block id have overlapping stop times. Parsing of a CSV file failed. One common case of the problem is when a cell value contains more than 4096 characters. - + -#### DecreasingOrEqualShapeDistanceNotice +#### DecreasingShapeDistanceNotice -When sorted by `shape.shape_pt_sequence`, two consecutive shape points should have increasing values for `shape_dist_traveled`. If the values are equal, this is considered as an error. +When sorted by `shape.shape_pt_sequence`, two consecutive shape points must not have decreasing values for `shape_dist_traveled`. ##### References: * [shapes.txt specification](https://gtfs.org/reference/static#shapestxt) @@ -262,6 +264,15 @@ Empty csv file found in the archive: file does not have any headers, or is a req ##### References: * [GTFS files requirements](https://gtfs.org/reference/static#file-requirements) +#### EqualShapeDistanceDiffCoordinatesNotice + + + +When sorted by `shape.shape_pt_sequence`, the values for `shape_dist_traveled` must increase along a shape. Two consecutive points with equal values for `shape_dist_traveled` and different coordinates indicate an error. + +##### References: +* [shapes.txt specification](https://gtfs.org/reference/static#shapestxt) + #### ForeignKeyViolationNotice @@ -685,6 +696,15 @@ A row in the input file has only spaces. ##### References: * [GTFS file requirements](http://gtfs.org/reference/static/#file-requirements) +#### EqualShapeDistanceSameCoordinatesNotice + + + +When sorted by `shape.shape_pt_sequence`, the values for `shape_dist_traveled` must increase along a shape. Two consecutive points with equal values for `shape_dist_traveled` and the same coordinates indicate a duplicative shape point. + +##### References: +* [shapes.txt specification](https://gtfs.org/reference/static#shapestxt) + #### FastTravelBetweenConsecutiveStopsNotice diff --git a/docs/NOTICES.md b/docs/NOTICES.md index 43c79599a9..dad4c85fa5 100644 --- a/docs/NOTICES.md +++ b/docs/NOTICES.md @@ -9,13 +9,14 @@ |-------------------------------------------------------- |----------------------------------------------------------------------------------------------------------------- | | `block_trips_with_overlapping_stop_times` | [`BlockTripsWithOverlappingStopTimesNotice`](#BlockTripsWithOverlappingStopTimesNotice) | | `csv_parsing_failed` | [`CsvParsingFailedNotice`](#CsvParsingFailedNotice) | -| `decreasing_or_equal_shape_distance` | [`DecreasingOrEqualShapeDistanceNotice`](#DecreasingOrEqualShapeDistanceNotice) | +| `decreasing_shape_distance` | [`DecreasingShapeDistanceNotice`](#DecreasingShapeDistanceNotice) | | `decreasing_or_equal_stop_time_distance` | [`DecreasingOrEqualStopTimeDistanceNotice`](#DecreasingOrEqualStopTimeDistanceNotice) | | `duplicated_column` | [`DuplicatedColumnNotice`](#DuplicatedColumnNotice) | | `duplicate_fare_rule_zone_id_fields` | [`DuplicateFareRuleZoneIdFieldsNotice`](#DuplicateFareRuleZoneIdFieldsNotice) | | `duplicate_key` | [`DuplicateKeyNotice`](#DuplicateKeyNotice) | | `empty_column_name` | [`EmptyColumnNameNotice`](#EmptyColumnNameNotice) | | `empty_file` | [`EmptyFileNotice`](#EmptyFileNotice) | +| `equal_shape_distance_diff_coordinates` | [`EqualShapeDistanceDiffCoordinatesNotice`](#EqualShapeDistanceDiffCoordinatesNotice) | | `foreign_key_violation` | [`ForeignKeyViolationNotice`](#ForeignKeyViolationNotice) | | `inconsistent_agency_timezone` | [`InconsistentAgencyTimezoneNotice`](#InconsistentAgencyTimezoneNotice) | | `invalid_color` | [`InvalidColorNotice`](#InvalidColorNotice) | @@ -88,7 +89,7 @@ ##### Affected files [All GTFS files supported by the specification.](http://gtfs.org/reference/static#dataset-files) -#### [`DecreasingOrEqualShapeDistanceNotice`](/RULES.md#DecreasingOrEqualShapeDistanceNotice) +#### [`DecreasingShapeDistanceNotice`](/RULES.md#DecreasingShapeDistanceNotice) ##### Fields description | Field name | Description | Type | @@ -183,6 +184,23 @@ ##### Affected files [All GTFS files supported by the specification.](http://gtfs.org/reference/static#dataset-files) +#### [`EqualShapeDistanceDiffCoordinatesNotice`](/RULES.md#EqualShapeDistanceDiffCoordinatesNotice) +##### Fields description + +| Field name | Description | Type | +|----------------------- |------------------------------------------------------------------------------------------------- |--------- | +| `shapeId` | The id of the faulty shape. | String | +| `csvRowNumber` | The row number from `shapes.txt`. | Long | +| `shapeDistTraveled` | Actual distance traveled along the shape from the first shape point to the faulty record. | Double | +| `shapePtSequence` | The faulty record's `shapes.shape_pt_sequence`. | Integer | +| `prevCsvRowNumber` | The row number from `shapes.txt` of the previous shape point. | Long | +| `prevShapeDistTraveled` | Actual distance traveled along the shape from the first shape point to the previous shape point. | Double | +| `prevShapePtSequence` | The previous record's `shapes.shape_pt_sequence`. | Integer | + +##### Affected files +* [`stops.txt`](http://gtfs.org/reference/static#stopstxt) +* [`shapes.txt`](http://gtfs.org/reference/static#shapestxt) + #### [`ForeignKeyViolationNotice`](/RULES.md#ForeignKeyViolationNotice) ##### Fields description @@ -731,6 +749,7 @@ | `attribution_without_role` | [`AttributionWithoutRoleNotice`](#AttributionWithoutRoleNotice) | | `duplicate_route_name` | [`DuplicateRouteNameNotice`](#DuplicateRouteNameNotice) | | `empty_row` | [`EmptyRowNotice`](#EmptyRowNotice) | +| `equal_shape_distance_same_coordinates` | [`EqualShapeDistanceSameCoordinatesNotice`](#EqualShapeDistanceSameCoordinatesNotice) | | `fast_travel_between_consecutive_stops` | [`FastTravelBetweenConsecutiveStopsNotice`](#FastTravelBetweenConsecutiveStopsNotice) | | `fast_travel_between_far_stops` | [`FastTravelBetweenFarStopsNotice`](#FastTravelBetweenFarStopsNotice) | | `feed_expiration_date` | [`FeedExpirationDateNotice`](#FeedExpirationDateNotice) | @@ -805,6 +824,24 @@ ##### Affected files [All GTFS files supported by the specification.](http://gtfs.org/reference/static#dataset-files) + +#### [EqualShapeDistanceSameCoordinatesNotice](/RULES.md#EqualShapeDistanceSameCoordinatesNotice) +##### Fields description + +| Field name | Description | Type | +|----------------------- |------------------------------------------------------------------------------------------------- |--------- | +| `shapeId` | The id of the faulty shape. | String | +| `csvRowNumber` | The row number from `shapes.txt`. | Long | +| `shapeDistTraveled` | Actual distance traveled along the shape from the first shape point to the faulty record. | Double | +| `shapePtSequence` | The faulty record's `shapes.shape_pt_sequence`. | Integer | +| `prevCsvRowNumber` | The row number from `shapes.txt` of the previous shape point. | Long | +| `prevShapeDistTraveled` | Actual distance traveled along the shape from the first shape point to the previous shape point. | Double | +| `prevShapePtSequence` | The previous record's `shapes.shape_pt_sequence`. | Integer | + +##### Affected files +* [`stops.txt`](http://gtfs.org/reference/static#stopstxt) +* [`shapes.txt`](http://gtfs.org/reference/static#shapestxt) + #### [FeedExpirationDateNotice](/RULES.md#FeedExpirationDateNotice) ##### Fields description diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidator.java index f0a7c4c887..be701e457e 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidator.java @@ -16,6 +16,8 @@ package org.mobilitydata.gtfsvalidator.validator; +import static org.mobilitydata.gtfsvalidator.util.S2Earth.getDistanceMeters; + import com.google.common.collect.Multimaps; import java.util.List; import javax.inject.Inject; @@ -27,12 +29,19 @@ import org.mobilitydata.gtfsvalidator.table.GtfsShapeTableContainer; /** - * Validates that shape_dist_traveled along a shape in "shapes.txt" are not decreasing. + * Validates that the shape_dist_traveled along a shape in "shapes.txt" is increasing. + * + *

Generated notice: * - *

Generated notice: {@link DecreasingOrEqualShapeDistanceNotice}. + *

*/ @GtfsValidator public class ShapeIncreasingDistanceValidator extends FileValidator { + private final GtfsShapeTableContainer table; @Inject @@ -47,33 +56,101 @@ public void validate(NoticeContainer noticeContainer) { for (int i = 1; i < shapeList.size(); ++i) { GtfsShape prev = shapeList.get(i - 1); GtfsShape curr = shapeList.get(i); - if (prev.hasShapeDistTraveled() - && curr.hasShapeDistTraveled() - && prev.shapeDistTraveled() >= curr.shapeDistTraveled()) { + if (!(prev.hasShapeDistTraveled() && curr.hasShapeDistTraveled())) { + continue; + } + if (prev.shapeDistTraveled() > curr.shapeDistTraveled()) { + noticeContainer.addValidationNotice(new DecreasingShapeDistanceNotice(prev, curr)); + continue; + } + if (prev.shapeDistTraveled() != curr.shapeDistTraveled()) { + continue; + } + // equal shape_dist_traveled and different coordinates + if (!(curr.shapePtLon() == prev.shapePtLon() && curr.shapePtLat() == prev.shapePtLat())) { noticeContainer.addValidationNotice( - new DecreasingOrEqualShapeDistanceNotice( - curr.shapeId(), - curr.csvRowNumber(), - curr.shapeDistTraveled(), - curr.shapePtSequence(), - prev.csvRowNumber(), - prev.shapeDistTraveled(), - prev.shapePtSequence())); + new EqualShapeDistanceDiffCoordinatesNotice(prev, curr)); + } else { + // equal shape_dist_traveled and same coordinates + noticeContainer.addValidationNotice( + new EqualShapeDistanceSameCoordinatesNotice(prev, curr)); } } } } /** - * When sorted on `shapes.shape_pt_sequence` key, shape points should have strictly increasing - * values for `shapes.shape_dist_traveled` + * When sorted by {@code shape.shape_pt_sequence}, the values for {@code shape_dist_traveled} must + * increase along a shape. Two consecutive points with equal values for {@code + * shape_dist_traveled} and different coordinates indicate an error. + * + *

"Values must increase along with shape_pt_sequence." + * (http://gtfs.org/reference/static/#shapestxt) + * + *

Severity: {@code SeverityLevel.ERROR} + */ + static class DecreasingShapeDistanceNotice extends ValidationNotice { + private final String shapeId; + private final long csvRowNumber; + private final double shapeDistTraveled; + private final int shapePtSequence; + private final long prevCsvRowNumber; + private final double prevShapeDistTraveled; + private final int prevShapePtSequence; + + DecreasingShapeDistanceNotice(GtfsShape current, GtfsShape previous) { + super(SeverityLevel.ERROR); + this.shapeId = current.shapeId(); + this.csvRowNumber = current.csvRowNumber(); + this.shapeDistTraveled = current.shapeDistTraveled(); + this.shapePtSequence = current.shapePtSequence(); + this.prevCsvRowNumber = previous.csvRowNumber(); + this.prevShapeDistTraveled = previous.shapeDistTraveled(); + this.prevShapePtSequence = previous.shapePtSequence(); + } + } + + /** + * When sorted by {@code shape.shape_pt_sequence}, the values for {@code shape_dist_traveled} must + * increase along a shape. Two consecutive points with equal values for {@code + * shape_dist_traveled} and the same coordinates indicate a duplicative shape point. + * + *

"Values must increase along with shape_pt_sequence." + * (http://gtfs.org/reference/static/#shapestxt) + * + *

Severity: {@code SeverityLevel.WARNING} + */ + static class EqualShapeDistanceSameCoordinatesNotice extends ValidationNotice { + private final String shapeId; + private final long csvRowNumber; + private final double shapeDistTraveled; + private final int shapePtSequence; + private final long prevCsvRowNumber; + private final double prevShapeDistTraveled; + private final int prevShapePtSequence; + + EqualShapeDistanceSameCoordinatesNotice(GtfsShape previous, GtfsShape current) { + super(SeverityLevel.WARNING); + this.shapeId = current.shapeId(); + this.csvRowNumber = current.csvRowNumber(); + this.shapeDistTraveled = current.shapeDistTraveled(); + this.shapePtSequence = current.shapePtSequence(); + this.prevCsvRowNumber = previous.csvRowNumber(); + this.prevShapeDistTraveled = previous.shapeDistTraveled(); + this.prevShapePtSequence = previous.shapePtSequence(); + } + } + + /** + * When sorted on {@code shapes.shape_pt_sequence} key, shape points with different coordinates + * must not have equal values for {@code shapes.shape_dist_traveled} * *

"Values must increase along with shape_pt_sequence." * (http://gtfs.org/reference/static/#shapestxt) * *

Severity: {@code SeverityLevel.ERROR} */ - static class DecreasingOrEqualShapeDistanceNotice extends ValidationNotice { + static class EqualShapeDistanceDiffCoordinatesNotice extends ValidationNotice { private final String shapeId; private final long csvRowNumber; private final double shapeDistTraveled; @@ -81,23 +158,19 @@ static class DecreasingOrEqualShapeDistanceNotice extends ValidationNotice { private final long prevCsvRowNumber; private final double prevShapeDistTraveled; private final int prevShapePtSequence; + private final double actualDistanceBetweenShapePoints; - DecreasingOrEqualShapeDistanceNotice( - String shapeId, - long csvRowNumber, - double shapeDistTraveled, - int shapePtSequence, - long prevCsvRowNumber, - double prevShapeDistTraveled, - int prevShapePtSequence) { + EqualShapeDistanceDiffCoordinatesNotice(GtfsShape previous, GtfsShape current) { super(SeverityLevel.ERROR); - this.shapeId = shapeId; - this.csvRowNumber = csvRowNumber; - this.shapeDistTraveled = shapeDistTraveled; - this.shapePtSequence = shapePtSequence; - this.prevCsvRowNumber = prevCsvRowNumber; - this.prevShapeDistTraveled = prevShapeDistTraveled; - this.prevShapePtSequence = prevShapePtSequence; + this.shapeId = current.shapeId(); + this.csvRowNumber = current.csvRowNumber(); + this.shapeDistTraveled = current.shapeDistTraveled(); + this.shapePtSequence = current.shapePtSequence(); + this.prevCsvRowNumber = previous.csvRowNumber(); + this.prevShapeDistTraveled = previous.shapeDistTraveled(); + this.prevShapePtSequence = previous.shapePtSequence(); + this.actualDistanceBetweenShapePoints = + getDistanceMeters(current.shapePtLatLon(), previous.shapePtLatLon()); } } } diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/StopTimeIncreasingDistanceValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/StopTimeIncreasingDistanceValidator.java index 403c06d0e0..03ff01a1fe 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/StopTimeIncreasingDistanceValidator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/StopTimeIncreasingDistanceValidator.java @@ -25,12 +25,11 @@ import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; import org.mobilitydata.gtfsvalidator.table.GtfsStopTime; import org.mobilitydata.gtfsvalidator.table.GtfsStopTimeTableContainer; -import org.mobilitydata.gtfsvalidator.validator.ShapeIncreasingDistanceValidator.DecreasingOrEqualShapeDistanceNotice; /** * Validates: stop times of a trip have increasing distance (stops.shape_dist_traveled) * - *

Generated notice: {@link DecreasingOrEqualShapeDistanceNotice}. + *

Generated notice: {@link DecreasingOrEqualStopTimeDistanceNotice}. */ @GtfsValidator public class StopTimeIncreasingDistanceValidator extends FileValidator { diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidatorTest.java index f8efb50e7a..1456a01cba 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ShapeIncreasingDistanceValidatorTest.java @@ -25,7 +25,9 @@ import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; import org.mobilitydata.gtfsvalidator.table.GtfsShape; import org.mobilitydata.gtfsvalidator.table.GtfsShapeTableContainer; -import org.mobilitydata.gtfsvalidator.validator.ShapeIncreasingDistanceValidator.DecreasingOrEqualShapeDistanceNotice; +import org.mobilitydata.gtfsvalidator.validator.ShapeIncreasingDistanceValidator.DecreasingShapeDistanceNotice; +import org.mobilitydata.gtfsvalidator.validator.ShapeIncreasingDistanceValidator.EqualShapeDistanceDiffCoordinatesNotice; +import org.mobilitydata.gtfsvalidator.validator.ShapeIncreasingDistanceValidator.EqualShapeDistanceSameCoordinatesNotice; public class ShapeIncreasingDistanceValidatorTest { public static GtfsShape createShapePoint( @@ -34,7 +36,7 @@ public static GtfsShape createShapePoint( double shapePtLat, double shapePtLon, int shapePtSequence, - double shapeDistTraveled) { + Double shapeDistTraveled) { return new GtfsShape.Builder() .setCsvRowNumber(csvRowNumber) .setShapeId(shapeId) @@ -66,37 +68,56 @@ public void increasingDistanceAlongShapeShouldNotGenerateNotice() { @Test public void lastShapeWithDecreasingDistanceAlongShapeShouldGenerateNotice() { + GtfsShape previous = createShapePoint(2, "first shape", 31.0d, 42, 2, 45.0d); + GtfsShape current = createShapePoint(3, "first shape", 29.0d, 46, 3, 40.0); assertThat( generateNotices( ImmutableList.of( - createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), - createShapePoint(2, "first shape", 31.0d, 42, 2, 45.0d), - createShapePoint(3, "first shape", 29.0d, 46, 3, 40.0)))) - .containsExactly( - new DecreasingOrEqualShapeDistanceNotice("first shape", 3, 40.0d, 3, 2, 45.0d, 2)); + createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), previous, current))) + .containsExactly(new DecreasingShapeDistanceNotice(previous, current)); } @Test public void oneIntermediateShapeWithDecreasingDistanceAlongShapeShouldGenerateNotice() { + GtfsShape previous = createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d); + GtfsShape current = createShapePoint(2, "first shape", 31.0d, 42, 2, 9.0d); assertThat( generateNotices( ImmutableList.of( - createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), - createShapePoint(2, "first shape", 31.0d, 42, 2, 9.0d), - createShapePoint(3, "first shape", 45.0d, 46, 3, 40.0)))) - .containsExactly( - new DecreasingOrEqualShapeDistanceNotice("first shape", 2, 9.0d, 2, 1, 10.0d, 1)); + previous, current, createShapePoint(3, "first shape", 45.0d, 46, 3, 40.0)))) + .containsExactly(new DecreasingShapeDistanceNotice(previous, current)); } @Test - public void shapeWithEqualDistanceAlongShapeShouldGenerateNotice() { + public void shapeWithEqualShapeDistance_differentGpsCoordinates_shouldGenerateNotice() { + GtfsShape previous = createShapePoint(2, "first shape", 31.0d, 42, 2, 45.0d); + GtfsShape current = createShapePoint(3, "first shape", 29.0d, 46, 3, 45.0); assertThat( generateNotices( ImmutableList.of( - createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), - createShapePoint(2, "first shape", 31.0d, 42, 2, 45.0d), - createShapePoint(3, "first shape", 29.0d, 46, 3, 45.0)))) - .containsExactly( - new DecreasingOrEqualShapeDistanceNotice("first shape", 3, 45.0d, 3, 2, 45.0d, 2)); + createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), previous, current))) + .containsExactly(new EqualShapeDistanceDiffCoordinatesNotice(previous, current)); + } + + @Test + public void shapeWithEqualShapeDistance_sameGpsCoordinates_shouldGenerateWarningNotice() { + GtfsShape previous = createShapePoint(2, "first shape", 31.0d, 42, 2, 45.0d); + GtfsShape current = createShapePoint(3, "first shape", 31.0d, 42, 4, 45.0d); + assertThat( + generateNotices( + ImmutableList.of( + createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), previous, current))) + .containsExactly(new EqualShapeDistanceSameCoordinatesNotice(previous, current)); + } + + @Test + public void noShapeDistTravelled_shouldNotGenerateNotice() { + GtfsShape previous = createShapePoint(2, "first shape", 31.0d, 42, 2, null); + GtfsShape current = createShapePoint(3, "first shape", 31.0d, 42, 4, 45.0d); + assertThat( + generateNotices( + ImmutableList.of( + createShapePoint(1, "first shape", 30.0d, 45, 1, 10.0d), previous, current))) + .isEmpty(); } }