MATSIM
NetworkExpandNode.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * NetworkExpandNode.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.network.algorithms;
22 
23 import org.matsim.api.core.v01.Coord;
24 import org.matsim.api.core.v01.Id;
31 
32 import java.util.*;
33 
40 public final class NetworkExpandNode {
41 
42  private final Network network;
43  private double expRadius = 1.0;
44  private double offset = 0.0;
45  private double dist = 1.0; // cache a value that is often used in the method expandNode
46 
47  public NetworkExpandNode(final Network network, final double expRadius, final double offset) {
48  if (network == null) {
49  throw new IllegalArgumentException("network must not be null.");
50  }
51  this.network = network;
52  this.setExpRadius(expRadius);
53  this.setOffset(offset);
54  }
55 
60  void setExpRadius(final double expRadius) {
61  if (Double.isNaN(expRadius)) {
62  throw new IllegalArgumentException("expansion radius must not be NaN.");
63  }
64  this.expRadius = expRadius;
65  this.dist = Math.sqrt(this.expRadius * this.expRadius - this.offset * this.offset);
66  }
67 
72  void setOffset(final double offset) {
73  if (Double.isNaN(offset)) {
74  throw new IllegalArgumentException("expansion offset must not be NaN.");
75  }
76  this.offset = offset;
77  this.dist = Math.sqrt(this.expRadius * this.expRadius - this.offset * this.offset);
78  }
79 
177  public final Tuple<List<Node>,List<Link>> expandNode(final Id<Node> nodeId, final List<TurnInfo> turns) {
178  double e = this.offset;
179  Node node = network.getNodes().get(nodeId);
180  if (node == null) {
181  throw new IllegalArgumentException("nodeid="+nodeId+": not found in the network.");
182  }
183  if (turns == null) {
184  throw new IllegalArgumentException("nodeid="+nodeId+": turn list not defined!");
185  }
186 
187  for (TurnInfo turn1 : turns) {
188  Id<Link> first = turn1.getFromLinkId();
189  if (first == null) {
190  throw new IllegalArgumentException("given list contains 'null' values.");
191  }
192  if (!node.getInLinks().containsKey(first)) {
193  throw new IllegalArgumentException("nodeid=" + nodeId + ", linkid=" + first + ": link not an inlink of given node.");
194  }
195  Id<Link> second = turn1.getToLinkId();
196  if (second == null) {
197  throw new IllegalArgumentException("given list contains 'null' values.");
198  }
199  if (!node.getOutLinks().containsKey(second)) {
200  throw new IllegalArgumentException("nodeid=" + nodeId + ", linkid=" + second + ": link not an outlink of given node.");
201  }
202  }
203 
204  // remove the node
205  Map<Id<Link>,Link> inlinks = new TreeMap<>(node.getInLinks());
206  Map<Id<Link>,Link> outlinks = new TreeMap<>(node.getOutLinks());
207  if (network.removeNode(node.getId()) == null) { throw new RuntimeException("nodeid="+nodeId+": Failed to remove node from the network."); }
208 
209  ArrayList<Node> newNodes = new ArrayList<>(inlinks.size()+outlinks.size());
210  ArrayList<Link> newLinks = new ArrayList<>(turns.size());
211  // add new nodes and connect them with the in and out links
212  int nodeIdCnt = 0;
213  double d = this.dist;
214  for (Link inlink : inlinks.values()) {
215  Coord c = node.getCoord();
216  Coord p = inlink.getFromNode().getCoord();
217  Coord cp = new Coord(p.getX() - c.getX(), p.getY() - c.getY());
218  double lcp = Math.sqrt(cp.getX()*cp.getX()+cp.getY()*cp.getY());
219  if (Math.abs(lcp) < 1e-8) {
220  // c and p seem to lay on top of each other, leading to Double.NaN in some calculations
221  lcp = d;
222  }
223  double dx = cp.getX() / lcp;
224  double dy = cp.getY() / lcp;
225  double x = c.getX() + d * dx - e * dy;
226  double y = c.getY() + d * dy + e * dx;
227 
228  Node n = network.getFactory().createNode(Id.create(node.getId()+"-"+nodeIdCnt, Node.class), new Coord(x, y));
229  network.addNode(n);
230  newNodes.add(n);
231  nodeIdCnt++;
232  Link l = network.getFactory().createLink(inlink.getId(), inlink.getFromNode(), n);
233  l.setLength(inlink.getLength());
234  l.setFreespeed(inlink.getFreespeed());
235  l.setCapacity(inlink.getCapacity());
236  l.setNumberOfLanes(inlink.getNumberOfLanes());
237  l.setAllowedModes(inlink.getAllowedModes());
240  network.addLink(l);
241  }
242  for (Link outlink : outlinks.values()) {
243  Coord c = node.getCoord();
244  Coord p = outlink.getToNode().getCoord();
245  Coord cp = new Coord(p.getX() - c.getX(), p.getY() - c.getY());
246  double lcp = Math.sqrt(cp.getX()*cp.getX()+cp.getY()*cp.getY());
247  if (Math.abs(lcp) < 1e-8) {
248  // c and p seem to lay on top of each other, leading to Double.NaN in some calculations
249  lcp = d;
250  }
251  double dx = cp.getX() / lcp;
252  double dy = cp.getY() / lcp;
253  double x = c.getX() + d * dx + e * dy;
254  double y = c.getY() + d * dy - e * dx;
255  Node n = network.getFactory().createNode(Id.create(node.getId()+"-"+nodeIdCnt, Node.class), new Coord(x, y));
256  network.addNode(n);
257  newNodes.add(n);
258  nodeIdCnt++;
259  Link l = network.getFactory().createLink(outlink.getId(), n, outlink.getToNode());
260  l.setLength(outlink.getLength());
261  l.setFreespeed(outlink.getFreespeed());
262  l.setCapacity(outlink.getCapacity());
263  l.setNumberOfLanes(outlink.getNumberOfLanes());
264  l.setAllowedModes(outlink.getAllowedModes());
267  network.addLink(l);
268  }
269 
270  // add virtual links for the turn restrictions
271  for (int i=0; i<turns.size(); i++) {
272  TurnInfo turn = turns.get(i);
273  Link fromLink = network.getLinks().get(turn.getFromLinkId());
274  Link toLink = network.getLinks().get(turn.getToLinkId());
275  Link l = network.getFactory().createLink(Id.create(fromLink.getId()+"-"+i, Link.class), fromLink.getToNode(), toLink.getFromNode());
276  double dist = CoordUtils.calcEuclideanDistance(toLink.getFromNode().getCoord(), fromLink.getToNode().getCoord());
277  if (dist < 0.1 * this.expRadius) {
278  // mostly the case when nodes are on top of each other
279  // use a small length, but not really hard-coded because of different coordinate systems ("1" is not always 1 meter)
280  dist = 0.1 * this.expRadius;
281  }
282  l.setLength(dist);
283  l.setFreespeed(fromLink.getFreespeed());
284  l.setCapacity(fromLink.getCapacity());
285  l.setNumberOfLanes(fromLink.getNumberOfLanes());
286  if (turn.getModes() == null) {
287  l.setAllowedModes(fromLink.getAllowedModes());
288  } else {
289  l.setAllowedModes(turn.getModes());
290  }
293  network.addLink(l);
294  newLinks.add(l);
295  }
296  return new Tuple<List<Node>, List<Link>>(newNodes,newLinks);
297  }
298 
314  public boolean turnsAreSameAsSingleNode(final Id<Node> nodeId, final List<TurnInfo> turns, final boolean ignoreUTurns) {
315  Node node = this.network.getNodes().get(nodeId);
316 
317  // first create list of all possible turning options
318  Map<Id<Link>, Map<Id<Link>, Set<String>>> allTurns = new HashMap<>();
319 
320  for (Link inLink : node.getInLinks().values()) {
321  Map<Id<Link>, Set<String>> t2 = new HashMap<>();
322  for (Link outLink : node.getOutLinks().values()) {
323  if (inLink.getFromNode() != outLink.getToNode() || !ignoreUTurns) {
324  HashSet<String> modes = new HashSet<>();
325  modes.addAll(inLink.getAllowedModes());
326  modes.retainAll(outLink.getAllowedModes()); // modes contains now all useful modes
327  t2.put(outLink.getId(), modes);
328  }
329  }
330  allTurns.put(inLink.getId(), t2);
331  }
332 
333  /* now compare the given turning options and remove them from all possible options.
334  * if there remain some options in the "all possibles", we know that the given turns
335  * act indeed as restrictions. */
336  for (TurnInfo ti : turns) {
337  Id<Link> from = ti.fromLinkId;
338  Id<Link> to = ti.toLinkId;
339  Map<Id<Link>, Set<String>> t2 = allTurns.get(from);
340  if (t2 != null) {
341  Set<String> modes = t2.get(to);
342  if (modes != null) {
343  if (ti.getModes() == null) {
344  // no specific modes are set, so remove all modes from the inLink
345  // because the modes are the intersection of modes from inLink and outLink, we can just clear it
346  modes.clear();
347  } else {
348  modes.removeAll(ti.getModes());
349  }
350  }
351  }
352  }
353 
354  // now do the check
355  for (Map<Id<Link>, Set<String>> m : allTurns.values()) {
356  for (Set<String> modes : m.values()) {
357  if (modes.size() > 0) {
358  return false;
359  }
360  }
361  }
362 
363  return true;
364  }
365 
366  public static class TurnInfo {
367 
368  private final Id<Link> fromLinkId;
369  private final Id<Link> toLinkId;
370  private final Set<String> modes;
371 
372  public TurnInfo(final Id<Link> fromLinkId, final Id<Link> toLinkId) {
373  this.fromLinkId = fromLinkId;
374  this.toLinkId = toLinkId;
375  this.modes = null;
376  }
377 
378  public TurnInfo(final Id<Link> fromLinkId, final Id<Link> toLinkId, final Set<String> modes) {
379  this.fromLinkId = fromLinkId;
380  this.toLinkId = toLinkId;
381  this.modes = modes;
382  }
383 
385  return this.fromLinkId;
386  }
387 
389  return this.toLinkId;
390  }
391 
392  public Set<String> getModes() {
393  return this.modes;
394  }
395 
396  @Override
397  public boolean equals(Object obj) {
398  if (!(obj instanceof TurnInfo)) {
399  return false;
400  }
401  TurnInfo ti = (TurnInfo) obj;
402  return (ti.fromLinkId.equals(this.fromLinkId))
403  && (ti.toLinkId.equals(this.toLinkId))
404  && ((ti.modes == null && this.modes == null)
405  || (ti.modes != null && ti.modes.equals(this.modes))
406  );
407  }
408 
409  @Override
410  public int hashCode() {
411  return this.fromLinkId.hashCode() & this.toLinkId.hashCode() & this.modes.hashCode();
412  }
413 
414  }
415 
416 }
static void setOrigId(final Node node, final String id)
static< T > Id< T > get(int index, final Class< T > type)
Definition: Id.java:112
Map< Id< Node >, ? extends Node > getNodes()
TurnInfo(final Id< Link > fromLinkId, final Id< Link > toLinkId, final Set< String > modes)
static double calcEuclideanDistance(Coord coord, Coord other)
boolean turnsAreSameAsSingleNode(final Id< Node > nodeId, final List< TurnInfo > turns, final boolean ignoreUTurns)
Node removeNode(final Id< Node > nodeId)
Map< Id< Link >, ? extends Link > getInLinks()
static String getOrigId(Node node)
final Tuple< List< Node >, List< Link > > expandNode(final Id< Node > nodeId, final List< TurnInfo > turns)
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
static String getType(Node node)
Link createLink(final Id< Link > id, final Node fromNode, final Node toNode)
NetworkExpandNode(final Network network, final double expRadius, final double offset)
Map< Id< Link >, ? extends Link > getLinks()
Node createNode(final Id< Node > id, final Coord coord)
static void setType(Node node, final String type)
TurnInfo(final Id< Link > fromLinkId, final Id< Link > toLinkId)
Map< Id< Link >, ? extends Link > getOutLinks()