001    /**
002     * 
003     */
004    package org.wdssii.core;
005    
006    import java.util.Date;
007    
008    /**
009     * @author lakshman
010     * 
011     */
012    public class RadialSet extends DataType {
013            private final float elevation;
014    
015            protected final Radial[] radials;
016    
017            private final CPoint radarLocation;
018    
019            private final CVector myUx;
020    
021            private final CVector myUy;
022    
023            private final CVector myUz;
024    
025            private final float rangeToFirstGate;
026            
027            /**
028             * {@inheritDoc}
029             * @param master
030             */
031            public RadialSet(RadialSet master){
032                    super(master);
033                    this.elevation = master.elevation;
034                    this.radials = new Radial[ master.radials.length ];
035                    for (int i=0; i < radials.length; ++i){
036                            radials[i] = new Radial( master.radials[i] );
037                    }
038                    this.radarLocation = master.radarLocation;
039                    this.myUx = master.myUx;
040                    this.myUy = master.myUy;
041                    this.myUz = master.myUz;
042                    this.rangeToFirstGate = master.rangeToFirstGate;
043            }
044            
045            public RadialSet(float elevation, Location radarLoc, Date scanTime,
046                            float rangeToFirstGate,
047                            String typeName, Radial[] radials) {
048                    super(radarLoc, scanTime, typeName);
049                    this.radials = radials;
050                    this.elevation = elevation;
051                    this.rangeToFirstGate = rangeToFirstGate;
052                    // set up the co-ordinate system
053                    radarLocation = originLocation.getCPoint();
054                    myUz = radarLocation.minus(new CPoint(0, 0, 0)).unit();
055                    myUx = new CVector(0, 0, 1).crossProduct(myUz).unit();
056                    myUy = myUz.crossProduct(myUx);
057            }
058    
059            public float getElevation() {
060                    return elevation;
061            }
062    
063            /** meters */
064            public float getRangeToFirstGateKms() {
065                    return rangeToFirstGate;
066            }
067    
068            /**
069             * beamwidth of first radial, or 1 degree if there is no radial.
070             */
071            public float getBeamWidth() {
072                    if (radials.length > 0)
073                            return getRadial(0).getBeamWidth();
074                    return 1;
075            }
076            
077            /**
078             * nyquist of first radial, or 0 if there is no radial.
079             */
080            public float getNyquist() {
081                    if (radials.length > 0)
082                            return getRadial(0).getNyquist();
083                    return 0;
084            }
085    
086            /**
087             * mean azimuthal spacing of all radials.
088             */
089            public float getAzimuthalSpacing() {
090                    float as = 0;
091                    for (Radial r : radials){
092                            as += r.getAzimuthalSpacing();
093                    }
094                    float result = (radials.length <= 1)? as : (as / radials.length);
095                    return result;
096            }
097            
098            /**
099             * gatewidth of first radial, or 1 km if there is no radial.
100             */
101            public float getGateWidthKms() {
102                    if (radials.length > 0)
103                            return getRadial(0).getGateWidthKms();
104                    return 1.0f;
105            }
106    
107            /**
108             * number of gates of first radial, or 0 if there is no radial.
109             */
110            public int getNumGates() {
111                    if (radials.length > 0)
112                            return getRadial(0).getNumGates();
113                    return 0;
114            }
115    
116            public int getNumRadials() {
117                    return radials.length;
118            }
119    
120            public Location getRadarLocation() {
121                    return originLocation;
122            }
123    
124            public CPoint getRadarCPoint() {
125                    return radarLocation;
126            }
127    
128            public Radial[] getRadials() {
129                    return radials;
130            }
131    
132            public Radial getRadial(int index) {
133                    return radials[index];
134            }
135    
136            /** @return 0 if vcp is unknown. */
137            int getVCP() {
138                    try {
139                            return Integer.parseInt(getAttribute("vcp").toString());
140                    } catch (Exception e) {
141                            // number format, null, etc.
142                            return 0;
143                    }
144            }
145    
146            public double getRawValue(float az, float rn) {
147                    Radial r = getRadial(az);
148                    if (r == null)
149                            return MissingData;
150                    return r.getRawValue(rn);
151            }
152    
153            /**
154             * a very slow implementation
155             * 
156             * @see NormalizedRadialSet.
157             */
158            public Radial getRadial(float az) {
159                    // move backwards to get more recent radials first
160                    for (int i = radials.length - 1; i >= 0; --i) {
161                            Radial r = getRadial(i);
162                            if (r.contains(az))
163                                    return r;
164                    }
165                    return null;
166            }
167    
168            public double getRawValue(Location loc) {
169                    return getRawValue(loc.getCPoint());
170            }
171    
172            public double getRawValue(CPoint pt) {
173                    // vector from radar location to point
174                    // in earth-based co-ordinate system
175                    CVector fromConeApex = pt.minus(radarLocation);
176                    return getValueInConicalSpace(fromConeApex);
177            }
178    
179            /** returns the location of this point in space. */
180            public CPoint getLocation(int radialno, int gateno) {
181                    return getRadial(radialno).getLocation(elevation, gateno,
182                                    radarLocation, myUx, myUy, myUz);
183            }
184    
185            /**
186             * The CVector here should in a co-ordinate system where the radar location
187             * is the origin, and the z-axis is perpendicular to the earth's surface,
188             * and the y-axis is north-ward.
189             */
190            public double getValueInConicalSpace(CVector v) {
191                    if (radials.length == 0)
192                            return MissingData;
193    
194                    Radial first = getRadial(0);
195                    double norm = v.norm();
196                    double gw = first.getGateWidthKms();
197                    if (norm < gw)
198                            return MissingData; // before first gate ..
199    
200                    // what is the elevation of this point?
201                    double bw = first.getBeamWidth();
202                    double elev = Math.asin(v.dotProduct(myUz) / norm) * 180 / Math.PI;
203                    double elev_diff = elev - elevation;
204                    if (elev_diff > bw)
205                            return MissingData;
206    
207                    // within this elevation ...
208                    // find az (range approximately equals the norm)
209                    CVector vxy = v.crossProduct(myUz);
210                    double dotx = vxy.dotProduct(myUx);
211                    double doty = vxy.dotProduct(myUy);
212                    double az = 180 + Math.atan2(doty, dotx) * 180 / Math.PI;
213                    az = Radial.normalizeAzimuth((float) az);
214                    return getRawValue((float) az, (float) norm);
215            }
216    
217            /** debugging output */
218            public String toStringDB() {
219                    String s = "RadialSet " + getTypeName() + " at " + elevation + " has "
220                                    + radials.length + " radials." + " the first:\n"
221                                    + getRadial(0).toStringDB() + super.toStringDB();
222                    return s;
223            }
224    
225    }