Skip to content

Commit

Permalink
feat: New rule - SingleShapePointValidator for flagging shapes with a…
Browse files Browse the repository at this point in the history
… single shape point (#1753)

* new rule for shapes with single shape point

* Ensure Google Java formatting

* formatted code

* change error description

---------

Co-authored-by: David Gamez <1192523+davidgamez@users.noreply.github.com>
Co-authored-by: Jingsi Lu <jingsi@mobilitydata.org>
  • Loading branch information
3 people authored Jun 6, 2024
1 parent 3c12ebc commit edf9867
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.mobilitydata.gtfsvalidator.validator;

import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.WARNING;

import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
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;

/**
* Validates that every shape (identified by shape_id) has more than one shape_point
*
* <p>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<String, Integer> shapePointCounts = new HashMap<>();
Map<String, Integer> 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<String, Integer> 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.
*
* <p>A shape should contain more than one shape point to visualize the route
*/
@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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.mobilitydata.gtfsvalidator.validator;

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableList;
import java.util.List;
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;

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<ValidationNotice> generateNotices(List<GtfsShape> 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));
}
}

0 comments on commit edf9867

Please sign in to comment.