/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.visualization.thematicmap; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.logging.Logger; import org.jfree.util.PaintList; import edu.cornell.cs.voronoi.IPnt; import edu.cornell.cs.voronoi.Pnt; import at.tuwien.ifs.somtoolbox.data.SOMLibClassInformation; import at.tuwien.ifs.somtoolbox.layers.Unit; /** * @author Taha Abdel Aziz * @version $Id: SOMRegion.java 3883 2010-11-02 17:13:23Z frank $ */ public class SOMRegion extends Polygon implements IPnt { private static final long serialVersionUID = 1L; Pnt center; ArrayList<Segment> segments = new ArrayList<Segment>(); Color borderColor = Color.BLACK; Color fillcolor = Color.YELLOW; public ArrayList<SOMClass> classes = new ArrayList<SOMClass>(); public SOMClass mainClass; Unit unit; SOMLibClassInformation classInfo; boolean resolved = false; double area = 0; RndIndexGenerator indexGenerator; ArrayList<Grid> grids; private ArrayList<Polygon> polygons; double min_visible_class = 0; private PaintList paintList; /** Creates a new instance of SOMNode */ /* * public SOMRegion(Pnt center) { super(center.coord(0), center.coord(1)); } */ public SOMRegion(Unit unit, SOMLibClassInformation classInfo, PaintList paintList, int zoom) { this.center = new Pnt(unit.getXPos() * zoom + RegionManager.BORDER, unit.getYPos() * zoom + RegionManager.BORDER); this.unit = unit; this.classInfo = classInfo; this.paintList = paintList; double[] values = new double[classInfo.numClasses()]; for (int v = 0; v < values.length; v++) { values[v] = 0; } for (int i = 0; i < unit.getNumberOfMappedInputs(); i++) { int classIndex = classInfo.getClassIndex(unit.getMappedInputName(i)); if (classIndex == -1) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe( "Class index could not be retrieved for item " + unit.getMappedInputName(i) + "; ignoring item."); } else { values[classIndex] += 1; } } // int clssIndex=0; double maxValue = 0; double allHits = 0; for (int i = 0; i < classInfo.numClasses(); i++) { if (values[i] > 0) { SOMClass clss = new SOMClass(i, values[i]); classes.add(clss); allHits += values[i]; if (values[i] > maxValue) { maxValue = values[i]; mainClass = clss; } } } for (int i = 0; i < classes.size(); i++) { SOMClass sc = classes.get(i); sc.share = sc.hits / allHits; } indexGenerator = new RndIndexGenerator(classes); } public SOMRegion(Unit unit, IPnt pnt, int clssIndex, SOMLibClassInformation classInfo, PaintList paintList) { center = new Pnt(pnt.coord(0), pnt.coord(1)); this.unit = unit; this.classInfo = classInfo; this.paintList = paintList; // this.mainClassIndex=clssIndex; double[] values = new double[classInfo.numClasses()]; for (int v = 0; v < values.length; v++) { values[v] = 0; } for (int i = 0; i < unit.getNumberOfMappedInputs(); i++) { values[classInfo.getClassIndex(unit.getMappedInputNames()[i])] += 1; } double maxValue = 0; double allHits = 0; for (int i = 0; i < classInfo.numClasses(); i++) { if (values[i] > 0) { SOMClass clss = new SOMClass(i, values[i]); classes.add(clss); allHits += values[i]; if (values[i] > maxValue) { maxValue = values[i]; mainClass = clss; } } } for (int i = 0; i < classes.size(); i++) { SOMClass sc = classes.get(i); sc.share = sc.hits / allHits; } indexGenerator = new RndIndexGenerator(classes); } public void addSegment(Segment seg) { segments.add(seg); } public Unit getUnit() { return unit; } public void sortSegments() { if (segments.isEmpty()) { return; } Segment s = segments.get(0); ArrayList<Segment> res = new ArrayList<Segment>(); res.add(s); segments.remove(s); int n = 0; boolean done = true; while (!segments.isEmpty()) { if (n > 100) { done = false; break; } n++; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); if (s.end2.equals(ss.end1) && !s.end1.equals(ss.end2)) { res.add(ss); segments.remove(ss); s = ss; break; } else if (s.end2.equals(ss.end2) && !s.end1.equals(ss.end1)) { res.add(ss); segments.remove(ss); ss.flip(); s = ss; break; } } } if (!done) { if (!res.isEmpty()) { s = res.get(0); } else { s = segments.get(segments.size() - 1); } n = 0; while (!segments.isEmpty()) { if (n > 100) { break; } n++; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); if (s.end1.equals(ss.end2) && !s.end2.equals(ss.end1)) { res.add(0, ss); segments.remove(ss); s = ss; break; } else if (s.end1.equals(ss.end1) && !s.end2.equals(ss.end2)) { res.add(0, ss); segments.remove(ss); ss.flip(); s = ss; break; } } } } segments = res; int len = segments.size(); int[] xx = new int[len]; int[] yy = new int[len]; Point[] points = new Point[len]; for (int i = 0; i < len; i++) { Segment seg = segments.get(i); xx[i] = (int) seg.end1.coord(0); yy[i] = (int) seg.end1.coord(1); points[i] = new Point(xx[i], yy[i]); } super.xpoints = xx; super.ypoints = yy; super.npoints = len; super.getBounds(); } public void makeUniqe() { if (segments.isEmpty()) { return; } ArrayList<Segment> res = new ArrayList<Segment>(); while (!segments.isEmpty()) { Segment s = segments.get(0); if (s.end1.equals(s.end2)) { segments.remove(s); continue; } res.add(s); segments.remove(s); IPnt p1 = s.end1; IPnt p2 = s.end2; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); IPnt pp1 = ss.end1; IPnt pp2 = ss.end2; if (p1.equals(pp1) && p2.equals(pp2) || p1.equals(pp2) && p2.equals(pp1)) { segments.remove(ss); } } } segments = res; } public void drawRegion(Graphics2D g) { if (segments.size() > 0) { g.setColor(borderColor); g.setStroke(new BasicStroke(0.0f)); g.drawPolygon(this); } } public void fillRegion(Graphics2D g, boolean chessboard) { if (!resolved) { fillcolor = (Color) paintList.getPaint(mainClass.classIndex); Color c = repairColor(fillcolor); g.setColor(c); if (segments.isEmpty()) { return; } g.fillPolygon(this); } else { if (chessboard) { if (polygons == null) { // calculate polygons polygons = new ArrayList<Polygon>(); Rectangle2D rect = getBounds2D(); double w = rect.getWidth(); double h = rect.getHeight(); if (h > 200 || w > 200) { Logger.getLogger("at.tuwien.ifs.somtoolbox").info("ERROR: h>200 & w>200"); return; } int x = (int) rect.getX(); int y = (int) rect.getY(); int xSteps = (int) (w / (int) Grid.SIZE); int ySteps = (int) (h / (int) Grid.SIZE); // int n = classes.size(); for (int i = 0; i < xSteps; i++) { for (int j = 0; j < ySteps; j++) { Polygon p = new Polygon(); p.addPoint((int) (x + i * Grid.SIZE), (int) (y + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE + Grid.SIZE), (int) (y + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE + Grid.SIZE), (int) (y + Grid.SIZE + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE), (int) (y + Grid.SIZE + j * Grid.SIZE)); if (!this.contains(p.getBounds())) { continue; } SOMClass clss = indexGenerator.getNextIndex(); g.setColor((Color) paintList.getPaint(clss.classIndex)); g.fillPolygon(p); polygons.add(p); } } } else { // use pre-calculated polygons for (int i = 0; i < polygons.size(); i++) { SOMClass clss = indexGenerator.getNextIndex(); g.setColor((Color) paintList.getPaint(clss.classIndex)); Polygon p = polygons.get(i); g.fillPolygon(p); } } } else { for (int i = 0; i < grids.size(); i++) { Grid grid = grids.get(i); if (grid.clss == null) { continue; } g.setColor((Color) paintList.getPaint(grid.clss.classIndex)); g.fillRect((int) grid.topLeft.coord(0), (int) grid.topLeft.coord(1), (int) Grid.SIZE, (int) Grid.SIZE); } } } } private Color repairColor(Color color) { int red = Math.min(color.getRed() + 10, 255); int green = Math.min(color.getGreen() + 10, 255); int blue = Math.min(color.getBlue() + 10, 255); return new Color(red, green, blue); } public void setRegionBorderColor(Color col) { this.borderColor = col; } public void setFillColor(Color col) { this.fillcolor = col; } public SOMClass getClass(int index) { for (int i = 0; i < classes.size(); i++) { SOMClass c = classes.get(i); if (c.classIndex == index) { return c; } } return null; } @Override public String toString() { String s = ""; for (int i = 0; i < segments.size(); i++) { Segment seg = segments.get(i); s += seg + "\n"; } return s + "-----------------------"; } int getRelationNumber(int index) { int ret = 0; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); if (ss.neighborRegion != null) { for (int n = 0; n < ss.neighborRegion.classes.size(); n++) { SOMClass sc = ss.neighborRegion.classes.get(n); if (sc.classIndex == index) { ret++; } } } } return ret; } double[] getRelationNumberAndWeights(int index) { double[] ret = new double[2]; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); if (ss.neighborRegion != null) { for (int n = 0; n < ss.neighborRegion.classes.size(); n++) { SOMClass sc = ss.neighborRegion.classes.get(n); if (sc.classIndex == index) { ret[0]++; ret[1] += sc.hits; } } } } return ret; } public void cut(int width, int height) { int left = 0; int right = 1; int oben = 0; int unten = 1; for (int i = 0; i < segments.size(); i++) { Segment ss = segments.get(i); double x1 = ss.end1.coord(0); double y1 = ss.end1.coord(1); double x2 = ss.end2.coord(0); double y2 = ss.end2.coord(1); int hor1 = -1; int ver1 = -1; int hor2 = -1; int ver2 = -1; if (x1 > width) { ss.end1 = new Pnt(width, y1); hor1 = right; } if (x1 < 0) { ss.end1 = new Pnt(0, y1); hor1 = left; } if (y1 > height) { ss.end1 = new Pnt(x1, height); ver1 = unten; } if (y1 < 0) { ss.end1 = new Pnt(x1, 0); ver1 = oben; } if (x2 > width) { ss.end2 = new Pnt(width, y2); hor2 = right; } if (x2 < 0) { ss.end2 = new Pnt(0, y2); hor2 = left; } if (y2 > height) { ss.end2 = new Pnt(x2, height); ver2 = unten; } if (y2 < 0) { ss.end2 = new Pnt(x2, 0); ver2 = oben; } if (ver2 == oben && hor1 == right || ver1 == oben && hor2 == right) { segments.remove(ss); segments.add(new Segment(new Pnt(x1, y1), new Pnt(width, 0))); segments.add(new Segment(new Pnt(width, 0), new Pnt(x2, y2))); } if (ver2 == unten && hor1 == right || ver1 == unten && hor2 == right) { segments.remove(ss); segments.add(new Segment(new Pnt(x1, y1), new Pnt(width, height))); segments.add(new Segment(new Pnt(width, height), new Pnt(x2, y2))); } if (ver2 == oben && hor1 == left || ver1 == oben && hor2 == left) { segments.remove(ss); segments.add(new Segment(new Pnt(x1, y1), new Pnt(0, 0))); segments.add(new Segment(new Pnt(0, 0), new Pnt(x2, y2))); } if (ver2 == unten && hor1 == left || ver1 == unten && hor2 == left) { segments.remove(ss); segments.add(new Segment(new Pnt(x1, y1), new Pnt(0, height))); segments.add(new Segment(new Pnt(0, height), new Pnt(x2, y2))); } } } public void calcRelations() { double max = 0; for (int n = 0; n < classes.size(); n++) { SOMClass sc = classes.get(n); double[] relationNumberAndWeights = getRelationNumberAndWeights(sc.classIndex); sc.relationNum = (int) relationNumberAndWeights[0]; sc.relationWeight = relationNumberAndWeights[1]; if (sc.relationWeight > max) { max = sc.relationNum; mainClass = sc; } } mainClass.finished = true; } public void resolve(double min_visible_class) { this.min_visible_class = min_visible_class; resolved = true; if (grids == null) { grids = new ArrayList<Grid>(); calcGrids(); assignClassGrids(); } } public void calcGrids() { Rectangle2D rect = getBounds2D(); double w = rect.getWidth(); double h = rect.getHeight(); if (h > 150 || w > 150) { Logger.getLogger("at.tuwien.ifs.somtoolbox").fine("Error: " + this); return; } int x = (int) rect.getX(); int y = (int) rect.getY(); int xSteps = (int) (w / (int) Grid.SIZE); int ySteps = (int) (h / (int) Grid.SIZE); for (int i = 0; i < xSteps; i++) { for (int j = 0; j < ySteps; j++) { Polygon p = new Polygon(); p.addPoint((int) (x + i * Grid.SIZE), (int) (y + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE + Grid.SIZE), (int) (y + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE + Grid.SIZE), (int) (y + Grid.SIZE + j * Grid.SIZE)); p.addPoint((int) (x + i * Grid.SIZE), (int) (y + Grid.SIZE + j * Grid.SIZE)); if (this.contains(p.getBounds().x + Grid.SIZE / 2, p.getBounds().y + Grid.SIZE / 2)) { Pnt topLeft = new Pnt(x + i * Grid.SIZE, y + j * Grid.SIZE); Pnt bottomRight = new Pnt(x + i * Grid.SIZE + Grid.SIZE, y + Grid.SIZE + j * Grid.SIZE); Grid grid = new Grid(topLeft, bottomRight); grids.add(grid); } } } } static int x = 0; public void assignClassGrids() { Iterator<SOMClass> it = classes.iterator(); while (it.hasNext()) { SOMClass clss = it.next(); if (clss.finished) { continue; } if (clss.share == 0) { System.out.println("Class share == 0!"); } if (clss.share < min_visible_class) { continue; } int n = getRelationNumber(clss.classIndex); if (n == 0) { // isoliert Segment virtualConnection = new Segment(this, this); assignClassGrids(clss, virtualConnection, 1.0, 1, 1); } else { for (int i = 0; i < segments.size(); i++) { Segment s = segments.get(i); SOMRegion nachbar = s.neighborRegion; if (nachbar == null) { continue; } SOMClass otherClass = nachbar.getClass(clss.classIndex); if (otherClass == null) { continue; } Segment virtualConnection; if (n > 1) { int nextSegInd = i + 1; if (nextSegInd >= segments.size()) { nextSegInd = 0; } Segment nextSeg = segments.get(nextSegInd); SOMRegion nextReg = nextSeg.neighborRegion; SOMClass c = null; if (nextReg != null) { c = nextReg.getClass(clss.classIndex); } if (c == null) { nextSegInd = i - 1; if (nextSegInd < 0) { nextSegInd = segments.size() - 1; } nextSeg = segments.get(nextSegInd); nextReg = nextSeg.neighborRegion; if (nextReg != null) { c = nextReg.getClass(clss.classIndex); } if (c == null) { virtualConnection = new Segment(this, s.getMidPoint()); } else { virtualConnection = new Segment(s.end1, s.end1); } } else { virtualConnection = new Segment(s.end2, s.end2); } // virtualConnection = new Segment(s.end1, s.end2); assignClassGrids(clss, virtualConnection, 1.0 / n, clss.share, otherClass.share); } else { // virtualConnection = new Segment(s.end1, s.end2); virtualConnection = new Segment(this, s.getMidPoint()); assignClassGrids(clss, virtualConnection, 1.0, clss.share, otherClass.share); } } } // clss.finished=true; } } public void assignClassGrids(SOMClass clss, Segment seg, double anteilVonAnteil, double weight1, double weight2) { Collections.sort(grids, new SegDistComperator(seg, weight1, weight2)); int n = (int) (grids.size() * clss.share * anteilVonAnteil); int finisched = 0; for (int i = 0; i < grids.size() && finisched < n; i++) { Grid grid = grids.get(i); if (grid.occupied) { continue; } grid.occupied = true; grid.clss = clss; finisched++; } // clss.finished=true; } // private class ClassComparator implements Comparator{ // public int compare(Object o1, Object o2){ // SOMClass c1 = (SOMClass)o1; // SOMClass c2 = (SOMClass)o2; // if(c1.anteil < c2.anteil) // return 1; // else if (c1.anteil > c2.anteil) // return -1; // else // return 0; // } // // } // @Override public Pnt add(Pnt p) { return center.add(p); } @Override public double angle(Pnt p) { return center.angle(p); } @Override public Pnt bisector(Pnt point) { return center.bisector(point); } @Override public double coord(int i) { return center.coord(i); } @Override public int dimCheck(Pnt p) { return center.dimCheck(p); } @Override public int dimension() { return center.dimension(); } @Override public double dot(Pnt p) { return center.dot(p); } @Override public boolean equals(Object other) { return center.equals(other, true); } @Override public boolean equals(Object other, boolean round) { return center.equals(other, round); } @Override public Pnt extend(double[] coords) { return center.extend(coords); } @Override public int hashCode() { int retValue; retValue = center.hashCode(); return retValue; } @Override public boolean isInside(Pnt[] simplex) { return center.isInside(simplex); } @Override public Pnt isOn(Pnt[] simplex) { return center.isOn(simplex); } @Override public Pnt isOutside(Pnt[] simplex) { return center.isOutside(simplex); } @Override public double magnitude() { return center.magnitude(); } @Override public int[] relation(Pnt[] simplex) { return center.relation(simplex); } @Override public Pnt subtract(Pnt p) { return center.subtract(p); } @Override public int vsCircumcircle(Pnt[] simplex) { return center.vsCircumcircle(simplex); } /** * Resolves the Main Class Index from any given ClassIndex in the Region, or -1 if the ClassINdex is not part of the * Region */ public int resolveMainClassIndex(int index) { for (int i = 0; i < classes.size(); i++) { SOMClass clss = this.classes.get(i); if (index == clss.classIndex) { return this.mainClass.classIndex; } } return -1; } /** Returns the SOMClass with the specified index or NULL otherwise */ public SOMClass getClassWithIndex(int index) { for (int i = 0; i < this.classes.size(); i++) { SOMClass clss = this.classes.get(i); if (clss.classIndex == index) { return clss; } } return null; } /** Calculates the Entropy in the SOM Region */ public double calculateEntropy() { int n = 0; double e = 0.0; // calculate the amount of inputs for (int i = 0; i < this.classes.size(); i++) { SOMClass clss = classes.get(i); n += (int) clss.hits; } // calculate the entropy for (int i = 0; i < this.classes.size(); i++) { SOMClass clss = classes.get(i); // double c = clss.hits / n; // double v = Math.log(clss.hits / n) / Math.log(2.0); e -= clss.hits / n * Math.log(clss.hits / n) / Math.log(2.0); } return e; } /** Returns the class names of the Region on [0] and its hits on [1] */ public String[][] getClasses() { String[][] cNames = new String[this.classes.size()][2]; for (int i = 0; i < cNames.length; i++) { int cIndex = this.classes.get(i).classIndex; cNames[i][0] = classInfo.classNames()[cIndex]; cNames[i][1] = String.valueOf((int) this.classes.get(i).hits); } return cNames; } }