-
Notifications
You must be signed in to change notification settings - Fork 457
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adding a railway osm reader, similar to the existing highway approach
- Loading branch information
Showing
6 changed files
with
276 additions
and
5 deletions.
There are no files selected for viewing
143 changes: 143 additions & 0 deletions
143
contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/OsmRailwayParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package org.matsim.contrib.osm.networkReader; | ||
|
||
import de.topobyte.osm4j.core.model.iface.OsmNode; | ||
import de.topobyte.osm4j.core.model.iface.OsmWay; | ||
import de.topobyte.osm4j.core.model.util.OsmModelUtil; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.matsim.api.core.v01.Coord; | ||
import org.matsim.core.utils.geometry.CoordinateTransformation; | ||
|
||
import java.nio.file.Path; | ||
import java.text.NumberFormat; | ||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.function.BiPredicate; | ||
import java.util.stream.Collectors; | ||
|
||
class OsmRailwayParser extends OsmNetworkParser { | ||
|
||
private static final Logger log = LogManager.getLogger(OsmRailwayParser.class); | ||
private static final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.UK); | ||
|
||
private final CoordinateTransformation transformation; | ||
private final Map<String, LinkProperties> linkProperties; | ||
private final BiPredicate<Coord, Integer> linkFilter; | ||
final ExecutorService executor; | ||
Map<Long, ProcessedOsmWay> ways; | ||
Map<Long, ProcessedOsmNode> nodes; | ||
Map<Long, List<ProcessedOsmWay>> nodeReferences; | ||
|
||
OsmRailwayParser(CoordinateTransformation transformation, Map<String, LinkProperties> linkProperties, BiPredicate<Coord, Integer> linkFilter, ExecutorService executor) { | ||
super(transformation, linkProperties, linkFilter, executor); | ||
|
||
this.transformation = transformation; | ||
this.linkProperties = linkProperties; | ||
this.linkFilter = linkFilter; | ||
this.executor = executor; | ||
} | ||
|
||
public Map<Long, ProcessedOsmWay> getWays() { | ||
return ways; | ||
} | ||
|
||
public Map<Long, ProcessedOsmNode> getNodes() { | ||
return nodes; | ||
} | ||
|
||
void parse(Path inputFile) { | ||
|
||
// make sure we have empty collections | ||
ways = new ConcurrentHashMap<>(); | ||
nodes = new ConcurrentHashMap<>(); | ||
nodeReferences = new ConcurrentHashMap<>(); | ||
|
||
new PbfParser.Builder() | ||
.setWaysHandler(this::handleWay) | ||
.setExecutor(executor) | ||
.build() | ||
.parse(inputFile); | ||
|
||
log.info("Finished reading ways"); | ||
log.info("Starting to read nodes"); | ||
|
||
new PbfParser.Builder() | ||
.setNodeHandler(this::handleNode) | ||
.setExecutor(executor) | ||
.build() | ||
.parse(inputFile); | ||
|
||
log.info("finished reading nodes"); | ||
} | ||
|
||
void handleNode(OsmNode osmNode) { | ||
|
||
if (nodeReferences.containsKey(osmNode.getId())) { | ||
|
||
List<ProcessedOsmWay> waysThatReferenceNode = nodeReferences.get(osmNode.getId()); | ||
Coord transformedCoord = transformation.transform(new Coord(osmNode.getLongitude(), osmNode.getLatitude())); | ||
|
||
// 'testWhetherReferencingLinksAreInFilter' may be expensive because it might include a test whether the | ||
// supplied coordinate is within a shape. Therefore we want to test as few cases as possible. Two cases are tested anyway: | ||
// 1. if more than one way references this node, this node is an intersection and a possible end node for a | ||
// matsim link. | ||
// 2. if this node is either the start- or end node of a referencing way which makes it a candidate for a node | ||
// in a matsim link as well | ||
// | ||
// if a way has both ends outside the filter and no intersections within the filter it will not be included | ||
// in the final network. I think this is unlikely in real world scenarios, so I think we can live with this | ||
// to achieve faster execution | ||
List<ProcessedOsmWay> filteredReferencingLinks; | ||
if (waysThatReferenceNode.size() > 1 || isEndNodeOfReferencingLink(osmNode, waysThatReferenceNode.get(0))) | ||
filteredReferencingLinks = testWhetherReferencingLinksAreInFilter(transformedCoord, waysThatReferenceNode); | ||
else | ||
filteredReferencingLinks = Collections.emptyList(); | ||
|
||
ProcessedOsmNode result = new ProcessedOsmNode(osmNode.getId(), filteredReferencingLinks, transformedCoord); | ||
this.nodes.put(result.getId(), result); | ||
|
||
if (nodes.size() % 100000 == 0) { | ||
log.info("Added " + numberFormat.format(nodes.size()) + " nodes"); | ||
} | ||
} | ||
} | ||
|
||
void handleWay(OsmWay osmWay) { | ||
|
||
Map<String, String> tags = OsmModelUtil.getTagsAsMap(osmWay); | ||
|
||
if (isStreetOfInterest(tags)) { | ||
LinkProperties linkProperty = linkProperties.get(tags.get(OsmTags.RAILWAY)); | ||
ProcessedOsmWay processedWay = ProcessedOsmWay.create(osmWay, tags, linkProperty); | ||
ways.put(osmWay.getId(), processedWay); | ||
|
||
// keep track of which node is referenced by which way | ||
for (int i = 0; i < osmWay.getNumberOfNodes(); i++) { | ||
|
||
long nodeId = osmWay.getNodeId(i); | ||
nodeReferences.computeIfAbsent(nodeId, id -> Collections.synchronizedList(new ArrayList<>())) | ||
.add(processedWay); | ||
} | ||
|
||
if (ways.size() % 10000 == 0) { | ||
log.info("Added " + numberFormat.format(ways.size()) + " ways"); | ||
} | ||
} | ||
} | ||
|
||
private boolean isStreetOfInterest(Map<String, String> tags) { | ||
return tags.containsKey(OsmTags.RAILWAY) && linkProperties.containsKey(tags.get(OsmTags.RAILWAY)); | ||
} | ||
|
||
private boolean isEndNodeOfReferencingLink(OsmNode node, ProcessedOsmWay processedOsmWay) { | ||
return processedOsmWay.getEndNodeId() == node.getId() || processedOsmWay.getStartNode() == node.getId(); | ||
} | ||
|
||
private List<ProcessedOsmWay> testWhetherReferencingLinksAreInFilter(Coord coord, List<ProcessedOsmWay> waysThatReferenceNode) { | ||
|
||
return waysThatReferenceNode.stream() | ||
.filter(way -> linkFilter.test(coord, way.getLinkProperties().hierarchyLevel)) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/OsmRailwayReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package org.matsim.contrib.osm.networkReader; | ||
|
||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.Executors; | ||
import java.util.function.BiPredicate; | ||
import java.util.function.Predicate; | ||
|
||
import org.matsim.api.core.v01.Coord; | ||
import org.matsim.api.core.v01.network.Link; | ||
import org.matsim.core.network.NetworkUtils; | ||
|
||
public final class OsmRailwayReader extends SupersonicOsmNetworkReader { | ||
|
||
public OsmRailwayReader(OsmNetworkParser parser, | ||
Predicate<Long> preserveNodeWithId, | ||
BiPredicate<Coord, Integer> includeLinkAtCoordWithHierarchy, | ||
AfterLinkCreated afterLinkCreated, double freeSpeedFactor, double adjustCapacityLength, boolean storeOriginalGeometry) { | ||
|
||
super(parser, preserveNodeWithId, includeLinkAtCoordWithHierarchy, (link, tags, direction) -> handleLink(link, tags, direction, afterLinkCreated), freeSpeedFactor, adjustCapacityLength, storeOriginalGeometry); | ||
} | ||
|
||
private static void handleLink(Link link, Map<String, String> tags, SupersonicOsmNetworkReader.Direction direction, AfterLinkCreated outfacingCallback) { | ||
|
||
String railwayType = tags.get(OsmTags.RAILWAY); | ||
link.getAttributes().putAttribute("osm_way_type", "railway"); | ||
link.getAttributes().putAttribute(NetworkUtils.TYPE, railwayType); | ||
|
||
for (String tag : tags.keySet()) { | ||
link.getAttributes().putAttribute(tag, tags.get(tag)); | ||
} | ||
|
||
setAllowedModes(link, tags); | ||
|
||
outfacingCallback.accept(link, tags, direction); | ||
} | ||
|
||
private static void setAllowedModes(Link link, Map<String, String> tags) { | ||
if (tags.containsKey(OsmTags.SERVICE) && tags.get(OsmTags.SERVICE).equals("yard")) { | ||
Set<String> allowedModes = new HashSet<>(); | ||
link.setAllowedModes(allowedModes); | ||
} else { | ||
// everything else should be allowed for rail | ||
Set<String> allowedModes = new HashSet<>(); | ||
allowedModes.add("rail"); | ||
link.setAllowedModes(allowedModes); | ||
} | ||
} | ||
|
||
@Override | ||
Collection<Link> createLinks(WaySegment segment) { | ||
Collection<Link> links = super.createLinks(segment); | ||
return links; | ||
} | ||
|
||
public static class Builder extends AbstractBuilder<OsmRailwayReader> { | ||
|
||
@Override | ||
OsmRailwayReader createInstance() { | ||
OsmRailwayParser parser = new OsmRailwayParser(coordinateTransformation, linkProperties, includeLinkAtCoordWithHierarchy, Executors.newWorkStealingPool()); | ||
return new OsmRailwayReader(parser, preserveNodeWithId, includeLinkAtCoordWithHierarchy, afterLinkCreated, freeSpeedFactor, adjustCapacityLength, storeOriginalGeometry); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
.../railsim/src/main/java/ch/sbb/matsim/contrib/railsim/prepare/RunRailOsmNetworkReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package ch.sbb.matsim.contrib.railsim.prepare; | ||
|
||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
||
import org.matsim.api.core.v01.network.Network; | ||
import org.matsim.api.core.v01.network.NetworkWriter; | ||
import org.matsim.contrib.osm.networkReader.LinkProperties; | ||
import org.matsim.contrib.osm.networkReader.OsmRailwayReader; | ||
import org.matsim.contrib.osm.networkReader.OsmTags; | ||
import org.matsim.core.network.algorithms.NetworkCleaner; | ||
import org.matsim.core.utils.geometry.CoordinateTransformation; | ||
import org.matsim.core.utils.geometry.transformations.TransformationFactory; | ||
|
||
public class RunRailOsmNetworkReader { | ||
|
||
private static final String inputFile = "path/to/file.osm.pbf"; | ||
private static final String outputFile = "path/to/network.xml.gz"; | ||
|
||
private static final CoordinateTransformation coordinateTransformation = TransformationFactory.getCoordinateTransformation(TransformationFactory.WGS84, "EPSG:2056"); | ||
|
||
public static void main(String[] args) { | ||
|
||
ConcurrentMap<String, LinkProperties> linkProperties = new ConcurrentHashMap<>(); | ||
linkProperties.put(OsmTags.RAIL, new LinkProperties(1, 1, 30., 1000., false)); | ||
linkProperties.put(OsmTags.NARROW_GAUGE, new LinkProperties(2, 1, 30., 1000., false)); | ||
linkProperties.put(OsmTags.LIGHT_RAIL, new LinkProperties(3, 1, 30., 1000., false)); | ||
linkProperties.put(OsmTags.SUBWAY, new LinkProperties(4, 1, 30., 1000., false)); | ||
linkProperties.put(OsmTags.MONORAIL, new LinkProperties(5, 1, 30., 1000., false)); | ||
|
||
Network network = new OsmRailwayReader.Builder() | ||
.setCoordinateTransformation(coordinateTransformation) | ||
.setLinkProperties(linkProperties) | ||
.setPreserveNodeWithId(id -> true) // this filter keeps the detailed geometries | ||
.build() | ||
.read(inputFile); | ||
|
||
network.getAttributes().putAttribute("data_origin", "OSM"); | ||
|
||
new NetworkCleaner().run(network); | ||
new NetworkWriter(network).write(outputFile); | ||
} | ||
} |