1 package org.opentrafficsim.road.gtu.strategical.od;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.djunits.unit.FrequencyUnit;
12 import org.djunits.unit.TimeUnit;
13 import org.djunits.value.StorageType;
14 import org.djunits.value.ValueException;
15 import org.djunits.value.vdouble.scalar.Duration;
16 import org.djunits.value.vdouble.scalar.Frequency;
17 import org.djunits.value.vdouble.vector.DurationVector;
18 import org.djunits.value.vdouble.vector.FrequencyVector;
19 import org.opentrafficsim.core.geometry.OTSPoint3D;
20 import org.opentrafficsim.core.network.NetworkException;
21 import org.opentrafficsim.core.network.Node;
22 import org.opentrafficsim.core.network.OTSNetwork;
23 import org.opentrafficsim.core.network.OTSNode;
24 import org.opentrafficsim.core.network.route.Route;
25
26 import nl.tudelft.simulation.language.Throw;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 public class ODMatrix implements Serializable
42 {
43
44
45 private static final long serialVersionUID = 20160921L;
46
47
48 private final String id;
49
50
51 private final List<Node> origins;
52
53
54 private final List<Node> destinations;
55
56
57 private final Categorization categorization;
58
59
60 private final DurationVector globalTimeVector;
61
62
63 private final Interpolation globalInterpolation;
64
65
66 private final Map<Node, Map<Node, Map<Category, ODEntry>>> demandData = new HashMap<>();
67
68
69
70
71
72
73
74
75
76
77
78 public ODMatrix(final String id, final List<Node> origins, final List<Node> destinations,
79 final Categorization categorization, final DurationVector globalTimeVector, final Interpolation globalInterpolation)
80 {
81 Throw.whenNull(id, "Id may not be null.");
82 Throw.when(origins == null || origins.contains(null), NullPointerException.class,
83 "Origin may not be or contain null.");
84 Throw.when(destinations == null || destinations.contains(null), NullPointerException.class,
85 "Destination may not be or contain null.");
86 Throw.whenNull(categorization, "Categorization may not be null.");
87 Throw.whenNull(globalTimeVector, "Global time vector may not be null.");
88 Throw.whenNull(globalInterpolation, "Global interpolation may not be null.");
89 this.id = id;
90 this.origins = new ArrayList<>(origins);
91 this.destinations = new ArrayList<>(destinations);
92 this.categorization = categorization;
93 this.globalTimeVector = globalTimeVector;
94 this.globalInterpolation = globalInterpolation;
95
96 for (Node origin : origins)
97 {
98 Map<Node, Map<Category, ODEntry>> map = new HashMap<>();
99 for (Node destination : destinations)
100 {
101 map.put(destination, new HashMap<>());
102 }
103 this.demandData.put(origin, map);
104 }
105 }
106
107
108
109
110 public final String getId()
111 {
112 return this.id;
113 }
114
115
116
117
118 public final List<Node> getOrigins()
119 {
120 return new ArrayList<>(this.origins);
121 }
122
123
124
125
126 public final List<Node> getDestinations()
127 {
128 return new ArrayList<>(this.destinations);
129 }
130
131
132
133
134 public final Categorization getCategorization()
135 {
136 return this.categorization;
137 }
138
139
140
141
142 public final DurationVector getGlobalTimeVector()
143 {
144 return this.globalTimeVector;
145 }
146
147
148
149
150 public final Interpolation getGlobalInterpolation()
151 {
152 return this.globalInterpolation;
153 }
154
155
156
157
158
159
160
161
162
163
164 public final void putDemandVector(final Node origin, final Node destination, final Category category,
165 final FrequencyVector demand)
166 {
167 putDemandVector(origin, destination, category, demand, this.globalTimeVector, this.globalInterpolation);
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181 public final void putDemandVector(final Node origin, final Node destination, final Category category,
182 final FrequencyVector demand, final DurationVector timeVector, final Interpolation interpolation)
183 {
184 Throw.whenNull(origin, "Origin may not be null.");
185 Throw.whenNull(destination, "Destination may not be null.");
186 Throw.whenNull(category, "Category may not be null.");
187 Throw.whenNull(demand, "Demand data may not be null.");
188 Throw.whenNull(timeVector, "Time vector may not be null.");
189 Throw.whenNull(interpolation, "Interpolation may not be null.");
190 Throw.when(!this.origins.contains(origin), IllegalArgumentException.class,
191 "Origin '%s' is not part of the OD matrix.", origin);
192 Throw.when(!this.destinations.contains(destination), IllegalArgumentException.class,
193 "Destination '%s' is not part of the OD matrix.", destination);
194 Throw.when(!this.categorization.equals(category.getCategorization()), IllegalArgumentException.class,
195 "Provided category %s does not belong to the categorization %s.", category, this.categorization);
196 ODEntry odEntry = new ODEntry(demand, timeVector, interpolation);
197 this.demandData.get(origin).get(destination).put(category, odEntry);
198 }
199
200
201
202
203
204
205
206
207
208
209 public final FrequencyVector getDemandVector(final Node origin, final Node destination, final Category category)
210 {
211 ODEntry odEntry = getODEntry(origin, destination, category);
212 if (odEntry == null)
213 {
214 return null;
215 }
216 return odEntry.getDemandVector();
217 }
218
219
220
221
222
223
224
225
226
227
228 public final DurationVector getTimeVector(final Node origin, final Node destination, final Category category)
229 {
230 ODEntry odEntry = getODEntry(origin, destination, category);
231 if (odEntry == null)
232 {
233 return null;
234 }
235 return odEntry.getTimeVector();
236 }
237
238
239
240
241
242
243
244
245
246
247 public final Interpolation getInterpolation(final Node origin, final Node destination, final Category category)
248 {
249 ODEntry odEntry = getODEntry(origin, destination, category);
250 if (odEntry == null)
251 {
252 return null;
253 }
254 return odEntry.getInterpolation();
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269 public final Frequency
270 getDemand(final Node origin, final Node destination, final Category category, final Duration time)
271 {
272 Throw.whenNull(time, "Time may not be null.");
273 ODEntry odEntry = getODEntry(origin, destination, category);
274 if (odEntry == null)
275 {
276 return new Frequency(0.0, FrequencyUnit.PER_HOUR);
277 }
278 return odEntry.getDemand(time);
279 }
280
281
282
283
284
285
286
287
288
289
290 private ODEntry getODEntry(final Node origin, final Node destination, final Category category)
291 {
292 Throw.whenNull(origin, "Origin may not be null.");
293 Throw.whenNull(destination, "Destination may not be null.");
294 Throw.whenNull(category, "Category may not be null.");
295 Throw.when(!this.origins.contains(origin), IllegalArgumentException.class,
296 "Origin '%s' is not part of the OD matrix", origin);
297 Throw.when(!this.destinations.contains(destination), IllegalArgumentException.class,
298 "Destination '%s' is not part of the OD matrix.", destination);
299 Throw.when(!this.categorization.equals(category.getCategorization()), IllegalArgumentException.class,
300 "Provided category %s does not belong to the categorization %s.", category, this.categorization);
301 return this.demandData.get(origin).get(destination).get(category);
302 }
303
304
305
306
307
308
309
310
311
312
313 public final boolean contains(final Node origin, final Node destination, final Category category)
314 {
315 return getODEntry(origin, destination, category) != null;
316 }
317
318
319
320
321
322
323
324
325
326 public final Set<Category> getCategories(final Node origin, final Node destination)
327 {
328 Throw.whenNull(origin, "Origin may not be null.");
329 Throw.whenNull(destination, "Destination may not be null.");
330 Throw.when(!this.origins.contains(origin), IllegalArgumentException.class,
331 "Origin '%s' is not part of the OD matrix", origin);
332 Throw.when(!this.destinations.contains(destination), IllegalArgumentException.class,
333 "Destination '%s' is not part of the OD matrix.", destination);
334 return new HashSet<>(this.demandData.get(origin).get(destination).keySet());
335 }
336
337
338 @SuppressWarnings("checkstyle:designforextension")
339 public String toString()
340 {
341 return "ODMatrix [" + this.origins.size() + " origins, " + this.destinations.size() + " destinations, "
342 + this.categorization + " ]";
343 }
344
345
346
347
348 public final void print()
349 {
350 int originLength = 0;
351 for (Node origin : this.origins)
352 {
353 originLength = originLength >= origin.getId().length() ? originLength : origin.getId().length();
354 }
355 int destinLength = 0;
356 for (Node destination : this.destinations)
357 {
358 destinLength = destinLength >= destination.getId().length() ? destinLength : destination.getId().length();
359 }
360 String format = "%-" + originLength + "s -> %-" + destinLength + "s | ";
361 for (Node origin : this.origins)
362 {
363 Map<Node, Map<Category, ODEntry>> destinationMap = this.demandData.get(origin);
364 for (Node destination : this.destinations)
365 {
366 Map<Category, ODEntry> categoryMap = destinationMap.get(destination);
367 if (categoryMap.isEmpty())
368 {
369 System.out.println(String.format(format, origin.getId(), destination.getId()) + "-no data-");
370 }
371 else
372 {
373 for (Category category : categoryMap.keySet())
374 {
375 System.out.println(String.format(format, origin.getId(), destination.getId()) + category + " | "
376 + categoryMap.get(category).getDemandVector());
377 }
378 }
379 }
380 }
381 }
382
383
384 @Override
385 public final int hashCode()
386 {
387 final int prime = 31;
388 int result = 1;
389 result = prime * result + ((this.categorization == null) ? 0 : this.categorization.hashCode());
390 result = prime * result + ((this.demandData == null) ? 0 : this.demandData.hashCode());
391 result = prime * result + ((this.destinations == null) ? 0 : this.destinations.hashCode());
392 result = prime * result + ((this.globalInterpolation == null) ? 0 : this.globalInterpolation.hashCode());
393 result = prime * result + ((this.globalTimeVector == null) ? 0 : this.globalTimeVector.hashCode());
394 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
395 result = prime * result + ((this.origins == null) ? 0 : this.origins.hashCode());
396 return result;
397 }
398
399
400 @Override
401 public final boolean equals(final Object obj)
402 {
403 if (this == obj)
404 {
405 return true;
406 }
407 if (obj == null)
408 {
409 return false;
410 }
411 if (getClass() != obj.getClass())
412 {
413 return false;
414 }
415 ODMatrix other = (ODMatrix) obj;
416 if (this.categorization == null)
417 {
418 if (other.categorization != null)
419 {
420 return false;
421 }
422 }
423 else if (!this.categorization.equals(other.categorization))
424 {
425 return false;
426 }
427 if (this.demandData == null)
428 {
429 if (other.demandData != null)
430 {
431 return false;
432 }
433 }
434 else if (!this.demandData.equals(other.demandData))
435 {
436 return false;
437 }
438 if (this.destinations == null)
439 {
440 if (other.destinations != null)
441 {
442 return false;
443 }
444 }
445 else if (!this.destinations.equals(other.destinations))
446 {
447 return false;
448 }
449 if (this.globalInterpolation != other.globalInterpolation)
450 {
451 return false;
452 }
453 if (this.globalTimeVector == null)
454 {
455 if (other.globalTimeVector != null)
456 {
457 return false;
458 }
459 }
460 else if (!this.globalTimeVector.equals(other.globalTimeVector))
461 {
462 return false;
463 }
464 if (this.id == null)
465 {
466 if (other.id != null)
467 {
468 return false;
469 }
470 }
471 else if (!this.id.equals(other.id))
472 {
473 return false;
474 }
475 if (this.origins == null)
476 {
477 if (other.origins != null)
478 {
479 return false;
480 }
481 }
482 else if (!this.origins.equals(other.origins))
483 {
484 return false;
485 }
486 return true;
487 }
488
489
490 public static void main(final String[] args) throws ValueException, NetworkException
491 {
492
493 int aa = 10;
494 System.out.println(aa);
495 aa = aa + (aa >> 1);
496 System.out.println(aa);
497 aa = aa + (aa >> 1);
498 System.out.println(aa);
499 aa = aa + (aa >> 1);
500 System.out.println(aa);
501 aa = aa + (aa >> 1);
502 System.out.println(aa);
503
504 OTSNetwork net = new OTSNetwork("test");
505 OTSPoint3D point = new OTSPoint3D(0, 0, 0);
506 Node a = new OTSNode(net, "A", point);
507 Node b = new OTSNode(net, "Barendrecht", point);
508 Node c = new OTSNode(net, "C", point);
509 Node d = new OTSNode(net, "Delft", point);
510 Node e = new OTSNode(net, "E", point);
511 List<Node> origins = new ArrayList<>();
512 origins.add(a);
513 origins.add(b);
514 origins.add(c);
515 List<Node> destinations = new ArrayList<>();
516 destinations.add(a);
517 destinations.add(c);
518 destinations.add(d);
519 Categorization categorization = new Categorization("test", Route.class, String.class);
520 Route ac1 = new Route("AC1");
521 Route ac2 = new Route("AC2");
522 Route ad1 = new Route("AD1");
523 Route bc1 = new Route("BC1");
524 Route bc2 = new Route("BC2");
525 Route bd1 = new Route("BD1");
526
527 DurationVector timeVector = new DurationVector(new double[] {0, 1200, 3600}, TimeUnit.SECOND, StorageType.DENSE);
528 ODMatrix odMatrix = new ODMatrix("TestOD", origins, destinations, categorization, timeVector, Interpolation.LINEAR);
529
530 Category category = new Category(categorization, ac1, "car");
531 odMatrix.putDemandVector(a, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
532 StorageType.DENSE));
533 category = new Category(categorization, ac2, "car");
534 odMatrix.putDemandVector(a, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
535 StorageType.DENSE));
536 category = new Category(categorization, ad1, "car");
537 odMatrix.putDemandVector(a, d, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
538 StorageType.DENSE));
539 category = new Category(categorization, ac1, "car");
540 odMatrix.putDemandVector(a, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
541 StorageType.DENSE));
542 category = new Category(categorization, ac2, "truck");
543 odMatrix.putDemandVector(a, c, category, new FrequencyVector(new double[] {100, 200, 500}, FrequencyUnit.PER_HOUR,
544 StorageType.DENSE));
545 category = new Category(categorization, ad1, "truck");
546 odMatrix.putDemandVector(a, d, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
547 StorageType.DENSE));
548 category = new Category(categorization, bc1, "truck");
549 odMatrix.putDemandVector(b, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
550 StorageType.DENSE));
551 category = new Category(categorization, bc2, "truck");
552 odMatrix.putDemandVector(b, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
553 StorageType.DENSE));
554 category = new Category(categorization, bd1, "car");
555 odMatrix.putDemandVector(b, d, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
556 StorageType.DENSE));
557 category = new Category(categorization, bc1, "car");
558 odMatrix.putDemandVector(b, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
559 StorageType.DENSE));
560 category = new Category(categorization, bc2, "car");
561 odMatrix.putDemandVector(b, c, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
562 StorageType.DENSE));
563 category = new Category(categorization, bd1, "truck");
564 odMatrix.putDemandVector(b, d, category, new FrequencyVector(new double[] {100, 200, 300}, FrequencyUnit.PER_HOUR,
565 StorageType.DENSE));
566
567 odMatrix.print();
568 System.out.println(odMatrix);
569
570 category = new Category(categorization, ac2, "truck");
571 for (double t = -100; t <= 3700; t += 100)
572 {
573 Duration time = new Duration(t, TimeUnit.SECOND);
574 System.out.println("@ t = " + time + ", q = " + odMatrix.getDemand(a, c, category, time));
575 }
576
577 System.out.println("For OD that does not exist; q = " + odMatrix.getDemand(c, a, category, Duration.ZERO));
578 category = new Category(categorization, ac2, "does not exist");
579 System.out.println("For category that does not exist; q = " + odMatrix.getDemand(a, c, category, Duration.ZERO));
580
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595 private class ODEntry
596 {
597
598
599 private final FrequencyVector demandVector;
600
601
602 private final DurationVector timeVector;
603
604
605 private final Interpolation interpolation;
606
607
608
609
610
611
612
613 ODEntry(final FrequencyVector demandVector, final DurationVector timeVector, final Interpolation interpolation)
614 {
615 Throw.when(demandVector.size() != timeVector.size(), IllegalArgumentException.class,
616 "Demand data has different length than time vector.");
617 this.demandVector = demandVector;
618 this.timeVector = timeVector;
619 this.interpolation = interpolation;
620 }
621
622
623
624
625
626
627
628 public final Frequency getDemand(final Duration time)
629 {
630 try
631 {
632
633 if (this.timeVector.size() == 0 || time.lt(this.timeVector.get(0))
634 || time.ge(this.timeVector.get(this.timeVector.size() - 1)))
635 {
636 return new Frequency(0.0, FrequencyUnit.PER_HOUR);
637 }
638
639 for (int i = 0; i < this.timeVector.size() - 1; i++)
640 {
641 if (this.timeVector.get(i + 1).ge(time))
642 {
643 return this.interpolation.interpolate(this.demandVector.get(i), this.timeVector.get(i),
644 this.demandVector.get(i + 1), this.timeVector.get(i + 1), time);
645 }
646 }
647 }
648 catch (ValueException ve)
649 {
650
651 throw new RuntimeException("Index out of bounds.", ve);
652 }
653
654 throw new RuntimeException("Demand interpolation failed.");
655 }
656
657
658
659
660 final FrequencyVector getDemandVector()
661 {
662 return this.demandVector;
663 }
664
665
666
667
668 final DurationVector getTimeVector()
669 {
670 return this.timeVector;
671 }
672
673
674
675
676 final Interpolation getInterpolation()
677 {
678 return this.interpolation;
679 }
680
681
682 @Override
683 public final int hashCode()
684 {
685 final int prime = 31;
686 int result = 1;
687 result = prime * result + getOuterType().hashCode();
688 result = prime * result + ((this.demandVector == null) ? 0 : this.demandVector.hashCode());
689 result = prime * result + ((this.interpolation == null) ? 0 : this.interpolation.hashCode());
690 result = prime * result + ((this.timeVector == null) ? 0 : this.timeVector.hashCode());
691 return result;
692 }
693
694
695 @Override
696 public final boolean equals(final Object obj)
697 {
698 if (this == obj)
699 {
700 return true;
701 }
702 if (obj == null)
703 {
704 return false;
705 }
706 if (getClass() != obj.getClass())
707 {
708 return false;
709 }
710 ODEntry other = (ODEntry) obj;
711 if (!getOuterType().equals(other.getOuterType()))
712 {
713 return false;
714 }
715 if (this.demandVector == null)
716 {
717 if (other.demandVector != null)
718 {
719 return false;
720 }
721 }
722 else if (!this.demandVector.equals(other.demandVector))
723 {
724 return false;
725 }
726 if (this.interpolation != other.interpolation)
727 {
728 return false;
729 }
730 if (this.timeVector == null)
731 {
732 if (other.timeVector != null)
733 {
734 return false;
735 }
736 }
737 else if (!this.timeVector.equals(other.timeVector))
738 {
739 return false;
740 }
741 return true;
742 }
743
744
745
746
747
748 private ODMatrix getOuterType()
749 {
750 return ODMatrix.this;
751 }
752
753
754 @Override
755 public String toString()
756 {
757 return "ODEntry [demandVector=" + this.demandVector + ", timeVector=" + this.timeVector + ", interpolation="
758 + this.interpolation + "]";
759 }
760
761 }
762
763 }