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