diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/MTAStationAccessibilityStrategy.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/MTAStationAccessibilityStrategy.java index 16d701e7c..b22fd7201 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/MTAStationAccessibilityStrategy.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/MTAStationAccessibilityStrategy.java @@ -31,7 +31,7 @@ import java.util.*; import static org.onebusaway.gtfs_transformer.csv.CSVUtil.readCsv; -import static org.onebusaway.gtfs_transformer.impl.MTAEntrancesStrategy.WHEELCHAIR_ACCESSIBLE; +import static org.onebusaway.gtfs_transformer.csv.MTAStation.*; /** * Based on a CSV of MTAStations set the associated stops accessible as specified. @@ -77,18 +77,24 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { "Entrances file does not exist: " + stationsFile.getName()); } - // we have a file, load the contents + // see MTAStationAccessibilityStrategyTest for discussion of how this works List stations = getStations(); for (MTAStation station : stations) { - if (station.getAda() == MTAStation.ADA_FULLY_ACCESSIBLE) { - markStopAccessible(dao, station.getStopId(), "N"); - markStopAccessible(dao, station.getStopId(), "S"); - } else if (station.getAda() == MTAStation.ADA_PARTIALLY_ACCESSIBLE) { - if (station.getAdaNorthBound() == WHEELCHAIR_ACCESSIBLE) { - markStopAccessible(dao, station.getStopId(), "N"); + markStopAccessible(dao, station.getStopId(), "", station.getAda()); + if (ADA_NOT_ACCESSIBLE == station.getAda() + || ADA_FULLY_ACCESSIBLE == station.getAda()) { + markStopAccessible(dao, station.getStopId(), "N", station.getAda()); + markStopAccessible(dao, station.getStopId(), "S", station.getAda()); + } else if (ADA_PARTIALLY_ACCESSIBLE == station.getAda()) { + if (station.getAdaNorthBound() < 0) { + markStopAccessible(dao, station.getStopId(), "N", ADA_NOT_ACCESSIBLE); + } else { + markStopAccessible(dao, station.getStopId(), "N", station.getAdaNorthBound()); } - if (station.getAdaSouthBound() == WHEELCHAIR_ACCESSIBLE) { - markStopAccessible(dao, station.getStopId(), "S"); + if (station.getAdaSouthBound() < 0) { + markStopAccessible(dao, station.getStopId(), "S", ADA_NOT_ACCESSIBLE); + } else { + markStopAccessible(dao, station.getStopId(), "S", station.getAdaSouthBound()); } } } @@ -101,14 +107,15 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { } - private void markStopAccessible(GtfsMutableRelationalDao dao, String stopId, String compassDirection) { + private void markStopAccessible(GtfsMutableRelationalDao dao, String stopId, String compassDirection, + int accessibilityQualifier) { String unqualifedStopId = stopId + compassDirection; Stop stopForId = idToStopMap.get(unqualifedStopId); if (stopForId == null) { _log.error("no such stop for stopId {}", unqualifedStopId); return; } - stopForId.setWheelchairBoarding(WHEELCHAIR_ACCESSIBLE); + stopForId.setWheelchairBoarding(accessibilityQualifier); this.accessibleStops.add(stopForId); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java new file mode 100644 index 000000000..68eccf6c4 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2023 Cambridge Systematics, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs_transformer.updates; + +import org.junit.Before; +import org.junit.Test; +import org.onebusaway.gtfs.model.AgencyAndId; +import org.onebusaway.gtfs.model.Stop; +import org.onebusaway.gtfs.services.GtfsRelationalDao; +import org.onebusaway.gtfs_transformer.AbstractTestSupport; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Document and test partial and fully accessible MTAStations. + */ +public class MTAStationAccessibilityStrategyTest extends AbstractTestSupport { + + @Before + public void setup() throws Exception { + File stations_csv = new File(getClass().getResource( + "/org/onebusaway/gtfs_transformer/stations/stations.csv").toURI()); + addModification("{\"op\": \"transform\", \"class\":\"org.onebusaway.gtfs_transformer.impl.MTAStationAccessibilityStrategy\", \"stations_csv\": \""+stations_csv+"\"}"); + } + + @Test + public void testStations() throws Exception { + _gtfs.putAgencies(1); + _gtfs.putNamedStops("R15,49 St", "A25,50 St", "233,Hoyt St", "626,86 St", + "R15N,49 St", "A25N,50 St", "233N,Hoyt St", "626N,86 St", + "R15S,49 St", "A25S,50 St", "233S,Hoyt St", "626S,86 St"); + _gtfs.putRoutes(1); + _gtfs.putTrips(12, "r0", "sid0"); + _gtfs.putStopTimes("t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11", "R15,A25,233,626,R15N,A25N,233N,626N,R15S,A25S,233S,626S"); + _gtfs.putCalendars(1, "start_date=20120903", "end_date=20120916"); + + GtfsRelationalDao dao = transform(); + assertStation(dao, "R15", 2, 1, 0); // 49 St (ADA=2, ADA NB=1, ADA SB=0) + assertStation(dao, "A25", 2, 0, 1); // 50 St (ADA=2, ADA NB=0, ADA SB=1 + assertStation(dao, "233", 2, null, null); // Hoyt St (ADA=2, ADA NB=blank, ADA SB=blank) + assertStation(dao, "626", 2, 2, 0); // 86 St (ADA=2, ADA NB=2, ADA SB=0) + } + + private void assertStation(GtfsRelationalDao dao, String stopId, int ada, Integer northBoundFlag, Integer southBoundFlag) { + Stop parentStop = dao.getStopForId(new AgencyAndId("a0", stopId)); + assertNotNull(parentStop); + Stop nStop = dao.getStopForId(new AgencyAndId("a0", stopId+"N")); + assertNotNull(nStop); + Stop sStop = dao.getStopForId(new AgencyAndId("a0", stopId+"S")); + assertNotNull(sStop); + + assertEquals("expecting ada flag to match wheelchairBoarding flag for stop " + parentStop.getId(), + ada, parentStop.getWheelchairBoarding()); + if (northBoundFlag == null) { + assertEquals("expecting N/A wheelchairBoarding for northbound stop " + nStop,0, nStop.getWheelchairBoarding()); // default is 0 + } else { + assertEquals("expecting northBoundFlag to match wheelchairBoarding flag for stop" + nStop, northBoundFlag.intValue(), nStop.getWheelchairBoarding()); + } + if (southBoundFlag == null) { + assertEquals("expecting N/A wheelchairBoarding for southbound stop " + sStop,0, sStop.getWheelchairBoarding()); + } else { + assertEquals("expecting southBoundFlag to match wheelchairBoarding flag for stop" + sStop, southBoundFlag.intValue(), sStop.getWheelchairBoarding()); + } + } +} diff --git a/onebusaway-gtfs-transformer/src/test/resources/org/onebusaway/gtfs_transformer/stations/stations.csv b/onebusaway-gtfs-transformer/src/test/resources/org/onebusaway/gtfs_transformer/stations/stations.csv new file mode 100644 index 000000000..fa95d282e --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/resources/org/onebusaway/gtfs_transformer/stations/stations.csv @@ -0,0 +1,5 @@ +Station ID,Complex ID,GTFS Stop ID,Division,Line,Stop Name,Borough,Daytime Routes,Structure,GTFS Latitude,GTFS Longitude,North Direction Label,South Direction Label,ADA,ADA Direction Notes,ADA NB,ADA SB,Capital Outage NB,Capital Outage SB +10,10,R15,BMT,Broadway - Brighton,49 St,M,N R W,Subway,40.759901,-73.984139,Uptown & Queens,Downtown & Brooklyn,2,Uptown & Queens,1,0,, +162,162,A25,IND,8th Av - Fulton St,50 St,M,C E,Subway,40.762456,-73.985984,Uptown - Queens,Downtown & Brooklyn,2,Downtown & Brooklyn only,0,1,, +336,336,233,IRT,Eastern Pky,Hoyt St,Bk,2 3,Subway,40.690545,-73.985065,Manhattan,Flatbush - New Lots,2,,,,, +397,397,626,IRT,Lexington Av,86 St,M,4 5 6,Subway,40.779492,-73.955589,Uptown & The Bronx,Downtown & Brooklyn,2,Uptown & The Bronx local only,2,0,, diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/MockGtfs.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/MockGtfs.java index 268274a53..844cb3dfc 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/MockGtfs.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/MockGtfs.java @@ -164,6 +164,36 @@ public void putStops(int numberOfRows, String... columns) { putFile("stops.txt", b.build()); } + /** + * specify stop_id and stop_name, have stop generated with fake lat/lon + * @param rows + */ + public void putNamedStops(String...rows) { + int numberOfRows = rows.length; + List stopIds = new ArrayList<>(); + List stopNames = new ArrayList<>(); + List stopLats = new ArrayList(); + List stopLons = new ArrayList(); + TableBuilder b = new TableBuilder(numberOfRows); + for (String column : rows) { + String[] parts = column.split(","); + stopIds.add(parts[0]); + stopNames.add(parts[1]); + } + for (int i = 0; i < numberOfRows; ++i) { + double lat = 47.65383950857904 + 0.004 * i; + double lon = -122.30782950811766; + stopLats.add(Double.toString(lat)); + stopLons.add(Double.toString(lon)); + } + + b.addColumnSpec("stop_id", stopIds); + b.addColumnSpec("stop_name", stopNames); + b.addColumnSpec("stop_lat", stopLats); + b.addColumnSpec("stop_lon", stopLons); + putFile("stops.txt", b.build()); + } + public void putDefaultStops() { putDefaultAgencies(); putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon",