MATSIM
NetworkImpl.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *********************************************************************** *
4  * *
5  * copyright : (C) 2007 by the members listed in the COPYING, *
6  * LICENSE and WARRANTY file. *
7  * email : info at matsim dot org *
8  * *
9  * *********************************************************************** *
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * See also COPYING, LICENSE and WARRANTY file *
16  * *
17  * *********************************************************************** */
18 
19 package org.matsim.core.network;
20 
21 import org.apache.logging.log4j.LogManager;
22 import org.apache.logging.log4j.Logger;
23 import org.matsim.api.core.v01.Coord;
24 import org.matsim.api.core.v01.Id;
25 import org.matsim.api.core.v01.IdMap;
34 
35 import java.util.*;
36 
48 /*deliberately package*/ final class NetworkImpl implements Network, Lockable, TimeDependentNetwork, SearchableNetwork {
49 
50  private final static Logger log = LogManager.getLogger(NetworkImpl.class);
51 
52  private double capacityPeriod = 3600.0 ;
53 
54  private final IdMap<Node, Node> nodes = new IdMap<>(Node.class);
55 
56  private final IdMap<Link, Link> links = new IdMap<>(Link.class);
57 
58  private QuadTree<Node> nodeQuadTree = null;
59 
60  private LinkQuadTree linkQuadTree = null;
61 
62  private static final double DEFAULT_EFFECTIVE_CELL_SIZE = 7.5;
63 
64  private double effectiveCellSize = DEFAULT_EFFECTIVE_CELL_SIZE;
65 
66  private double effectiveLaneWidth = 3.75;
67 
68  private NetworkFactory factory;
69 
70 // private final Collection<NetworkChangeEvent> networkChangeEvents = new ArrayList<>();
71 
72  private final Queue<NetworkChangeEvent> networkChangeEvents
73 // = new PriorityQueue<>(11, new Comparator<NetworkChangeEvent>() {
74 // @Override
75 // public int compare(NetworkChangeEvent arg0, NetworkChangeEvent arg1) {
76 // return Double.compare(arg0.getStartTime(), arg1.getStartTime()) ;
77 // }
78 // });
79  = new PriorityQueue<>(11, new NetworkChangeEvent.StartTimeComparator() ) ;
80 
81  private String name = null;
82 
83  private int counter=0;
84 
85  private int nextMsg=1;
86 
87  private int counter2=0;
88 
89  private int nextMsg2=1;
90 
91  private boolean locked = false ;
92  private final Attributes attributes = new AttributesImpl();
93 
94  NetworkImpl(LinkFactory linkFactory) {
95  this.factory = new NetworkFactoryImpl(this, linkFactory);
96  }
97 
98  @Override
99  public void addLink(final Link link) {
100  Link testLink = links.get(link.getId());
101  if (testLink != null) {
102  if (testLink == link) {
103  log.warn("Trying to add a link a second time to the network. link id = " + link.getId().toString());
104  return;
105  }
106  throw new IllegalArgumentException("There exists already a link with id = " + link.getId().toString() +
107  ".\nExisting link: " + testLink + "\nLink to be added: " + link +
108  ".\nLink is not added to the network.");
109  }
110 
111  /* Check if the link's nodes are in the network. */
112  Node fromNode = nodes.get(link.getFromNode().getId());
113  if (fromNode == null) {
114  throw new IllegalArgumentException("Trying to add link = " + link.getId() + ", but its fromNode = " + link.getFromNode().getId() + " has not been added to the network.");
115  }
116  Node toNode = nodes.get(link.getToNode().getId());
117  if (toNode == null) {
118  throw new IllegalArgumentException("Trying to add link = " + link.getId() + ", but its toNode = " + link.getToNode().getId() + " has not been added to the network.");
119  }
120 
121  if (!fromNode.getOutLinks().containsKey(link.getId()))
122  fromNode.addOutLink(link);
123  if (!toNode.getInLinks().containsKey(link.getId()))
124  toNode.addInLink(link);
125 
126  link.setFromNode(fromNode);
127  link.setToNode(toNode);
128 
129  links.put(link.getId(), link);
130 
131  if (this.linkQuadTree != null) {
132  double linkMinX = Math.min(link.getFromNode().getCoord().getX(), link.getToNode().getCoord().getX());
133  double linkMaxX = Math.max(link.getFromNode().getCoord().getX(), link.getToNode().getCoord().getX());
134  double linkMinY = Math.min(link.getFromNode().getCoord().getY(), link.getToNode().getCoord().getY());
135  double linkMaxY = Math.max(link.getFromNode().getCoord().getY(), link.getToNode().getCoord().getY());
136  if (Double.isInfinite(this.linkQuadTree.getMinEasting())) {
137  // looks like the quad tree was initialized with infinite bounds, see MATSIM-278.
138  this.linkQuadTree = null;
139  } else if (this.linkQuadTree.getMinEasting() <= linkMinX && this.linkQuadTree.getMaxEasting() > linkMaxX
140  && this.linkQuadTree.getMinNorthing() <= linkMinY && this.linkQuadTree.getMaxNorthing() > linkMaxY) {
141  this.linkQuadTree.put(link);
142  } else {
143  // we add a link outside the current bounds, invalidate it
144  this.linkQuadTree = null;
145  }
146  }
147 
148 
149  // show counter
150  this.counter++;
151  if (this.counter % this.nextMsg == 0) {
152  this.nextMsg *= 4;
153  printLinksCount();
154  }
155  if ( this.locked && link instanceof Lockable ) {
156  ((Lockable)link).setLocked() ;
157  }
158  }
159 
160  private void printLinksCount() {
161  log.info(" link # " + this.counter);
162  }
163 
164  private void printNodesCount() {
165  log.info(" node # " + this.counter2);
166  }
167 
168  @Override
169  public void addNode(final Node nn) {
170  Id<Node> id = nn.getId() ;
171  Node node = this.nodes.get(id);
172  if (node != null) {
173  if (node == nn) {
174  log.warn("Trying to add a node a second time to the network. node id = " + id.toString());
175  return;
176  }
177  throw new IllegalArgumentException("There exists already a node with id = " + id.toString() +
178  ".\nExisting node: " + node + "\nNode to be added: " + nn +
179  ".\nNode is not added to the network.");
180  }
181  this.nodes.put(id, nn);
182  if (this.nodeQuadTree != null) {
183  if (Double.isInfinite(this.nodeQuadTree.getMinEasting())) {
184  // looks like the quad tree was initialized with infinite bounds, see MATSIM-278.
185  this.nodeQuadTree.clear();
186  this.nodeQuadTree = null;
187  } else if (this.nodeQuadTree.getMinEasting() <= nn.getCoord().getX() && this.nodeQuadTree.getMaxEasting() > nn.getCoord().getX()
188  && this.nodeQuadTree.getMinNorthing() <= nn.getCoord().getY() && this.nodeQuadTree.getMaxNorthing() > nn.getCoord().getY()) {
189  this.nodeQuadTree.put(nn.getCoord().getX(), nn.getCoord().getY(), nn);
190  } else {
191  // we add a node outside the current bounds, invalidate it
192  this.nodeQuadTree.clear();
193  this.nodeQuadTree = null;
194  }
195  }
196 
197  // show counter
198  this.counter2++;
199  if (this.counter2 % this.nextMsg2 == 0) {
200  this.nextMsg2 *= 4;
201  printNodesCount();
202  }
203 
204  if ( this.locked && nn instanceof Lockable ) {
205  ((Lockable)nn).setLocked() ;
206  }
207  }
208  // ////////////////////////////////////////////////////////////////////
209  // remove methods
210  // ////////////////////////////////////////////////////////////////////
211 
212  @Override
213  public Node removeNode(final Id<Node> nodeId) {
214  Node n = this.nodes.remove(nodeId);
215  if (n == null) {
216  return null;
217  }
218  HashSet<Link> links1 = new HashSet<>();
219  links1.addAll(n.getInLinks().values());
220  links1.addAll(n.getOutLinks().values());
221  for (Link l : links1) {
222  removeLink(l.getId());
223  }
224  if (this.nodeQuadTree != null) {
225  this.nodeQuadTree.remove(n.getCoord().getX(),n.getCoord().getY(),n);
226  }
227  return n;
228  }
229 
230  @Override
231  public Link removeLink(final Id<Link> linkId) {
232  Link l = this.links.remove(linkId);
233  if (l == null) {
234  return null;
235  }
236  l.getFromNode().removeOutLink(l.getId()) ;
237  l.getToNode().removeInLink(l.getId()) ;
238 
239  if (this.linkQuadTree != null) {
240  this.linkQuadTree.remove(l);
241  }
242 
243  return l;
244  }
245 
246  // ////////////////////////////////////////////////////////////////////
247  // set methods
248  // ////////////////////////////////////////////////////////////////////
249 
253  @Override
254  public void setCapacityPeriod(final double capPeriod) {
255  testForLocked() ;
256  this.capacityPeriod = (int) capPeriod;
257  }
258  @Override
259  public void setEffectiveCellSize(final double effectiveCellSize) {
260  testForLocked() ;
261  if (this.effectiveCellSize != effectiveCellSize) {
262  if (effectiveCellSize != DEFAULT_EFFECTIVE_CELL_SIZE) {
263  log.warn("Setting effectiveCellSize to a non-default value of " + effectiveCellSize);
264  } else {
265  log.info("Setting effectiveCellSize to " + effectiveCellSize);
266  }
267  this.effectiveCellSize = effectiveCellSize;
268  }
269  }
270  @Override
271  public void setEffectiveLaneWidth(final double effectiveLaneWidth) {
272  testForLocked() ;
273  if (!Double.isNaN(this.effectiveLaneWidth) && this.effectiveLaneWidth != effectiveLaneWidth) {
274  log.warn(this + "[effectiveLaneWidth=" + this.effectiveLaneWidth + " already set. Will be overwritten with " + effectiveLaneWidth + "]");
275  }
276  this.effectiveLaneWidth = effectiveLaneWidth;
277  }
278 
286  @Override public void setNetworkChangeEvents(final List<NetworkChangeEvent> events) {
287  this.networkChangeEvents.clear();
288  for(Link link : getLinks().values()) {
289  if (link instanceof TimeVariantLinkImpl) {
290  ((TimeVariantLinkImpl)link).clearEvents();
291  }
292  // Presumably, there is no exception here if this fails because it can be interpreted: maybe only some links are time-dependent
293  // and others are not, and it is sufficient if the time-dependent ones can be configured by the addNetworkChangeEvent method.
294  // kai, jul'16
295  }
296  for (NetworkChangeEvent event : events) {
297  this.addNetworkChangeEvent(event);
298  }
299  }
300 
308  @Override
309  public void addNetworkChangeEvent(final NetworkChangeEvent event) {
310  this.networkChangeEvents.add(event);
311  for (Link link : event.getLinks()) {
312  if (link instanceof TimeVariantLinkImpl) {
313  ((TimeVariantLinkImpl)link).applyEvent(event);
314  } else {
315  throw new IllegalArgumentException("Link " + link.getId().toString() + " is not timeVariant. "
316  + "Did you make the network factory time variant? The easiest way to achieve this is "
317  + "either in the config file, or syntax of the type\n"
318  + "config.network().setTimeVariantNetwork(true);\n"
319  + "Scenario scenario = ScenarioUtils.load/createScenario(config);\n"
320  + "Note that the scenario needs to be created _after_ the config option is set, otherwise"
321  + "the factory will already be there.");
322  }
323  }
324  }
325 
326  @Override
327  public double getCapacityPeriod() {
328  return this.capacityPeriod;
329  }
330  @Override
331  public double getEffectiveCellSize() {
332  return this.effectiveCellSize;
333  }
334 
335  @Override
336  public double getEffectiveLaneWidth() {
337  return this.effectiveLaneWidth;
338  }
339 
340  @Override
341  public Map<Id<Node>, Node> getNodes() {
342  return Collections.unmodifiableMap(this.nodes);
343  }
344 
345  @Override public Link getNearestLinkExactly(final Coord coord) {
346  return this.getLinkQuadTree().getNearest(coord.getX(), coord.getY());
347  }
348 
355  @Override public Node getNearestNode(final Coord coord) {
356  return this.getNodeQuadTree().getClosest(coord.getX(), coord.getY());
357  }
358 
366  @Override public Collection<Node> getNearestNodes(final Coord coord, final double distance) {
367  return this.getNodeQuadTree().getDisk(coord.getX(), coord.getY(), distance);
368  }
369 
370  @Override
371  public Queue<NetworkChangeEvent> getNetworkChangeEvents() {
372  return this.networkChangeEvents;
373  }
374  @Override
375  public NetworkFactory getFactory() {
376  return this.factory;
377  }
378 
379  // ////////////////////////////////////////////////////////////////////
380  // print methods
381  // ////////////////////////////////////////////////////////////////////
382 
383  @Override
384  public String toString() {
385  return super.toString() +
386  "[capperiod=" + this.capacityPeriod + "]" +
387  "[nof_nodes=" + this.nodes.size() + "]";
388  }
389 
390  // public void connect() {
391  // buildQuadTree();
392  // }
393  // it is safer if all functionality that could be done here is either done lazily or directly when nodes/links are added. kai, jul'16
394 
395  synchronized private void buildQuadTree() {
396  /* the method must be synchronized to ensure we only build one quadTree
397  * in case that multiple threads call a method that requires the quadTree.
398  */
399  if (this.nodeQuadTree != null) {
400  return;
401  }
402  double startTime = System.currentTimeMillis();
403  double minx = Double.POSITIVE_INFINITY;
404  double miny = Double.POSITIVE_INFINITY;
405  double maxx = Double.NEGATIVE_INFINITY;
406  double maxy = Double.NEGATIVE_INFINITY;
407  for (Node n : this.nodes.values()) {
408  if (n.getCoord().getX() < minx) { minx = n.getCoord().getX(); }
409  if (n.getCoord().getY() < miny) { miny = n.getCoord().getY(); }
410  if (n.getCoord().getX() > maxx) { maxx = n.getCoord().getX(); }
411  if (n.getCoord().getY() > maxy) { maxy = n.getCoord().getY(); }
412  }
413  minx -= 1.0;
414  miny -= 1.0;
415  maxx += 1.0;
416  maxy += 1.0;
417  // yy the above four lines are problematic if the coordinate values are much smaller than one. kai, oct'15
418 
419  log.info("building QuadTree for nodes: xrange(" + minx + "," + maxx + "); yrange(" + miny + "," + maxy + ")");
420  QuadTree<Node> quadTree = new QuadTree<>(minx, miny, maxx, maxy);
421  for (Node n : this.nodes.values()) {
422  quadTree.put(n.getCoord().getX(), n.getCoord().getY(), n);
423  }
424  /* assign the quadTree at the very end, when it is complete.
425  * otherwise, other threads may already start working on an incomplete quadtree
426  */
427  this.nodeQuadTree = quadTree;
428  log.info("Building QuadTree took " + ((System.currentTimeMillis() - startTime) / 1000.0) + " seconds.");
429  }
430 
431  synchronized private void buildLinkQuadTree() {
432  if (this.linkQuadTree != null) {
433  return;
434  }
435  double startTime = System.currentTimeMillis();
436  double minx = Double.POSITIVE_INFINITY;
437  double miny = Double.POSITIVE_INFINITY;
438  double maxx = Double.NEGATIVE_INFINITY;
439  double maxy = Double.NEGATIVE_INFINITY;
440  for (Node n : this.nodes.values()) {
441  if (n.getCoord().getX() < minx) { minx = n.getCoord().getX(); }
442  if (n.getCoord().getY() < miny) { miny = n.getCoord().getY(); }
443  if (n.getCoord().getX() > maxx) { maxx = n.getCoord().getX(); }
444  if (n.getCoord().getY() > maxy) { maxy = n.getCoord().getY(); }
445  }
446  minx -= 1.0;
447  miny -= 1.0;
448  maxx += 1.0;
449  maxy += 1.0;
450  // yy the above four lines are problematic if the coordinate values are much smaller than one. kai, oct'15
451 
452  log.info("building LinkQuadTree for nodes: xrange(" + minx + "," + maxx + "); yrange(" + miny + "," + maxy + ")");
453  LinkQuadTree qt = new LinkQuadTree(minx, miny, maxx, maxy);
454  for (Link l : this.links.values()) {
455  qt.put(l);
456  }
457  this.linkQuadTree = qt;
458  log.info("Building LinkQuadTree took " + ((System.currentTimeMillis() - startTime) / 1000.0) + " seconds.");
459  }
460 
461  @Override
462  public Map<Id<Link>, Link> getLinks() {
463  return Collections.unmodifiableMap(links);
464  }
465 
466  void setFactory(final NetworkFactory networkFactory) {
467  this.factory = networkFactory;
468  }
469 
470  @Override
471  public String getName() {
472  return this.name;
473  }
474  @Override
475  public void setName(String name) {
476  this.name = name;
477  }
478 
479  @Override
480  public void setLocked() {
481  this.locked = true ;
482  for ( Link link : this.links.values() ) {
483  if ( link instanceof Lockable ) {
484  ((Lockable) link).setLocked();
485  }
486  }
487  for ( Node node : this.nodes.values() ) {
488  if ( node instanceof Lockable ) {
489  ((Lockable) node).setLocked();
490  }
491  }
492  }
493  private void testForLocked() {
494  if ( locked ) {
495  throw new RuntimeException( "Network is locked; too late to do this. See comments in code.") ;
496  }
497  }
498  @Override public Attributes getAttributes() {
499  return attributes;
500  }
501  @Override public LinkQuadTree getLinkQuadTree() {
502  if (this.linkQuadTree == null) buildLinkQuadTree();
503  return this.linkQuadTree ;
504  }
505  @Override public QuadTree<Node> getNodeQuadTree() {
506  if (this.nodeQuadTree == null) buildQuadTree();
507  return this.nodeQuadTree ;
508  }
509 }