MATSIM
OsmNetworkReader.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * OSMReader.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2008 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.utils.io;
22 
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;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Set;
36 import java.util.Stack;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.function.Supplier;
39 
40 import org.apache.logging.log4j.LogManager;
41 import org.apache.logging.log4j.Logger;
42 import org.matsim.api.core.v01.Coord;
43 import org.matsim.api.core.v01.Id;
52 import org.xml.sax.Attributes;
53 
85 @Deprecated
86 public class OsmNetworkReader implements MatsimSomeReader {
87 
88  private final static Logger log = LogManager.getLogger(OsmNetworkReader.class);
89 
90  private final static String TAG_LANES = "lanes";
91  private final static String TAG_LANES_FORWARD = "lanes:forward";
92  private final static String TAG_LANES_BACKWARD = "lanes:backward";
93  protected final static String TAG_HIGHWAY = "highway";
94  private final static String TAG_MAXSPEED = "maxspeed";
95  protected final static String TAG_JUNCTION = "junction";
96  protected final static String TAG_ONEWAY = "oneway";
97  private final static String TAG_ACCESS = "access";
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));
100 
101  protected final Map<Long, OsmNode> nodes = new HashMap<>();
102  protected final Map<Long, OsmWay> ways = new HashMap<>();
103  private final Set<String> unknownHighways = new HashSet<>();
104  private final Set<String> unknownMaxspeedTags = new HashSet<>();
105  private final Set<String> unknownLanesTags = new HashSet<>();
106  protected long id = 0;
107  protected final Map<String, OsmHighwayDefaults> highwayDefaults = new HashMap<>();
108  protected final Network network;
110  private boolean keepPaths = false;
111  private boolean scaleMaxSpeed = false;
112 
113  private boolean slowButLowMemory = false;
114 
115  private boolean useVspAdjustments = false; // Adjustments discussed on 2018-04-30, kn,ik,dz. apr'18 (Might become default after testing)
116 
117  /*package*/ final List<OsmFilter> hierarchyLayers = new ArrayList<>();
118 
119  // nodes that are definitely to be kept (e.g. for counts later)
120  private Set<Long> nodeIDsToKeep = null;
121 
123 
124 
131  public OsmNetworkReader(final Network network, final CoordinateTransformation transformation) {
132  this(network, transformation, true);
133  }
134 
142  public OsmNetworkReader(final Network network, final CoordinateTransformation transformation, final boolean useHighwayDefaults) {
143  this(network, transformation, useHighwayDefaults, false);
144  }
145 
159  public OsmNetworkReader(final Network network, final CoordinateTransformation transformation, final boolean useHighwayDefaults, final boolean useVspAdjustments) {
160  this.network = network;
161  this.transform = transformation;
162  this.useVspAdjustments = useVspAdjustments;
163 
164  if (useHighwayDefaults) {
165  log.info("Falling back to default values.");
166  this.setHighwayDefaults(1, "motorway", 2, 120.0/3.6, 1.0, 2000, true);
167  this.setHighwayDefaults(1, "motorway_link", 1, 80.0/3.6, 1.0, 1500, true);
168  this.setHighwayDefaults(2, "trunk", 1, 80.0/3.6, 1.0, 2000);
169  this.setHighwayDefaults(2, "trunk_link", 1, 50.0/3.6, 1.0, 1500);
170  if (useVspAdjustments) {
171  this.setHighwayDefaults(3, "primary", 1, 80.0/3.6, 1.0, 1000);
172  this.setHighwayDefaults(3, "primary_link", 1, 60.0/3.6, 1.0, 1000);
173  } else {
174  this.setHighwayDefaults(3, "primary", 1, 80.0/3.6, 1.0, 1500);
175  this.setHighwayDefaults(3, "primary_link", 1, 60.0/3.6, 1.0, 1500);
176  }
177 
178 // this.setHighwayDefaults(4, "secondary", 1, 60.0/3.6, 1.0, 1000);
179 // this.setHighwayDefaults(5, "tertiary", 1, 45.0/3.6, 1.0, 600);
180 // this.setHighwayDefaults(6, "minor", 1, 45.0/3.6, 1.0, 600);
181 // this.setHighwayDefaults(6, "unclassified", 1, 45.0/3.6, 1.0, 600);
182 // this.setHighwayDefaults(6, "residential", 1, 30.0/3.6, 1.0, 600);
183 // this.setHighwayDefaults(6, "living_street", 1, 15.0/3.6, 1.0, 300);
184 
185  // Setting the following to considerably smaller values, since there are often traffic signals/non-prio intersections.
186  // If someone does a systematic study, please report. kai, jul'16
187  //
188  // We revised the below street types (removed "minor" and put "living_street", "residential", and "unclassified" into
189  // different hierarchy layers), trying to make this more reasonable based on the
190  // <a href="OMS Wiki">http://wiki.openstreetmap.org/wiki/DE:Key:highway</a>, ts/aa/dz, oct'17
191  if (useVspAdjustments) {
192  this.setHighwayDefaults(4, "secondary", 1, 30.0/3.6, 1.0, 800);
193  this.setHighwayDefaults(4, "secondary_link", 1, 30.0/3.6, 1.0, 800);
194  } else {
195  this.setHighwayDefaults(4, "secondary", 1, 30.0/3.6, 1.0, 1000);
196  this.setHighwayDefaults(4, "secondary_link", 1, 30.0/3.6, 1.0, 1000);
197  }
198  this.setHighwayDefaults(5, "tertiary", 1, 25.0/3.6, 1.0, 600);
199  this.setHighwayDefaults(5, "tertiary_link", 1, 25.0/3.6, 1.0, 600);
200  this.setHighwayDefaults(6, "unclassified", 1, 15.0/3.6, 1.0, 600);
201  this.setHighwayDefaults(7, "residential", 1, 15.0/3.6, 1.0, 600);
202  this.setHighwayDefaults(8, "living_street", 1, 10.0/3.6, 1.0, 300);
203  // changing the speed values failed the evacuation ScenarioGenerator test because of a different network -- DESPITE
204  // the fact that all the speed values are reset to some other value there. No idea what happens there. kai, jul'16
205  }
206 
207  this.parser = new OsmXmlParser(this.nodes, this.ways, this.transform);
208  }
209 
216  public final void parse(final String osmFilename) {
217  parse(osmFilename, null);
218  }
219 
227  public final void parse(final Supplier<InputStream> streamSupplier) throws UncheckedIOException {
228  parse(null, streamSupplier);
229  }
230 
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.");
241  }
242 
243  if (this.slowButLowMemory) {
244  log.info("parsing osm file first time: identifying nodes used by ways");
245  this.parser.enableOptimization(1);
246  if (streamSupplier != null) {
247  try (InputStream is = streamSupplier.get()) {
248  this.parser.parse(is);
249  } catch (IOException e) {
250  throw new UncheckedIOException(e);
251  }
252  } else {
253  this.parser.readFile(osmFilename);
254  }
255  log.info("parsing osm file second time: loading required nodes and ways");
256  this.parser.enableOptimization(2);
257  if (streamSupplier != null) {
258  try (InputStream is = streamSupplier.get()) {
259  this.parser.parse(is);
260  } catch (IOException e) {
261  throw new UncheckedIOException(e);
262  }
263  } else {
264  this.parser.readFile(osmFilename);
265  }
266  log.info("done loading data");
267  } else {
268  if (streamSupplier!= null) {
269  try (InputStream is = streamSupplier.get()) {
270  this.parser.parse(is);
271  } catch (IOException e) {
272  throw new UncheckedIOException(e);
273  }
274  } else {
275  this.parser.readFile(osmFilename);
276  }
277  log.info("done loading data");
278  }
279  convert();
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());
285 
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 + "\"");
290  }
291  }
292  log.info("= end of conversion statistics ====================");
293  }
294 
299  public void setParser(OsmXmlParser parser) {
300  this.parser = parser;
301  }
302 
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);
317  }
318 
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));
333  }
334 
346  public final void setKeepPaths(final boolean keepPaths) {
347  this.keepPaths = keepPaths;
348  }
349 
359  public final void setScaleMaxSpeed(final boolean scaleMaxSpeed) {
360  this.scaleMaxSpeed = scaleMaxSpeed;
361  }
362 
372  public final void setHierarchyLayer(final double coordNWNorthing, final double coordNWEasting, final double coordSENorthing, final double coordSEEasting, final int hierarchy) {
373  this.hierarchyLayers.add(new OsmFilterImpl(this.transform.transform(new Coord(coordNWEasting, coordNWNorthing)), this.transform.transform(new Coord(coordSEEasting, coordSENorthing)), hierarchy));
374  }
375  public final void setHierarchyLayer(final int hierarchy) {
376  this.hierarchyLayers.add(new GeographicallyNonrestrictingOsmFilterImpl(hierarchy));
377  }
378 
383  public final void addOsmFilter(final OsmFilter osmFilter) {
384  this.hierarchyLayers.add(osmFilter);
385  }
386 
394  public final void setMemoryOptimization(final boolean memoryEnabled) {
395  this.slowButLowMemory = memoryEnabled;
396  }
397 
398  public final void setNodeIDsToKeep(Set<Long> nodeIDsToKeep){
399  if(nodeIDsToKeep != null && !nodeIDsToKeep.isEmpty()){
400  this.nodeIDsToKeep = nodeIDsToKeep;
401  }
402  }
403 
404  protected void addWayTags(List<String> wayTagsToAdd) {
405  allTags.addAll(wayTagsToAdd);
406  }
407 
408  private void convert() {
409  this.network.setCapacityPeriod(3600);
410 
412 
413  simplifyOsmData();
414 
416 
417  // free up memory
418  this.nodes.clear();
419  this.ways.clear();
420  }
421 
430  protected void preprocessOsmData() {
431  log.info("Remove ways that have at least one node that was not read previously ...");
432  // yy I _think_ this is what it does. kai, may'16
433  Iterator<Entry<Long, OsmWay>> it = this.ways.entrySet().iterator();
434  int counter = 0;
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) {
439  it.remove();
440  break;
441  }
442  }
443  }
444  log.info("... done removing " + counter + "ways that have at least one node that was not read previously.");
445 
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))) {
450  // check to which level a way belongs
451  way.hierarchy = this.highwayDefaults.get(highway).hierarchy;
452 
453  // first and last node are marked as endpoints, so they are kept in all cases
454  this.nodes.get(way.nodes.get(0)).endPoint = true;
455  this.nodes.get(way.nodes.get(way.nodes.size() - 1)).endPoint = true;
456 
457  // save all ways a node is located on in a special map
458  for (Long nodeId : way.nodes) {
459  this.nodes.get(nodeId).ways.put(way.id, way);
460  }
461  }
462  }
463  log.info("... done filling ways and nodes with additional information.");
464  }
465 
475  protected void simplifyOsmData() {
476  log.info("Mark OSM nodes that shoud be kept...");
477  for (OsmNode node : this.nodes.values()) {
478  // check whether the node belongs to a way that fulfills the hierarchyLayers (i.e. is 'big' enough)
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()) {
483  node.used = true;
484  break;
485  } else {
486  for (OsmFilter osmFilter : this.hierarchyLayers) {
487  if (osmFilter.coordInFilter(node.coord, way.hierarchy)) {
488  node.used = true;
489  break wayLoop;
490  }
491  }
492  }
493  }
494  }
495 
496  // mark nodes that can be removed/simplified (no endpoint, no intersections)
497  if (canNodeBeRemoved(node)) {
498  node.used = false;
499  }
500 
501  // keep special nodes (e.g. nodes with counts data), also when they don't fulfill hierarchyLayers (e.g. are located on smaller ways)
502  if ((nodeIDsToKeep != null) && nodeIDsToKeep.contains(node.id)) {
503  node.used = true;
504  }
505  }
506  log.info("... done marking OSM nodes that shoud be kept.");
507 
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));
514 
515  for (int i = 1; i < way.nodes.size(); i++) {
516  OsmNode node = this.nodes.get(way.nodes.get(i));
517  if (node.used) {
518  if (prevRealNode == node) {
519  /* We detected a loop between two "real" nodes.
520  * Set some nodes between the start/end-loop-node to "used" again.
521  * But don't set all of them to "used", as we still want to do some network-thinning.
522  * I decided to use sqrt(.)-many nodes in between...
523  */
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;
530  }
531  }
532  prevRealNodeIndex = i;
533  prevRealNode = node;
534  }
535  }
536  }
537  }
538  log.info("... done verifying that we did not mark nodes that build a loop.");
539  }
540 
547  protected boolean canNodeBeRemoved(OsmNode node) {
548  return !this.keepPaths && node.ways.size()<=1 && !node.endPoint;
549  }
550 
555  protected void createMatsimData() {
556  log.info("Create the required nodes ...") ;
557  for (OsmNode node : this.nodes.values()) {
558  if (node.used) {
559  Node nn = this.network.getFactory().createNode(Id.create(node.id, Node.class), node.coord);
560  setOrModifyNodeAttributes(nn, node);
561  this.network.addNode(nn);
562  }
563  }
564  log.info("... done creating the required nodes.");
565 
566  log.info( "Create the links ...") ;
567  this.id = 1;
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));
572  double length = 0.0;
573  OsmNode lastToNode = fromNode;
574  if (fromNode.used) {
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) {
578  length += CoordUtils.calcEuclideanDistance(lastToNode.coord, toNode.coord);
579  if (toNode.used) {
580 
581  if(this.hierarchyLayers.isEmpty()) {
582  createLink(this.network, way, fromNode, toNode, length);
583  } else {
584  for (OsmFilter osmFilter : this.hierarchyLayers) {
585  if(osmFilter.coordInFilter(fromNode.coord, way.hierarchy)){
586  createLink(this.network, way, fromNode, toNode, length);
587  break;
588  }
589  if(osmFilter.coordInFilter(toNode.coord, way.hierarchy)){
590  createLink(this.network, way, fromNode, toNode, length);
591  break;
592  }
593  }
594  }
595 
596  fromNode = toNode;
597  length = 0.0;
598  }
599  lastToNode = toNode;
600  }
601  }
602  }
603  }
604  }
605  log.info("... done creating the links.");
606  }
607 
608  private void createLink(final Network network, final OsmWay way, final OsmNode fromNode, final OsmNode toNode,
609  final double length) {
610  String highway = way.tags.get(TAG_HIGHWAY);
611 
612  if ("no".equals(way.tags.get(TAG_ACCESS))) {
613  return;
614  }
615 
616  // load defaults
617  OsmHighwayDefaults defaults = this.highwayDefaults.get(highway);
618  if (defaults == null) {
619  this.unknownHighways.add(highway);
620  return;
621  }
622 
623  double nofLanesForward = defaults.lanesPerDirection;
624  double nofLanesBackward = defaults.lanesPerDirection;
625  double laneCapacity = defaults.laneCapacity;
626  double freespeed = defaults.freespeed;
627  double freespeedFactor = defaults.freespeedFactor;
628 
629  boolean oneway = isOneway(way);
630  boolean onewayReverse = isOnewayReverse(way);
631 
632  // In case trunks, primary and secondary roads are marked as oneway,
633  // the default number of lanes should be two instead of one.
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;
639  }
640  }
641 
642  String maxspeedTag = way.tags.get(TAG_MAXSPEED);
643  if (maxspeedTag != null) {
644  try {
645  if(maxspeedTag.endsWith("mph")) {
646  freespeed = Double.parseDouble(maxspeedTag.replace("mph", "").trim()) * 1.609344 / 3.6; // convert mph to m/s
647  } else {
648  freespeed = Double.parseDouble(maxspeedTag) / 3.6; // convert km/h to m/s
649  }
650  if (useVspAdjustments) {
651  // For links whose maxspeed is known, we assume that those with maxspeed lower than or equl to 51km/h are 'urban' links
652  // For these, we reduced speeds to 50% to account for traffic lights/intersections now, kn,ik,dz, apr'18
653  if (freespeed <= 51./3.6) {
654  freespeed = freespeed/2;
655  }
656  }
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.");
661  }
662  }
663  } else {
664  if (useVspAdjustments) {
665  // For links whose maxspeed is unknown, we assume that links with a length (after removal of purely geometric nodes) of more
666  // than 300m are 'rural' others 'urban'. 'Rural' speed is 100km/h, 'urban' linearly increasing from 10km/h at zero length to
667  // the 'rural' speed for links with a length of 300m. kn,ik,dz, apr'18
668  if(highway.equalsIgnoreCase("primary") || highway.equalsIgnoreCase("secondary") || highway.equalsIgnoreCase("tertiary")
669  || highway.equalsIgnoreCase("primary_link") || highway.equalsIgnoreCase("secondary_link") || highway.equalsIgnoreCase("tertiary_link")) {
670  if (length > 300.) {
671  freespeed = 80. / 3.6; // Might be different (but also not too much different) in other countries
672  } else {
673  freespeed = (10. + 70./300 * length) / 3.6;
674  }
675  }
676  }
677  }
678 
679  if (useVspAdjustments) {
680  // Adjustments that KN had been using for a while: For short links, often roundabouts or short u-turns, etc.
681  if (length < 100 ) {
682  laneCapacity = 2 * laneCapacity;
683  }
684  }
685 
686  // check tag "lanes"
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);
690  try {
691  /* If the tag "lanes:forward" or "lanes:backward" is set, use them.
692  * If not, give each direction half of the total number of lanes (except it is a oneway street)
693  * fzwick,nkuehnel, apr'17
694  * If only one of them is set and total number of lanes is known (as it is often the case),
695  * calculate missing value as the difference... tthunig, oct'17
696  */
697  if (lanesForwardTag != null && lanesBackwardTag != null){
698  nofLanesForward = Double.parseDouble(lanesForwardTag);
699  nofLanesBackward = Double.parseDouble(lanesBackwardTag);
700  // done
701  } else if (oneway) {
702  nofLanesBackward = 0;
703  if (lanesForwardTag != null)
704  nofLanesForward = Double.parseDouble(lanesForwardTag);
705  else if (lanesTag != null)
706  nofLanesForward = Double.parseDouble(lanesTag);
707  // else keep default
708  } else if (onewayReverse) {
709  nofLanesForward = 0;
710  if (lanesBackwardTag != null)
711  nofLanesBackward = Double.parseDouble(lanesBackwardTag);
712  else if (lanesTag != null)
713  nofLanesBackward = Double.parseDouble(lanesTag);
714  // else keep default
715  } else if (lanesForwardTag != null) {
716  // i.e. lanesBackwardTag is null
717  nofLanesForward = Double.parseDouble(lanesForwardTag);
718  if (lanesTag != null)
719  nofLanesBackward = Double.parseDouble(lanesTag) - nofLanesForward;
720  // else keep default
721  }
722  else if (lanesBackwardTag != null) {
723  // i.e. lanesForwardTag is null
724  nofLanesBackward = Double.parseDouble(lanesBackwardTag);
725  if (lanesTag != null)
726  nofLanesForward = Double.parseDouble(lanesTag) - nofLanesBackward;
727  // else keep default
728  } else { // i.e. lanesForwardTag and lanesBackwardTag are null. no oneway
729  if (lanesTag != null) {
730  /* By default, the OSM lanes tag specifies the total number of lanes in both direction.
731  * So, let's distribute them between both directions. michalm, jan'16
732  */
733  nofLanesForward = Double.parseDouble(lanesTag) / 2;
734  nofLanesBackward = nofLanesForward;
735  }
736  // else keep default
737  }
738  } catch (Exception e) {
739  if (!this.unknownLanesTags.contains(lanesTag)) {
740  this.unknownLanesTags.add(lanesTag);
741  log.warn("Could not parse lanes tag:" + e.getMessage() + ". Ignoring it.");
742  }
743  }
744 
745  double capacityForward = nofLanesForward * laneCapacity;
746  double capacityBackward = nofLanesBackward * laneCapacity;
747 
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");
753  }
754  }
755 
756  // only create link, if both nodes were found, node could be null, since nodes outside a layer were dropped
757  Id<Node> fromId = Id.create(fromNode.id, Node.class);
758  Id<Node> toId = Id.create(toNode.id, Node.class);
759  if(network.getNodes().get(fromId) != null && network.getNodes().get(toId) != null){
760  String origId = Long.toString(way.id);
761 
762  // Forward direction (in relation to the direction of the OSM way object)
763  if (forwardDirectionExists(way)) {
764  Link l = network.getFactory().createLink(Id.create(this.id, Link.class), network.getNodes().get(fromId), network.getNodes().get(toId));
765  l.setLength(length);
766  l.setFreespeed(freespeed);
767  l.setCapacity(capacityForward);
768  l.setNumberOfLanes(nofLanesForward);
769  NetworkUtils.setOrigId(l, origId);
770  NetworkUtils.setType(l, highway);
771  setOrModifyLinkAttributes(l, way, true);
772  network.addLink(l);
773  this.id++;
774  }
775  // Backward/reverse direction (in relation to the direction of the OSM way object)
776  if (reverseDirectionExists(way)) {
777  Link l = network.getFactory().createLink(Id.create(this.id, Link.class), network.getNodes().get(toId), network.getNodes().get(fromId));
778  l.setLength(length);
779  l.setFreespeed(freespeed);
780  l.setCapacity(capacityBackward);
781  l.setNumberOfLanes(nofLanesBackward);
782  NetworkUtils.setOrigId(l, origId);
783  NetworkUtils.setType(l, highway);
784  setOrModifyLinkAttributes(l, way, false);
785  network.addLink(l);
786  this.id++;
787  }
788  }
789  }
790 
791  protected boolean isOneway(OsmWay way) {
792  String onewayTag = way.tags.get(TAG_ONEWAY);
793  if (onewayTag != null) {
794  if ("yes".equals(onewayTag) || "true".equals(onewayTag) || "1".equals(onewayTag)) {
795  return true;
796  } else if ("-1".equals(onewayTag) || "reverse".equals(onewayTag)) {
797  return false;
798  } else if ("no".equals(onewayTag) || "false".equals(onewayTag) || "0".equals(onewayTag)) {
799  return false; // may be used to overwrite defaults
800  }
801  else {
802  log.warn("Could not interpret oneway tag:" + onewayTag + ". Ignoring it.");
803  }
804  }
805  // If no oneway tag was found, see if it is a link in a roundabout
806  if ("roundabout".equals(way.tags.get(TAG_JUNCTION))) {
807  // if "junction" is not set in tags, get() returns null and equals() evaluates to false
808  return true;
809  }
810  // If no direction-specific tag was found, use default value
811  OsmHighwayDefaults defaults = this.highwayDefaults.get(way.tags.get(TAG_HIGHWAY));
812  return defaults.oneway;
813  }
814 
815  protected boolean isOnewayReverse(OsmWay way) {
816  String onewayTag = way.tags.get(TAG_ONEWAY);
817  if (onewayTag != null) {
818  if ("yes".equals(onewayTag) || "true".equals(onewayTag) || "1".equals(onewayTag)) {
819  return false;
820  } else if ("-1".equals(onewayTag) || "reverse".equals(onewayTag)) {
821  return true;
822  } else if ("no".equals(onewayTag) || "false".equals(onewayTag) || "0".equals(onewayTag)) {
823  return false; // may be used to overwrite defaults
824  }
825  else {
826  log.warn("Could not interpret oneway tag:" + onewayTag + ". Ignoring it.");
827  }
828  }
829  // Was set like this before; rather get this from defaults like for (forward) oneway? tt, aug'17
830  return false;
831  }
832 
836  protected void setOrModifyNodeAttributes(Node n, OsmNode node) {
837  }
838 
842  protected void setOrModifyLinkAttributes(Link l, OsmWay way, boolean forwardDirection) {
843  }
844 
848  protected boolean reverseDirectionExists(OsmWay way) {
849  return !isOneway(way);
850  }
851 
855  protected boolean forwardDirectionExists(OsmWay way) {
856  return !isOnewayReverse(way);
857  }
858 
859  public static interface OsmFilter {
860  boolean coordInFilter( final Coord coord, final int hierarchyLevel ) ;
861  }
862 
863  private static class OsmFilterImpl implements OsmFilter {
864  private final Coord coordNW;
865  private final Coord coordSE;
866  private final int hierarchy;
867 
868  OsmFilterImpl(final Coord coordNW, final Coord coordSE, final int hierarchy) {
869  this.coordNW = coordNW;
870  this.coordSE = coordSE;
871  this.hierarchy = hierarchy;
872  }
873 
874  @Override
875  public boolean coordInFilter(final Coord coord, final int hierarchyLevel){
876  if(this.hierarchy < hierarchyLevel){
877  return false;
878  }
879 
880  return ((this.coordNW.getX() < coord.getX() && coord.getX() < this.coordSE.getX()) &&
881  (this.coordNW.getY() > coord.getY() && coord.getY() > this.coordSE.getY()));
882  }
883  }
884 
885  private static class GeographicallyNonrestrictingOsmFilterImpl implements OsmFilter {
886  private final int hierarchy;
887  GeographicallyNonrestrictingOsmFilterImpl(final int hierarchy) {
888  this.hierarchy = hierarchy;
889  }
890 
891  @Override
892  public boolean coordInFilter(final Coord coord, final int hierarchyLevel){
893  if(this.hierarchy < hierarchyLevel){
894  return false;
895  }
896  return true ;
897  }
898  }
899 
900  protected final static class OsmNode {
901  public final long id;
902  public boolean used = false;
903  public Map<Long, OsmWay> ways = new HashMap<>();
904  public Coord coord;
905  public boolean endPoint = false;
906 
907  public OsmNode(final long id, final Coord coord) {
908  this.id = id;
909  this.coord = coord;
910  }
911  }
912 
913  protected final static class OsmWay {
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;
918 
919  public OsmWay(final long id) {
920  this.id = id;
921  }
922  }
923 
924  protected static class OsmHighwayDefaults {
925 
926  public final int hierarchy;
927  public final double lanesPerDirection;
928  public final double freespeed;
929  public final double freespeedFactor;
930  public final double laneCapacity;
931  public final boolean oneway;
932 
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;
940  }
941  }
942 
943  protected class OsmXmlParser extends MatsimXmlParser {
944 
945  protected OsmWay currentWay = null;
946  protected OsmNode currentNode = null;
947  protected final Map<Long, OsmNode> nodes;
948  protected final Map<Long, OsmWay> ways;
949  /*package*/ final Counter nodeCounter = new Counter("node ");
950  /*package*/ final Counter wayCounter = new Counter("way ");
952  private boolean loadNodes = true;
953  private boolean loadWays = true;
954  private boolean mergeNodes = false;
955  private boolean collectNodes = false;
956 
957  public OsmXmlParser(final Map<Long, OsmNode> nodes, final Map<Long, OsmWay> ways, final CoordinateTransformation transform) {
959  this.nodes = nodes;
960  this.ways = ways;
961  this.transform = transform;
962  this.setValidating(false);
963  }
964 
965  public void enableOptimization(final int step) {
966  this.loadNodes = false;
967  this.loadWays = false;
968  this.collectNodes = false;
969  this.mergeNodes = false;
970  if (step == 1) {
971  this.collectNodes = true;
972  } else if (step == 2) {
973  this.mergeNodes = true;
974  this.loadWays = true;
975  }
976  }
977 
978  @Override
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"));
985  this.currentNode = new OsmNode(id, this.transform.transform(new Coord(lon, lat)));
986  this.nodes.put(id, currentNode);
987  this.nodeCounter.incCounter();
988  } else if (this.mergeNodes) {
989  OsmNode node = this.nodes.get(Long.valueOf(atts.getValue("id")));
990  if (node != null) {
991  double lat = Double.parseDouble(atts.getValue("lat"));
992  double lon = Double.parseDouble(atts.getValue("lon"));
993  node.coord = this.transform.transform(new Coord(lon, lat)) ;
994  this.nodeCounter.incCounter();
995  }
996  }
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")));
1002  }
1003  } else if ("tag".equals(name)) {
1004  if (this.currentWay != null) {
1005  String key = StringCache.get(atts.getValue("k"));
1006  for (String tag : allTags) {
1007  if (tag.equals(key)) {
1008  this.currentWay.tags.put(key, StringCache.get(atts.getValue("v")));
1009  break;
1010  }
1011  }
1012  }
1013  }
1014  }
1015 
1016  @Override
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;
1021  OsmHighwayDefaults osmHighwayDefaults = OsmNetworkReader.this.highwayDefaults.get(this.currentWay.tags.get(TAG_HIGHWAY));
1022  if (osmHighwayDefaults != null) {
1023  int hierarchy = osmHighwayDefaults.hierarchy;
1024  this.currentWay.hierarchy = hierarchy;
1025  if (OsmNetworkReader.this.hierarchyLayers.isEmpty()) {
1026  used = true;
1027  }
1028  if (this.collectNodes) {
1029  used = true;
1030  } else {
1031  for (OsmFilter osmFilter : OsmNetworkReader.this.hierarchyLayers) {
1032  for (Long nodeId : this.currentWay.nodes) {
1033  OsmNode node = this.nodes.get(nodeId);
1034  if(node != null && osmFilter.coordInFilter(node.coord, this.currentWay.hierarchy)){
1035  used = true;
1036  break;
1037  }
1038  }
1039  if (used) {
1040  break;
1041  }
1042  }
1043  }
1044  }
1045  if (used) {
1046  if (this.collectNodes) {
1047  for (long id : this.currentWay.nodes) {
1048  this.nodes.put(id, new OsmNode(id, new Coord(0, 0)));
1049  }
1050  } else if (this.loadWays) {
1051  this.ways.put(this.currentWay.id, this.currentWay);
1052  this.wayCounter.incCounter();
1053  }
1054  }
1055  }
1056  this.currentWay = null;
1057  } else if ("node".equals(name)) {
1058  // was already added in startTag(...)
1059  this.currentNode = null;
1060  }
1061  }
1062 
1063  }
1064 
1065  private static class StringCache {
1066  private static final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>(10000);
1067 
1075  public static String get(final String string) {
1076  String s = cache.putIfAbsent(string, string);
1077  if (s == null) {
1078  return string;
1079  }
1080  return s;
1081  }
1082  }
1083 }
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)
Map< Id< Node >, ? extends Node > getNodes()
static double calcEuclideanDistance(Coord coord, Coord other)
final void setScaleMaxSpeed(final boolean scaleMaxSpeed)
void parse(final String osmFilename, final Supplier< InputStream > streamSupplier)
OsmNetworkReader(final Network network, final CoordinateTransformation transformation)
final void setHierarchyLayer(final int hierarchy)
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 Map< String, OsmHighwayDefaults > highwayDefaults
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
void addWayTags(List< String > wayTagsToAdd)
boolean coordInFilter(final Coord coord, final int hierarchyLevel)
void createLink(final Network network, final OsmWay way, final OsmNode fromNode, final OsmNode toNode, final double length)
void setOrModifyLinkAttributes(Link l, OsmWay way, boolean forwardDirection)
final void addOsmFilter(final OsmFilter osmFilter)
Link createLink(final Id< Link > id, final Node fromNode, final Node toNode)
final void setMemoryOptimization(final boolean memoryEnabled)
boolean coordInFilter(final Coord coord, final int hierarchyLevel)
OsmNetworkReader(final Network network, final CoordinateTransformation transformation, final boolean useHighwayDefaults)
void setCapacityPeriod(double capPeriod)
Map< Id< Link >, ? extends Link > getLinks()
Node createNode(final Id< Node > id, final Coord coord)
final void parse(final Supplier< InputStream > streamSupplier)
OsmXmlParser(final Map< Long, OsmNode > nodes, final Map< Long, OsmWay > ways, final CoordinateTransformation transform)
final void readFile(final String filename)
static void setType(Node node, final String type)
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)
final void setHighwayDefaults(final int hierarchy, final String highwayType, final double lanesPerDirection, final double freespeed, final double freespeedFactor, final double laneCapacity_vehPerHour)
final void setKeepPaths(final boolean keepPaths)
final CoordinateTransformation transform
void setOrModifyNodeAttributes(Node n, OsmNode node)