001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * --------------------------
028     * NonGridContourDataset.java
029     * --------------------------
030     * (C) Copyright 2002-2005, by David M. O'Donnell.
031     *
032     * Original Author:  David M. O'Donnell;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: NonGridContourDataset.java,v 1.3.2.1 2005/10/25 21:30:20 mungady Exp $
036     *
037     * Changes (from 24-Jul-2003)
038     * --------------------------
039     * 24-Jul-2003 : Added standard header (DG);
040     *
041     */
042    
043    package org.jfree.data.contour;
044    
045    import org.jfree.data.Range;
046    
047    /**
048     * A convenience class that extends the {@link DefaultContourDataset} to 
049     * accommodate non-grid data.
050     */
051    public class NonGridContourDataset extends DefaultContourDataset {
052    
053        /** Default number of x values. */
054        static final int DEFAULT_NUM_X = 50;
055        
056        /** Default number of y values. */
057        static final int DEFAULT_NUM_Y = 50;
058        
059        /** Default power. */
060        static final int DEFAULT_POWER = 4;
061    
062        /**
063         * Default constructor.
064         */
065        public NonGridContourDataset() {
066            super();
067        }
068    
069        /**
070         * Constructor for NonGridContourDataset.  Uses default values for grid 
071         * dimensions and weighting.
072         * 
073         * @param seriesName  the series name.
074         * @param xData  the x values.
075         * @param yData  the y values.
076         * @param zData  the z values.
077         */
078        public NonGridContourDataset(String seriesName,
079                                     Object[] xData, Object[] yData, 
080                                     Object[] zData) {
081            super(seriesName, xData, yData, zData);
082            buildGrid(DEFAULT_NUM_X, DEFAULT_NUM_Y, DEFAULT_POWER);
083        }
084    
085        /**
086         * Constructor for NonGridContourDataset.
087         * 
088         * @param seriesName  the series name.
089         * @param xData  the x values.
090         * @param yData  the y values.
091         * @param zData  the z values.
092         * @param numX  number grid cells in along the x-axis
093         * @param numY  number grid cells in along the y-axis
094         * @param power  exponent for inverse distance weighting
095         */
096        public NonGridContourDataset(String seriesName, 
097                                     Object[] xData, Object[] yData, 
098                                     Object[] zData,
099                                     int numX, int numY, int power) {
100            super(seriesName, xData, yData, zData);
101            buildGrid(numX, numY, power);
102        }
103    
104        /**
105         * Builds a regular grid.  Maps the non-grid data into the regular grid 
106         * using an inverse distance between grid and non-grid points.  Weighting 
107         * of distance can be controlled by setting through the power parameter 
108         * that controls the exponent used on the distance weighting 
109         * (e.g., distance^power).
110         * 
111         * @param numX  number grid points in along the x-axis
112         * @param numY  number grid points in along the y-axis
113         * @param power  exponent for inverse distance weighting
114         */
115        protected void buildGrid(int numX, int numY, int power) {
116    
117            int numValues = numX * numY;
118            double[] xGrid = new double[numValues];
119            double[] yGrid = new double [numValues];
120            double[] zGrid = new double [numValues];
121    
122            // Find min, max for the x and y axes
123            double xMin = 1.e20;
124            for (int k = 0; k < this.xValues.length; k++) {
125                xMin = Math.min(xMin, this.xValues[k].doubleValue());
126            }
127    
128            double xMax = -1.e20;
129            for (int k = 0; k < this.xValues.length; k++) {
130                xMax = Math.max(xMax, this.xValues[k].doubleValue());
131            }
132    
133            double yMin = 1.e20;
134            for (int k = 0; k < this.yValues.length; k++) {
135                yMin = Math.min(yMin, this.yValues[k].doubleValue());
136            }
137    
138            double yMax = -1.e20;
139            for (int k = 0; k < this.yValues.length; k++) {
140                yMax = Math.max(yMax, this.yValues[k].doubleValue());
141            }
142    
143            Range xRange = new Range(xMin, xMax);
144            Range yRange = new Range(yMin, yMax);
145    
146            xRange.getLength();
147            yRange.getLength();
148    
149            // Determine the cell size
150            double dxGrid = xRange.getLength() / (numX - 1);
151            double dyGrid = yRange.getLength() / (numY - 1);
152    
153            // Generate the grid
154            double x = 0.0;
155            for (int i = 0; i < numX; i++) {
156                if (i == 0) {
157                    x = xMin;
158                }
159                else {
160                    x += dxGrid;
161                }
162                double y = 0.0;
163                for (int j = 0; j < numY; j++) {
164                    int k = numY * i + j;
165                    xGrid[k] = x;
166                    if (j == 0) {
167                        y = yMin;
168                    }
169                    else {
170                        y += dyGrid;
171                    }
172                    yGrid[k] = y;
173                }
174            }
175    
176            // Map the nongrid data into the new regular grid
177            for (int kGrid = 0; kGrid < xGrid.length; kGrid++) {
178                double dTotal = 0.0;
179                zGrid[kGrid] = 0.0;
180                for (int k = 0; k < this.xValues.length; k++) {
181                    double xPt = this.xValues[k].doubleValue();
182                    double yPt = this.yValues[k].doubleValue();
183                    double d = distance(xPt, yPt, xGrid[kGrid], yGrid[kGrid]);
184                    if (power != 1) {
185                        d = Math.pow(d, power);
186                    }
187                    d = Math.sqrt(d);
188                    if (d > 0.0) {
189                        d = 1.0 / d;
190                    }
191                    else { // if d is real small set the inverse to a large number 
192                           // to avoid INF
193                        d = 1.e20;
194                    }
195                    if (this.zValues[k] != null) {
196                        // scale by the inverse of distance^power
197                        zGrid[kGrid] += this.zValues[k].doubleValue() * d; 
198                    }
199                    dTotal += d;
200                }
201                zGrid[kGrid] = zGrid[kGrid] / dTotal;  //remove distance of the sum
202            }
203    
204            //initalize xValues, yValues, and zValues arrays.
205            initialize(
206                formObjectArray(xGrid), formObjectArray(yGrid), 
207                formObjectArray(zGrid)
208            );
209    
210        }
211    
212        /**
213         * Calculates the distance between two points.
214         * 
215         * @param xDataPt  the x coordinate.
216         * @param yDataPt  the y coordinate.
217         * @param xGrdPt  the x grid coordinate.
218         * @param yGrdPt  the y grid coordinate.
219         * 
220         * @return The distance between two points.
221         */
222        protected double distance(double xDataPt, 
223                                  double yDataPt, 
224                                  double xGrdPt, 
225                                  double yGrdPt) {
226            double dx = xDataPt - xGrdPt;
227            double dy = yDataPt - yGrdPt;
228            return Math.sqrt(dx * dx + dy * dy);
229        }
230    
231    }