1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.rugged.raster;
18
19 import java.util.Arrays;
20
21 import org.hipparchus.geometry.euclidean.threed.Vector3D;
22 import org.hipparchus.util.FastMath;
23 import org.hipparchus.util.Precision;
24 import org.orekit.rugged.errors.DumpManager;
25 import org.orekit.rugged.errors.RuggedException;
26 import org.orekit.rugged.errors.RuggedMessages;
27 import org.orekit.rugged.utils.MaxSelector;
28 import org.orekit.rugged.utils.MinSelector;
29 import org.orekit.rugged.utils.NormalizedGeodeticPoint;
30
31
32
33
34
35
36
37 public class SimpleTile implements Tile {
38
39
40 private static final double TOLERANCE = 1.0 / 8.0;
41
42
43 private double minLatitude;
44
45
46 private double minLongitude;
47
48
49 private double latitudeStep;
50
51
52 private double longitudeStep;
53
54
55 private int latitudeRows;
56
57
58 private int longitudeColumns;
59
60
61 private double minElevation;
62
63
64 private int minElevationLatitudeIndex;
65
66
67 private int minElevationLongitudeIndex;
68
69
70 private double maxElevation;
71
72
73 private int maxElevationLatitudeIndex;
74
75
76 private int maxElevationLongitudeIndex;
77
78
79 private double[] elevations;
80
81
82
83
84
85
86 protected SimpleTile() {
87 }
88
89
90 @Override
91 public void setGeometry(final double newMinLatitude, final double newMinLongitude,
92 final double newLatitudeStep, final double newLongitudeStep,
93 final int newLatitudeRows, final int newLongitudeColumns) {
94 this.minLatitude = newMinLatitude;
95 this.minLongitude = newMinLongitude;
96 this.latitudeStep = newLatitudeStep;
97 this.longitudeStep = newLongitudeStep;
98 this.latitudeRows = newLatitudeRows;
99 this.longitudeColumns = newLongitudeColumns;
100 this.minElevation = Double.POSITIVE_INFINITY;
101 this.minElevationLatitudeIndex = -1;
102 this.minElevationLongitudeIndex = -1;
103 this.maxElevation = Double.NEGATIVE_INFINITY;
104 this.maxElevationLatitudeIndex = -1;
105 this.maxElevationLongitudeIndex = -1;
106
107 if (newLatitudeRows < 1 || newLongitudeColumns < 1) {
108 throw new RuggedException(RuggedMessages.EMPTY_TILE, newLatitudeRows, newLongitudeColumns);
109 }
110 this.elevations = new double[newLatitudeRows * newLongitudeColumns];
111 Arrays.fill(elevations, Double.NaN);
112
113 }
114
115
116 @Override
117 public void tileUpdateCompleted() {
118 processUpdatedElevation(elevations);
119 }
120
121
122
123
124
125
126
127
128
129 protected void processUpdatedElevation(final double[] elevationsArray) {
130
131 }
132
133
134 @Override
135 public double getMinimumLatitude() {
136 return getLatitudeAtIndex(0);
137 }
138
139
140 @Override
141 public double getLatitudeAtIndex(final int latitudeIndex) {
142 return minLatitude + latitudeStep * latitudeIndex;
143 }
144
145
146 @Override
147 public double getMaximumLatitude() {
148 return getLatitudeAtIndex(latitudeRows - 1);
149 }
150
151
152 @Override
153 public double getMinimumLongitude() {
154 return getLongitudeAtIndex(0);
155 }
156
157
158 @Override
159 public double getLongitudeAtIndex(final int longitudeIndex) {
160 return minLongitude + longitudeStep * longitudeIndex;
161 }
162
163
164 @Override
165 public double getMaximumLongitude() {
166 return getLongitudeAtIndex(longitudeColumns - 1);
167 }
168
169
170 @Override
171 public double getLatitudeStep() {
172 return latitudeStep;
173 }
174
175
176 @Override
177 public double getLongitudeStep() {
178 return longitudeStep;
179 }
180
181
182 @Override
183 public int getLatitudeRows() {
184 return latitudeRows;
185 }
186
187
188 @Override
189 public int getLongitudeColumns() {
190 return longitudeColumns;
191 }
192
193
194 @Override
195 public double getMinElevation() {
196 return minElevation;
197 }
198
199
200 @Override
201 public int getMinElevationLatitudeIndex() {
202 return minElevationLatitudeIndex;
203 }
204
205
206 @Override
207 public int getMinElevationLongitudeIndex() {
208 return minElevationLongitudeIndex;
209 }
210
211
212 @Override
213 public double getMaxElevation() {
214 return maxElevation;
215 }
216
217
218 @Override
219 public int getMaxElevationLatitudeIndex() {
220 return maxElevationLatitudeIndex;
221 }
222
223
224 @Override
225 public int getMaxElevationLongitudeIndex() {
226 return maxElevationLongitudeIndex;
227 }
228
229
230 @Override
231 public void setElevation(final int latitudeIndex, final int longitudeIndex, final double elevation) {
232
233 if (latitudeIndex < 0 || latitudeIndex > (latitudeRows - 1) ||
234 longitudeIndex < 0 || longitudeIndex > (longitudeColumns - 1)) {
235 throw new RuggedException(RuggedMessages.OUT_OF_TILE_INDICES,
236 latitudeIndex, longitudeIndex,
237 latitudeRows - 1, longitudeColumns - 1);
238 }
239 if (MinSelector.getInstance().selectFirst(elevation, minElevation)) {
240 minElevation = elevation;
241 minElevationLatitudeIndex = latitudeIndex;
242 minElevationLongitudeIndex = longitudeIndex;
243 }
244 if (MaxSelector.getInstance().selectFirst(elevation, maxElevation)) {
245 maxElevation = elevation;
246 maxElevationLatitudeIndex = latitudeIndex;
247 maxElevationLongitudeIndex = longitudeIndex;
248 }
249 elevations[latitudeIndex * getLongitudeColumns() + longitudeIndex] = elevation;
250 }
251
252
253 @Override
254 public double getElevationAtIndices(final int latitudeIndex, final int longitudeIndex) {
255 final double elevation = elevations[latitudeIndex * getLongitudeColumns() + longitudeIndex];
256 DumpManager.dumpTileCell(this, latitudeIndex, longitudeIndex, elevation);
257 return elevation;
258 }
259
260
261
262
263
264
265
266 @Override
267 public double interpolateElevation(final double latitude, final double longitude) {
268
269 final double doubleLatitudeIndex = getDoubleLatitudeIndex(latitude);
270 final double doubleLongitudeIndex = getDoubleLongitudeIndex(longitude);
271 if (doubleLatitudeIndex < -TOLERANCE || doubleLatitudeIndex >= (latitudeRows - 1 + TOLERANCE) ||
272 doubleLongitudeIndex < -TOLERANCE || doubleLongitudeIndex >= (longitudeColumns - 1 + TOLERANCE)) {
273 throw new RuggedException(RuggedMessages.OUT_OF_TILE_ANGLES,
274 FastMath.toDegrees(latitude),
275 FastMath.toDegrees(longitude),
276 FastMath.toDegrees(getMinimumLatitude()),
277 FastMath.toDegrees(getMaximumLatitude()),
278 FastMath.toDegrees(getMinimumLongitude()),
279 FastMath.toDegrees(getMaximumLongitude()));
280 }
281
282 final int latitudeIndex = FastMath.max(0,
283 FastMath.min(latitudeRows - 2,
284 (int) FastMath.floor(doubleLatitudeIndex)));
285 final int longitudeIndex = FastMath.max(0,
286 FastMath.min(longitudeColumns - 2,
287 (int) FastMath.floor(doubleLongitudeIndex)));
288
289
290 final double dLat = doubleLatitudeIndex - latitudeIndex;
291 final double dLon = doubleLongitudeIndex - longitudeIndex;
292 final double e00 = getElevationAtIndices(latitudeIndex, longitudeIndex);
293 final double e10 = getElevationAtIndices(latitudeIndex, longitudeIndex + 1);
294 final double e01 = getElevationAtIndices(latitudeIndex + 1, longitudeIndex);
295 final double e11 = getElevationAtIndices(latitudeIndex + 1, longitudeIndex + 1);
296
297 return (e00 * (1.0 - dLon) + dLon * e10) * (1.0 - dLat) +
298 (e01 * (1.0 - dLon) + dLon * e11) * dLat;
299
300 }
301
302
303 @Override
304 public NormalizedGeodeticPoint cellIntersection(final NormalizedGeodeticPoint p, final Vector3D los,
305 final int latitudeIndex, final int longitudeIndex) {
306
307
308 final int iLat = FastMath.max(0, FastMath.min(latitudeRows - 2, latitudeIndex));
309 final int jLong = FastMath.max(0, FastMath.min(longitudeColumns - 2, longitudeIndex));
310
311
312 final double x00 = getLongitudeAtIndex(jLong);
313 final double y00 = getLatitudeAtIndex(iLat);
314 final double z00 = getElevationAtIndices(iLat, jLong);
315 final double z01 = getElevationAtIndices(iLat + 1, jLong);
316 final double z10 = getElevationAtIndices(iLat, jLong + 1);
317 final double z11 = getElevationAtIndices(iLat + 1, jLong + 1);
318
319
320 final NormalizedGeodeticPoint tileP = new NormalizedGeodeticPoint(p.getLatitude(),
321 p.getLongitude(),
322 p.getAltitude(),
323 x00);
324
325
326 final double dxA = (tileP.getLongitude() - x00) / longitudeStep;
327 final double dyA = (tileP.getLatitude() - y00) / latitudeStep;
328 final double dzA = tileP.getAltitude();
329 final double dxB = dxA + los.getX() / longitudeStep;
330 final double dyB = dyA + los.getY() / latitudeStep;
331 final double dzB = dzA + los.getZ();
332
333
334
335
336
337
338
339
340 final double u = (dxA - dxB) * (dyA - dyB) * (z00 - z10 - z01 + z11);
341 final double v = ((dxA - dxB) * (1 - dyA) + (dyA - dyB) * (1 - dxA)) * z00 +
342 (dxA * (dyA - dyB) - (dxA - dxB) * (1 - dyA)) * z10 +
343 (dyA * (dxA - dxB) - (dyA - dyB) * (1 - dxA)) * z01 +
344 ((dxB - dxA) * dyA + (dyB - dyA) * dxA) * z11;
345 final double w = (1 - dxA) * ((1 - dyA) * z00 + dyA * z01) +
346 dxA * ((1 - dyA) * z10 + dyA * z11);
347
348
349
350 final double a = u;
351 final double b = v + dzA - dzB;
352 final double c = w - dzA;
353
354
355 final double t1;
356 final double t2;
357 if (FastMath.abs(a) <= Precision.EPSILON * FastMath.abs(c)) {
358
359 final double t = -c / b;
360 t1 = Double.isNaN(t) ? 0.0 : t;
361 t2 = Double.POSITIVE_INFINITY;
362 } else {
363
364 final double b2 = b * b;
365 final double fac = 4 * a * c;
366 if (b2 < fac) {
367
368 return null;
369 }
370 final double s = FastMath.sqrt(b2 - fac);
371 t1 = (b < 0) ? (s - b) / (2 * a) : -2 * c / (b + s);
372 t2 = c / (a * t1);
373
374 }
375
376 final NormalizedGeodeticPoint p1 = interpolate(t1, tileP, dxA, dyA, los, x00);
377 final NormalizedGeodeticPoint p2 = interpolate(t2, tileP, dxA, dyA, los, x00);
378
379
380 if (p1 == null) {
381 return p2;
382 } else if (p2 == null) {
383 return p1;
384 } else {
385 return t1 <= t2 ? p1 : p2;
386 }
387
388 }
389
390
391
392
393
394
395
396
397
398
399
400 private NormalizedGeodeticPoint interpolate(final double t, final NormalizedGeodeticPoint tileP,
401 final double dxP, final double dyP,
402 final Vector3D los, final double centralLongitude) {
403
404 if (Double.isInfinite(t)) {
405 return null;
406 }
407
408 final double dx = dxP + t * los.getX() / longitudeStep;
409 final double dy = dyP + t * los.getY() / latitudeStep;
410 if (dx >= -TOLERANCE && dx <= 1 + TOLERANCE && dy >= -TOLERANCE && dy <= 1 + TOLERANCE) {
411 return new NormalizedGeodeticPoint(tileP.getLatitude() + t * los.getY(),
412 tileP.getLongitude() + t * los.getX(),
413 tileP.getAltitude() + t * los.getZ(),
414 centralLongitude);
415 } else {
416 return null;
417 }
418
419 }
420
421
422 @Override
423 public int getFloorLatitudeIndex(final double latitude) {
424 return (int) FastMath.floor(getDoubleLatitudeIndex(latitude));
425 }
426
427
428 @Override
429 public int getFloorLongitudeIndex(final double longitude) {
430 return (int) FastMath.floor(getDoubleLongitudeIndex(longitude));
431 }
432
433
434
435
436
437 protected double getDoubleLatitudeIndex(final double latitude) {
438 return (latitude - minLatitude) / latitudeStep;
439 }
440
441
442
443
444
445 protected double getDoubleLongitudeIndex(final double longitude) {
446 return (longitude - minLongitude) / longitudeStep;
447 }
448
449
450 @Override
451 public Location getLocation(final double latitude, final double longitude) {
452 final int latitudeIndex = getFloorLatitudeIndex(latitude);
453 final int longitudeIndex = getFloorLongitudeIndex(longitude);
454 if (longitudeIndex < 0) {
455 if (latitudeIndex < 0) {
456 return Location.SOUTH_WEST;
457 } else if (latitudeIndex <= (latitudeRows - 2)) {
458 return Location.WEST;
459 } else {
460 return Location.NORTH_WEST;
461 }
462 } else if (longitudeIndex <= (longitudeColumns - 2)) {
463 if (latitudeIndex < 0) {
464 return Location.SOUTH;
465 } else if (latitudeIndex <= (latitudeRows - 2)) {
466 return Location.HAS_INTERPOLATION_NEIGHBORS;
467 } else {
468 return Location.NORTH;
469 }
470 } else {
471 if (latitudeIndex < 0) {
472 return Location.SOUTH_EAST;
473 } else if (latitudeIndex <= (latitudeRows - 2)) {
474 return Location.EAST;
475 } else {
476 return Location.NORTH_EAST;
477 }
478 }
479 }
480 }