-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Make use of node tags like 'highway' or 'crossing' #2705
Changes from 21 commits
ac52a74
1e70c4a
8a09a17
c7d978b
717d1f7
971ac6f
cdbf3ad
6e6cbe9
9324f5a
b5ec43e
f0efbd5
b4d9983
39b8fbf
e764512
dd896ed
adf3ef9
48f3634
8f4cbf1
9d88f1d
0e4f70f
b785cc0
c0e29fa
5bdce99
95f8358
7b22889
f02421a
8287848
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 |
---|---|---|
|
@@ -38,17 +38,13 @@ | |
import java.io.File; | ||
import java.io.IOException; | ||
import java.text.ParseException; | ||
import java.util.ArrayList; | ||
import java.util.Date; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.*; | ||
import java.util.function.Consumer; | ||
import java.util.function.LongToIntFunction; | ||
import java.util.function.Predicate; | ||
|
||
import static com.graphhopper.reader.osm.OSMNodeData.*; | ||
import static com.graphhopper.util.Helper.nf; | ||
import static java.util.Collections.emptyMap; | ||
|
||
/** | ||
* This class parses a given OSM file and splits OSM ways into 'segments' at all intersections (or 'junctions'). | ||
|
@@ -68,6 +64,7 @@ | |
*/ | ||
public class WaySegmentParser { | ||
private static final Logger LOGGER = LoggerFactory.getLogger(WaySegmentParser.class); | ||
private static final Set<String> INCLUDE_IF_NODE_TAGS = new HashSet<>(Arrays.asList("barrier", "highway", "railway", "crossing", "ford")); | ||
|
||
private final ElevationProvider eleProvider; | ||
private final Predicate<ReaderWay> wayFilter; | ||
|
@@ -224,14 +221,27 @@ public void handleNode(ReaderNode node) { | |
|
||
acceptedNodes++; | ||
|
||
// we keep node tags for barrier nodes | ||
// remember which nodes we want to split | ||
if (splitNodeFilter.test(node)) { | ||
if (nodeType == JUNCTION_NODE) { | ||
LOGGER.debug("OSM node {} at {},{} is a barrier node at a junction. The barrier will be ignored", | ||
node.getId(), Helper.round(node.getLat(), 7), Helper.round(node.getLon(), 7)); | ||
ignoredSplitNodes++; | ||
} else | ||
nodeData.setSplitNode(node.getId()); | ||
} | ||
|
||
// store node tags if at least one important tag is included and make this available for the edge handler | ||
for (Map.Entry<String, Object> e : node.getTags().entrySet()) { | ||
if (INCLUDE_IF_NODE_TAGS.contains(e.getKey())) { | ||
node.removeTag("created_by"); | ||
node.removeTag("source"); | ||
node.removeTag("note"); | ||
node.removeTag("fixme"); | ||
node.setTags(new HashMap<>(node.getTags())); // create compact Map | ||
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. I'd rather create the compact map in OSMNodeData.setTags, so it is kind of up to this 'storage' class how to the tags are stored (and compacted and such). |
||
nodeData.setTags(node); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -251,7 +261,7 @@ public void handleWay(ReaderWay way) { | |
return; | ||
List<SegmentNode> segment = new ArrayList<>(way.getNodes().size()); | ||
for (LongCursor node : way.getNodes()) | ||
segment.add(new SegmentNode(node.value, nodeData.getId(node.value))); | ||
segment.add(new SegmentNode(node.value, nodeData.getId(node.value), nodeData.getTags(node.value))); | ||
wayPreprocessor.preprocessWay(way, osmNodeId -> nodeData.getCoordinates(nodeData.getId(osmNodeId))); | ||
splitWayAtJunctionsAndEmptySections(segment, way); | ||
} | ||
|
@@ -304,9 +314,11 @@ private void splitSegmentAtSplitNodes(List<SegmentNode> parentSegment, ReaderWay | |
List<SegmentNode> segment = new ArrayList<>(); | ||
for (int i = 0; i < parentSegment.size(); i++) { | ||
SegmentNode node = parentSegment.get(i); | ||
Map<String, Object> nodeTags = nodeData.getTags(node.osmNodeId); | ||
// so far we only consider node tags of split nodes, so if there are node tags we split the node | ||
if (!nodeTags.isEmpty()) { | ||
if (nodeData.isSplitNode(node.osmNodeId)) { | ||
// do not split this node again. for example a barrier can be connecting two ways (appear in both | ||
// ways) and we only want to add a barrier edge once (but we want to add one). | ||
nodeData.unsetSplitNode(node.osmNodeId); | ||
|
||
// this node is a barrier. we will copy it and add an extra edge | ||
SegmentNode barrierFrom = node; | ||
SegmentNode barrierTo = nodeData.addCopyOfNode(node); | ||
|
@@ -318,28 +330,30 @@ private void splitSegmentAtSplitNodes(List<SegmentNode> parentSegment, ReaderWay | |
} | ||
if (!segment.isEmpty()) { | ||
segment.add(barrierFrom); | ||
handleSegment(segment, way, emptyMap()); | ||
handleSegment(segment, way); | ||
segment = new ArrayList<>(); | ||
} | ||
|
||
// mark barrier edge | ||
way.setTag("gh:barrier_edge", true); | ||
segment.add(barrierFrom); | ||
segment.add(barrierTo); | ||
handleSegment(segment, way, nodeTags); | ||
handleSegment(segment, way); | ||
way.removeTag("gh:barrier_edge"); | ||
|
||
segment = new ArrayList<>(); | ||
segment.add(barrierTo); | ||
|
||
// ignore this barrier node from now. for example a barrier can be connecting two ways (appear in both | ||
// ways) and we only want to add a barrier edge once (but we want to add one). | ||
nodeData.removeTags(node.osmNodeId); | ||
} else { | ||
segment.add(node); | ||
} | ||
} | ||
if (segment.size() > 1) | ||
handleSegment(segment, way, emptyMap()); | ||
handleSegment(segment, way); | ||
} | ||
|
||
void handleSegment(List<SegmentNode> segment, ReaderWay way, Map<String, Object> nodeTags) { | ||
void handleSegment(List<SegmentNode> segment, ReaderWay way) { | ||
final PointList pointList = new PointList(segment.size(), nodeData.is3D()); | ||
final List<Map<String, Object>> nodeTags = new ArrayList<>(segment.size()); | ||
int from = -1; | ||
int to = -1; | ||
for (int i = 0; i < segment.size(); i++) { | ||
|
@@ -359,6 +373,7 @@ else if (i == segment.size() - 1) | |
else if (isTowerNode(id)) | ||
throw new IllegalStateException("Tower nodes should only appear at the end of segments, way: " + way.getId()); | ||
nodeData.addCoordinatesToPointList(id, pointList); | ||
nodeTags.add(node.tags); | ||
} | ||
if (from < 0 || to < 0) | ||
throw new IllegalStateException("The first and last nodes of a segment must be tower nodes, way: " + way.getId()); | ||
|
@@ -547,7 +562,7 @@ default void onFinish() { | |
} | ||
|
||
public interface EdgeHandler { | ||
void handleEdge(int from, int to, PointList pointList, ReaderWay way, Map<String, Object> nodeTags); | ||
void handleEdge(int from, int to, PointList pointList, ReaderWay way, List<Map<String, Object>> nodeTags); | ||
} | ||
|
||
public interface RelationProcessor { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.graphhopper.routing.ev; | ||
|
||
import com.graphhopper.util.Helper; | ||
|
||
public enum Crossing { | ||
MISSING, // no information | ||
RAILWAY_BARRIER, // railway crossing with barrier | ||
RAILWAY, // railway crossing with road | ||
TRAFFIC_SIGNALS, // with light signals | ||
UNCONTROLLED, // with crosswalk, without traffic lights | ||
MARKED, // with crosswalk, with or without traffic lights | ||
UNMARKED, // without markings or traffic lights | ||
NO; // crossing is impossible or illegal | ||
public static final String KEY = "crossing"; | ||
|
||
@Override | ||
public String toString() { | ||
return Helper.toLowerCase(name()); | ||
} | ||
|
||
public static Crossing find(String name) { | ||
if (name == null) | ||
return MISSING; | ||
try { | ||
return Crossing.valueOf(Helper.toUpperCase(name)); | ||
} catch (IllegalArgumentException ex) { | ||
return MISSING; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,15 +79,13 @@ public ConditionalTagInspector getConditionalTagInspector() { | |
* Updates the given edge flags based on node tags | ||
*/ | ||
protected void handleNodeTags(IntsRef edgeFlags, Map<String, Object> nodeTags) { | ||
if (!nodeTags.isEmpty()) { | ||
// for now we just create a dummy reader node, because our encoders do not make use of the coordinates anyway | ||
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. Let's rename handleNodeTags? It sounds like something general, but cit is only called for edges tagged as |
||
ReaderNode readerNode = new ReaderNode(0, 0, 0, nodeTags); | ||
// block access for barriers | ||
if (isBarrier(readerNode)) { | ||
BooleanEncodedValue accessEnc = getAccessEnc(); | ||
accessEnc.setBool(false, edgeFlags, false); | ||
accessEnc.setBool(true, edgeFlags, false); | ||
} | ||
// for now we just create a dummy reader node, because our encoders do not make use of the coordinates anyway | ||
ReaderNode readerNode = new ReaderNode(0, 0, 0, nodeTags); | ||
// block access for barriers | ||
if (isBarrier(readerNode)) { | ||
BooleanEncodedValue accessEnc = getAccessEnc(); | ||
accessEnc.setBool(false, edgeFlags, false); | ||
accessEnc.setBool(true, edgeFlags, false); | ||
} | ||
} | ||
|
||
|
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.
In my last commit I added this LongSet to replace the artificial (and possibly confusing?)
gh:split_node
tag, and so we do not need the remove operation for the tags (in case we like to store the node tags with EdgeKVStorage or similar).Now we use the following data structures for the node data:
idsByOSMNodeIds is a (very large) map that maps the full range of possible (64bit) OSM node IDs to a smaller (32bit) range of indices of nodes we actually use to build the graph (typically those contained in ways using the
highway
tag).nodeTagIndicesByOSMNodeIds is a similar, but smaller, map that does the same, but only for nodes for which we keep the tags for
nodeTags is a list that maps the int indices we get via nodeTagIndicesByOSMNodeIds to the actual tags
nodesToBeSplit is a separate hash set of OSM node IDS that shall be split. for this we need a way to remove/unset values.
Quite possibly we could combine some of these to either save memory or speed up the import, or both? For example we could combine idsByOsmNodeIds and nodeTagIndicesByOsmNodeIds, which would definitely be an improvement if we stored tags for all nodes. Or we could use the int index we get via nodeTagIndicesByOsmNodeIds to replace the nodesToBeSplit LongSet with a BitSet (using the int index).
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 looks like whenever we do a lookup into nodeTagIndicesByOSMNodeIds we also do a lookup into idsByOSMNodeIds anyway, which means we could get rid of the lookup into nodeTagIndicesByOSMNodeIds. But since there are less nodes for which we store tags than there are nodes we need to map the (larger) int index we get from idsByOSMNodeIds to a smaller, more compact index. At least as long as we want to keep the node tags list compact. So actually we would then need another int-int lookup, and I'm not sure if overall this will be better, but it might be worth a try.
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.
Did you check for how many nodes we now store the tags (compared to the total number of nodes we map the coordinates for)?
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.
A bit off-topic, but it also might speed up the import if we replaced:
with something like
but not sure if using an index like this is possible with GHLongIntBTreeMap.