    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2011, 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     * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025     * Other names may be trademarks of their respective owners.]
    026     *
    027     * --------------------------
    028     * DefaultHeatMapDataset.java
    029     * --------------------------
    030     * (C) Copyright 2009, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes:
    036     * --------
    037     * 28-Jan-2009 : Version 1 (DG);
    038     *
    039     */
    040
    041    package org.jfree.data.general;
    042
    043    import java.io.Serializable;
    044    import org.jfree.data.DataUtilities;
    045    import org.jfree.util.PublicCloneable;
    046
    047    /**
    048     * A default implementation of the {@link HeatMapDataset} interface.
    049     *
    050     * @since 1.0.13
    051     */
    052    public class DefaultHeatMapDataset extends AbstractDataset
    053            implements HeatMapDataset, Cloneable, PublicCloneable, Serializable {
    054
    055        /** The number of samples in this dataset for the x-dimension. */
    056        private int xSamples;
    057
    058        /** The number of samples in this dataset for the y-dimension. */
    059        private int ySamples;
    060
    061        /** The minimum x-value in the dataset. */
    062        private double minX;
    063
    064        /** The maximum x-value in the dataset. */
    065        private double maxX;
    066
    067        /** The minimum y-value in the dataset. */
    068        private double minY;
    069
    070        /** The maximum y-value in the dataset. */
    071        private double maxY;
    072
    073        /** Storage for the z-values. */
    074        private double[][] zValues;
    075
    076        /**
    077         * Creates a new dataset where all the z-values are initially 0.  This is
    078         * a fixed size array of z-values.
    079         *
    080         * @param xSamples  the number of x-values.
    081         * @param ySamples  the number of y-values
    082         * @param minX  the minimum x-value in the dataset.
    083         * @param maxX  the maximum x-value in the dataset.
    084         * @param minY  the minimum y-value in the dataset.
    085         * @param maxY  the maximum y-value in the dataset.
    086         */
    087        public DefaultHeatMapDataset(int xSamples, int ySamples, double minX,
    088                double maxX, double minY, double maxY) {
    089
    090            if (xSamples < 1) {
    091                throw new IllegalArgumentException("Requires 'xSamples' > 0");
    092            }
    093            if (ySamples < 1) {
    094                throw new IllegalArgumentException("Requires 'ySamples' > 0");
    095            }
    096            if (Double.isInfinite(minX) || Double.isNaN(minX)) {
    097                throw new IllegalArgumentException("'minX' cannot be INF or NaN.");
    098            }
    099            if (Double.isInfinite(maxX) || Double.isNaN(maxX)) {
    100                throw new IllegalArgumentException("'maxX' cannot be INF or NaN.");
    101            }
    102            if (Double.isInfinite(minY) || Double.isNaN(minY)) {
    103                throw new IllegalArgumentException("'minY' cannot be INF or NaN.");
    104            }
    105            if (Double.isInfinite(maxY) || Double.isNaN(maxY)) {
    106                throw new IllegalArgumentException("'maxY' cannot be INF or NaN.");
    107            }
    108
    109            this.xSamples = xSamples;
    110            this.ySamples = ySamples;
    111            this.minX = minX;
    112            this.maxX = maxX;
    113            this.minY = minY;
    114            this.maxY = maxY;
    115            this.zValues = new double[xSamples][];
    116            for (int x = 0; x < xSamples; x++) {
    117                this.zValues[x] = new double[ySamples];
    118            }
    119        }
    120
    121        /**
    122         * Returns the number of x values across the width of the dataset.  The
    123         * values are evenly spaced between {@link #getMinimumXValue()} and
    124         * {@link #getMaximumXValue()}.
    125         *
    126         * @return The number of x-values (always > 0).
    127         */
    128        public int getXSampleCount() {
    129            return this.xSamples;
    130        }
    131
    132        /**
    133         * Returns the number of y values (or samples) for the dataset.  The
    134         * values are evenly spaced between {@link #getMinimumYValue()} and
    135         * {@link #getMaximumYValue()}.
    136         *
    137         * @return The number of y-values (always > 0).
    138         */
    139        public int getYSampleCount() {
    140            return this.ySamples;
    141        }
    142
    143        /**
    144         * Returns the lowest x-value represented in this dataset.  A requirement
    145         * of this interface is that this method must never return infinite or
    146         * Double.NAN values.
    147         *
    148         * @return The lowest x-value represented in this dataset.
    149         */
    150        public double getMinimumXValue() {
    151            return this.minX;
    152        }
    153
    154        /**
    155         * Returns the highest x-value represented in this dataset.  A requirement
    156         * of this interface is that this method must never return infinite or
    157         * Double.NAN values.
    158         *
    159         * @return The highest x-value represented in this dataset.
    160         */
    161        public double getMaximumXValue() {
    162            return this.maxX;
    163        }
    164
    165        /**
    166         * Returns the lowest y-value represented in this dataset.  A requirement
    167         * of this interface is that this method must never return infinite or
    168         * Double.NAN values.
    169         *
    170         * @return The lowest y-value represented in this dataset.
    171         */
    172        public double getMinimumYValue() {
    173            return this.minY;
    174        }
    175
    176        /**
    177         * Returns the highest y-value represented in this dataset.  A requirement
    178         * of this interface is that this method must never return infinite or
    179         * Double.NAN values.
    180         *
    181         * @return The highest y-value represented in this dataset.
    182         */
    183        public double getMaximumYValue() {
    184            return this.maxY;
    185        }
    186
    187        /**
    188         * A convenience method that returns the x-value for the given index.
    189         *
    190         * @param xIndex  the xIndex.
    191         *
    192         * @return The x-value.
    193         */
    194        public double getXValue(int xIndex) {
    195            double x = this.minX
    196                    + (this.maxX - this.minX) * (xIndex / (double) this.xSamples);
    197            return x;
    198        }
    199
    200        /**
    201         * A convenience method that returns the y-value for the given index.
    202         *
    203         * @param yIndex  the yIndex.
    204         *
    205         * @return The y-value.
    206         */
    207        public double getYValue(int yIndex) {
    208            double y = this.minY
    209                    + (this.maxY - this.minY) * (yIndex / (double) this.ySamples);
    210            return y;
    211        }
    212
    213        /**
    214         * Returns the z-value at the specified sample position in the dataset.
    215         * For a missing or unknown value, this method should return Double.NAN.
    216         *
    217         * @param xIndex  the position of the x sample in the dataset.
    218         * @param yIndex  the position of the y sample in the dataset.
    219         *
    220         * @return The z-value.
    221         */
    222        public double getZValue(int xIndex, int yIndex) {
    223            return this.zValues[xIndex][yIndex];
    224        }
    225
    226        /**
    227         * Returns the z-value at the specified sample position in the dataset.
    228         * In this implementation, where the underlying values are stored in an
    229         * array of double primitives, you should avoid using this method and
    230         * use {@link #getZValue(int, int)} instead.
    231         *
    232         * @param xIndex  the position of the x sample in the dataset.
    233         * @param yIndex  the position of the y sample in the dataset.
    234         *
    235         * @return The z-value.
    236         */
    237        public Number getZ(int xIndex, int yIndex) {
    238            return new Double(getZValue(xIndex, yIndex));
    239        }
    240
    241        /**
    242         * Updates a z-value in the dataset and sends a {@link DatasetChangeEvent}
    243         * to all registered listeners.
    244         *
    245         * @param xIndex  the x-index.
    246         * @param yIndex  the y-index.
    247         * @param z  the new z-value.
    248         */
    249        public void setZValue(int xIndex, int yIndex, double z) {
    250            setZValue(xIndex, yIndex, z, true);
    251        }
    252
    253        /**
    254         * Updates a z-value in the dataset and, if requested, sends a
    255         * {@link DatasetChangeEvent} to all registered listeners.
    256         *
    257         * @param xIndex  the x-index.
    258         * @param yIndex  the y-index.
    259         * @param z  the new z-value.
    260         * @param notify  notify listeners?
    261         */
    262        public void setZValue(int xIndex, int yIndex, double z, boolean notify) {
    263            this.zValues[xIndex][yIndex] = z;
    264            if (notify) {
    265                fireDatasetChanged();
    266            }
    267        }
    268
    269        /**
    270         * Tests this dataset for equality with an arbitrary object.
    271         *
    272         * @param obj  the object (<code>null</code> permitted).
    273         *
    274         * @return A boolean.
    275         */
    276        public boolean equals(Object obj) {
    277            if (obj == this) {
    278                return true;
    279            }
    280            if (!(obj instanceof DefaultHeatMapDataset)) {
    281                return false;
    282            }
    283            DefaultHeatMapDataset that = (DefaultHeatMapDataset) obj;
    284            if (this.xSamples != that.xSamples) {
    285                return false;
    286            }
    287            if (this.ySamples != that.ySamples) {
    288                return false;
    289            }
    290            if (this.minX != that.minX) {
    291                return false;
    292            }
    293            if (this.maxX != that.maxX) {
    294                return false;
    295            }
    296            if (this.minY != that.minY) {
    297                return false;
    298            }
    299            if (this.maxY != that.maxY) {
    300                return false;
    301            }
    302            if (!DataUtilities.equal(this.zValues, that.zValues)) {
    303                return false;
    304            }
    305            // can't find any differences
    306            return true;
    307        }
    308
    309        /**
    310         * Returns an independent copy of this dataset.
    311         *
    312         * @return A clone.
    313         *
    314         * @throws java.lang.CloneNotSupportedException
    315         */
    316        public Object clone() throws CloneNotSupportedException {
    317            DefaultHeatMapDataset clone = (DefaultHeatMapDataset) super.clone();
    318            clone.zValues = DataUtilities.clone(this.zValues);
    319            return clone;
    320        }
    321
    322    }