comparison gnv-artifacts/src/main/java/de/intevation/gnv/math/Interpolation2D.java @ 519:4e347624ee7c

Last part to fix gnv/issue153. Now 'Profilschnitte', 'Horizontalschnitte' and 'horizontale Schnittprofile' all use the same x/y interpolation code. gnv-artifacts/trunk@613 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sat, 23 Jan 2010 21:16:45 +0000
parents d9d933e06875
children 44415ae01ddb
comparison
equal deleted inserted replaced
518:464e03bf786b 519:4e347624ee7c
1 package de.intevation.gnv.math; 1 package de.intevation.gnv.math;
2 2
3 import com.vividsolutions.jts.geom.Coordinate; 3 import com.vividsolutions.jts.geom.Coordinate;
4 import com.vividsolutions.jts.geom.Envelope; 4 import com.vividsolutions.jts.geom.Envelope;
5 5
6 import com.vividsolutions.jts.index.quadtree.Quadtree; 6 import com.vividsolutions.jts.index.strtree.STRtree;
7 7
8 import gnu.trove.TDoubleArrayList;
9
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.List; 8 import java.util.List;
14
15 import org.apache.commons.math.stat.descriptive.moment.Mean;
16 import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
17 9
18 import org.apache.log4j.Logger; 10 import org.apache.log4j.Logger;
19 11
20 /** 12 /**
21 * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) 13 * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
22 */ 14 */
23 public final class Interpolation2D 15 public final class Interpolation2D
24 { 16 {
25 private static Logger log = Logger.getLogger(Interpolation2D.class); 17 private static Logger log = Logger.getLogger(Interpolation2D.class);
26 18
19 public static final int CULL_POINT_THRESHOLD = Integer.getInteger(
20 "gnv.interpolation2d.cull.point.threshold", 1000);
21
22 public static final double EPS = 1e-6d;
23
27 public interface Consumer { 24 public interface Consumer {
28 void interpolated(Coordinate point, boolean success); 25 void interpolated(Coordinate point, boolean success);
29 } // interface Consumer 26 } // interface Consumer
30 27
31 private Interpolation2D() { 28 private Interpolation2D() {
32 } 29 }
33 30
34 private static final double dist(double a, double b) { 31 public static Envelope pathBoundingBox(
35 a -= b; 32 List<? extends Coordinate> path,
36 return Math.sqrt(a*a); 33 double extra
37 } 34 ) {
35 int N = path.size();
36 Envelope area = new Envelope(path.get(N-1));
38 37
39 private static final double OUTLIER_EPS = 0.4d; 38 for (int i = N-2; i >= 0; --i) {
40 39 area.expandToInclude(path.get(i));
41 public static final double [] calculateBufferRemoveOutlier(
42 List <? extends Point2d> points
43 ) {
44 HashMap<Integer, ArrayList<Point2d>> iMap =
45 new HashMap<Integer, ArrayList<Point2d>>();
46
47 HashMap<Integer, ArrayList<Point2d>> jMap =
48 new HashMap<Integer, ArrayList<Point2d>>();
49
50 for (int k = points.size()-1; k >= 0; --k) {
51 Point2d p = points.get(k);
52
53 ArrayList<Point2d> jList = jMap.get(p.j);
54 ArrayList<Point2d> iList = iMap.get(p.i);
55
56 if (jList == null) {
57 jMap.put(p.j, jList = new ArrayList<Point2d>());
58 }
59 jList.add(p);
60
61 if (iList == null) {
62 iMap.put(p.i, iList = new ArrayList<Point2d>());
63 }
64 iList.add(p);
65 } 40 }
66 41
67 TDoubleArrayList deltas = new TDoubleArrayList(); 42 area.expandBy(
43 extra*area.getWidth(),
44 extra*area.getHeight());
68 45
69 Mean mean = new Mean(); 46 return area;
70 StandardDeviation sd = new StandardDeviation();
71
72 for (ArrayList<Point2d> v: jMap.values()) {
73 Collections.sort(v, Point2d.Y_COMPARATOR);
74 for (int i = 1, L = v.size(); i < L; ++i) {
75 // double dy = Math.abs(v.get(i).y - v.get(i-1).y);
76 double dy = v.get(i).distance(v.get(i-1));
77 deltas.add(dy);
78 mean.increment(dy);
79 sd .increment(dy);
80 }
81 }
82
83 deltas.sort();
84
85 double m = mean.getResult();
86 double s = sd .getResult();
87 double eps = OUTLIER_EPS*s;
88
89 int yi = deltas.size() - 1;
90 while (yi > 0 && dist(deltas.get(yi), m) > eps) {
91 --yi;
92 }
93
94 double dyMax = deltas.get(yi) + 1e-5d;
95
96 if (log.isDebugEnabled()) {
97 log.debug("mean y: " + m);
98 log.debug("sigma y: " + s);
99 }
100
101 deltas.reset();
102 mean.clear();
103 sd .clear();
104
105 for (ArrayList<Point2d> v: iMap.values()) {
106 Collections.sort(v, Point2d.X_COMPARATOR);
107 for (int i = 1, L = v.size(); i < L; ++i) {
108 //double dx = Math.abs(v.get(i).x - v.get(i-1).x);
109 double dx = v.get(i).distance(v.get(i-1));
110 deltas.add(dx);
111 mean.increment(dx);
112 sd .increment(dx);
113 }
114 }
115
116 deltas.sort();
117
118 m = mean.getResult();
119 s = sd .getResult();
120 eps = OUTLIER_EPS*s;
121
122 int xi = deltas.size() - 1;
123
124 while (xi > 0 && dist(deltas.get(xi), m) > eps) {
125 --xi;
126 }
127
128 double dxMax = deltas.get(xi) + 1e-5d;
129
130 if (log.isDebugEnabled()) {
131 log.debug("mean y: " + m);
132 log.debug("sigma y: " + s);
133 }
134
135 return new double [] { dxMax, dyMax };
136 }
137
138 public static final double [] calculateBuffer(
139 List <? extends Point2d> points
140 ) {
141 HashMap<Integer, ArrayList<Point2d>> iMap =
142 new HashMap<Integer, ArrayList<Point2d>>();
143
144 HashMap<Integer, ArrayList<Point2d>> jMap =
145 new HashMap<Integer, ArrayList<Point2d>>();
146
147 for (int k = points.size()-1; k >= 0; --k) {
148 Point2d p = points.get(k);
149
150 ArrayList<Point2d> jList = jMap.get(p.j);
151 ArrayList<Point2d> iList = iMap.get(p.i);
152
153 if (jList == null) {
154 jMap.put(p.j, jList = new ArrayList<Point2d>());
155 }
156 jList.add(p);
157
158 if (iList == null) {
159 iMap.put(p.i, iList = new ArrayList<Point2d>());
160 }
161 iList.add(p);
162 }
163
164 double dxMax = -Double.MAX_VALUE;
165 double dyMax = -Double.MAX_VALUE;
166
167 for (ArrayList<Point2d> v: jMap.values()) {
168 Collections.sort(v, Point2d.Y_COMPARATOR);
169 for (int i = 1, L = v.size(); i < L; ++i) {
170 double dy = Math.abs(v.get(i).y - v.get(i-1).y);
171 if (dy > dyMax) {
172 dyMax = dy;
173 }
174 }
175 }
176
177 dyMax += 1e-5d;
178
179 for (ArrayList<Point2d> v: iMap.values()) {
180 Collections.sort(v, Point2d.X_COMPARATOR);
181 for (int i = 1, L = v.size(); i < L; ++i) {
182 double dx = Math.abs(v.get(i).x - v.get(i-1).x);
183 if (dx > dxMax) {
184 dxMax = dx;
185 }
186 }
187 }
188
189 dxMax += 1e-5d;
190
191 return new double [] { dxMax, dyMax };
192 } 47 }
193 48
194 public static void interpolate( 49 public static void interpolate(
195 List<? extends Coordinate> path, 50 List<? extends Coordinate> path,
196 List<? extends Point2d> points, 51 List<? extends Point2d> points,
212 67
213 if (M < 1 || N < 2) { // nothing to do 68 if (M < 1 || N < 2) { // nothing to do
214 return; 69 return;
215 } 70 }
216 71
217 double [] buffer = calculateBuffer(points); 72 Envelope relevantArea = M > CULL_POINT_THRESHOLD
218 double dxMax = buffer[0]; 73 ? pathBoundingBox(path, 0.05d)
219 double dyMax = buffer[1]; 74 : null;
75
76 List<GridCell> cells = GridCell.pointsToGridCells(
77 points, relevantArea);
220 78
221 if (debug) { 79 if (cells.isEmpty()) {
222 log.debug("buffer size: " + dxMax + " / " + dyMax); 80 log.warn("no cells found");
81 return;
223 } 82 }
224 83
225 // put into spatial index to speed up finding neighbors. 84 // put into spatial index to speed up finding neighbors.
226 Quadtree spatialIndex = new Quadtree(); 85 STRtree spatialIndex = new STRtree();
227 86
228 for (int i = 0; i < M; ++i) { 87 for (GridCell cell: cells) {
229 Point2d p = points.get(i); 88 spatialIndex.insert(cell.getEnvelope(), cell);
230 spatialIndex.insert(p.envelope(), p);
231 } 89 }
232 90
233 LinearToMap linearToMap = new LinearToMap( 91 LinearToMap linearToMap = new LinearToMap(
234 path, from, to, metrics); 92 path, from, to, metrics);
235 93
236 double dP = (to - from)/steps; 94 double dP = (to - from)/steps;
237 95
238 Coordinate center = new Coordinate(); 96 Coordinate center = new Coordinate();
239 97 Envelope queryBuffer = new Envelope();
240 Envelope queryBuffer = new Envelope(); 98 GridCell.CellFinder finder = new GridCell.CellFinder();
241
242 Point2d [] neighbors = new Point2d[4];
243 99
244 int missedInterpolations = 0; 100 int missedInterpolations = 0;
245 int interpolations = 0; 101 int interpolations = 0;
246
247 L1Comparator invL1 = new L1Comparator(center);
248 102
249 for (double p = from; p <= to; p += dP) { 103 for (double p = from; p <= to; p += dP) {
250 if (!linearToMap.locate(p, center)) { 104 if (!linearToMap.locate(p, center)) {
251 continue; 105 continue;
252 } 106 }
253 queryBuffer.init( 107 queryBuffer.init(
254 center.x - dxMax, center.x + dxMax, 108 center.x - EPS, center.x + EPS,
255 center.y - dyMax, center.y + dyMax); 109 center.y - EPS, center.y + EPS);
256 110
257 List potential = spatialIndex.query(queryBuffer); 111 finder.prepare(center);
112 spatialIndex.query(queryBuffer, finder);
258 113
259 Collections.sort(potential, invL1); 114 GridCell found = finder.found;
260 115
261 neighbors[0] = neighbors[1] = 116 if (found == null) {
262 neighbors[2] = neighbors[3] = null; 117 consumer.interpolated(center, false);
263 118 ++missedInterpolations;
264 /* bit code of neighbors 119 continue;
265 0---1
266 | x |
267 2---3
268 */
269
270 int mask = 0;
271 // reversed order is essential here!
272 for (int i = potential.size()-1; i >= 0; --i) {
273 Point2d n = (Point2d)potential.get(i);
274 int code = n.x > center.x ? 1 : 0;
275 if (n.y > center.y) code |= 2;
276 neighbors[code] = n;
277 mask |= 1 << code;
278 } 120 }
279 121
280 int numNeighbors = Integer.bitCount(mask); 122 Point2d n0 = found.p1;
123 Point2d n1 = found.p2;
124 Point2d n2 = found.p3;
125 Point2d n3 = found.p4;
281 126
282 // only interpolate if we have all four neighbors 127 double z1 = interpolate(
283 // and we do not have any gaps. 128 n0.x, n0.z,
284 if (numNeighbors == 4 129 n1.x, n1.z,
285 && !neighbors[0].hasIGap(neighbors[1]) 130 center.x);
286 && !neighbors[1].hasJGap(neighbors[3]) 131 double z2 = interpolate(
287 && !neighbors[3].hasIGap(neighbors[2]) 132 n2.x, n2.z,
288 && !neighbors[2].hasJGap(neighbors[0]) 133 n3.x, n3.z,
289 ) { 134 center.x);
290 double z1 = interpolate( 135 double y1 = interpolate(
291 neighbors[0].x, neighbors[0].z, 136 n0.x, n0.y,
292 neighbors[1].x, neighbors[1].z, 137 n1.x, n1.y,
293 center.x); 138 center.x);
294 double z2 = interpolate( 139 double y2 = interpolate(
295 neighbors[2].x, neighbors[2].z, 140 n2.x, n2.y,
296 neighbors[3].x, neighbors[3].z, 141 n3.x, n3.y,
297 center.x); 142 center.x);
298 double y1 = interpolate( 143 center.z = interpolate(
299 neighbors[0].x, neighbors[0].y, 144 y1, z1,
300 neighbors[1].x, neighbors[1].y, 145 y2, z2,
301 center.x); 146 center.y);
302 double y2 = interpolate( 147 consumer.interpolated(center, true);
303 neighbors[2].x, neighbors[2].y, 148 ++interpolations;
304 neighbors[3].x, neighbors[3].y,
305 center.x);
306 center.z = interpolate(
307 y1, z1,
308 y2, z2,
309 center.y);
310 consumer.interpolated(center, true);
311 ++interpolations;
312 }
313 else {
314 consumer.interpolated(center, false);
315 ++missedInterpolations;
316 }
317 } 149 }
318 150
319 if (debug) { 151 if (debug) {
320 log.debug("interpolations: " + 152 log.debug("interpolations: " +
321 interpolations + " / " + missedInterpolations); 153 interpolations + " / " + missedInterpolations);

http://dive4elements.wald.intevation.org