001    /**
002     * 
003     */
004    package org.wdssii.core;
005    
006    import java.util.Date;
007    
008    import org.apache.commons.logging.Log;
009    import org.apache.commons.logging.LogFactory;
010    
011    import ucar.units.ConversionException;
012    import ucar.units.Converter;
013    import ucar.units.Unit;
014    
015    /**
016     * 
017     * A regularly spaced grid where the first dimension is latitude and the second dimension is longitude.
018     * The (0,0) location is the north-west corner.  In the first dimension, latitude decreases (southward)
019     * and in the second dimension, longitude increases (eastward).
020     * 
021     * @author lakshman
022     *
023     */
024    public class LatLonGrid extends DataType {
025            private static Log log = LogFactory.getLog(LatLonGrid.class);
026            private final float deltaLat, deltaLon;
027            private float[][] values;
028            
029            /** {@inheritDoc} */
030            public LatLonGrid(LatLonGrid master){
031                    super(master);
032                    deltaLat = master.deltaLat;
033                    deltaLon = master.deltaLon;
034                    values = copyOf(master.values);
035            }
036            
037            public LatLonGrid(Location nwCorner, Date time, String typeName, float deltaLat, float deltaLon, float[][] values){
038                    super(nwCorner, time, typeName);
039                    this.deltaLat = deltaLat;
040                    this.deltaLon = deltaLon;
041                    this.values = values;
042            }
043    
044            /** All the values are initialized to zero */
045            public LatLonGrid(Location nwCorner, Date time, String typeName, float deltaLat, float deltaLon, int numLat, int numLon){
046                    this(nwCorner, time, typeName, deltaLat, deltaLon, new float[numLat][numLon] );
047            }
048    
049            public float[][] getValues(){
050                    return values;
051            }
052            
053            public int getNumLat(){
054                    return values.length;
055            }
056            
057            public int getNumLon(){
058                    if ( values.length == 0 ){
059                            return 0;
060                    } else {
061                            return values[0].length;
062                    }
063            }
064            
065            /** positive number that indicates the size of a pixel in lat-direction */
066            public float getDeltaLat(){
067                    return deltaLat;
068            }
069            
070            /** positive number that indicates the size of a pixel in lon-direction */
071            public float getDeltaLon(){
072                    return deltaLon;
073            }
074            
075            /** Converts all the values in this grid to the specified unit.
076             *  @see UdUnits.parse()
077             *  @param grid
078             *  @param toUnit  Use UdUnits.parse() to create this Unit
079             */
080            public void convertToUnit(Unit toUnit) {
081                    if (this.getUnit() == null) {
082                            log.debug("Missing units in " + this.getTypeName() + " assuming "
083                                            + toUnit);
084                            return;
085                    }
086                    Unit gridUnit = UdUnits.parse(this.getUnit());
087                    if (gridUnit.equals(toUnit)) {
088                            return;
089                    }
090                    try {
091                            Converter converter = gridUnit.getConverterTo(toUnit);
092                            float[][] values = this.getValues();
093                            for (int i = 0; i < values.length; ++i) {
094                                    for (int j = 0; j < values.length; ++j) {
095                                            values[i][j] = converter.convert(values[i][j]);
096                                    }
097                            }
098                    } catch (ConversionException e) {
099                            log.error("Unit in " + this.getTypeName() + " is " + gridUnit
100                                            + "; expected something compatible with " + toUnit);
101                    }
102            }
103            
104            /** Get the value at a Location. The height is ignored and MissingData is returned for values outside */
105            public float getValue(Location loc){
106                    try{
107                            // not reusing Pixel code since this is more efficient
108                            int i = (int) Math.rint( (this.getLocation().getLatitude() - loc.getLatitude()) / this.deltaLat);
109                            int j = (int) Math.rint( (loc.getLongitude() - this.getLocation().getLongitude()) / this.deltaLon);
110                            return values[i][j];
111                    } catch(Exception e) {
112                            return DataType.MissingData;
113                    }
114            }
115            
116            /** returns the Pixel corresponding to the location. The height is ignored but a null value is
117             * returned if the location is outside the grid.
118             */
119            public Pixel getPixel(Location loc){
120                    int i = (int) Math.rint( (this.getLocation().getLatitude() - loc.getLatitude()) / this.deltaLat);
121                    int j = (int) Math.rint( (loc.getLongitude() - this.getLocation().getLongitude()) / this.deltaLon);
122                    if ( i >= 0 && i < getNumLat() && j >= 0 && j < getNumLon() ){
123                            return new Pixel(i,j);
124                    } else {
125                            return null;
126                    }
127            }
128            
129            public float getValue(Pixel p){
130                    return values[p.x][p.y];
131            }
132            
133            public static class Pixel {
134                    public int x;
135                    public int y;
136                    public Pixel(int x, int y){
137                            this.x = x;
138                            this.y = y;
139                    }
140            }
141    
142            /** Can be used to set all the values of the grid at once. */
143            public void setValues(float[][] values2) {
144                    values = values2;
145            }
146    }