MATSIM
NetworkTeleatlasAddManeuverRestrictions.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * NetworkTeleatlasAddManeuverRestrictions.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 java.io.FileInputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.TreeMap;
28 
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.Logger;
31 import org.geotools.api.data.SimpleFeatureSource;
32 import org.geotools.api.feature.simple.SimpleFeature;
33 import org.geotools.data.shapefile.dbf.DbaseFileReader;
34 import org.geotools.data.simple.SimpleFeatureIterator;
35 import org.matsim.api.core.v01.Id;
44 import org.matsim.core.utils.io.IOUtils;
45 
55 
57  // member variables
59 
60  private final static Logger log = LogManager.getLogger(NetworkTeleatlasAddManeuverRestrictions.class);
61 
65  private final String mnShpFileName; // teleatlas maneuvers shape file name
66 
70  private final String mpDbfFileName; // teleatlas maneuver paths dbf file name
71 
77  public boolean removeUTurns = false;
78 
85  public double expansionRadius = 0.000030; // WGS84
86 
93  public double linkSeparation = 0.000005; // WGS84
94 
95  private static final String MN_ID_NAME = "ID";
96  private static final String MN_FEATTYP_NAME = "FEATTYP";
97  private static final String MN_JNCTID_NAME = "JNCTID";
98 
99  private static final String MP_ID_NAME = "ID";
100  private static final String MP_SEQNR_NAME = "SEQNR";
101  private static final String MP_TRPELID_NAME = "TRPELID";
102 
104  // constructors
106 
113  public NetworkTeleatlasAddManeuverRestrictions(final String mnShpFileName, final String mpDbfFileName) {
114  log.info("init " + this.getClass().getName() + " module...");
115  this.mnShpFileName = mnShpFileName;
116  this.mpDbfFileName = mpDbfFileName;
117  log.info("done.");
118  }
119 
121  // run method
123 
160  @Override
161  public void run(final Network network) {
162  try {
163  run2(network);
164  } catch (Exception e) {
165  throw new RuntimeException(e);
166  }
167  }
168 
169  private void run2(final Network network) throws Exception {
170  log.info("running " + this.getClass().getName() + " module...");
171  NetworkExpandNode neModule = new NetworkExpandNode(network, expansionRadius, this.linkSeparation);
172 
173  TreeMap<String, TreeMap<Integer,Id<Link>>> mSequences = new TreeMap<>();
174 
175  try (FileInputStream fis = new FileInputStream(this.mpDbfFileName)) {
176  DbaseFileReader r = new DbaseFileReader(fis.getChannel(), true, IOUtils.CHARSET_WINDOWS_ISO88591);
177  // get header indices
178  int mpIdNameIndex = -1;
179  int mpSeqNrNameIndex = -1;
180  int mpTrpelIDNameIndex = -1;
181  for (int i=0; i<r.getHeader().getNumFields(); i++) {
182  if (r.getHeader().getFieldName(i).equals(MP_ID_NAME)) { mpIdNameIndex = i; }
183  if (r.getHeader().getFieldName(i).equals(MP_SEQNR_NAME)) { mpSeqNrNameIndex = i; }
184  if (r.getHeader().getFieldName(i).equals(MP_TRPELID_NAME)) { mpTrpelIDNameIndex = i; }
185  }
186  if (mpIdNameIndex < 0) { throw new NoSuchFieldException("Field name '"+MP_ID_NAME+"' not found."); }
187  if (mpSeqNrNameIndex < 0) { throw new NoSuchFieldException("Field name '"+MP_SEQNR_NAME+"' not found."); }
188  if (mpTrpelIDNameIndex < 0) { throw new NoSuchFieldException("Field name '"+MP_TRPELID_NAME+"' not found."); }
189  log.trace(" FieldName-->Index:");
190  log.trace(" "+MP_ID_NAME+"-->"+mpIdNameIndex);
191  log.trace(" "+MP_SEQNR_NAME+"-->"+mpSeqNrNameIndex);
192  log.trace(" "+MP_TRPELID_NAME+"-->"+mpTrpelIDNameIndex);
193 
194  // create mp data structure
195  // TreeMap<mpId,TreeMap<mpSeqNr,linkId>>
196  log.info(" parsing meneuver paths dbf file...");
197  while (r.hasNext()) {
198  Object[] entries = r.readEntry();
199  String mpId = entries[mpIdNameIndex].toString();
200  int mpSeqNr = Integer.parseInt(entries[mpSeqNrNameIndex].toString());
201  Id<Link> linkId = Id.create(entries[mpTrpelIDNameIndex].toString(), Link.class);
202  TreeMap<Integer,Id<Link>> mSequence = mSequences.get(mpId);
203  if (mSequence == null) {
204  mSequence = new TreeMap<>();
205  mSequences.put(mpId, mSequence);
206  }
207  if (mSequence.put(mpSeqNr,linkId) != null) {
208  fis.close();
209  throw new IllegalArgumentException(MP_ID_NAME+"="+mpId+": "+MP_SEQNR_NAME+" "+mpSeqNr+" already exists.");
210  }
211  }
212  log.info(" "+mSequences.size()+" maneuvers sequences stored.");
213  log.info(" done.");
214  r.close();
215  }
216 
217  // store the maneuver list of the nodes
218  // TreeMap<NodeId,ArrayList<Tuple<MnId,MnFeatType>>>
219  log.info(" parsing meneuver shape file...");
220  TreeMap<Id<Node>, ArrayList<Tuple<String, Integer>>> maneuvers = new TreeMap<>();
221  SimpleFeatureSource fs = GeoFileReader.readDataFile(this.mnShpFileName);
222  SimpleFeatureIterator fIt = fs.getFeatures().features();
223  while (fIt.hasNext()) {
224  SimpleFeature f = fIt.next();
225  int featType = Integer.parseInt(f.getAttribute(MN_FEATTYP_NAME).toString());
226  if ((featType == 2103) || (featType == 2102) || (featType == 2101)) {
227  // keep 'Prohibited Maneuver' (2103), 'Restricted Maneuver' (2102) and 'Calculated/Derived Prohibited Maneuver' (2101)
228  Id<Node> nodeId = Id.create(f.getAttribute(MN_JNCTID_NAME).toString(), Node.class);
229  ArrayList<Tuple<String, Integer>> ms = maneuvers.get(nodeId);
230  if (ms == null) {
231  ms = new ArrayList<>();
232  }
233  Tuple<String, Integer> m = new Tuple<>(f.getAttribute(MN_ID_NAME).toString(), featType);
234  ms.add(m);
235  maneuvers.put(nodeId, ms);
236  }
237  else if ((featType == 9401) || (featType == 2104)) {
238  //ignore 'Bifurcation' (9401) and 'Priority Maneuver' (2104)
239  }
240  else {
241  throw new IllegalArgumentException("mnId="+f.getAttribute(MN_ID_NAME)+": "+MN_FEATTYP_NAME+"="+featType+" not known.");
242  }
243  }
244  fIt.close();
245  log.info(" "+maneuvers.size()+" nodes with maneuvers stored.");
246  log.info(" done.");
247 
248  // create a maneuver matrix for each given node and
249  // expand those nodes
250  log.info(" expand nodes according to the given manveuvers...");
251  int nodesIgnoredCnt = 0;
252  int nodesAssignedCnt = 0;
253  int maneuverIgnoredCnt = 0;
254  int maneuverAssignedCnt = 0;
255  int virtualNodesCnt = 0;
256  int virtualLinksCnt = 0;
257  for (Map.Entry<Id<Node>, ArrayList<Tuple<String, Integer>>> entry : maneuvers.entrySet()) {
258  Id<Node> nodeId = entry.getKey();
259  if (network.getNodes().get(nodeId) == null) {
260  log.trace(" nodeid="+nodeId+": maneuvers exist for that node but node is missing. Ignoring and proceeding anyway...");
261  nodesIgnoredCnt++;
262  } else {
263  // node found
264  Node n = network.getNodes().get(nodeId);
265  // init maneuver matrix
266  // TreeMap<fromLinkId,TreeMap<toLinkId,turnAllowed>>
267  TreeMap<Id<Link>, TreeMap<Id<Link>, Boolean>> mmatrix = new TreeMap<>();
268  // assign maneuvers for given node to the matrix
269  ArrayList<Tuple<String, Integer>> ms = entry.getValue();
270  for (Tuple<String, Integer> m : ms) {
271  // get maneuver path sequence for given maneuver
272  TreeMap<Integer,Id<Link>> mSequence = mSequences.get(m.getFirst());
273  if (mSequence == null) { throw new Exception("nodeid="+nodeId+"; mnId="+m.getFirst()+": no maneuver sequence given."); }
274  if (mSequence.size() < 2) { throw new Exception("nodeid="+nodeId+"; mnId="+m.getFirst()+": mSequenceSize="+mSequence.size()+" not alowed!"); }
275  // get the first element of the sequence, defining the start link for the maneuver
276  Id<Link> firstLinkid = mSequence.values().iterator().next();
277  // go through each other element (target link of the maneuver) of the sequence by sequence number
278  for (Id<Link> otherLinkId : mSequence.values()) {
279  // get the start link and the target link of the maneuver
280  Link inLink = n.getInLinks().get(Id.create(firstLinkid+"FT", Link.class));
281  if (inLink == null) {
282  inLink = n.getInLinks().get(Id.create(firstLinkid+"TF", Link.class));
283  }
284  Link outLink = n.getOutLinks().get(Id.create(otherLinkId+"FT", Link.class));
285  if (outLink == null) {
286  outLink = n.getOutLinks().get(Id.create(otherLinkId+"TF", Link.class));
287  }
288  if ((inLink != null) && (outLink != null)) {
289  // start and target link found and they are incident to the given node
290  if (m.getSecond() == 2102) {
291  // restricted maneuver: given start and target link path is allowed to drive
292  // store it to the matrix
293  TreeMap<Id<Link>, Boolean> outLinkMap = mmatrix.get(inLink.getId());
294  if (outLinkMap == null) {
295  outLinkMap = new TreeMap<>();
296  }
297  outLinkMap.put(outLink.getId(), Boolean.TRUE);
298  mmatrix.put(inLink.getId(),outLinkMap);
299  }
300  else {
301  // prohibited maneuver: given start and target link path is not allowed to drive
302  // store it to the matrix
303  TreeMap<Id<Link>,Boolean> outLinkMap = mmatrix.get(inLink.getId());
304  if (outLinkMap == null) {
305  outLinkMap = new TreeMap<>();
306  }
307  outLinkMap.put(outLink.getId(), Boolean.FALSE);
308  mmatrix.put(inLink.getId(),outLinkMap);
309  }
310  maneuverAssignedCnt++;
311  }
312  else { maneuverIgnoredCnt++; }
313  }
314  }
315  // complete the matrix
316  for (TreeMap<Id<Link>, Boolean> fromLinkEntry : mmatrix.values()) {
317  // detect inlinks with restricted maneuvers
318  boolean hasRestrictedManeuver = false;
319  for (Boolean b : fromLinkEntry.values()) {
320  if (b) { hasRestrictedManeuver = true; }
321  }
322  // add missing toLink maneuvers
323  for (Id<Link> toLinkId : n.getOutLinks().keySet()) {
324  if (!fromLinkEntry.containsKey(toLinkId)) {
325  fromLinkEntry.put(toLinkId, !hasRestrictedManeuver);
326  }
327  }
328  }
329  // add allowed maneuvers for fromLinks which were not assigned yet.
330  for (Id<Link> fromLinkId : n.getInLinks().keySet()) {
331  if (!mmatrix.containsKey(fromLinkId)) {
332  mmatrix.put(fromLinkId, new TreeMap<Id<Link>, Boolean>());
333  for (Id<Link> toLinkId : n.getOutLinks().keySet()) {
334  mmatrix.get(fromLinkId).put(toLinkId, Boolean.TRUE);
335  }
336  }
337  }
338  // remove all U-turns from the matrix
339  if (this.removeUTurns) {
340  for (Id<Link> fromLinkId : n.getInLinks().keySet()) {
341  String str1 = fromLinkId.toString().substring(0,fromLinkId.toString().length()-2);
342  for (Id<Link> toLinkId : n.getOutLinks().keySet()) {
343  String str2 = toLinkId.toString().substring(0,toLinkId.toString().length()-2);
344  if (str1.equals(str2)) {
345  mmatrix.get(fromLinkId).put(toLinkId, Boolean.FALSE);
346  }
347  }
348  }
349  }
350  // create arraylist with turn tuples
351  ArrayList<TurnInfo> turns = new ArrayList<>();
352  for (Map.Entry<Id<Link>, TreeMap<Id<Link>, Boolean>> fromLinkEntry : mmatrix.entrySet()) {
353  Id<Link> fromLinkId = fromLinkEntry.getKey();
354  for (Map.Entry<Id<Link>, Boolean> toLinkEntry : fromLinkEntry.getValue().entrySet()) {
355  if (toLinkEntry.getValue()) {
356  turns.add(new TurnInfo(fromLinkId, toLinkEntry.getKey()));
357  }
358  }
359  }
360  // expand the node
361  Tuple<List<Node>, List<Link>> t = neModule.expandNode(nodeId, turns);
362  virtualNodesCnt += t.getFirst().size();
363  virtualLinksCnt += t.getSecond().size();
364  nodesAssignedCnt++;
365  }
366  }
367  log.info(" "+nodesAssignedCnt+" nodes expanded.");
368  log.info(" "+maneuverAssignedCnt+" maneuvers assigned.");
369  log.info(" "+virtualNodesCnt+" new nodes created.");
370  log.info(" "+virtualLinksCnt+" new links created.");
371  log.info(" "+nodesIgnoredCnt+" nodes with given maneuvers (2103, 2102 or 2101) ignored.");
372  log.info(" "+maneuverIgnoredCnt+" maneuvers ignored (while node was found).");
373  log.info(" done.");
374  log.info("done.");
375  }
376 
378  // print methods
380 
386  public final void printInfo(final String prefix) {
387  System.out.println(prefix+"configuration of "+this.getClass().getName()+":");
388  System.out.println(prefix+" options:");
389  System.out.println(prefix+" removeUTurns: "+removeUTurns);
390  System.out.println(prefix+" expansionRadius: "+expansionRadius);
391  System.out.println(prefix+" linkSeparation: "+linkSeparation);
392  System.out.println(prefix+" maneuver shape:");
393  System.out.println(prefix+" mnShpFileName: "+mnShpFileName);
394  System.out.println(prefix+" MN_ID_NAME: "+MN_ID_NAME);
395  System.out.println(prefix+" MN_FEATTYP_NAME: "+MN_FEATTYP_NAME);
396  System.out.println(prefix+" MN_JNCTID_NAME: "+MN_JNCTID_NAME);
397  System.out.println(prefix+" maneuver path dbf:");
398  System.out.println(prefix+" mpDbfFileName: "+mpDbfFileName);
399  System.out.println(prefix+" MP_ID_NAME: "+MP_ID_NAME);
400  System.out.println(prefix+" MP_SEQNR_NAME: "+MP_SEQNR_NAME);
401  System.out.println(prefix+" MP_TRPELID_NAME: "+MP_TRPELID_NAME);
402  System.out.println(prefix+"done.");
403  }
404 }
Map< Id< Link >, ? extends Link > getInLinks()
static final Charset CHARSET_WINDOWS_ISO88591
Definition: IOUtils.java:183
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 SimpleFeatureSource readDataFile(final String filename)
Map< Id< Link >, ? extends Link > getOutLinks()