|
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