Unverified Commit 066f29ae authored by tomrink's avatar tomrink Committed by GitHub
Browse files

Merge pull request #152 from tomrink/GriddedLatLonSet

Gridded lat lon set
parents ec8aced4 65081b43
......@@ -7,7 +7,6 @@ package visad;
import java.util.ArrayList;
import java.rmi.RemoteException;
import javax.media.j3d.*;
public class FixGeomSizeAppearance implements ControlListener {
......
......@@ -29,8 +29,13 @@ package visad;
import java.io.*;
/**
GriddedLatLonSet represents a finite set of samples of R^2.<P>
*/
*
* @author rink
*
* GriddedLatLonSet represents a finite set of samples with a 2D grid topology on the surface
* of the Earth mapped to R^2: (Longitude, Latitude) or (Latitude, Longitude). This class
* exists primarily to override valueToGrid and gridToValue.
*/
public class GriddedLatLonSet extends Gridded2DSet {
int LengthX, LengthY, TrackLen, latI, lonI;
......@@ -46,7 +51,7 @@ public class GriddedLatLonSet extends Gridded2DSet {
null errors, CoordinateSystem and Units are defaults from type */
public GriddedLatLonSet(MathType type, float[][] samples, int lengthX, int lengthY)
throws VisADException {
this(type, samples, lengthX, lengthY, null, null, null, false, false);
this(type, samples, lengthX, lengthY, null, null, null, false);
}
/** a 2-D set whose topology is a lengthX x lengthY grid;
......@@ -59,23 +64,14 @@ public class GriddedLatLonSet extends Gridded2DSet {
public GriddedLatLonSet(MathType type, float[][] samples, int lengthX, int lengthY,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors) throws VisADException {
this(type, samples, lengthX, lengthY, coord_sys, units, errors,
true, true);
this(type, samples, lengthX, lengthY, coord_sys, units, errors, false);
}
public GriddedLatLonSet(MathType type, float[][] samples, int lengthX, int lengthY,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors, boolean copy)
throws VisADException {
this(type, samples, lengthX, lengthY, coord_sys, units, errors,
copy, true);
}
public GriddedLatLonSet(MathType type, float[][] samples, int lengthX, int lengthY,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors, boolean copy, boolean test)
throws VisADException {
super(type, samples, lengthX, lengthY, coord_sys, units, errors, copy, test);
super(type, samples, lengthX, lengthY, coord_sys, units, errors, copy, false);
LowX = Low[0];
HiX = Hi[0];
......@@ -136,15 +132,17 @@ public class GriddedLatLonSet extends Gridded2DSet {
System.arraycopy(lons, lons0.length, lons1, 0, lons1.length);
System.arraycopy(lats, lats0.length, lats1, 0, lats1.length);
granules[0] = new GriddedLatLonSet(type, new float[][] {lons0, lats0}, LengthX, TrackLen0, coord_sys, units, errors, copy, test);
granules[1] = new GriddedLatLonSet(type, new float[][] {lons1, lats1}, LengthX, TrackLen1, coord_sys, units, errors, copy, test);
granules[0] = new GriddedLatLonSet(type, new float[][] {lons0, lats0}, LengthX, TrackLen0, coord_sys, units, errors, copy);
granules[1] = new GriddedLatLonSet(type, new float[][] {lons1, lats1}, LengthX, TrackLen1, coord_sys, units, errors, copy);
}
}
/** transform an array of non-integer grid coordinates to an array
of values in R^DomainDimension */
/** transform an array of non-integer grid coordinates to an array of values in (Longitude, Latitude).
* Returns nearest neighbor if the cell which contains the grid coordinate straddles
* the dateline or prime meridian.
*/
public float[][] gridToValue(float[][] grid) throws VisADException {
if (grid.length != ManifoldDimension) {
throw new SetException("Gridded2DSet.gridToValue: grid dimension " +
......@@ -258,6 +256,16 @@ public class GriddedLatLonSet extends Gridded2DSet {
return valueToGrid(value, null);
}
/** transform an array of values in R^2 (Longitude, Latitude) to an array
* of non-integer grid coordinates by walking the grid from guess to incoming
* target values by minimizing the angle between vectors from Earth center to the
* their respective Earth coordinates.
* @param value (Longitude, Latitude)
* @param guess Integer grid coordinate. Is replaced by last valid grid located
* by the search. If null, center point of domain is used as guess.
* @return Fractional grid coordinates for incoming values
* @throws visad.VisADException
*/
@Override
public synchronized float[][] valueToGrid(float[][] value, int[] guess) throws VisADException {
......@@ -537,16 +545,16 @@ public class GriddedLatLonSet extends Gridded2DSet {
offGrid = !insideTriangle(gg, CC, LL, DD, tt);
}
else if (gy == 0) {
offGrid = !insideTriangle(gg, LL, UU, RR, tt);
offGrid = !(insideTriangle(gg, CC, UU, LL, tt) || insideTriangle(gg, CC, RR, UU, tt));
}
else if (gy == TrackLen-1) {
offGrid = !insideTriangle(gg, LL, DD, RR, tt);
offGrid = !(insideTriangle(gg, CC, LL, DD, tt) || insideTriangle(gg, CC, DD, RR, tt));
}
else if (gx == 0) {
offGrid = !insideTriangle(gg, UU, RR, DD, tt);
offGrid = !(insideTriangle(gg, CC, UU, RR, tt) || insideTriangle(gg, CC, DD, RR, tt));
}
else if (gx == LengthX-1) {
offGrid = !insideTriangle(gg, UU, LL, DD, tt);
offGrid = !(insideTriangle(gg, CC, UU, LL, tt) || insideTriangle(gg, CC, DD, LL, tt));
}
if (!offGrid) {
......@@ -633,6 +641,13 @@ public class GriddedLatLonSet extends Gridded2DSet {
public static boolean insideTriangle(float[] v0, float[] v1, float[] v2, float[] pt) {
/* Eventhough chance of this happening is small (that the incoming target would fall on a domain point)*/
if ((pt[0] == v0[0] && pt[1] == v0[1] && pt[2] == v0[2]) ||
(pt[0] == v1[0] && pt[1] == v1[1] && pt[2] == v1[2]) ||
(pt[0] == v2[0] && pt[1] == v2[1] && pt[2] == v2[2])) {
return true;
}
float[] triNorm = TrajectoryManager.AxB(new float[] {v1[0]-v0[0], v1[1]-v0[1], v1[2]-v0[2]}, new float[] {v2[0]-v0[0], v2[1]-v0[1], v2[2]-v0[2]}, true);
double[] coeffs = TrajectoryManager.getPlaneCoeffsFromNormalAndPoint(new double[] {(float)triNorm[0], (float)triNorm[1], (float)triNorm[2]}, new double[]{(float)v0[0], (float)v0[1], (float)v0[2]});
......
......@@ -202,7 +202,7 @@ public class GriddedSet extends SampledSet implements GriddedSetIface {
if (type.equals(RealTupleType.LatitudeLongitudeTuple) || type.equals(RealTupleType.SpatialEarth2DTuple)) {
return new GriddedLatLonSet(type, samples,
lengths[0], lengths[1],
coord_sys, units, errors, copy, test);
coord_sys, units, errors, copy);
}
else {
return new Gridded2DSet(type, samples,
......
......@@ -173,20 +173,25 @@ public class Trajectory {
if (terrain != null) {
adjustFlowAtTerrain(terrain, color_values);
}
}
}
addPair(startPts, stopPts, startColor, stopColor);
if (indices[0] == null) {
offGrid = true;
}
else {
addPair(startPts, stopPts, startColor, stopColor);
uVecPath[0] = stopPts[0] - startPts[0];
uVecPath[1] = stopPts[1] - startPts[1];
uVecPath[2] = stopPts[2] - startPts[2];
uVecPath[0] = stopPts[0] - startPts[0];
uVecPath[1] = stopPts[1] - startPts[1];
uVecPath[2] = stopPts[2] - startPts[2];
float mag = (float) Math.sqrt(uVecPath[0]*uVecPath[0] + uVecPath[1]*uVecPath[1] + uVecPath[2]*uVecPath[2]);
uVecPath[0] /= mag;
uVecPath[1] /= mag;
uVecPath[2] /= mag;
update();
float mag = (float) Math.sqrt(uVecPath[0]*uVecPath[0] + uVecPath[1]*uVecPath[1] + uVecPath[2]*uVecPath[2]);
uVecPath[0] /= mag;
uVecPath[1] /= mag;
uVecPath[2] /= mag;
update();
}
}
}
......@@ -400,23 +405,30 @@ public class Trajectory {
}
}
addPair(startPts, stopPts, startColor, stopColor);
if (indices[0] == null) {
offGrid = true;
}
else {
addPair(startPts, stopPts, startColor, stopColor);
uVecPath[0] = stopPts[0] - startPts[0];
uVecPath[1] = stopPts[1] - startPts[1];
uVecPath[2] = stopPts[2] - startPts[2];
uVecPath[0] = stopPts[0] - startPts[0];
uVecPath[1] = stopPts[1] - startPts[1];
uVecPath[2] = stopPts[2] - startPts[2];
float mag = (float) Math.sqrt(uVecPath[0]*uVecPath[0] + uVecPath[1]*uVecPath[1] + uVecPath[2]*uVecPath[2]);
uVecPath[0] /= mag;
uVecPath[1] /= mag;
uVecPath[2] /= mag;
update();
float mag = (float) Math.sqrt(uVecPath[0]*uVecPath[0] + uVecPath[1]*uVecPath[1] + uVecPath[2]*uVecPath[2]);
uVecPath[0] /= mag;
uVecPath[1] /= mag;
uVecPath[2] /= mag;
update();
}
}
}
/*
Add start/stop pair segment to the TrajectoryManager.
*/
private void addPair(float[] startPt, float[] stopPt, byte[] startColor, byte[] stopColor) {
indexes[npairs] = trajMan.getCoordinateCount();
......@@ -435,6 +447,9 @@ public class Trajectory {
}
}
/*
Advance forecast (stop) point location, color and intrp info to the start for the next displacement interval.
*/
private void update() throws VisADException {
startPts[0] = stopPts[0];
......
This diff is collapsed.
......@@ -54,11 +54,19 @@ public class TrajectoryParams {
Euler;
}
public static enum InterpolationMethod {
Cubic,
Linear,
None;
}
public static final int LINE = 0;
public static final int RIBBON = 1;
public static final int CYLINDER = 2;
public static final int DEFORM_RIBBON = 3;
public static final int POINT = 4;
public static final int TRACER = 5;
public static final int TRACER_POINT = 6;
double trajVisibilityTimeWindow = 86400.0;
double trajRefreshInterval = 86400.0;
......@@ -67,13 +75,16 @@ public class TrajectoryParams {
SmoothParams smoothParams = SmoothParams.LIGHT;
boolean forward = true;
int direction = 1; //1: forward, -1: backward
boolean doIntrp = true;
//boolean doIntrp = true;
float markerSize = 1f;
boolean markerEnabled = false;
boolean manualIntrpPts = false;
boolean autoSizeMarker = true;
boolean cachingEnabled = true;
boolean terrainFollowEnabled = true;
boolean trcrStreamingEnabled = false;
boolean saveTracerLocations = false;
boolean trajDoIntrp = true;
int trajForm = LINE;
float cylWidth = 0.00014f;
......@@ -89,6 +100,9 @@ public class TrajectoryParams {
FlatField terrain = null;
Method method = Method.HySplit; //Default
InterpolationMethod interpMethod = InterpolationMethod.Cubic;
double timeStepScaleFactor = 1;
public TrajectoryParams() {
}
......@@ -101,7 +115,6 @@ public class TrajectoryParams {
this.smoothParams = params.getSmoothParams();
this.forward = params.getDirectionFlag();
this.direction = params.getDirection();
this.doIntrp = params.getDoIntrp();
this.markerSize = params.getMarkerSize();
this.markerEnabled = params.getMarkerEnabled();
this.manualIntrpPts = params.getManualIntrpPts();
......@@ -117,6 +130,11 @@ public class TrajectoryParams {
this.terrain = params.getTerrain();
this.terrainFollowEnabled = params.getTerrainFollowing();
this.method = params.getMethod();
this.interpMethod = params.getInterpolationMethod();
this.trcrStreamingEnabled = params.getTracerStreamingEnabled();
this.saveTracerLocations = params.getSaveTracerLocations();
this.timeStepScaleFactor = params.getTimeStepScaleFactor();
this.trajDoIntrp = params.getTrajDoIntrp();
}
public TrajectoryParams(double trajVisibilityTimeWindow, double trajRefreshInterval, int numIntrpPts, int startSkip, SmoothParams smoothParams) {
......@@ -183,10 +201,6 @@ public class TrajectoryParams {
return forward;
}
public void setDoIntrp(boolean yesno) {
this.doIntrp = yesno;
}
public void setNumIntrpPts(int numIntrpPts) {
this.numIntrpPts = numIntrpPts;
this.manualIntrpPts = true;
......@@ -224,6 +238,10 @@ public class TrajectoryParams {
this.method = method;
}
public void setInterpolationMethod(InterpolationMethod m) {
this.interpMethod = m;
}
public void setCachingEnabled(boolean yesno) {
this.cachingEnabled = yesno;
}
......@@ -288,10 +306,6 @@ public class TrajectoryParams {
return direction;
}
public boolean getDoIntrp() {
return this.doIntrp;
}
public float getMarkerSize() {
return this.markerSize;
}
......@@ -308,6 +322,10 @@ public class TrajectoryParams {
return method;
}
public InterpolationMethod getInterpolationMethod() {
return interpMethod;
}
public void setStartPoints(float[][] startPts) {
this.startPoints = startPts;
this.startType = Display.DisplaySpatialCartesianTuple;
......@@ -346,6 +364,38 @@ public class TrajectoryParams {
return this.cachingEnabled;
}
public boolean getTracerStreamingEnabled() {
return this.trcrStreamingEnabled;
}
public void setTracerStreamingEnabled(boolean yesno) {
this.trcrStreamingEnabled = yesno;
}
public boolean getSaveTracerLocations() {
return this.saveTracerLocations;
}
public void setSaveTracerLocations(boolean yesno) {
this.saveTracerLocations = yesno;
}
public double getTimeStepScaleFactor() {
return this.timeStepScaleFactor;
}
public void setTimeStepScaleFactor(double fac) {
this.timeStepScaleFactor = fac;
}
public boolean getTrajDoIntrp() {
return this.trajDoIntrp;
}
public void setTrajDoIntrp(boolean yesno) {
this.trajDoIntrp = yesno;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof TrajectoryParams)) {
return false;
......@@ -376,9 +426,6 @@ public class TrajectoryParams {
else if (this.trajForm != trajParams.trajForm) {
return false;
}
else if (this.doIntrp != trajParams.doIntrp) {
return false;
}
else if (this.forward != trajParams.forward) {
return false;
}
......@@ -400,6 +447,18 @@ public class TrajectoryParams {
else if (this.method != trajParams.method) {
return false;
}
else if (this.trcrStreamingEnabled != trajParams.trcrStreamingEnabled) {
return false;
}
else if (this.saveTracerLocations != trajParams.saveTracerLocations) {
return false;
}
else if (this.timeStepScaleFactor != trajParams.timeStepScaleFactor) {
return false;
}
else if (this.trajDoIntrp != trajParams.trajDoIntrp) {
return false;
}
}
return true;
}
......
//
// XTrackScanLatLonSet
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2018 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad;
/**
*
* @author rink
*
* Specialized extension to GriddedLatLonSet for a contiguous collection of spatially overlapping sets
* wherein it's trusted that samples of an individual set are spatially coherent (non-degenerative: no bow-ties).
* Examples are MODIS and VIIRS whose granules are comprised of multiple scans, each with a fixed number of
* detectors, and perpendicular to the polar orbit track, but may overlap when navigated to the Earth's surface.
* The primary purpose of this class is to override valueToGrid.
*
*/
public class XTrackScanLatLonSet extends GriddedLatLonSet {
GriddedLatLonSet[] scanSets;
int numOfScans;
int linesPerScan;
int lastSetIdx;
public XTrackScanLatLonSet(MathType type, float[][] samples, int XTrackLen, int TrackLen, int linesPerScan) throws VisADException {
super(type, samples, XTrackLen, TrackLen, null, null, null, false);
if ((TrackLen % linesPerScan) != 0) {
throw new VisADException("There must be an intergral number of scans with detectorsPerScan: "+linesPerScan+" per "
+ "TrackLen: "+TrackLen);
}
this.linesPerScan = linesPerScan;
numOfScans = TrackLen/linesPerScan;
scanSets = new GriddedLatLonSet[numOfScans];
int scanLen = linesPerScan*XTrackLen;
for (int k=0; k<numOfScans; k++) {
float[] scanLonArray = new float[linesPerScan*XTrackLen];
float[] scanLatArray = new float[linesPerScan*XTrackLen];
System.arraycopy(lons, k*scanLen, scanLonArray, 0, scanLen);
System.arraycopy(lats, k*scanLen, scanLatArray, 0, scanLen);
scanSets[k] = new GriddedLatLonSet(RealTupleType.SpatialEarth2DTuple, new float[][] {scanLonArray, scanLatArray}, XTrackLen, linesPerScan);
}
}
public float[][] valueToGrid(float[][] value) throws VisADException {
return valueToGrid(value, null);
}
public float[][] valueToGrid(float[][] value, int[] guess) throws VisADException {
if (value.length < DomainDimension) {
throw new SetException("Gridded2DSet.valueToGrid: value dimension " +
value.length + " not equal to Domain dimension " +
DomainDimension);
}
if (ManifoldDimension < 2) {
throw new SetException("Gridded2DSet.valueToGrid: Manifold dimension " +
"must be 2, not " + ManifoldDimension);
}
if (Length > 1 && (Lengths[0] < 2 || Lengths[1] < 2)) {
throw new SetException("Gridded2DSet.valueToGrid: requires all grid " +
"dimensions to be > 1");
}
if (guess != null && guess.length != 2) {
throw new SetException("Gridded2DSet.valueToGrid: guess length "
+ guess.length + " must equal 2");
}
int length = Math.min(value[0].length, value[1].length);
float[][] grid = new float[ManifoldDimension][length];
// (gx, gy) is the current grid box guess
int gx = (LengthX-1)/2;
int gy = (TrackLen-1)/2;
if (guess != null && guess[0] >= 0 && guess[1] >= 0) {
gx = guess[0];
gy = guess[1];
}
else if (lgxy[0] != -1 && lgxy[1] != -1) {
gx = lgxy[0];
gy = lgxy[1];
}
int setIdx = gy/linesPerScan;
//int gy_set = gy % linesPerScan;
lastSetIdx = setIdx;
int idx = gy*LengthX+gx;
if (Float.isNaN(lons[idx]) || Float.isNaN(lats[idx]) || (Math.abs(lats[idx]) > 90)) {
throw new VisADException("initial grid box guess cannot be invalid or missing");
}
float[][] lonlat = new float[2][1];
int[] guess_set = new int[2];
guess_set[0] = gx;
guess_set[1] = linesPerScan/2;
for (int i=0; i<length; i++) {
if (Length == 1) {
if (Float.isNaN(value[0][i]) || Float.isNaN(value[1][i])) {
grid[0][i] = grid[1][i] = Float.NaN;
} else {
grid[0][i] = 0;
grid[1][i] = 0;
}
continue;
}
// test for missing
if ( (i != 0) && grid[0][i-1] != grid[0][i-1] ) {
// use last valid
if (lgxy[0] != -1 && lgxy[1] != -1) {
gx = lgxy[0];
gy = lgxy[1];
}
}
// if the loop doesn't find the answer, the result should be NaN
grid[0][i] = grid[1][i] = Float.NaN;
float targetLon = value[lonI][i];
float targetLat = value[latI][i];
lonlat[lonI][0] = targetLon;
lonlat[latI][0] = targetLat;
float[][] gxgy;
/* Should never exceed numOfScans */
int cnt = 0;
/* Used to detect infinite stepping back and forth between consecutive scans.
This may happen if the target falls in a gap between two adjacent scans,
for example, near MODIS, VIIRS Nadir. In this situation, NaN is returned but
may consider other options in the future.
*/
int dir = 0;
/* Beginnig with lastSetIdx start walking through the GriddedLatLonSet to find grid
cell which contains the target. If none found, use last test position (must be
on an edge) as guess position for the adjacent scan.
*/
while ((lastSetIdx >= 0 && lastSetIdx < numOfScans) && cnt < numOfScans) {
GriddedLatLonSet scanSet = scanSets[lastSetIdx];
gxgy = scanSet.valueToGrid(lonlat, guess_set);
if (Float.isNaN(gxgy[0][0]) || Float.isNaN(gxgy[1][0])) {
int gx_set = guess_set[0];
int gy_set = guess_set[1];
if (gy_set == 0) {
lastSetIdx -= 1;
guess_set[0] = gx_set;
guess_set[1] = linesPerScan-1;
if (dir == 1) {
break;
}
dir = -1;
}
else if (gy_set == linesPerScan-1) {