21 package org.matsim.core.utils.io;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.UncheckedIOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
34 import java.util.Map.Entry;
36 import java.util.Stack;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.function.Supplier;
40 import org.apache.logging.log4j.LogManager;
41 import org.apache.logging.log4j.Logger;
52 import org.xml.sax.Attributes;
98 private static final List<String>
allTags =
new LinkedList<>(Arrays.asList(TAG_LANES, TAG_LANES_FORWARD,
99 TAG_LANES_BACKWARD, TAG_HIGHWAY, TAG_MAXSPEED, TAG_JUNCTION, TAG_ONEWAY, TAG_ACCESS));
101 protected final Map<Long, OsmNode>
nodes =
new HashMap<>();
102 protected final Map<Long, OsmWay>
ways =
new HashMap<>();
106 protected long id = 0;
117 final List<OsmFilter> hierarchyLayers =
new ArrayList<>();
132 this(
network, transformation,
true);
143 this(
network, transformation, useHighwayDefaults,
false);
161 this.transform = transformation;
164 if (useHighwayDefaults) {
165 log.info(
"Falling back to default values.");
170 if (useVspAdjustments) {
191 if (useVspAdjustments) {
207 this.parser =
new OsmXmlParser(this.nodes, this.ways, this.transform);
216 public final void parse(
final String osmFilename) {
217 parse(osmFilename, null);
227 public final void parse(
final Supplier<InputStream> streamSupplier)
throws UncheckedIOException {
228 parse(null, streamSupplier);
238 private void parse(
final String osmFilename,
final Supplier<InputStream> streamSupplier)
throws UncheckedIOException {
239 if(this.hierarchyLayers.isEmpty()){
240 log.warn(
"No hierarchy layer specified. Will convert every highway specified by setHighwayDefaults.");
243 if (this.slowButLowMemory) {
244 log.info(
"parsing osm file first time: identifying nodes used by ways");
246 if (streamSupplier != null) {
247 try (InputStream is = streamSupplier.get()) {
248 this.parser.
parse(is);
249 }
catch (IOException e) {
250 throw new UncheckedIOException(e);
255 log.info(
"parsing osm file second time: loading required nodes and ways");
257 if (streamSupplier != null) {
258 try (InputStream is = streamSupplier.get()) {
259 this.parser.
parse(is);
260 }
catch (IOException e) {
261 throw new UncheckedIOException(e);
266 log.info(
"done loading data");
268 if (streamSupplier!= null) {
269 try (InputStream is = streamSupplier.get()) {
270 this.parser.
parse(is);
271 }
catch (IOException e) {
272 throw new UncheckedIOException(e);
277 log.info(
"done loading data");
280 log.info(
"= conversion statistics: ==========================");
281 log.info(
"osm: # nodes read: " + this.parser.nodeCounter.
getCounter());
282 log.info(
"osm: # ways read: " + this.parser.wayCounter.
getCounter());
283 log.info(
"MATSim: # nodes created: " + this.network.
getNodes().size());
284 log.info(
"MATSim: # links created: " + this.network.
getLinks().size());
286 if (this.unknownHighways.size() > 0) {
287 log.info(
"The following highway-types had no defaults set and were thus NOT converted:");
288 for (String highwayType : this.unknownHighways) {
289 log.info(
"- \"" + highwayType +
"\"");
292 log.info(
"= end of conversion statistics ====================");
315 public final void setHighwayDefaults(
final int hierarchy ,
final String highwayType,
final double lanesPerDirection,
final double freespeed,
final double freespeedFactor,
final double laneCapacity_vehPerHour) {
316 setHighwayDefaults(hierarchy, highwayType, lanesPerDirection, freespeed, freespeedFactor, laneCapacity_vehPerHour,
false);
330 public final void setHighwayDefaults(
final int hierarchy,
final String highwayType,
final double lanesPerDirection,
final double freespeed,
331 final double freespeedFactor,
final double laneCapacity_vehPerHour,
final boolean oneway) {
332 this.highwayDefaults.put(highwayType,
new OsmHighwayDefaults(hierarchy, lanesPerDirection, freespeed, freespeedFactor, laneCapacity_vehPerHour, oneway));
372 public final void setHierarchyLayer(
final double coordNWNorthing,
final double coordNWEasting,
final double coordSENorthing,
final double coordSEEasting,
final int hierarchy) {
384 this.hierarchyLayers.add(osmFilter);
395 this.slowButLowMemory = memoryEnabled;
399 if(nodeIDsToKeep != null && !nodeIDsToKeep.isEmpty()){
405 allTags.addAll(wayTagsToAdd);
431 log.info(
"Remove ways that have at least one node that was not read previously ...");
433 Iterator<Entry<Long, OsmWay>> it = this.ways.entrySet().iterator();
435 while (it.hasNext()) {
436 Entry<Long, OsmWay> entry = it.next();
437 for (Long nodeId : entry.getValue().nodes) {
438 if (this.nodes.get(nodeId) == null) {
444 log.info(
"... done removing " + counter +
"ways that have at least one node that was not read previously.");
446 log.info(
"Fill ways and nodes with additional information: the hierarchy layer for ways, end points of ways...");
447 for (
OsmWay way : this.ways.values()) {
448 String highway = way.tags.get(TAG_HIGHWAY);
449 if ((highway != null) && (this.highwayDefaults.containsKey(highway))) {
451 way.hierarchy = this.highwayDefaults.get(highway).hierarchy;
454 this.nodes.get(way.nodes.get(0)).endPoint =
true;
455 this.nodes.get(way.nodes.get(way.nodes.size() - 1)).endPoint =
true;
458 for (Long nodeId : way.nodes) {
459 this.nodes.get(nodeId).ways.put(way.id, way);
463 log.info(
"... done filling ways and nodes with additional information.");
476 log.info(
"Mark OSM nodes that shoud be kept...");
477 for (
OsmNode node : this.nodes.values()) {
479 wayLoop:
for (
OsmWay way : node.ways.values()) {
480 String highway = way.tags.get(TAG_HIGHWAY);
481 if ((highway != null) && (this.highwayDefaults.containsKey(highway))) {
482 if (this.hierarchyLayers.isEmpty()) {
486 for (
OsmFilter osmFilter : this.hierarchyLayers) {
487 if (osmFilter.coordInFilter(node.coord, way.hierarchy)) {
502 if ((nodeIDsToKeep != null) && nodeIDsToKeep.contains(node.id)) {
506 log.info(
"... done marking OSM nodes that shoud be kept.");
508 log.info(
"Verify we did not mark nodes that build a loop ...");
509 for (
OsmWay way : this.ways.values()) {
510 String highway = way.tags.get(TAG_HIGHWAY);
511 if ((highway != null) && (this.highwayDefaults.containsKey(highway))) {
512 int prevRealNodeIndex = 0;
513 OsmNode prevRealNode = this.nodes.get(way.nodes.get(prevRealNodeIndex));
515 for (
int i = 1; i < way.nodes.size(); i++) {
516 OsmNode node = this.nodes.get(way.nodes.get(i));
518 if (prevRealNode == node) {
524 double increment = Math.sqrt(i - prevRealNodeIndex);
525 double nextNodeToKeep = prevRealNodeIndex + increment;
526 for (
double j = nextNodeToKeep; j < i; j += increment) {
527 int index = (int) Math.floor(j);
528 OsmNode intermediaryNode = this.nodes.get(way.nodes.get(index));
529 intermediaryNode.
used =
true;
532 prevRealNodeIndex = i;
538 log.info(
"... done verifying that we did not mark nodes that build a loop.");
548 return !this.keepPaths && node.
ways.size()<=1 && !node.
endPoint;
556 log.info(
"Create the required nodes ...") ;
557 for (
OsmNode node : this.nodes.values()) {
564 log.info(
"... done creating the required nodes.");
566 log.info(
"Create the links ...") ;
568 for (
OsmWay way : this.ways.values()) {
569 String highway = way.tags.get(TAG_HIGHWAY);
570 if (highway != null) {
571 OsmNode fromNode = this.nodes.get(way.nodes.get(0));
575 for (
int i = 1, n = way.nodes.size(); i < n; i++) {
576 OsmNode toNode = this.nodes.get(way.nodes.get(i));
577 if (toNode != lastToNode) {
581 if(this.hierarchyLayers.isEmpty()) {
582 createLink(this.network, way, fromNode, toNode, length);
584 for (
OsmFilter osmFilter : this.hierarchyLayers) {
585 if(osmFilter.coordInFilter(fromNode.
coord, way.hierarchy)){
586 createLink(this.network, way, fromNode, toNode, length);
589 if(osmFilter.coordInFilter(toNode.
coord, way.hierarchy)){
590 createLink(this.network, way, fromNode, toNode, length);
605 log.info(
"... done creating the links.");
609 final double length) {
610 String highway = way.
tags.get(TAG_HIGHWAY);
612 if (
"no".equals(way.
tags.get(TAG_ACCESS))) {
618 if (defaults == null) {
619 this.unknownHighways.add(highway);
634 if(highway.equalsIgnoreCase(
"trunk") || highway.equalsIgnoreCase(
"primary") || highway.equalsIgnoreCase(
"secondary")){
635 if (oneway && nofLanesForward == 1.0) {
636 nofLanesForward = 2.0;
637 }
else if(onewayReverse && nofLanesBackward == 1.0){
638 nofLanesBackward = 2.0;
642 String maxspeedTag = way.
tags.get(TAG_MAXSPEED);
643 if (maxspeedTag != null) {
645 if(maxspeedTag.endsWith(
"mph")) {
646 freespeed = Double.parseDouble(maxspeedTag.replace(
"mph",
"").trim()) * 1.609344 / 3.6;
648 freespeed = Double.parseDouble(maxspeedTag) / 3.6;
650 if (useVspAdjustments) {
653 if (freespeed <= 51./3.6) {
654 freespeed = freespeed/2;
657 }
catch (NumberFormatException e) {
658 if (!this.unknownMaxspeedTags.contains(maxspeedTag)) {
659 this.unknownMaxspeedTags.add(maxspeedTag);
660 log.warn(
"Could not parse maxspeed tag:" + e.getMessage() +
". Ignoring it.");
664 if (useVspAdjustments) {
668 if(highway.equalsIgnoreCase(
"primary") || highway.equalsIgnoreCase(
"secondary") || highway.equalsIgnoreCase(
"tertiary")
669 || highway.equalsIgnoreCase(
"primary_link") || highway.equalsIgnoreCase(
"secondary_link") || highway.equalsIgnoreCase(
"tertiary_link")) {
671 freespeed = 80. / 3.6;
673 freespeed = (10. + 70./300 * length) / 3.6;
679 if (useVspAdjustments) {
682 laneCapacity = 2 * laneCapacity;
687 String lanesTag = way.
tags.get(TAG_LANES);
688 String lanesForwardTag = way.
tags.get(TAG_LANES_FORWARD);
689 String lanesBackwardTag = way.
tags.get(TAG_LANES_BACKWARD);
697 if (lanesForwardTag != null && lanesBackwardTag != null){
698 nofLanesForward = Double.parseDouble(lanesForwardTag);
699 nofLanesBackward = Double.parseDouble(lanesBackwardTag);
702 nofLanesBackward = 0;
703 if (lanesForwardTag != null)
704 nofLanesForward = Double.parseDouble(lanesForwardTag);
705 else if (lanesTag != null)
706 nofLanesForward = Double.parseDouble(lanesTag);
708 }
else if (onewayReverse) {
710 if (lanesBackwardTag != null)
711 nofLanesBackward = Double.parseDouble(lanesBackwardTag);
712 else if (lanesTag != null)
713 nofLanesBackward = Double.parseDouble(lanesTag);
715 }
else if (lanesForwardTag != null) {
717 nofLanesForward = Double.parseDouble(lanesForwardTag);
718 if (lanesTag != null)
719 nofLanesBackward = Double.parseDouble(lanesTag) - nofLanesForward;
722 else if (lanesBackwardTag != null) {
724 nofLanesBackward = Double.parseDouble(lanesBackwardTag);
725 if (lanesTag != null)
726 nofLanesForward = Double.parseDouble(lanesTag) - nofLanesBackward;
729 if (lanesTag != null) {
733 nofLanesForward = Double.parseDouble(lanesTag) / 2;
734 nofLanesBackward = nofLanesForward;
739 if (!this.unknownLanesTags.contains(lanesTag)) {
740 this.unknownLanesTags.add(lanesTag);
741 log.warn(
"Could not parse lanes tag:" + e.getMessage() +
". Ignoring it.");
745 double capacityForward = nofLanesForward * laneCapacity;
746 double capacityBackward = nofLanesBackward * laneCapacity;
748 if (this.scaleMaxSpeed) {
749 freespeed = freespeed * freespeedFactor;
750 if (useVspAdjustments) {
751 throw new RuntimeException(
"Max speed scaling and VSP adjustments used at the same time. Both reduce speeds. It is most likely " 752 +
"unintended to use them both at the same time. ik,dz, spr'18");
759 if(network.
getNodes().get(fromId) != null && network.
getNodes().get(toId) != null){
760 String origId = Long.toString(way.
id);
792 String onewayTag = way.
tags.get(TAG_ONEWAY);
793 if (onewayTag != null) {
794 if (
"yes".equals(onewayTag) ||
"true".equals(onewayTag) ||
"1".equals(onewayTag)) {
796 }
else if (
"-1".equals(onewayTag) ||
"reverse".equals(onewayTag)) {
798 }
else if (
"no".equals(onewayTag) ||
"false".equals(onewayTag) ||
"0".equals(onewayTag)) {
802 log.warn(
"Could not interpret oneway tag:" + onewayTag +
". Ignoring it.");
806 if (
"roundabout".equals(way.
tags.get(TAG_JUNCTION))) {
816 String onewayTag = way.
tags.get(TAG_ONEWAY);
817 if (onewayTag != null) {
818 if (
"yes".equals(onewayTag) ||
"true".equals(onewayTag) ||
"1".equals(onewayTag)) {
820 }
else if (
"-1".equals(onewayTag) ||
"reverse".equals(onewayTag)) {
822 }
else if (
"no".equals(onewayTag) ||
"false".equals(onewayTag) ||
"0".equals(onewayTag)) {
826 log.warn(
"Could not interpret oneway tag:" + onewayTag +
". Ignoring it.");
869 this.coordNW = coordNW;
870 this.coordSE = coordSE;
871 this.hierarchy = hierarchy;
876 if(this.hierarchy < hierarchyLevel){
880 return ((this.coordNW.
getX() < coord.
getX() && coord.
getX() < this.coordSE.
getX()) &&
881 (this.coordNW.
getY() > coord.
getY() && coord.
getY() > this.coordSE.
getY()));
888 this.hierarchy = hierarchy;
893 if(this.hierarchy < hierarchyLevel){
901 public final long id;
902 public boolean used =
false;
903 public Map<Long, OsmWay> ways =
new HashMap<>();
905 public boolean endPoint =
false;
914 public final long id;
915 public final List<Long> nodes =
new ArrayList<>(4);
916 public final Map<String, String> tags =
new HashMap<>(4);
917 public int hierarchy = -1;
933 public OsmHighwayDefaults(
final int hierarchy,
final double lanesPerDirection,
final double freespeed,
final double freespeedFactor,
final double laneCapacity,
final boolean oneway) {
934 this.hierarchy = hierarchy;
935 this.lanesPerDirection = lanesPerDirection;
936 this.freespeed = freespeed;
937 this.freespeedFactor = freespeedFactor;
938 this.laneCapacity = laneCapacity;
939 this.oneway = oneway;
947 protected final Map<Long, OsmNode>
nodes;
948 protected final Map<Long, OsmWay>
ways;
952 private boolean loadNodes =
true;
953 private boolean loadWays =
true;
954 private boolean mergeNodes =
false;
955 private boolean collectNodes =
false;
962 this.setValidating(
false);
966 this.loadNodes =
false;
967 this.loadWays =
false;
968 this.collectNodes =
false;
969 this.mergeNodes =
false;
971 this.collectNodes =
true;
972 }
else if (step == 2) {
973 this.mergeNodes =
true;
974 this.loadWays =
true;
979 public void startTag(
final String name,
final Attributes atts,
final Stack<String> context) {
980 if (
"node".equals(name)) {
981 if (this.loadNodes) {
982 Long
id = Long.valueOf(atts.getValue(
"id"));
983 double lat = Double.parseDouble(atts.getValue(
"lat"));
984 double lon = Double.parseDouble(atts.getValue(
"lon"));
986 this.nodes.put(
id, currentNode);
988 }
else if (this.mergeNodes) {
989 OsmNode node = this.nodes.get(Long.valueOf(atts.getValue(
"id")));
991 double lat = Double.parseDouble(atts.getValue(
"lat"));
992 double lon = Double.parseDouble(atts.getValue(
"lon"));
997 }
else if (
"way".equals(name)) {
998 this.currentWay =
new OsmWay(Long.parseLong(atts.getValue(
"id")));
999 }
else if (
"nd".equals(name)) {
1000 if (this.currentWay != null) {
1001 this.currentWay.
nodes.add(Long.parseLong(atts.getValue(
"ref")));
1003 }
else if (
"tag".equals(name)) {
1004 if (this.currentWay != null) {
1006 for (String tag : allTags) {
1007 if (tag.equals(key)) {
1017 public void endTag(
final String name,
final String content,
final Stack<String> context) {
1018 if (
"way".equals(name)) {
1019 if (!this.currentWay.
nodes.isEmpty()) {
1020 boolean used =
false;
1022 if (osmHighwayDefaults != null) {
1023 int hierarchy = osmHighwayDefaults.
hierarchy;
1028 if (this.collectNodes) {
1032 for (Long nodeId : this.currentWay.
nodes) {
1033 OsmNode node = this.nodes.get(nodeId);
1046 if (this.collectNodes) {
1047 for (
long id : this.currentWay.
nodes) {
1050 }
else if (this.loadWays) {
1051 this.ways.put(this.currentWay.
id,
this.currentWay);
1056 this.currentWay = null;
1057 }
else if (
"node".equals(name)) {
1059 this.currentNode = null;
1066 private static final ConcurrentHashMap<String, String> cache =
new ConcurrentHashMap<>(10000);
1075 public static String
get(
final String string) {
1076 String s = cache.putIfAbsent(
string,
string);
boolean coordInFilter(final Coord coord, final int hierarchyLevel)
static final List< String > allTags
boolean isOneway(OsmWay way)
final void setHierarchyLayer(final double coordNWNorthing, final double coordNWEasting, final double coordSENorthing, final double coordSEEasting, final int hierarchy)
static void setOrigId(final Node node, final String id)
void setNumberOfLanes(double lanes)
boolean useVspAdjustments
final double laneCapacity
final CoordinateTransformation transform
Map< Id< Node >, ? extends Node > getNodes()
void setFreespeed(double freespeed)
void enableOptimization(final int step)
static double calcEuclideanDistance(Coord coord, Coord other)
final void setScaleMaxSpeed(final boolean scaleMaxSpeed)
final Map< Long, OsmNode > nodes
final Map< Long, OsmWay > ways
void parse(final String osmFilename, final Supplier< InputStream > streamSupplier)
static final String TAG_LANES_FORWARD
OsmNetworkReader(final Network network, final CoordinateTransformation transformation)
final void setHierarchyLayer(final int hierarchy)
final Set< String > unknownMaxspeedTags
static final String TAG_HIGHWAY
NetworkFactory getFactory()
final void setHighwayDefaults(final int hierarchy, final String highwayType, final double lanesPerDirection, final double freespeed, final double freespeedFactor, final double laneCapacity_vehPerHour, final boolean oneway)
final void parse(final String osmFilename)
OsmHighwayDefaults(final int hierarchy, final double lanesPerDirection, final double freespeed, final double freespeedFactor, final double laneCapacity, final boolean oneway)
final Set< String > unknownLanesTags
final double lanesPerDirection
final void parse(final URL url)
final Map< String, OsmHighwayDefaults > highwayDefaults
static< T > Id< T > create(final long key, final Class< T > type)
void addWayTags(List< String > wayTagsToAdd)
boolean coordInFilter(final Coord coord, final int hierarchyLevel)
OsmNode(final long id, final Coord coord)
void createLink(final Network network, final OsmWay way, final OsmNode fromNode, final OsmNode toNode, final double length)
static String get(final String string)
void setOrModifyLinkAttributes(Link l, OsmWay way, boolean forwardDirection)
final double freespeedFactor
final void addOsmFilter(final OsmFilter osmFilter)
Link createLink(final Id< Link > id, final Node fromNode, final Node toNode)
final void setMemoryOptimization(final boolean memoryEnabled)
final Map< Long, OsmWay > ways
boolean coordInFilter(final Coord coord, final int hierarchyLevel)
void setLength(double length)
OsmNetworkReader(final Network network, final CoordinateTransformation transformation, final boolean useHighwayDefaults)
final Map< String, String > tags
boolean forwardDirectionExists(OsmWay way)
Set< Long > nodeIDsToKeep
void setCapacityPeriod(double capPeriod)
Map< Id< Link >, ? extends Link > getLinks()
Node createNode(final Id< Node > id, final Coord coord)
boolean isOnewayReverse(OsmWay way)
static final String TAG_LANES_BACKWARD
final void parse(final Supplier< InputStream > streamSupplier)
OsmXmlParser(final Map< Long, OsmNode > nodes, final Map< Long, OsmWay > ways, final CoordinateTransformation transform)
static final String TAG_ONEWAY
final void readFile(final String filename)
static void setType(Node node, final String type)
final Set< String > unknownHighways
static final String TAG_LANES
static final String TAG_MAXSPEED
OsmNetworkReader(final Network network, final CoordinateTransformation transformation, final boolean useHighwayDefaults, final boolean useVspAdjustments)
void endTag(final String name, final String content, final Stack< String > context)
void startTag(final String name, final Attributes atts, final Stack< String > context)
final void setNodeIDsToKeep(Set< Long > nodeIDsToKeep)
void setParser(OsmXmlParser parser)
final void setHighwayDefaults(final int hierarchy, final String highwayType, final double lanesPerDirection, final double freespeed, final double freespeedFactor, final double laneCapacity_vehPerHour)
static final String TAG_JUNCTION
final Map< Long, OsmNode > nodes
final void setKeepPaths(final boolean keepPaths)
static final String TAG_ACCESS
boolean reverseDirectionExists(OsmWay way)
boolean canNodeBeRemoved(OsmNode node)
void setCapacity(double capacity)
final CoordinateTransformation transform
void setOrModifyNodeAttributes(Node n, OsmNode node)