| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| SOMDataset |
|
| 3.0;3 | ||||
| SOMDataset$SOMDatasetIterator |
|
| 3.0;3 |
| 1 | /* ======================================================================= | |
| 2 | * A visualisation library extension for JFreeChart. Please see JFreeChart | |
| 3 | * for further information. | |
| 4 | * ======================================================================= | |
| 5 | * Copyright (C) 2006 University of Helsinki, Department of Computer Science | |
| 6 | * | |
| 7 | * This library is free software; you can redistribute it and/or | |
| 8 | * modify it under the terms of the GNU Lesser General Public | |
| 9 | * License as published by the Free Software Foundation; either | |
| 10 | * version 2.1 of the License, or (at your option) any later version. | |
| 11 | * | |
| 12 | * This library is distributed in the hope that it will be useful, | |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 15 | * Lesser General Public License for more details. | |
| 16 | * | |
| 17 | * You should have received a copy of the GNU Lesser General Public | |
| 18 | * License along with this library; if not, write to the Free Software | |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| 20 | * ----------------------------- | |
| 21 | * Contact: ohtu@cs.helsinki.fi | |
| 22 | * ----------------------------- | |
| 23 | * | |
| 24 | */ | |
| 25 | ||
| 26 | ||
| 27 | package org.jfree.data.som; | |
| 28 | ||
| 29 | import java.util.*; | |
| 30 | import java.awt.Color; | |
| 31 | import java.io.Serializable; | |
| 32 | ||
| 33 | import org.jfree.data.general.AbstractDataset; | |
| 34 | ||
| 35 | ||
| 36 | /** | |
| 37 | * A dataset for dealing with SOM-map-cells. | |
| 38 | * | |
| 39 | * @author viski-project, Department of Computer Science, Univ. Helsinki | |
| 40 | */ | |
| 41 | public class SOMDataset extends AbstractDataset implements Cloneable, Serializable { | |
| 42 | ||
| 43 | /** Datastorage. */ | |
| 44 | protected SOMDataItem[][] data; | |
| 45 | ||
| 46 | /** | |
| 47 | * Creates a new SOMDataset with given columns and rows. | |
| 48 | * | |
| 49 | * @param columns the number of columns. | |
| 50 | * @param rows the number of rows. | |
| 51 | * @throws IllegalArgumentException If columns <= 0 or rows <= 0 | |
| 52 | */ | |
| 53 | 65 | public SOMDataset(int columns, int rows) throws IllegalArgumentException { |
| 54 | 65 | if (columns <= 0) |
| 55 | 5 | throw new IllegalArgumentException("Non-positive column count given to SOMDataset"); |
| 56 | 60 | if (rows <= 0) |
| 57 | 2 | throw new IllegalArgumentException("Non-positive row count given to SOMDataset"); |
| 58 | 58 | data = new SOMDataItem[columns][rows]; |
| 59 | 58 | } |
| 60 | ||
| 61 | /** | |
| 62 | * Returns the value at a specific (x,y) point in the datamatrix. | |
| 63 | * | |
| 64 | * @return A SOMDataItem. | |
| 65 | * @throws IndexOutOfBoundsException | |
| 66 | */ | |
| 67 | public SOMDataItem getValue(int x, int y) throws IndexOutOfBoundsException { | |
| 68 | 446 | return data[x][y]; |
| 69 | } | |
| 70 | ||
| 71 | /** | |
| 72 | * Returns the number of columns in a SOMDataset. | |
| 73 | * | |
| 74 | * @return The number of columns. | |
| 75 | */ | |
| 76 | public int getColumnCount() { | |
| 77 | 305 | return data.length; |
| 78 | } | |
| 79 | ||
| 80 | /** | |
| 81 | * Returns the number of rows in a SOMDataset. | |
| 82 | * | |
| 83 | * @return The number of rows. | |
| 84 | */ | |
| 85 | public int getRowCount() { | |
| 86 | 327 | return data[0].length; |
| 87 | } | |
| 88 | ||
| 89 | /** | |
| 90 | * Creates a new SOMDataItem from the params and adds it to the dataset. | |
| 91 | * | |
| 92 | * @param x the x-coordinate to add to in the matrix. | |
| 93 | * @param y the y-coordinate to add to in the matrix. | |
| 94 | * @param color the color of the dataitem. | |
| 95 | * @param description the textual description of the dataitem. | |
| 96 | * @param neuronWeights the numerical data of the dataitem. | |
| 97 | * | |
| 98 | * @throws IndexOutOfBoundsException | |
| 99 | */ | |
| 100 | public void addValue(int x, int y, Color color, String[] description, double[] neuronWeights) throws IndexOutOfBoundsException { | |
| 101 | 142 | data[x][y] = new SOMDataItem(color, description, neuronWeights); |
| 102 | 133 | } |
| 103 | ||
| 104 | /** | |
| 105 | * Adds a SOMDataItem to the dataset. | |
| 106 | * | |
| 107 | * @param x the x-coordinate to add to in the matrix. | |
| 108 | * @param y the y-coordinate to add to in the matrix. | |
| 109 | * @param item the SOMDataItem to add. | |
| 110 | * | |
| 111 | * @throws IndexOutOfBoundsException | |
| 112 | * @throws NullPointerException | |
| 113 | */ | |
| 114 | public void addValue(int x, int y, SOMDataItem item) throws IndexOutOfBoundsException { | |
| 115 | 4 | if (item == null) |
| 116 | 1 | throw new NullPointerException("item given to addValue was null."); |
| 117 | 3 | data[x][y] = item; |
| 118 | 3 | } |
| 119 | ||
| 120 | /** | |
| 121 | * Returns the neighbours of a dataitem in a {@link List}-object. | |
| 122 | * The distances are Euclidean distances | |
| 123 | * between the RGB-values of this data item and all other data items. | |
| 124 | * | |
| 125 | * @param x the x-coordinate in the matrix to start the search from. | |
| 126 | * @param y the y-coordinate in the matrix to start the search from. | |
| 127 | * @param maxDistance the distance between a neighbour (ie. similar color) and a non-neighbour | |
| 128 | * @param includeCenter Include the item at (x,y) in the list | |
| 129 | * | |
| 130 | * @return The list of neighbours. | |
| 131 | * @throws IndexOutOfBoundsException | |
| 132 | * @throws IllegalArgumentException If maxDistance < 0 | |
| 133 | */ | |
| 134 | public List getNeighbors(int x, int y, int maxDistance, boolean includeCenter) | |
| 135 | throws IndexOutOfBoundsException, IllegalArgumentException { | |
| 136 | 22 | SOMDataItem center = getValue(x, y); |
| 137 | ||
| 138 | 22 | return getNeighbors(center, maxDistance, includeCenter); |
| 139 | } | |
| 140 | ||
| 141 | /** | |
| 142 | * Returns the neighbours of a dataitem in a {@link List}-object. | |
| 143 | * The distances are Euclidean distances | |
| 144 | * between the RGB-values of this data item and all other data items. | |
| 145 | * | |
| 146 | * @param center the cell to which all other cells are compared | |
| 147 | * @param maxDistance the distance between a neighbour (ie. similar color) and a non-neighbour | |
| 148 | * @param includeCenter Include center in the list | |
| 149 | * | |
| 150 | * @return The list of neighbours. | |
| 151 | * @throws NullPointerException If center is null. | |
| 152 | * @throws IllegalArgumentException If maxDistance < 0. | |
| 153 | */ | |
| 154 | public List getNeighbors(SOMDataItem center, int maxDistance, boolean includeCenter) | |
| 155 | throws IllegalArgumentException, NullPointerException { | |
| 156 | 24 | if (center == null) |
| 157 | 1 | throw new NullPointerException("center given to getNeighbors() was null"); |
| 158 | 23 | if (maxDistance < 0) |
| 159 | 2 | throw new IllegalArgumentException("maxDistance given to getNeighbors() was negative."); |
| 160 | 21 | Color centerColor = center.getColor(); |
| 161 | 21 | LinkedList list = new LinkedList(); |
| 162 | ||
| 163 | 21 | Iterator i = new SOMDatasetIterator(this); |
| 164 | 107 | while (i.hasNext()) { |
| 165 | 86 | SOMDataItem item = (SOMDataItem) i.next(); |
| 166 | 86 | if (includeCenter || item != center) { |
| 167 | 74 | double diff = colorDistance(item.getColor(), centerColor); |
| 168 | 74 | if (diff <= maxDistance) |
| 169 | 43 | list.add(item); |
| 170 | } | |
| 171 | 86 | } |
| 172 | ||
| 173 | 21 | return list; |
| 174 | } | |
| 175 | ||
| 176 | /** | |
| 177 | * This method calculates the distance between two sets of integer color-values. | |
| 178 | * | |
| 179 | * @return The difference in color-values. | |
| 180 | */ | |
| 181 | private double colorDistance(Color color1, Color color2) { | |
| 182 | 74 | int r1 = color1.getRed(); |
| 183 | 74 | int g1 = color1.getGreen(); |
| 184 | 74 | int b1 = color1.getBlue(); |
| 185 | ||
| 186 | 74 | int r2 = color2.getRed(); |
| 187 | 74 | int g2 = color2.getGreen(); |
| 188 | 74 | int b2 = color2.getBlue(); |
| 189 | ||
| 190 | 74 | return Math.sqrt((r1-r2)*(r1-r2)+(g1-g2)*(g1-g2)+(b1-b2)*(b1-b2)); |
| 191 | } | |
| 192 | ||
| 193 | /** | |
| 194 | * Returns a {@link List} of {@link SOMDataItem} objects the are contained | |
| 195 | * inside the rectangle defined by two {@link SOMDataItem} objects. | |
| 196 | * | |
| 197 | * @param item1 corner of the rectangle | |
| 198 | * @param item2 corner of the rectangle | |
| 199 | * | |
| 200 | * @return The list of {@link SOMDataItem}s inside the area. | |
| 201 | * @throws NullPointerException If item1 == null or item2 == null | |
| 202 | * @throws IllegalArgumentException If item1 or item2 do not belong to this dataset. | |
| 203 | */ | |
| 204 | public List getArea(SOMDataItem item1, SOMDataItem item2) | |
| 205 | throws IllegalArgumentException, NullPointerException { | |
| 206 | 12 | if (item1 == null || item2 == null) |
| 207 | 2 | throw new NullPointerException("item1 or item2 given to getArea() was null"); |
| 208 | 10 | int[] xy1 = getCoordinates(item1); |
| 209 | 10 | int[] xy2 = getCoordinates(item2); |
| 210 | ||
| 211 | 10 | if (xy1 == null || xy2 == null) |
| 212 | 2 | throw new IllegalArgumentException(); |
| 213 | ||
| 214 | 8 | int width = Math.abs(xy1[0] - xy2[0]) + 1; |
| 215 | 8 | int height = Math.abs(xy1[1] - xy2[1]) + 1; |
| 216 | 8 | int startX = Math.min(xy1[0], xy2[0]); |
| 217 | 8 | int startY = Math.min(xy1[1], xy2[1]); |
| 218 | 8 | LinkedList list = new LinkedList(); |
| 219 | ||
| 220 | 23 | for (int y=0; y < height; ++y) { |
| 221 | 41 | for (int x=0; x < width; ++x) { |
| 222 | 26 | list.add(this.data[startX + x][startY + y]); |
| 223 | } | |
| 224 | } | |
| 225 | ||
| 226 | 8 | return list; |
| 227 | } | |
| 228 | ||
| 229 | /** | |
| 230 | * Returns the coordinates of a SOMDataItem. | |
| 231 | * | |
| 232 | * @param item the item whose coordinates we want. | |
| 233 | * | |
| 234 | * @return The coordinates. | |
| 235 | */ | |
| 236 | private int[] getCoordinates(SOMDataItem item) { | |
| 237 | 20 | int[] xy = new int[2]; |
| 238 | ||
| 239 | 33 | for (int y=0; y < this.data[0].length; ++y) { |
| 240 | 66 | for (int x=0; x < this.data.length; ++x) { |
| 241 | 53 | if (item == this.data[x][y]) { |
| 242 | 18 | xy[0] = x; |
| 243 | 18 | xy[1] = y; |
| 244 | 18 | return xy; |
| 245 | } | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | 2 | return null; |
| 250 | } | |
| 251 | ||
| 252 | /** | |
| 253 | * This method changes the color hue of all dataitems in a dataset for | |
| 254 | * given amount, negative or positive. The color-values of each dataitem | |
| 255 | * are retrieved, changed and new rgb-values are calculated and set. | |
| 256 | * | |
| 257 | * @param angle the amount to increase the hue angle. | |
| 258 | * @throws IllegalArgumentException If Math.abs(angle) >= 360. | |
| 259 | */ | |
| 260 | public void changeHueValues(int angle) | |
| 261 | throws IllegalArgumentException { | |
| 262 | 12 | if (Math.abs(angle) > 360) |
| 263 | 2 | throw new IllegalArgumentException("angle value given to changeHueValues() was not in [-360,360]."); |
| 264 | 10 | Iterator i = new SOMDatasetIterator(this); |
| 265 | 54 | while (i.hasNext()) { |
| 266 | 44 | SOMDataItem item = (SOMDataItem)i.next(); |
| 267 | ||
| 268 | 44 | if (item != null) { |
| 269 | 44 | Color c = item.getColor(); |
| 270 | 44 | float[] hsb = Color.RGBtoHSB( |
| 271 | c.getRed(), | |
| 272 | c.getGreen(), | |
| 273 | c.getBlue(), | |
| 274 | null); | |
| 275 | ||
| 276 | 44 | hsb[0] += angle/360.0 + 1.0; |
| 277 | 44 | hsb[0] %= 1.0; |
| 278 | 44 | item.setColor(Color.getHSBColor(hsb[0], hsb[1], hsb[2])); |
| 279 | } | |
| 280 | 44 | } |
| 281 | 10 | } |
| 282 | ||
| 283 | /** | |
| 284 | * This method returns an iterator that iterates all it's dataitems | |
| 285 | * | |
| 286 | * @return iterator | |
| 287 | */ | |
| 288 | public Iterator iterator() { | |
| 289 | 24 | return new SOMDatasetIterator(this); |
| 290 | } | |
| 291 | ||
| 292 | /** | |
| 293 | * Deselects all the dataitems in this SOMDataset. | |
| 294 | */ | |
| 295 | public void deselectAll() { | |
| 296 | 12 | Iterator i = iterator(); |
| 297 | 76 | while (i.hasNext()) { |
| 298 | 65 | SOMDataItem item = (SOMDataItem)i.next(); |
| 299 | 65 | item.setSelected(false); |
| 300 | 64 | } |
| 301 | 11 | } |
| 302 | ||
| 303 | /** | |
| 304 | * An inner class to deal with SOMDataItem-objects in the SOMDataset. This | |
| 305 | * class implements the {@link Iterator} interface | |
| 306 | */ | |
| 307 | private class SOMDatasetIterator implements Iterator { | |
| 308 | private SOMDataset dataset; | |
| 309 | private int x; | |
| 310 | private int y; | |
| 311 | ||
| 312 | /** | |
| 313 | * Creates a new SOMDatasetIterator for a dataset. | |
| 314 | * | |
| 315 | * @param dataset the SOMDataset to be iterated. | |
| 316 | */ | |
| 317 | 55 | public SOMDatasetIterator(SOMDataset dataset) { |
| 318 | 55 | this.dataset = dataset; |
| 319 | 55 | this.x = 0; |
| 320 | 55 | this.y = 0; |
| 321 | 55 | } |
| 322 | ||
| 323 | /** | |
| 324 | * Returns false, if the pointer already is at the last item. | |
| 325 | * | |
| 326 | * @return A boolean. | |
| 327 | */ | |
| 328 | public boolean hasNext() { | |
| 329 | 266 | return !((this.y == this.dataset.getRowCount()) && (this.x == 0)); |
| 330 | } | |
| 331 | ||
| 332 | /** | |
| 333 | * Moves the pointer to the next item. | |
| 334 | * | |
| 335 | * @return The SOMDataItem. | |
| 336 | * @throws NoSuchElementException | |
| 337 | */ | |
| 338 | public Object next() { | |
| 339 | SOMDataItem item; | |
| 340 | 230 | item = this.dataset.getValue(this.x++, this.y); |
| 341 | // if (item == null) | |
| 342 | // throw new NoSuchElementException(); | |
| 343 | ||
| 344 | 230 | this.x %= this.dataset.getColumnCount(); |
| 345 | 230 | if (this.x == 0) |
| 346 | 116 | this.y++; |
| 347 | ||
| 348 | 230 | return item; |
| 349 | } | |
| 350 | ||
| 351 | /** | |
| 352 | * Should remove an item from the iteration list. | |
| 353 | * This method does nothing. | |
| 354 | */ | |
| 355 | public void remove() throws UnsupportedOperationException { | |
| 356 | 1 | throw new UnsupportedOperationException("SOMDataset iterators do not implement remove()."); |
| 357 | } | |
| 358 | ||
| 359 | } | |
| 360 | ||
| 361 | /** | |
| 362 | * Tests if this object is equal to another. | |
| 363 | * | |
| 364 | * @param obj the other object. | |
| 365 | * | |
| 366 | * @return A boolean. | |
| 367 | */ | |
| 368 | public boolean equals(Object obj) { | |
| 369 | 12 | if (obj == this) { |
| 370 | 5 | return true; |
| 371 | } | |
| 372 | ||
| 373 | 7 | if (!(obj instanceof SOMDataset)) { |
| 374 | 1 | return false; |
| 375 | } | |
| 376 | 6 | SOMDataset that = (SOMDataset) obj; |
| 377 | 6 | int cols = this.getColumnCount(); |
| 378 | 6 | int rows = this.getRowCount(); |
| 379 | 6 | if (that.getColumnCount() != cols || |
| 380 | that.getRowCount() != rows) { | |
| 381 | 2 | return false; |
| 382 | } | |
| 383 | ||
| 384 | 4 | Iterator i1 = this.iterator(); |
| 385 | 4 | Iterator i2 = that.iterator(); |
| 386 | 14 | while (i1.hasNext()) { |
| 387 | 11 | SOMDataItem item1 = (SOMDataItem)i1.next(); |
| 388 | 11 | SOMDataItem item2 = (SOMDataItem)i2.next(); |
| 389 | 11 | if (item1 == null && item2 == null) { |
| 390 | ; | |
| 391 | 2 | } |
| 392 | 9 | else if (item1 == null || item2 == null || |
| 393 | item1.equals(item2) == false) | |
| 394 | 1 | return false; |
| 395 | 10 | } |
| 396 | ||
| 397 | 3 | return true; |
| 398 | } | |
| 399 | ||
| 400 | /** | |
| 401 | * Returns a clone of the dataset. | |
| 402 | * | |
| 403 | * @return A clone. | |
| 404 | * | |
| 405 | * @throws CloneNotSupportedException This class will not throw this | |
| 406 | * exception, but subclasses (if any) might. | |
| 407 | */ | |
| 408 | public Object clone() throws CloneNotSupportedException { | |
| 409 | 1 | SOMDataset clone = (SOMDataset) super.clone(); |
| 410 | 1 | clone.data = (SOMDataItem[][]) this.data.clone(); |
| 411 | 1 | return clone; |
| 412 | } | |
| 413 | ||
| 414 | } | |
| 415 |