1 package org.opentrafficsim.core.network.factory;
2
3 import java.awt.geom.Point2D;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import javax.vecmath.Point2d;
11 import javax.vecmath.Point3d;
12
13 import org.opentrafficsim.core.network.Network;
14 import org.opentrafficsim.core.network.NetworkException;
15 import org.opentrafficsim.core.network.Node;
16 import org.opentrafficsim.core.network.geotools.NodeGeotools;
17 import org.opentrafficsim.core.network.lane.CrossSectionElement;
18 import org.opentrafficsim.core.network.point2d.NodePoint2D;
19 import org.opentrafficsim.core.unit.LengthUnit;
20 import org.opentrafficsim.core.unit.SpeedUnit;
21 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
22
23 import com.vividsolutions.jts.geom.Coordinate;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class NetworkLaneParser
55 {
56
57 private final Class<?> networkIdClass;
58
59
60 private final Class<?> nodeClass;
61
62
63 private final Class<?> nodeIdClass;
64
65
66 private final Class<?> nodePointClass;
67
68
69 private final Class<?> linkIdClass;
70
71
72 private static final List<String> TOKENS = new ArrayList<>();
73
74
75 private static final List<String> LINK_TOKENS = new ArrayList<>();
76
77
78 private static final List<String> NODE_TOKENS = new ArrayList<>();
79
80
81 private static final Map<String, SpeedUnit> SPEED_UNITS = new HashMap<>();
82
83
84 private static final Map<String, LengthUnit> LENGTH_UNITS = new HashMap<>();
85
86
87 @SuppressWarnings("rawtypes")
88 private Map<String, Node> nodes = new HashMap<>();
89
90 static
91 {
92 TOKENS.addAll(Arrays.asList(new String[] {"NODE", "N", "LANE", "L", "GTU", "G"}));
93 NODE_TOKENS.addAll(Arrays.asList(new String[] {"COORDINATE", "C", "NAME", "N"}));
94 LINK_TOKENS.addAll(Arrays.asList(new String[] {"NAME", "N", "ELEMENTS", "E", "TYPE", "T", "LENGTH", "L", "SPEED",
95 "S", "RADIUS", "R", "ANGLE", "A", "FROMNODE", "FROM", "F", "TONODE", "TO", "WIDTH", "W"}));
96
97 SPEED_UNITS.put("km/h", SpeedUnit.KM_PER_HOUR);
98 SPEED_UNITS.put("mi/h", SpeedUnit.MILE_PER_HOUR);
99 SPEED_UNITS.put("m/s", SpeedUnit.METER_PER_SECOND);
100
101 LENGTH_UNITS.put("mm", LengthUnit.MILLIMETER);
102 LENGTH_UNITS.put("cm", LengthUnit.CENTIMETER);
103 LENGTH_UNITS.put("dm", LengthUnit.DECIMETER);
104 LENGTH_UNITS.put("dam", LengthUnit.DEKAMETER);
105 LENGTH_UNITS.put("hm", LengthUnit.HECTOMETER);
106 LENGTH_UNITS.put("m", LengthUnit.METER);
107 LENGTH_UNITS.put("km", LengthUnit.KILOMETER);
108 LENGTH_UNITS.put("mi", LengthUnit.MILE);
109 LENGTH_UNITS.put("y", LengthUnit.YARD);
110 LENGTH_UNITS.put("ft", LengthUnit.FOOT);
111 }
112
113
114
115
116
117
118
119
120 public NetworkLaneParser(final Class<?> networkIdClass, final Class<?> nodeClass, final Class<?> nodeIdClass,
121 final Class<?> nodePointClass, final Class<?> linkIdClass)
122 {
123 this.networkIdClass = networkIdClass;
124 this.nodeClass = nodeClass;
125 this.nodeIdClass = nodeIdClass;
126 this.nodePointClass = nodePointClass;
127 this.linkIdClass = linkIdClass;
128 }
129
130
131
132
133
134
135 @SuppressWarnings({"rawtypes", "unchecked"})
136 public final Network<?, ?> build(final String original) throws NetworkException
137 {
138
139 this.nodes.clear();
140
141
142 boolean keep = true;
143 boolean inString = false;
144 StringBuilder ns = new StringBuilder();
145 for (int i = 0; i < original.length(); i++)
146 {
147 char c = original.charAt(i);
148 if (c == '"')
149 {
150 inString = !inString;
151 }
152 if (c == '#')
153 {
154 keep = false;
155 }
156 else if (c == '\r' || c == '\n')
157 {
158 keep = true;
159 }
160 if (keep)
161 {
162 if (!inString && c != ',' && c != ';' & c != '\t' && c != '\r' && c != '\n')
163 {
164 ns.append(c);
165 }
166 else
167 {
168 ns.append(' ');
169 }
170 }
171 }
172
173 String networkId = "";
174
175 while (ns.length() > 0)
176 {
177 String token = eatToken(ns, TOKENS);
178 String args = eatArgs(ns, token);
179
180 switch (token)
181 {
182 case "NODE":
183 case "N":
184 parseNode(args);
185 break;
186
187 case "LINK":
188 case "L":
189 parseLink(args);
190 break;
191
192 default:
193 throw new NetworkException("Parsing network. NODE: unknown token: " + token);
194 }
195 }
196
197 Network network = new Network(makeId(this.networkIdClass, networkId));
198 return network;
199 }
200
201
202
203
204
205
206 @SuppressWarnings("rawtypes")
207 private Node parseNode(final String nodeArgs) throws NetworkException
208 {
209 StringBuilder ns = new StringBuilder(nodeArgs);
210 String nodeName = null;
211 Point3d coordinate = null;
212
213 while (ns.length() > 0)
214 {
215 String token = eatToken(ns, NODE_TOKENS);
216 switch (token)
217 {
218 case "NAME":
219 case "N":
220 nodeName = eatValue(ns, token);
221 nodeName.replace('"', ' ');
222 break;
223
224 case "COORDINATE":
225 case "C":
226 double x = eatDoubleValue(ns, token);
227 double y = eatDoubleValue(ns, token);
228 coordinate = new Point3d(x, y, 0);
229 break;
230
231 default:
232 throw new NetworkException("Parsing network. NODE: unknown token: " + token);
233 }
234 }
235 if (coordinate == null)
236 {
237 throw new NetworkException("Parsing network. NODE " + nodeName + ", no known coordinate");
238 }
239 if (nodeName == null)
240 {
241 throw new NetworkException("Parsing network. NODE has no name");
242 }
243 Node node = makeNode(this.nodeClass, makeId(this.nodeIdClass, nodeName), makePoint(this.nodePointClass, coordinate));
244 this.nodes.put(node.getId().toString(), node);
245 return node;
246 }
247
248
249
250
251
252 @SuppressWarnings("rawtypes")
253 private void parseLink(final String laneArgs) throws NetworkException
254 {
255 StringBuilder ls = new StringBuilder(laneArgs);
256
257 String linkName = null;
258 Node fromNode = null;
259 Node toNode = null;
260 String type = null;
261 DoubleScalar.Rel<LengthUnit> length = null;
262 DoubleScalar.Abs<SpeedUnit> speed = null;
263 DoubleScalar.Rel<LengthUnit> radius = null;
264 double angle = Double.NaN;
265 Map<String, CrossSectionElement> lanes = null;
266
267 while (ls.length() > 0)
268 {
269 String token = eatToken(ls, LINK_TOKENS);
270 switch (token)
271 {
272 case "TYPE":
273 case "T":
274 type = eatValue(ls, token);
275 break;
276
277 case "NAME":
278 case "N":
279 linkName = eatValue(ls, token);
280 break;
281
282 case "FROMNODE":
283 case "FROM":
284 case "F":
285 String fromNodeName = eatValue(ls, token);
286 fromNodeName.replace('"', ' ');
287 fromNode = this.nodes.get(fromNodeName);
288 break;
289
290 case "TONODE":
291 case "TO":
292 String toNodeName = eatValue(ls, token);
293 toNodeName.replace('"', ' ');
294 toNode = this.nodes.get(toNodeName);
295 break;
296
297 case "ELEMENTS":
298 case "E":
299 lanes = parseLanes(ls);
300 break;
301
302 case "LENGTH":
303 case "L":
304 length = eatLengthRel(ls, token);
305 break;
306
307 case "SPEED":
308 case "S":
309 speed = eatSpeedAbs(ls, token);
310
311 break;
312
313 case "RADIUS":
314 case "R":
315 radius = eatLengthRel(ls, token);
316 break;
317
318 case "ANGLE":
319 case "A":
320 angle = eatDoubleValue(ls, token);
321 break;
322
323 case "WIDTH":
324 case "W":
325
326
327 break;
328
329 default:
330 throw new NetworkException("Parsing network. LINK: unknown token: " + token);
331 }
332 }
333
334 if (linkName == null)
335 {
336 throw new NetworkException("Parsing network. LINK has no name");
337 }
338 if (fromNode == null || toNode == null || type == null || lanes == null)
339 {
340 throw new NetworkException("Parsing network. LINK " + linkName + " has missing elements");
341 }
342 if (type.equals("S") && length == null)
343 {
344 throw new NetworkException("Parsing network. LINK " + linkName + " (S) has missing length");
345 }
346 if (type.equals("C") && (radius == null || angle == Double.NaN))
347 {
348 throw new NetworkException("Parsing network. LINK " + linkName + " (C) has missing radius or angle");
349 }
350
351
352 }
353
354
355
356
357
358
359
360
361 private Object makeId(final Class<?> clazz, final String ids) throws NetworkException
362 {
363 Object id = null;
364 try
365 {
366 if (String.class.isAssignableFrom(clazz))
367 {
368 id = new String(ids);
369 }
370 else if (int.class.isAssignableFrom(clazz))
371 {
372 id = Integer.valueOf(ids);
373 }
374 else if (long.class.isAssignableFrom(clazz))
375 {
376 id = Long.valueOf(ids);
377 }
378 else
379 {
380 throw new NetworkException("Parsing network. ID class " + clazz.getName() + ": cannot instantiate.");
381 }
382 }
383 catch (NumberFormatException nfe)
384 {
385 throw new NetworkException("Parsing network. ID class " + clazz.getName() + ": cannot instantiate number: "
386 + ids, nfe);
387 }
388 return id;
389 }
390
391
392
393
394
395
396
397
398 private Object makePoint(final Class<?> clazz, final Point3d p) throws NetworkException
399 {
400 Object point = null;
401 if (Point3d.class.isAssignableFrom(clazz))
402 {
403 point = p;
404 }
405 else if (Point2D.class.isAssignableFrom(clazz))
406 {
407 point = new Point2D.Double(p.x, p.y);
408 }
409 else if (Point2d.class.isAssignableFrom(clazz))
410 {
411 point = new Point2d(new double[] {p.x, p.y});
412 }
413 else if (Coordinate.class.isAssignableFrom(clazz))
414 {
415 point = new Coordinate(p.x, p.y, p.z);
416 }
417 else
418 {
419 throw new NetworkException("Parsing network. Point class " + clazz.getName() + ": cannot instantiate.");
420 }
421 return point;
422 }
423
424
425
426
427
428
429
430
431 @SuppressWarnings({"unchecked", "rawtypes"})
432 private Node makeNode(final Class<?> clazz, final Object id, final Object point) throws NetworkException
433 {
434 if (NodeGeotools.class.isAssignableFrom(clazz))
435 {
436 if (point instanceof Coordinate)
437 {
438 return new NodeGeotools(id, (Coordinate) point);
439 }
440 throw new NetworkException("Parsing network. Node class " + clazz.getName()
441 + ": cannot instantiate. Wrong Coordinate type: " + point.getClass() + ", coordinate: " + point);
442 }
443 else if (NodePoint2D.class.isAssignableFrom(clazz))
444 {
445 if (point instanceof Point2D)
446 {
447 return new NodePoint2D(id, (Point2D) point);
448 }
449 throw new NetworkException("Parsing network. Node class " + clazz.getName()
450 + ": cannot instantiate. Wrong Point2D type: " + point.getClass() + ", coordinate: " + point);
451 }
452 else
453 {
454 throw new NetworkException("Parsing network. Node class " + clazz.getName() + ": cannot instantiate.");
455 }
456 }
457
458
459
460
461
462
463
464 @SuppressWarnings("checkstyle:finalparameters")
465 private String eatToken(StringBuilder ns, final List<String> tokens) throws NetworkException
466 {
467 ns.replace(0, ns.length() - 1, ns.toString().trim());
468 int eq = ns.indexOf("=");
469 String token = ns.substring(0, eq - 1).trim();
470 ns.delete(0, eq + 1);
471 if (!tokens.contains(token))
472 {
473 throw new NetworkException("Parsing network. Got token:" + token + ", expected one of:" + tokens);
474 }
475 return token;
476 }
477
478
479
480
481
482
483
484 @SuppressWarnings("checkstyle:finalparameters")
485 private String eatValue(StringBuilder args, final String token) throws NetworkException
486 {
487 args.replace(0, args.length() - 1, args.toString().trim());
488 if (args.length() == 0)
489 {
490 throw new NetworkException("Parsing network. Expected value for token: " + token + ", but none found");
491 }
492 int space = args.indexOf(" ");
493 String value = args.substring(0, space - 1).trim();
494 args.delete(0, space + 1);
495 return value;
496 }
497
498
499
500
501
502
503
504 @SuppressWarnings("checkstyle:finalparameters")
505 private double eatDoubleValue(StringBuilder args, final String token) throws NetworkException
506 {
507 String s = eatValue(args, token);
508 try
509 {
510 double value = Double.parseDouble(s);
511 return value;
512 }
513 catch (NumberFormatException nfe)
514 {
515 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate number: " + s, nfe);
516 }
517 }
518
519
520
521
522
523
524
525 @SuppressWarnings("checkstyle:finalparameters")
526 private SpeedUnit eatSpeedUnit(StringBuilder s, final String token) throws NetworkException
527 {
528 SpeedUnit u = null;
529 for (String us : SPEED_UNITS.keySet())
530 {
531 if (s.toString().contains(us))
532 {
533 u = SPEED_UNITS.get(us);
534 s.delete(s.indexOf(us), s.indexOf(us) + us.length() - 1);
535 }
536 }
537 if (u == null)
538 {
539 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate speed unit in: " + s);
540 }
541 return u;
542 }
543
544
545
546
547
548
549
550 @SuppressWarnings("checkstyle:finalparameters")
551 private DoubleScalar.Abs<SpeedUnit> eatSpeedAbs(StringBuilder s, final String token) throws NetworkException
552 {
553 SpeedUnit u = eatSpeedUnit(s, token);
554 try
555 {
556 double value = Double.parseDouble(s.toString());
557 return new DoubleScalar.Abs<SpeedUnit>(value, u);
558 }
559 catch (NumberFormatException nfe)
560 {
561 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate scalar: " + s, nfe);
562 }
563 }
564
565
566
567
568
569
570
571 @SuppressWarnings("checkstyle:finalparameters")
572 private DoubleScalar.Rel<SpeedUnit> eatSpeedRel(StringBuilder s, final String token) throws NetworkException
573 {
574 SpeedUnit u = eatSpeedUnit(s, token);
575 try
576 {
577 double value = Double.parseDouble(s.toString());
578 return new DoubleScalar.Rel<SpeedUnit>(value, u);
579 }
580 catch (NumberFormatException nfe)
581 {
582 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate scalar: " + s, nfe);
583 }
584 }
585
586
587
588
589
590
591
592 @SuppressWarnings("checkstyle:finalparameters")
593 private LengthUnit eatLengthUnit(StringBuilder s, final String token) throws NetworkException
594 {
595 LengthUnit u = null;
596 for (String us : LENGTH_UNITS.keySet())
597 {
598 if (s.toString().contains(us))
599 {
600 u = LENGTH_UNITS.get(us);
601 s.delete(s.indexOf(us), s.indexOf(us) + us.length() - 1);
602 }
603 }
604 if (u == null)
605 {
606 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate length unit in: " + s);
607 }
608 return u;
609 }
610
611
612
613
614
615
616
617 @SuppressWarnings("checkstyle:finalparameters")
618 private DoubleScalar.Abs<LengthUnit> eatLengthAbs(StringBuilder s, final String token) throws NetworkException
619 {
620 LengthUnit u = eatLengthUnit(s, token);
621 try
622 {
623 double value = Double.parseDouble(s.toString());
624 return new DoubleScalar.Abs<LengthUnit>(value, u);
625 }
626 catch (NumberFormatException nfe)
627 {
628 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate scalar: " + s, nfe);
629 }
630 }
631
632
633
634
635
636
637
638 @SuppressWarnings("checkstyle:finalparameters")
639 private DoubleScalar.Rel<LengthUnit> eatLengthRel(StringBuilder s, final String token) throws NetworkException
640 {
641 LengthUnit u = eatLengthUnit(s, token);
642 try
643 {
644 double value = Double.parseDouble(s.toString());
645 return new DoubleScalar.Rel<LengthUnit>(value, u);
646 }
647 catch (NumberFormatException nfe)
648 {
649 throw new NetworkException("Parsing network. Token " + token + ": cannot instantiate scalar: " + s, nfe);
650 }
651 }
652
653
654
655
656
657
658
659 @SuppressWarnings("checkstyle:finalparameters")
660 private String eatArgs(StringBuilder ns, final String token) throws NetworkException
661 {
662 ns.replace(0, ns.length() - 1, ns.toString().trim());
663 char bs;
664 char be;
665 if (ns.charAt(0) == '(')
666 {
667 bs = '(';
668 be = ')';
669 }
670 else if (ns.charAt(0) == '{')
671 {
672 bs = '{';
673 be = '}';
674 }
675 else if (ns.charAt(0) == '[')
676 {
677 bs = '[';
678 be = ']';
679 }
680 else
681 {
682 throw new NetworkException("Parsing network. After token:" + token + ", expected (, { or [ but got :"
683 + ns.charAt(0));
684 }
685 int index = 0;
686 int nrBracket = 0;
687 for (int i = 0; i < ns.length() && index == 0; i++)
688 {
689 if (ns.charAt(i) == bs)
690 {
691 nrBracket++;
692 }
693 else if (ns.charAt(i) == be)
694 {
695 nrBracket--;
696 }
697 if (nrBracket == 0)
698 {
699 index = i;
700 }
701 }
702
703 String args = ns.substring(0, index - 1).trim();
704 ns.replace(0, ns.length() - 1, ns.toString().trim());
705 return args;
706 }
707
708
709
710
711
712 private Map<String, CrossSectionElement> parseLanes(final StringBuilder elements)
713 {
714
715 return null;
716 }
717
718
719
720
721
722
723 public static void main(final String[] args) throws NetworkException
724 {
725 String s = "NODE = {NAME=N1, COORDINATE=(0,0)} # the first node\n";
726 s += "NODE = {NAME=N2} # the second node\n";
727 s += "LINK = {NAME=\"A4_12\", FROM=N1, TO=N2, E=\"S1|V2:V1|D|A1:A2|S2\", T=S, L=50m, s=80km/h, w=4m, \n";
728 s += " w(S1)=1m, w(S2=1m)} # a lane\n";
729 s += "NODE = {NAME=\"N3 b2\"}\n";
730 s += "LINK = {NAME=\"A4_13\", FROM=N2, TO=\"N3 b2\", E=\"S1|V2:V1|D|A1:A2|S2\", T=C, R=100m, A=+90, \n";
731 s += " s=80km/h, w=4m, w(S1)=1m, w(S2)=1m} # another lane\n";
732 s += "NODE = {NAME=\"N4\"}\n";
733 s += "LINK = {NAME=\"A4_14\", FROM=\"N3 b2\", TO=N4, E=\"S1|V2:V1|D|A1:A2<A3|S2\", T=S, L=50m, s=80km/h, \n";
734 s += " s(A3)=60km/h, w=4m, w(S1)=1m, w(S2)=1m}\n";
735 s += "NODE = {NAME=\"N5\"}\n";
736 s += "NODE = {NAME=\"ENTRY5\"}\n";
737 s += "LINK = {NAME=\"A4_15\", FROM=N4, TO=N5, E=\"S1|V2:V1|D|A1:A2|S2\", T=S, L=100m, s=80km/h, w=4m,\n";
738 s += " w(S1)=1m, w(S2)=1m}\n";
739 s += "LINK = {NAME=\"LE1\", FROM=\"ENTRY5\", TO=N4(A3), E=\"|A|\", T=C, R=50m, a=-45, s=60km/h, w=4m}\n";
740 s += "NODE = {NAME=\"ENTRY6\"}\n";
741 s += "LINK = {NAME=\"LE2\", FROM=\"ENTRY6\", TO=ENTRY5, E=\"|A|\", T=C, R=50m, a=-45, s=60km/h, w=4m}\n";
742 NetworkLaneParser nlp =
743 new NetworkLaneParser(String.class, NodeGeotools.class, String.class, Coordinate.class, String.class);
744 Network n = nlp.build(s);
745 }
746 }