From 669fa760e29e39482d5374d97a06b574cf65b7b5 Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Thu, 23 Jan 2025 13:05:57 -0500 Subject: [PATCH 1/8] added GeoJsonParsingFailedNotice --- .../schema/GeoJsonParsingFailedNotice.java | 31 +++++++++++++++++++ .../table/GeoJsonFileLoader.java | 3 ++ 2 files changed, 34 insertions(+) create mode 100644 core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java new file mode 100644 index 0000000000..935b20aa16 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java @@ -0,0 +1,31 @@ +package org.mobilitydata.gtfsvalidator.notice.schema; + +import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; +import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; + +/** + * The parsing of a GeoJSON file failed. + * + *

One common case of the problem is when the file is not a valid GeoJSON file. + */ +@GtfsValidationNotice(severity = ERROR) +public class GeoJsonParsingFailedNotice extends ValidationNotice { + /** The name of the faulty file. */ + private final String filename; + + /** The detailed message describing the error, and the internal state of the parser/writer. */ + private final String message; + + /** + * Constructor used while extracting notice information. + * + * @param filename the name of the file + * @param message the message describing the error + */ + public GeoJsonParsingFailedNotice(String filename, String message) { + this.filename = filename; + this.message = message; + } +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index db9bc81aa4..635fcca03d 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -13,6 +13,7 @@ import java.util.List; import org.locationtech.jts.geom.*; import org.mobilitydata.gtfsvalidator.notice.*; +import org.mobilitydata.gtfsvalidator.notice.schema.GeoJsonParsingFailedNotice; import org.mobilitydata.gtfsvalidator.util.geojson.GeoJsonGeometryValidator; import org.mobilitydata.gtfsvalidator.util.geojson.GeometryType; import org.mobilitydata.gtfsvalidator.util.geojson.UnparsableGeoJsonFeatureException; @@ -47,6 +48,8 @@ public GtfsEntityContainer load( noticeContainer.addSystemError(new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { + noticeContainer.addValidationNotice( + new GeoJsonParsingFailedNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (Exception ex) { From 405f7798f1e07da68cc5dd661af4556578e50aac Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Fri, 24 Jan 2025 10:54:43 -0500 Subject: [PATCH 2/8] removed GeoJsonParsingFailedNotice as MalformedJsonNotice and UnsupportedGeoJsonTypeNotice already covered the case. --- .../notice/MalformedJsonNotice.java | 6 +- .../notice/UnsupportedGeoJsonTypeNotice.java | 6 +- .../schema/GeoJsonParsingFailedNotice.java | 31 ---- .../table/GeoJsonFileLoader.java | 24 ++- .../table/GeoJsonFileLoaderTest.java | 149 ------------------ 5 files changed, 28 insertions(+), 188 deletions(-) delete mode 100644 core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java delete mode 100644 main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MalformedJsonNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MalformedJsonNotice.java index 6c4591ad17..c958b2e4d6 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MalformedJsonNotice.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MalformedJsonNotice.java @@ -26,7 +26,11 @@ public class MalformedJsonNotice extends ValidationNotice { /** The name of the faulty file. */ private final String filename; - public MalformedJsonNotice(String filename) { + /** The detailed message describing the error. */ + private final String message; + + public MalformedJsonNotice(String filename, String message) { this.filename = filename; + this.message = message; } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnsupportedGeoJsonTypeNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnsupportedGeoJsonTypeNotice.java index e75482c293..1c81ac3379 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnsupportedGeoJsonTypeNotice.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnsupportedGeoJsonTypeNotice.java @@ -30,7 +30,11 @@ public class UnsupportedGeoJsonTypeNotice extends ValidationNotice { /** The value of the unsupported GeoJSON type. */ private final String geoJsonType; - public UnsupportedGeoJsonTypeNotice(String geoJsonType) { + /** The detailed message describing the error. */ + private final String message; + + public UnsupportedGeoJsonTypeNotice(String geoJsonType, String message) { this.geoJsonType = geoJsonType; + this.message = message; } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java deleted file mode 100644 index 935b20aa16..0000000000 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/schema/GeoJsonParsingFailedNotice.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.mobilitydata.gtfsvalidator.notice.schema; - -import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; - -import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; -import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; - -/** - * The parsing of a GeoJSON file failed. - * - *

One common case of the problem is when the file is not a valid GeoJSON file. - */ -@GtfsValidationNotice(severity = ERROR) -public class GeoJsonParsingFailedNotice extends ValidationNotice { - /** The name of the faulty file. */ - private final String filename; - - /** The detailed message describing the error, and the internal state of the parser/writer. */ - private final String message; - - /** - * Constructor used while extracting notice information. - * - * @param filename the name of the file - * @param message the message describing the error - */ - public GeoJsonParsingFailedNotice(String filename, String message) { - this.filename = filename; - this.message = message; - } -} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index 635fcca03d..2af57adc2c 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -13,7 +13,6 @@ import java.util.List; import org.locationtech.jts.geom.*; import org.mobilitydata.gtfsvalidator.notice.*; -import org.mobilitydata.gtfsvalidator.notice.schema.GeoJsonParsingFailedNotice; import org.mobilitydata.gtfsvalidator.util.geojson.GeoJsonGeometryValidator; import org.mobilitydata.gtfsvalidator.util.geojson.GeometryType; import org.mobilitydata.gtfsvalidator.util.geojson.UnparsableGeoJsonFeatureException; @@ -41,15 +40,15 @@ public GtfsEntityContainer load( List entities = extractFeaturesFromStream(inputStream, noticeContainer); return geoJsonFileDescriptor.createContainerForEntities(entities, noticeContainer); } catch (JsonParseException jpex) { - noticeContainer.addValidationNotice(new MalformedJsonNotice(GtfsGeoJsonFeature.FILENAME)); + noticeContainer.addValidationNotice( + new MalformedJsonNotice(GtfsGeoJsonFeature.FILENAME, jpex.getMessage())); logger.atSevere().withCause(jpex).log("Malformed JSON in locations.geojson"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (IOException ioex) { - noticeContainer.addSystemError(new IOError(ioex)); + noticeContainer.addSystemError( + new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { - noticeContainer.addValidationNotice( - new GeoJsonParsingFailedNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (Exception ex) { @@ -58,6 +57,15 @@ public GtfsEntityContainer load( } } + /** + * Extracts features from the provided GeoJSON input stream. + * + * @param inputStream the input stream containing GeoJSON data + * @param noticeContainer the container to collect validation notices + * @return a list of parsed GeoJSON features + * @throws IOException if an I/O error occurs while reading the input stream + * @throws UnparsableGeoJsonFeatureException if any GeoJSON feature is unparsable + */ public List extractFeaturesFromStream( InputStream inputStream, NoticeContainer noticeContainer) throws IOException, UnparsableGeoJsonFeatureException { @@ -70,7 +78,11 @@ public List extractFeaturesFromStream( throw new UnparsableGeoJsonFeatureException("Missing required field 'type'"); } else if (!jsonObject.get("type").getAsString().equals("FeatureCollection")) { noticeContainer.addValidationNotice( - new UnsupportedGeoJsonTypeNotice(jsonObject.get("type").getAsString())); + new UnsupportedGeoJsonTypeNotice( + jsonObject.get("type").getAsString(), + "Unsupported GeoJSON type: " + + jsonObject.get("type").getAsString() + + ". Use 'FeatureCollection' instead.")); throw new UnparsableGeoJsonFeatureException("Unsupported GeoJSON type"); } JsonArray featuresArray = jsonObject.getAsJsonArray("features"); diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java deleted file mode 100644 index 93325594e4..0000000000 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.mobilitydata.gtfsvalidator.table; - -import static com.google.common.truth.Truth.assertThat; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mobilitydata.gtfsvalidator.notice.InvalidGeometryNotice; -import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; - -/** Runs GeoJsonFileLoader on test json data. */ -@RunWith(JUnit4.class) -public class GeoJsonFileLoaderTest { - - static String validGeoJsonData; - static String invalidPolygonGeoJsonData; - NoticeContainer noticeContainer; - - @BeforeClass - public static void setUpBeforeClass() { - // Create the valid and invalid JSON data strings, using single quotes for readability. - validGeoJsonData = - String.join( - "\n", - "{", - " 'type': 'FeatureCollection',", - " 'features': [", - " {", - " 'id': 'id1',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [100.0, 0.0],", - " [101.0, 0.0],", - " [101.0, 1.0],", - " [100.0, 1.0],", - " [100.0, 0.0]", - " ]", - " ]", - " },", - " 'properties': {}", - " },", - " {", - " 'id': 'id2',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [200.0, 0.0],", - " [201.0, 0.0],", - " [201.0, 2.0],", - " [200.0, 2.0],", - " [200.0, 0.0]", - " ]", - " ]", - " },", - " 'properties': {}", - " }", - " ]", - "}"); - - invalidPolygonGeoJsonData = - String.join( - "\n", - "{", - " 'type': 'FeatureCollection',", - " 'features': [", - " {", - " 'id': 'id_invalid',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [100.0, 0.0],", - " [101.0, 0.0],", - " [100.5, 0.5]" - + // Invalid Polygon: not closed - " ]", - " ]", - " },", - " 'properties': {}", - " }", - " ]", - "}"); - - // Replace single quotes with double quotes for JSON compliance - validGeoJsonData = validGeoJsonData.replace("'", "\""); - invalidPolygonGeoJsonData = invalidPolygonGeoJsonData.replace("'", "\""); - } - - @Before - public void setUp() { - noticeContainer = new NoticeContainer(); - } - - @Test - public void testGtfsGeoJsonFileLoader() /*throws ValidatorLoaderException*/ { - - var container = createLoader(validGeoJsonData); - var geoJsonContainer = (GtfsGeoJsonFeaturesContainer) container; - assertThat(container).isNotNull(); - assertThat(container.getTableStatus()).isEqualTo(TableStatus.PARSABLE_HEADERS_AND_ROWS); - assertThat(geoJsonContainer.entityCount()).isEqualTo(2); - assertThat(geoJsonContainer.getEntities().get(0).featureId()).isEqualTo("id1"); - assertThat(geoJsonContainer.getEntities().get(1).featureId()).isEqualTo("id2"); - } - - @Test - public void testBrokenJson() { - var container = createLoader("This is a broken json"); - assertThat(container.entityCount()).isEqualTo(0); - } - - @Test - public void testInvalidPolygonGeometry() { - // Testing for invalid polygon where coordinates do not form a closed ring - var container = createLoader(invalidPolygonGeoJsonData); - - // Check if the container is in the correct state - assertThat(container.getTableStatus()).isEqualTo(TableStatus.UNPARSABLE_ROWS); - - // Check if the correct validation notice is generated for the invalid geometry - List notices = - noticeContainer.getValidationNotices().stream() - .filter(InvalidGeometryNotice.class::isInstance) - .map(InvalidGeometryNotice.class::cast) - .collect(Collectors.toList()); - - assertThat(notices.size()).isGreaterThan(0); - } - - private GtfsEntityContainer createLoader(String jsonData) { - GeoJsonFileLoader loader = new GeoJsonFileLoader(); - var fileDescriptor = new GtfsGeoJsonFileDescriptor(); - InputStream inputStream = new ByteArrayInputStream(jsonData.getBytes(StandardCharsets.UTF_8)); - return loader.load(fileDescriptor, null, inputStream, noticeContainer); - } -} From d708020c74aa96bd72c1aed6ead265b43e66dbc3 Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Fri, 24 Jan 2025 11:08:39 -0500 Subject: [PATCH 3/8] formatted code --- .../mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index 2af57adc2c..9e6f4960ff 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -45,8 +45,7 @@ public GtfsEntityContainer load( logger.atSevere().withCause(jpex).log("Malformed JSON in locations.geojson"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (IOException ioex) { - noticeContainer.addSystemError( - new IOError(ioex)); + noticeContainer.addSystemError(new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); From 687ab27793955c0afda4a91fb9792b1536897e7a Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Mon, 27 Jan 2025 13:15:09 -0500 Subject: [PATCH 4/8] added a UnparsableGeoJsonFeatureNotice to cover all failed cases --- .../UnparsableGeoJsonFeatureNotice.java | 20 +++++++++++++++++++ .../table/GeoJsonFileLoader.java | 1 + 2 files changed, 21 insertions(+) create mode 100644 core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java new file mode 100644 index 0000000000..4a2bcb1093 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java @@ -0,0 +1,20 @@ +package org.mobilitydata.gtfsvalidator.notice; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; + +import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; + +/** A GeoJSON feature of locations.geojson could not be parsed. */ +@GtfsValidationNotice(severity = ERROR) +public class UnparsableGeoJsonFeatureNotice extends ValidationNotice { + /** The name of the faulty file. */ + private final String filename; + + /** The detailed message describing the error. */ + private final String message; + + public UnparsableGeoJsonFeatureNotice(String filename, String message) { + this.filename = filename; + this.message = message; + } +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index 9e6f4960ff..74d922ecb9 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -48,6 +48,7 @@ public GtfsEntityContainer load( noticeContainer.addSystemError(new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { + noticeContainer.addValidationNotice(new UnparsableGeoJsonFeatureNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (Exception ex) { From 2d7c9f4023355f929bab372c0f6a42afac7cebb6 Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Mon, 27 Jan 2025 13:17:20 -0500 Subject: [PATCH 5/8] formatted code --- .../gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java | 4 ++-- .../mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java index 4a2bcb1093..253155723e 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java @@ -1,9 +1,9 @@ package org.mobilitydata.gtfsvalidator.notice; -import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; - import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; + /** A GeoJSON feature of locations.geojson could not be parsed. */ @GtfsValidationNotice(severity = ERROR) public class UnparsableGeoJsonFeatureNotice extends ValidationNotice { diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index 74d922ecb9..e3639bce6e 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -48,7 +48,8 @@ public GtfsEntityContainer load( noticeContainer.addSystemError(new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { - noticeContainer.addValidationNotice(new UnparsableGeoJsonFeatureNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); + noticeContainer.addValidationNotice( + new UnparsableGeoJsonFeatureNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (Exception ex) { From 8e6a75605e758a432a7242acbb313dd380b6ef75 Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Mon, 27 Jan 2025 13:34:14 -0500 Subject: [PATCH 6/8] added back GeoJsonFileLoaderTest --- .../table/GeoJsonFileLoaderTest.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java new file mode 100644 index 0000000000..33b69fe0c6 --- /dev/null +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java @@ -0,0 +1,149 @@ +package org.mobilitydata.gtfsvalidator.table; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mobilitydata.gtfsvalidator.notice.InvalidGeometryNotice; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; + +/** Runs GeoJsonFileLoader on test json data. */ +@RunWith(JUnit4.class) +public class GeoJsonFileLoaderTest { + + static String validGeoJsonData; + static String invalidPolygonGeoJsonData; + NoticeContainer noticeContainer; + + @BeforeClass + public static void setUpBeforeClass() { + // Create the valid and invalid JSON data strings, using single quotes for readability. + validGeoJsonData = + String.join( + "\n", + "{", + " 'type': 'FeatureCollection',", + " 'features': [", + " {", + " 'id': 'id1',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [100.0, 0.0],", + " [101.0, 0.0],", + " [101.0, 1.0],", + " [100.0, 1.0],", + " [100.0, 0.0]", + " ]", + " ]", + " },", + " 'properties': {}", + " },", + " {", + " 'id': 'id2',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [200.0, 0.0],", + " [201.0, 0.0],", + " [201.0, 2.0],", + " [200.0, 2.0],", + " [200.0, 0.0]", + " ]", + " ]", + " },", + " 'properties': {}", + " }", + " ]", + "}"); + + invalidPolygonGeoJsonData = + String.join( + "\n", + "{", + " 'type': 'FeatureCollection',", + " 'features': [", + " {", + " 'id': 'id_invalid',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [100.0, 0.0],", + " [101.0, 0.0],", + " [100.5, 0.5]" + + // Invalid Polygon: not closed + " ]", + " ]", + " },", + " 'properties': {}", + " }", + " ]", + "}"); + + // Replace single quotes with double quotes for JSON compliance + validGeoJsonData = validGeoJsonData.replace("'", "\""); + invalidPolygonGeoJsonData = invalidPolygonGeoJsonData.replace("'", "\""); + } + + @Before + public void setUp() { + noticeContainer = new NoticeContainer(); + } + + @Test + public void testGtfsGeoJsonFileLoader() /*throws ValidatorLoaderException*/ { + + var container = createLoader(validGeoJsonData); + var geoJsonContainer = (GtfsGeoJsonFeaturesContainer) container; + assertThat(container).isNotNull(); + assertThat(container.getTableStatus()).isEqualTo(TableStatus.PARSABLE_HEADERS_AND_ROWS); + assertThat(geoJsonContainer.entityCount()).isEqualTo(2); + assertThat(geoJsonContainer.getEntities().get(0).featureId()).isEqualTo("id1"); + assertThat(geoJsonContainer.getEntities().get(1).featureId()).isEqualTo("id2"); + } + + @Test + public void testBrokenJson() { + var container = createLoader("This is a broken json"); + assertThat(container.entityCount()).isEqualTo(0); + } + + @Test + public void testInvalidPolygonGeometry() { + // Testing for invalid polygon where coordinates do not form a closed ring + var container = createLoader(invalidPolygonGeoJsonData); + + // Check if the container is in the correct state + assertThat(container.getTableStatus()).isEqualTo(TableStatus.UNPARSABLE_ROWS); + + // Check if the correct validation notice is generated for the invalid geometry + List notices = + noticeContainer.getValidationNotices().stream() + .filter(InvalidGeometryNotice.class::isInstance) + .map(InvalidGeometryNotice.class::cast) + .collect(Collectors.toList()); + + assertThat(notices.size()).isGreaterThan(0); + } + + private GtfsEntityContainer createLoader(String jsonData) { + GeoJsonFileLoader loader = new GeoJsonFileLoader(); + var fileDescriptor = new GtfsGeoJsonFileDescriptor(); + InputStream inputStream = new ByteArrayInputStream(jsonData.getBytes(StandardCharsets.UTF_8)); + return loader.load(fileDescriptor, null, inputStream, noticeContainer); + } +} From ac3bf77164cc87aa8cc50a951b891ef299389cdb Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Mon, 27 Jan 2025 13:35:38 -0500 Subject: [PATCH 7/8] formatted code --- .../table/GeoJsonFileLoaderTest.java | 136 +++++++++--------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java index 33b69fe0c6..93325594e4 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoaderTest.java @@ -27,72 +27,72 @@ public class GeoJsonFileLoaderTest { public static void setUpBeforeClass() { // Create the valid and invalid JSON data strings, using single quotes for readability. validGeoJsonData = - String.join( - "\n", - "{", - " 'type': 'FeatureCollection',", - " 'features': [", - " {", - " 'id': 'id1',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [100.0, 0.0],", - " [101.0, 0.0],", - " [101.0, 1.0],", - " [100.0, 1.0],", - " [100.0, 0.0]", - " ]", - " ]", - " },", - " 'properties': {}", - " },", - " {", - " 'id': 'id2',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [200.0, 0.0],", - " [201.0, 0.0],", - " [201.0, 2.0],", - " [200.0, 2.0],", - " [200.0, 0.0]", - " ]", - " ]", - " },", - " 'properties': {}", - " }", - " ]", - "}"); + String.join( + "\n", + "{", + " 'type': 'FeatureCollection',", + " 'features': [", + " {", + " 'id': 'id1',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [100.0, 0.0],", + " [101.0, 0.0],", + " [101.0, 1.0],", + " [100.0, 1.0],", + " [100.0, 0.0]", + " ]", + " ]", + " },", + " 'properties': {}", + " },", + " {", + " 'id': 'id2',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [200.0, 0.0],", + " [201.0, 0.0],", + " [201.0, 2.0],", + " [200.0, 2.0],", + " [200.0, 0.0]", + " ]", + " ]", + " },", + " 'properties': {}", + " }", + " ]", + "}"); invalidPolygonGeoJsonData = - String.join( - "\n", - "{", - " 'type': 'FeatureCollection',", - " 'features': [", - " {", - " 'id': 'id_invalid',", - " 'type': 'Feature',", - " 'geometry': {", - " 'type': 'Polygon',", - " 'coordinates': [", - " [", - " [100.0, 0.0],", - " [101.0, 0.0],", - " [100.5, 0.5]" - + // Invalid Polygon: not closed - " ]", - " ]", - " },", - " 'properties': {}", - " }", - " ]", - "}"); + String.join( + "\n", + "{", + " 'type': 'FeatureCollection',", + " 'features': [", + " {", + " 'id': 'id_invalid',", + " 'type': 'Feature',", + " 'geometry': {", + " 'type': 'Polygon',", + " 'coordinates': [", + " [", + " [100.0, 0.0],", + " [101.0, 0.0],", + " [100.5, 0.5]" + + // Invalid Polygon: not closed + " ]", + " ]", + " },", + " 'properties': {}", + " }", + " ]", + "}"); // Replace single quotes with double quotes for JSON compliance validGeoJsonData = validGeoJsonData.replace("'", "\""); @@ -132,10 +132,10 @@ public void testInvalidPolygonGeometry() { // Check if the correct validation notice is generated for the invalid geometry List notices = - noticeContainer.getValidationNotices().stream() - .filter(InvalidGeometryNotice.class::isInstance) - .map(InvalidGeometryNotice.class::cast) - .collect(Collectors.toList()); + noticeContainer.getValidationNotices().stream() + .filter(InvalidGeometryNotice.class::isInstance) + .map(InvalidGeometryNotice.class::cast) + .collect(Collectors.toList()); assertThat(notices.size()).isGreaterThan(0); } From 202ae902037267dfc9fe52c7cd95a5be781a69ec Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Tue, 28 Jan 2025 11:00:48 -0500 Subject: [PATCH 8/8] removed UnparsableGeoJsonFeatureNotice --- .../UnparsableGeoJsonFeatureNotice.java | 20 ------------------- .../table/GeoJsonFileLoader.java | 2 -- 2 files changed, 22 deletions(-) delete mode 100644 core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java deleted file mode 100644 index 253155723e..0000000000 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/UnparsableGeoJsonFeatureNotice.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.mobilitydata.gtfsvalidator.notice; - -import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; - -import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; - -/** A GeoJSON feature of locations.geojson could not be parsed. */ -@GtfsValidationNotice(severity = ERROR) -public class UnparsableGeoJsonFeatureNotice extends ValidationNotice { - /** The name of the faulty file. */ - private final String filename; - - /** The detailed message describing the error. */ - private final String message; - - public UnparsableGeoJsonFeatureNotice(String filename, String message) { - this.filename = filename; - this.message = message; - } -} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java index e3639bce6e..9e6f4960ff 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GeoJsonFileLoader.java @@ -48,8 +48,6 @@ public GtfsEntityContainer load( noticeContainer.addSystemError(new IOError(ioex)); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (UnparsableGeoJsonFeatureException ugex) { - noticeContainer.addValidationNotice( - new UnparsableGeoJsonFeatureNotice(GtfsGeoJsonFeature.FILENAME, ugex.getMessage())); logger.atSevere().withCause(ugex).log("Unparsable GeoJSON feature"); return fileDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } catch (Exception ex) {