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