|
1 | 1 | package org.opentripplanner.gtfs.mapping;
|
2 | 2 |
|
3 |
| -import java.util.Arrays; |
| 3 | +import gnu.trove.list.TDoubleList; |
| 4 | +import gnu.trove.list.array.TDoubleArrayList; |
| 5 | +import gnu.trove.map.TIntIntMap; |
| 6 | +import gnu.trove.map.hash.TIntIntHashMap; |
4 | 7 | import java.util.Iterator;
|
| 8 | +import java.util.PrimitiveIterator; |
| 9 | +import java.util.stream.IntStream; |
5 | 10 | import org.opentripplanner.model.ShapePoint;
|
6 | 11 |
|
7 | 12 | /**
|
|
19 | 24 | class CompactShape implements Iterable<ShapePoint> {
|
20 | 25 |
|
21 | 26 | private static final double NO_VALUE = -9999;
|
22 |
| - private static final int INCREASE = 50; |
23 |
| - private double[] lats; |
24 |
| - private double[] lons; |
25 |
| - // we only initialize this if we need it |
26 |
| - private double[] distTraveleds; |
27 |
| - private int maxSequence = (int) NO_VALUE; |
| 27 | + private static final int CAPACITY = 50; |
| 28 | + private final TDoubleList lats = new TDoubleArrayList(CAPACITY, NO_VALUE); |
| 29 | + private final TDoubleList lons = new TDoubleArrayList(CAPACITY, NO_VALUE); |
| 30 | + private final TDoubleList distTraveleds = new TDoubleArrayList(CAPACITY, NO_VALUE); |
| 31 | + /** |
| 32 | + * The mapping from GTFS sequence number to index in the array lists. This allows the |
| 33 | + * shape points to be inserted in any order and the sequence number to have very large |
| 34 | + * gaps - at the cost of consuming a bit more memory. |
| 35 | + */ |
| 36 | + private final TIntIntMap seqIndex = new TIntIntHashMap(); |
28 | 37 |
|
29 |
| - public CompactShape() { |
30 |
| - this.lats = new double[INCREASE]; |
31 |
| - this.lons = new double[INCREASE]; |
32 |
| - Arrays.fill(this.lats, NO_VALUE); |
33 |
| - Arrays.fill(this.lons, NO_VALUE); |
34 |
| - // distTraveleds is created on demand if required |
35 |
| - } |
| 38 | + CompactShape() {} |
36 | 39 |
|
37 | 40 | public void addPoint(org.onebusaway.gtfs.model.ShapePoint shapePoint) {
|
38 |
| - int index = shapePoint.getSequence(); |
39 |
| - ensureLatLonCapacity(index); |
40 |
| - lats[index] = shapePoint.getLat(); |
41 |
| - lons[index] = shapePoint.getLon(); |
| 41 | + lats.add(shapePoint.getLat()); |
| 42 | + lons.add(shapePoint.getLon()); |
42 | 43 | if (shapePoint.isDistTraveledSet()) {
|
43 |
| - ensureDistTraveledCapacity(index); |
44 |
| - distTraveleds[index] = shapePoint.getDistTraveled(); |
45 |
| - } |
46 |
| - if (maxSequence < index) { |
47 |
| - maxSequence = index; |
| 44 | + distTraveleds.add(shapePoint.getDistTraveled()); |
| 45 | + } else { |
| 46 | + distTraveleds.add(NO_VALUE); |
48 | 47 | }
|
| 48 | + |
| 49 | + int seq = shapePoint.getSequence(); |
| 50 | + seqIndex.put(seq, lats.size() - 1); |
49 | 51 | }
|
50 | 52 |
|
51 | 53 | @Override
|
52 | 54 | public Iterator<ShapePoint> iterator() {
|
53 | 55 | return new Iterator<>() {
|
54 |
| - private int index = 0; |
| 56 | + private final PrimitiveIterator.OfInt seqIterator = IntStream.of(seqIndex.keys()) |
| 57 | + .sorted() |
| 58 | + .iterator(); |
55 | 59 |
|
56 | 60 | @Override
|
57 | 61 | public boolean hasNext() {
|
58 |
| - return index <= maxSequence; |
| 62 | + return seqIterator.hasNext(); |
59 | 63 | }
|
60 | 64 |
|
61 | 65 | @Override
|
62 | 66 | public ShapePoint next() {
|
63 |
| - var lat = lats[index]; |
64 |
| - while (lat == NO_VALUE) { |
65 |
| - index++; |
66 |
| - lat = lats[index]; |
67 |
| - } |
68 |
| - var lon = lons[index]; |
69 |
| - Double distTraveled = null; |
70 |
| - if (distTraveleds != null && index - 1 < distTraveleds.length) { |
71 |
| - distTraveled = distTraveleds[index]; |
72 |
| - if (distTraveled == NO_VALUE) { |
73 |
| - distTraveled = null; |
74 |
| - } |
| 67 | + var seq = seqIterator.nextInt(); |
| 68 | + var index = seqIndex.get(seq); |
| 69 | + var lat = lats.get(index); |
| 70 | + var lon = lons.get(index); |
| 71 | + Double distTraveled = distTraveleds.get(index); |
| 72 | + if (distTraveled == NO_VALUE) { |
| 73 | + distTraveled = null; |
75 | 74 | }
|
76 |
| - var ret = new ShapePoint(index, lat, lon, distTraveled); |
77 |
| - index++; |
78 |
| - return ret; |
| 75 | + return new ShapePoint(seq, lat, lon, distTraveled); |
79 | 76 | }
|
80 | 77 | };
|
81 | 78 | }
|
82 |
| - |
83 |
| - private void ensureLatLonCapacity(int index) { |
84 |
| - if (lats.length - 1 < index) { |
85 |
| - int oldLength = lats.length; |
86 |
| - int newLength = increaseCapacity(index); |
87 |
| - lats = Arrays.copyOf(lats, newLength); |
88 |
| - lons = Arrays.copyOf(lons, newLength); |
89 |
| - for (var i = oldLength; i < newLength; i++) { |
90 |
| - lats[i] = NO_VALUE; |
91 |
| - lons[i] = NO_VALUE; |
92 |
| - } |
93 |
| - } |
94 |
| - } |
95 |
| - |
96 |
| - private void ensureDistTraveledCapacity(int index) { |
97 |
| - if (this.distTraveleds == null) { |
98 |
| - this.distTraveleds = new double[Math.max(INCREASE, index + 1)]; |
99 |
| - Arrays.fill(distTraveleds, NO_VALUE); |
100 |
| - } else if (distTraveleds.length - 1 < index) { |
101 |
| - int oldLength = distTraveleds.length; |
102 |
| - int newLength = increaseCapacity(index); |
103 |
| - this.distTraveleds = Arrays.copyOf(distTraveleds, newLength); |
104 |
| - for (var i = oldLength; i < newLength; i++) { |
105 |
| - distTraveleds[i] = NO_VALUE; |
106 |
| - } |
107 |
| - } |
108 |
| - } |
109 |
| - |
110 |
| - private static int increaseCapacity(int index) { |
111 |
| - return index + INCREASE; |
112 |
| - } |
113 | 79 | }
|
0 commit comments