From ceeabdcf45188dfe115b770135c2b0cd9bbff29a Mon Sep 17 00:00:00 2001 From: Praneeth Devunuri Date: Thu, 9 May 2024 11:18:08 -0500 Subject: [PATCH] new rule for shapes with single shape point --- .../validator/SingleShapePointValidator.java | 70 +++++++++++++++++++ .../SingleShapePointValidatorTest.java | 63 +++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 main/src/main/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidator.java create mode 100644 main/src/test/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidatorTest.java diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidator.java new file mode 100644 index 0000000000..41bcb2135f --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidator.java @@ -0,0 +1,70 @@ +package org.mobilitydata.gtfsvalidator.validator; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; +import org.mobilitydata.gtfsvalidator.table.GtfsShape; +import org.mobilitydata.gtfsvalidator.table.GtfsShapeSchema; +import org.mobilitydata.gtfsvalidator.table.GtfsShapeTableContainer; + +import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; + +import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.WARNING; + +/** + * Validates that every shape (identified by shape_id) has more than one shape_point + * + *

Generated notice: {@link SingleShapePointNotice}

+ */ +@GtfsValidator +public class SingleShapePointValidator extends FileValidator { + private final GtfsShapeTableContainer shapeTable; + + @Inject + SingleShapePointValidator(GtfsShapeTableContainer shapeTable){ + this.shapeTable = shapeTable; + } + + @Override + public void validate(NoticeContainer noticeContainer) { + Map shapePointCounts = new HashMap<>(); + Map shapePointRowNumbers = new HashMap<>(); + for (GtfsShape shape : shapeTable.getEntities()) { + String shapeId = shape.shapeId(); + shapePointCounts.put(shapeId, shapePointCounts.getOrDefault(shapeId, 0) + 1); + shapePointRowNumbers.put(shapeId, shape.csvRowNumber()); + } + + for (Map.Entry entry : shapePointCounts.entrySet()) { + if (entry.getValue() == 1) { + noticeContainer.addValidationNotice(new SingleShapePointNotice(entry.getKey(),shapePointRowNumbers.getOrDefault(entry.getKey(),0))); + } + } + } + + /** + * The shape within `shapes.txt` contains a single shape point. + * + *

A shape should contain more than one shape point to visualize the route on any GIS software (which require LineString).

+ */ + @GtfsValidationNotice( + severity = WARNING, + files = @GtfsValidationNotice.FileRefs({GtfsShapeSchema.class}) + ) + + static class SingleShapePointNotice extends ValidationNotice { + /** The faulty record's id. */ + private final String shapeId; + + /** The row number of the faulty record. */ + private final int csvRowNumber; + + SingleShapePointNotice(String shapeId, int csvRowNumber) { + this.shapeId = shapeId; + this.csvRowNumber = csvRowNumber; + } + } +} diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidatorTest.java new file mode 100644 index 0000000000..3e4e62ed36 --- /dev/null +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/SingleShapePointValidatorTest.java @@ -0,0 +1,63 @@ +package org.mobilitydata.gtfsvalidator.validator; + +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; +import org.mobilitydata.gtfsvalidator.table.GtfsShape; +import org.mobilitydata.gtfsvalidator.table.GtfsShapeTableContainer; + +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; + +public class SingleShapePointValidatorTest { + public static GtfsShape createShapePoint( + int csvRowNumber, + String shapeId, + double shapePtLat, + double shapePtLon, + int shapePtSequence, + double shapeDistTraveled) { + return new GtfsShape.Builder() + .setCsvRowNumber(csvRowNumber) + .setShapeId(shapeId) + .setShapePtLat(shapePtLat) + .setShapePtLon(shapePtLon) + .setShapePtSequence(shapePtSequence) + .setShapeDistTraveled(shapeDistTraveled) + .build(); + } + + + private static List generateNotices( + List shapes) { + NoticeContainer noticeContainer = new NoticeContainer(); + new SingleShapePointValidator( + GtfsShapeTableContainer.forEntities(shapes, noticeContainer)) + .validate(noticeContainer); + return noticeContainer.getValidationNotices(); + } + + @Test + public void shapeWithMoreThanOneShapePointShouldNotGenerateNotice() { + assertThat( + generateNotices( + ImmutableList.of( + createShapePoint(1, "first shape id", 45.0d, 45.0d, 1, 0.0d), + createShapePoint(2, "first shape id", 45.1d, 45.0d, 2, 40.0d) + )) + ).isEmpty(); + } + + @Test + public void unusedShapeShouldGenerateNotice() { + assertThat( + generateNotices( + ImmutableList.of( + createShapePoint(1, "first shape id", 45.0d, 45.0d, 1, 40.0d), + createShapePoint(2, "first shape id", 45.1d, 45.0d, 2, 40.0d), + createShapePoint(3, "second shape id", 45.0d, 45.0d, 1, 40.0d))) + ).containsExactly(new SingleShapePointValidator.SingleShapePointNotice("second shape id", 3)); + } +}