/
AccumulativeGridSampler.java
158 lines (141 loc) · 6.22 KB
/
AccumulativeGridSampler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.common.geometry;
import java.util.ArrayList;
import java.util.List;
import org.opentripplanner.common.geometry.ZSampleGrid.ZSamplePoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Helper class to fill-in a ZSampleGrid from a given loosely-defined set of sampling points.
*
* The process is customized by an "accumulative" metric which gives the behavior of cumulating
* several values onto one sampling point.
*
* To use this class, create an instance giving an AccumulativeMetric implementation as parameter.
* Then for each source sample, call "addSample" with the its TZ value. At the end, call close() to
* close the sample grid (ie add grid node at the edge to make sure borders are correctly defined,
* the definition of correct is left to the client).
*
* @author laurent
*/
public class AccumulativeGridSampler<TZ> {
/**
* An accumulative metric give the behavior of combining several samples to a regular sample
* grid, ie how we should weight and add several TZ values from inside a cell to compute the
* cell corner TZ values.
*
* @author laurent
* @param <TZ>
*/
public interface AccumulativeMetric<TZ> {
/**
* Callback function to handle a new added sample.
*
* @param C0 The initial position of the sample, as given in the addSample() call.
* @param Cs The position of the sample on the grid, never farther away than (dX,dY)
* @param z The z value of the initial sample, as given in the addSample() call.
* @param zS The previous z value of the sample. Can be null if this is the first time, it's
* up to the caller to initialize the z value.
* @param offRoadSpeed The offroad speed to assume.
* @return The modified z value for the sample.
*/
public TZ cumulateSample(Coordinate C0, Coordinate Cs, TZ z, TZ zS, double offRoadSpeed);
/**
* Callback function to handle a "closing" sample (that is a sample post-created to surround
* existing samples and provide nice and smooth edges for the algorithm).
*
* @param zUp Sampled value of the up neighbor. Can be null if undefined.
* @param zDown Idem
* @param zRight Idem
* @param zLeft Idem
* @return The z value for the closing sample.
*/
public TZ closeSample(TZ zUp, TZ zDown, TZ zRight, TZ zLeft);
}
private static final Logger LOG = LoggerFactory.getLogger(AccumulativeGridSampler.class);
private AccumulativeMetric<TZ> metric;
private ZSampleGrid<TZ> sampleGrid;
private boolean closed = false;
/**
* @param metric TZ data "behavior" and "metric".
*/
public AccumulativeGridSampler(ZSampleGrid<TZ> sampleGrid, AccumulativeMetric<TZ> metric) {
this.metric = metric;
this.sampleGrid = sampleGrid;
}
public final void addSamplingPoint(Coordinate C0, TZ z, double offRoadSpeed) {
if (closed)
throw new IllegalStateException("Can't add a sample after closing.");
int[] xy = sampleGrid.getLowerLeftIndex(C0);
int x = xy[0];
int y = xy[1];
@SuppressWarnings("unchecked")
ZSamplePoint<TZ>[] ABCD = new ZSamplePoint[4];
ABCD[0] = sampleGrid.getOrCreate(x, y);
ABCD[1] = sampleGrid.getOrCreate(x + 1, y);
ABCD[2] = sampleGrid.getOrCreate(x, y + 1);
ABCD[3] = sampleGrid.getOrCreate(x + 1, y + 1);
for (ZSamplePoint<TZ> P : ABCD) {
Coordinate C = sampleGrid.getCoordinates(P);
P.setZ(metric.cumulateSample(C0, C, z, P.getZ(), offRoadSpeed));
}
}
/**
* Surround all existing samples on the edge by a layer of closing samples.
*/
public final void close() {
if (closed)
return;
closed = true;
List<ZSamplePoint<TZ>> processList = new ArrayList<ZSamplePoint<TZ>>(sampleGrid.size());
for (ZSamplePoint<TZ> A : sampleGrid) {
processList.add(A);
}
int n = 0;
/*
* TODO The magic "2" below should be automatically computed according to some return value
* from the metric.
*/
for (int i = 0; i < 2; i++) {
List<ZSamplePoint<TZ>> newProcessList = new ArrayList<ZSamplePoint<TZ>>(
processList.size());
for (ZSamplePoint<TZ> A : processList) {
if (A.right() == null) {
newProcessList.add(closeSample(A.getX() + 1, A.getY()));
n++;
}
if (A.left() == null) {
newProcessList.add(closeSample(A.getX() - 1, A.getY()));
n++;
}
if (A.up() == null) {
newProcessList.add(closeSample(A.getX(), A.getY() + 1));
n++;
}
if (A.down() == null) {
newProcessList.add(closeSample(A.getX(), A.getY() - 1));
n++;
}
}
processList = newProcessList;
}
LOG.info("Added {} closing samples to get a total of {}.", n, sampleGrid.size());
}
private final ZSamplePoint<TZ> closeSample(int x, int y) {
ZSamplePoint<TZ> A = sampleGrid.getOrCreate(x, y);
A.setZ(metric.closeSample(A.up() != null ? A.up().getZ() : null, A.down() != null ? A
.down().getZ() : null, A.right() != null ? A.right().getZ() : null,
A.left() != null ? A.left().getZ() : null));
return A;
}
}