-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add netexMode and netexSubmode to GTFS GraphQL Trip type #6676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev-2.x
Are you sure you want to change the base?
Changes from all commits
edbc331
7085af9
29be6f3
40ea82e
93111a7
9d8c619
7a1b78c
73e3b26
e1b3b2c
809b2dc
cba5329
7b4819c
770ed57
cee387f
5225f2b
986a2fd
5fb3eb5
f64dd21
fcbd9d0
e4fa718
2b8d505
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package org.opentripplanner.model; | ||
|
||
import java.util.Arrays; | ||
|
||
public enum FeedType { | ||
GTFS("GTFS"), | ||
NETEX("NeTEx"); | ||
|
||
private final String value; | ||
|
||
FeedType(String value) { | ||
this.value = value; | ||
} | ||
|
||
public String getValue() { | ||
return value; | ||
} | ||
|
||
public static FeedType of(String value) { | ||
return Arrays.stream(FeedType.values()) | ||
.filter(ft -> ft.getValue().equals(value)) | ||
.findFirst() | ||
.orElse(null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.opentripplanner.model.impl; | ||
|
||
import org.opentripplanner.model.FeedType; | ||
|
||
/** | ||
* The key part of a row in submode-mapping.csv, consisting of a feed type (GTFS or NeTEx) | ||
* and a label, which is Route.type in GTFS and Trip.submode in NeTEx. | ||
* | ||
* @see SubmodeMappingService | ||
*/ | ||
public record SubmodeMappingMatcher(FeedType inputFeedType, String inputLabel) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package org.opentripplanner.model.impl; | ||
|
||
import com.csvreader.CsvReader; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does this dependency come from? Should we explicitly import it in pom (if it is not already)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Application/pom.xml
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.annotation.Nullable; | ||
import org.opentripplanner.datastore.api.DataSource; | ||
import org.opentripplanner.framework.application.OtpAppException; | ||
import org.opentripplanner.graph_builder.GraphBuilderDataSources; | ||
import org.opentripplanner.graph_builder.model.GraphBuilderModule; | ||
import org.opentripplanner.model.FeedType; | ||
import org.opentripplanner.transit.model.basic.TransitMode; | ||
import org.opentripplanner.transit.service.TimetableRepository; | ||
|
||
/** | ||
* Part of infra to map GTFS and NeTEx Trip.replacementMode similarly. | ||
* | ||
* @see SubmodeMappingService | ||
*/ | ||
public class SubmodeMappingModule implements GraphBuilderModule { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Javadoc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it feels a bit weird that this is both a dagger module and a GraphBuilderModule. |
||
|
||
private static final String INPUT_FEED_TYPE = "Input feed type"; | ||
private static final String INPUT_LABEL = "Input label"; | ||
private static final String NETEX_SUBMODE = "NeTEx submode"; | ||
private static final String REPLACEMENT_MODE = "Replacement mode"; | ||
private static final String ORIGINAL_MODE = "Original mode"; | ||
private static final String GTFS_REPLACEMENT_MODE = "GTFS replacement mode"; | ||
private static final String GTFS_REPLACEMENT_TYPE = "GTFS replacement type"; | ||
private static final String[] MANDATORY = { INPUT_FEED_TYPE, INPUT_LABEL }; | ||
|
||
private final TimetableRepository timetableRepository; | ||
|
||
@Nullable | ||
private final DataSource dataSource; | ||
|
||
public SubmodeMappingModule( | ||
GraphBuilderDataSources graphBuilderDataSources, | ||
TimetableRepository timetableRepository | ||
) { | ||
this.timetableRepository = timetableRepository; | ||
this.dataSource = graphBuilderDataSources.getSubmodeMappingDataSource().orElse(null); | ||
} | ||
|
||
private boolean isEmpty(@Nullable String string) { | ||
return string == null || string.isEmpty(); | ||
} | ||
|
||
private Map<SubmodeMappingMatcher, SubmodeMappingRow> read(DataSource dataSource) { | ||
var map = new HashMap<SubmodeMappingMatcher, SubmodeMappingRow>(); | ||
try { | ||
var reader = new CsvReader(dataSource.asInputStream(), StandardCharsets.UTF_8); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check dataSource.exists() here before we use it? |
||
reader.readHeaders(); | ||
var headers = reader.getHeaders(); | ||
for (var header : MANDATORY) { | ||
if (Arrays.stream(headers).noneMatch(h -> h.equals(header))) { | ||
throw new OtpAppException("submode mapping header not found: " + header); | ||
} | ||
} | ||
while (reader.readRecord()) { | ||
var inputFeedType = FeedType.of(reader.get(INPUT_FEED_TYPE)); | ||
if (inputFeedType == null) { | ||
throw new OtpAppException( | ||
"not a valid submode mapping feed type: " + reader.get(INPUT_FEED_TYPE) | ||
); | ||
} | ||
var inputLabel = reader.get(INPUT_LABEL); | ||
var netexSubmode = reader.get(NETEX_SUBMODE); | ||
var replacementMode = isEmpty(reader.get(REPLACEMENT_MODE)) | ||
? null | ||
: TransitMode.valueOf(reader.get(REPLACEMENT_MODE)); | ||
var originalMode = isEmpty(reader.get(ORIGINAL_MODE)) | ||
? null | ||
: TransitMode.valueOf(reader.get(ORIGINAL_MODE)); | ||
var gtfsReplacementMode = isEmpty(reader.get(GTFS_REPLACEMENT_MODE)) | ||
? null | ||
: TransitMode.valueOf(reader.get(GTFS_REPLACEMENT_MODE)); | ||
var gtfsReplacementType = isEmpty(reader.get(GTFS_REPLACEMENT_TYPE)) | ||
? null | ||
: Integer.parseInt(reader.get(GTFS_REPLACEMENT_TYPE)); | ||
var matcher = new SubmodeMappingMatcher(inputFeedType, inputLabel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the inputFeedType be an enum? |
||
var row = new SubmodeMappingRow( | ||
netexSubmode, | ||
replacementMode, | ||
originalMode, | ||
gtfsReplacementMode, | ||
gtfsReplacementType | ||
); | ||
map.put(matcher, row); | ||
} | ||
} catch (IOException ioe) { | ||
throw new OtpAppException("cannot read submode mapping config file", ioe); | ||
} | ||
return map; | ||
} | ||
|
||
private Map<SubmodeMappingMatcher, SubmodeMappingRow> useDefaultMapping() { | ||
var map = new HashMap<SubmodeMappingMatcher, SubmodeMappingRow>(); | ||
map.put( | ||
new SubmodeMappingMatcher(FeedType.GTFS, "714"), | ||
new SubmodeMappingRow("railReplacementBus", null, TransitMode.RAIL, null, null) | ||
); | ||
map.put( | ||
new SubmodeMappingMatcher(FeedType.NETEX, "railReplacementBus"), | ||
new SubmodeMappingRow("railReplacementBus", TransitMode.BUS, null, TransitMode.BUS, 714) | ||
); | ||
return map; | ||
} | ||
|
||
@Override | ||
public void buildGraph() { | ||
if (dataSource != null) { | ||
timetableRepository.setSubmodeMapping(read(dataSource)); | ||
} else { | ||
timetableRepository.setSubmodeMapping(useDefaultMapping()); | ||
} | ||
} | ||
|
||
@Override | ||
public void checkInputs() { | ||
if (dataSource != null && !dataSource.exists()) { | ||
throw new RuntimeException( | ||
"Submode mapping file " + dataSource.path() + " does not exist or cannot be read." | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is nullable? If so, this should be marked as such and moved downwards a bit.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not. I think it's better and it is definitely easier to instantiate the module and have the service contain an empty map, than try to make sure null handling is correct everywhere.
And it's not even an empty map, because there is a default mapping.