Skip to content

Commit

Permalink
Make OsmModule testable
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardehrenfried committed Jan 16, 2025
1 parent 4849d5b commit a8adf06
Show file tree
Hide file tree
Showing 18 changed files with 245 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.opentripplanner.netex.NetexModule;
import org.opentripplanner.netex.configure.NetexConfigure;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.osm.DefaultOsmProvider;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
Expand Down Expand Up @@ -69,7 +70,7 @@ static OsmModule provideOsmModule(
List<OsmProvider> providers = new ArrayList<>();
for (ConfiguredDataSource<OsmExtractParameters> osmConfiguredDataSource : dataSources.getOsmConfiguredDatasource()) {
providers.add(
new OsmProvider(
new DefaultOsmProvider(
osmConfiguredDataSource.dataSource(),
osmConfiguredDataSource.config().osmTagMapper(),
osmConfiguredDataSource.config().timeZone(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.graph_builder.module.osm.parameters.OsmProcessingParameters;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.osm.DefaultOsmProvider;
import org.opentripplanner.osm.model.OsmLevel;
import org.opentripplanner.osm.model.OsmNode;
import org.opentripplanner.osm.model.OsmWay;
Expand Down Expand Up @@ -112,7 +113,7 @@ public static OsmModuleBuilder of(

@Override
public void buildGraph() {
for (OsmProvider provider : providers) {
for (var provider : providers) {
LOG.info("Gathering OSM from provider: {}", provider);
LOG.info(
"Using OSM way configuration from {}.",
Expand All @@ -130,7 +131,7 @@ public void buildGraph() {

@Override
public void checkInputs() {
for (OsmProvider provider : providers) {
for (var provider : providers) {
provider.checkInputs();
}
}
Expand Down Expand Up @@ -613,7 +614,7 @@ private StreetEdge getEdgeForStreet(

private float getMaxCarSpeed() {
float maxSpeed = 0f;
for (OsmProvider provider : providers) {
for (var provider : providers) {
var carSpeed = provider.getOsmTagMapper().getMaxUsedCarSpeed(provider.getWayPropertySet());
if (carSpeed > maxSpeed) {
maxSpeed = carSpeed;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.opentripplanner.osm;

import crosby.binary.file.BlockInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import org.opentripplanner.datastore.api.DataSource;
import org.opentripplanner.datastore.api.FileType;
import org.opentripplanner.datastore.file.FileDataSource;
import org.opentripplanner.framework.application.OtpFileNames;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.module.osm.OsmDatabase;
import org.opentripplanner.osm.tagmapping.OsmTagMapper;
import org.opentripplanner.osm.tagmapping.OsmTagMapperSource;
import org.opentripplanner.osm.wayproperty.WayPropertySet;
import org.opentripplanner.utils.logging.ProgressTracker;
import org.opentripplanner.utils.tostring.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Parser for the OpenStreetMap PBF format. Parses files in three passes: First the relations, then
* the ways, then the nodes are also loaded.
*/
public class DefaultOsmProvider implements OsmProvider {

private static final Logger LOG = LoggerFactory.getLogger(DefaultOsmProvider.class);

private final DataSource source;
private final boolean cacheDataInMem;

private final ZoneId zoneId;

private boolean hasWarnedAboutMissingTimeZone = false;

private final OsmTagMapper osmTagMapper;

private final WayPropertySet wayPropertySet;
private byte[] cachedBytes = null;

/** For tests */
public DefaultOsmProvider(File file, boolean cacheDataInMem) {
this(
new FileDataSource(file, FileType.OSM),
OsmTagMapperSource.DEFAULT,
null,
cacheDataInMem,
DataImportIssueStore.NOOP
);
}

public DefaultOsmProvider(
DataSource dataSource,
OsmTagMapperSource tagMapperSource,
ZoneId zoneId,
boolean cacheDataInMem,
DataImportIssueStore issueStore
) {
this.source = dataSource;
this.zoneId = zoneId;
this.osmTagMapper = tagMapperSource.getInstance();
this.wayPropertySet = new WayPropertySet(issueStore);
osmTagMapper.populateProperties(wayPropertySet);
this.cacheDataInMem = cacheDataInMem;
}

public void readOsm(OsmDatabase osmdb) {
try {
OsmParser parser = new OsmParser(osmdb, this);

parsePhase(parser, OsmParserPhase.Relations);
osmdb.doneFirstPhaseRelations();

parsePhase(parser, OsmParserPhase.Ways);
osmdb.doneSecondPhaseWays();

parsePhase(parser, OsmParserPhase.Nodes);
osmdb.doneThirdPhaseNodes();
} catch (Exception ex) {
throw new IllegalStateException("error loading OSM from path " + source.path(), ex);
}
}

@Override
public String toString() {
return ToStringBuilder
.of(DefaultOsmProvider.class)
.addObj("source", source)
.addBool("cacheDataInMem", cacheDataInMem)
.toString();
}

public void checkInputs() {
if (!source.exists()) {
throw new RuntimeException("Can't read OSM path: " + source.path());
}
}

@SuppressWarnings("Convert2MethodRef")
private static InputStream track(OsmParserPhase phase, long size, InputStream inputStream) {
// Keep logging lambda, replacing it with a method-ref will cause the
// logging to report incorrect class and line number
return ProgressTracker.track("Parse OSM " + phase, 1000, size, inputStream, m -> LOG.info(m));
}

private void parsePhase(OsmParser parser, OsmParserPhase phase) throws IOException {
parser.setPhase(phase);
BlockInputStream in = null;
try {
in = new BlockInputStream(createInputStream(phase), parser);
in.process();
} finally {
// Close
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}

private InputStream createInputStream(OsmParserPhase phase) {
if (cacheDataInMem) {
if (cachedBytes == null) {
cachedBytes = source.asBytes();
}
return track(phase, cachedBytes.length, new ByteArrayInputStream(cachedBytes));
}
return track(phase, source.size(), source.asInputStream());
}

public ZoneId getZoneId() {
if (zoneId == null) {
if (!hasWarnedAboutMissingTimeZone) {
hasWarnedAboutMissingTimeZone = true;
LOG.warn(
"Missing time zone for OSM source {} - time-restricted entities will " +
"not be created, please configure it in the {}",
source.uri(),
OtpFileNames.BUILD_CONFIG_FILENAME
);
}
}
return zoneId;
}

public OsmTagMapper getOsmTagMapper() {
return osmTagMapper;
}

public WayPropertySet getWayPropertySet() {
return wayPropertySet;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ class OsmParser extends BinaryParser {

private final OsmDatabase osmdb;
private final Map<String, String> stringTable = new HashMap<>();
private final OsmProvider provider;
private final DefaultOsmProvider provider;
private OsmParserPhase parsePhase;

public OsmParser(OsmDatabase osmdb, OsmProvider provider) {
public OsmParser(OsmDatabase osmdb, DefaultOsmProvider provider) {
this.osmdb = Objects.requireNonNull(osmdb);
this.provider = Objects.requireNonNull(provider);
}
Expand Down
152 changes: 6 additions & 146 deletions application/src/main/java/org/opentripplanner/osm/OsmProvider.java
Original file line number Diff line number Diff line change
@@ -1,158 +1,18 @@
package org.opentripplanner.osm;

import crosby.binary.file.BlockInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import org.opentripplanner.datastore.api.DataSource;
import org.opentripplanner.datastore.api.FileType;
import org.opentripplanner.datastore.file.FileDataSource;
import org.opentripplanner.framework.application.OtpFileNames;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.module.osm.OsmDatabase;
import org.opentripplanner.osm.tagmapping.OsmTagMapper;
import org.opentripplanner.osm.tagmapping.OsmTagMapperSource;
import org.opentripplanner.osm.wayproperty.WayPropertySet;
import org.opentripplanner.utils.logging.ProgressTracker;
import org.opentripplanner.utils.tostring.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Parser for the OpenStreetMap PBF format. Parses files in three passes: First the relations, then
* the ways, then the nodes are also loaded.
*/
public class OsmProvider {
public interface OsmProvider {
void readOsm(OsmDatabase osmdb);

private static final Logger LOG = LoggerFactory.getLogger(OsmProvider.class);
OsmTagMapper getOsmTagMapper();

private final DataSource source;
private final boolean cacheDataInMem;
void checkInputs();

private final ZoneId zoneId;
WayPropertySet getWayPropertySet();

private boolean hasWarnedAboutMissingTimeZone = false;

private final OsmTagMapper osmTagMapper;

private final WayPropertySet wayPropertySet;
private byte[] cachedBytes = null;

/** For tests */
public OsmProvider(File file, boolean cacheDataInMem) {
this(
new FileDataSource(file, FileType.OSM),
OsmTagMapperSource.DEFAULT,
null,
cacheDataInMem,
DataImportIssueStore.NOOP
);
}

public OsmProvider(
DataSource dataSource,
OsmTagMapperSource tagMapperSource,
ZoneId zoneId,
boolean cacheDataInMem,
DataImportIssueStore issueStore
) {
this.source = dataSource;
this.zoneId = zoneId;
this.osmTagMapper = tagMapperSource.getInstance();
this.wayPropertySet = new WayPropertySet(issueStore);
osmTagMapper.populateProperties(wayPropertySet);
this.cacheDataInMem = cacheDataInMem;
}

public void readOsm(OsmDatabase osmdb) {
try {
OsmParser parser = new OsmParser(osmdb, this);

parsePhase(parser, OsmParserPhase.Relations);
osmdb.doneFirstPhaseRelations();

parsePhase(parser, OsmParserPhase.Ways);
osmdb.doneSecondPhaseWays();

parsePhase(parser, OsmParserPhase.Nodes);
osmdb.doneThirdPhaseNodes();
} catch (Exception ex) {
throw new IllegalStateException("error loading OSM from path " + source.path(), ex);
}
}

@Override
public String toString() {
return ToStringBuilder
.of(OsmProvider.class)
.addObj("source", source)
.addBool("cacheDataInMem", cacheDataInMem)
.toString();
}

public void checkInputs() {
if (!source.exists()) {
throw new RuntimeException("Can't read OSM path: " + source.path());
}
}

@SuppressWarnings("Convert2MethodRef")
private static InputStream track(OsmParserPhase phase, long size, InputStream inputStream) {
// Keep logging lambda, replacing it with a method-ref will cause the
// logging to report incorrect class and line number
return ProgressTracker.track("Parse OSM " + phase, 1000, size, inputStream, m -> LOG.info(m));
}

private void parsePhase(OsmParser parser, OsmParserPhase phase) throws IOException {
parser.setPhase(phase);
BlockInputStream in = null;
try {
in = new BlockInputStream(createInputStream(phase), parser);
in.process();
} finally {
// Close
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}

private InputStream createInputStream(OsmParserPhase phase) {
if (cacheDataInMem) {
if (cachedBytes == null) {
cachedBytes = source.asBytes();
}
return track(phase, cachedBytes.length, new ByteArrayInputStream(cachedBytes));
}
return track(phase, source.size(), source.asInputStream());
}

public ZoneId getZoneId() {
if (zoneId == null) {
if (!hasWarnedAboutMissingTimeZone) {
hasWarnedAboutMissingTimeZone = true;
LOG.warn(
"Missing time zone for OSM source {} - time-restricted entities will " +
"not be created, please configure it in the {}",
source.uri(),
OtpFileNames.BUILD_CONFIG_FILENAME
);
}
}
return zoneId;
}

public OsmTagMapper getOsmTagMapper() {
return osmTagMapper;
}

public WayPropertySet getWayPropertySet() {
return wayPropertySet;
}
ZoneId getZoneId();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.framework.i18n.TranslatedString;
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.osm.DefaultOsmProvider;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.transit.model.basic.Accessibility;
Expand Down
Loading

0 comments on commit a8adf06

Please sign in to comment.