use of de.lmu.ifi.dbs.elki.data.model.Model in project elki by elki-project.
the class KMLOutputHandler method writeClusteringResult.
private void writeClusteringResult(XMLStreamWriter xmlw, Clustering<Model> clustering, Database database) throws XMLStreamException {
xmlw.writeStartDocument();
xmlw.writeCharacters("\n");
xmlw.writeStartElement("kml");
xmlw.writeDefaultNamespace("http://earth.google.com/kml/2.2");
xmlw.writeStartElement("Document");
{
// TODO: can we automatically generate more helpful data here?
xmlw.writeStartElement("name");
xmlw.writeCharacters("ELKI KML output for " + clustering.getLongName());
// name
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
// TODO: e.g. list the settings in the description?
xmlw.writeStartElement("description");
xmlw.writeCharacters("ELKI KML output for " + clustering.getLongName());
// description
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
List<Cluster<Model>> clusters = clustering.getAllClusters();
Relation<NumberVector> coords = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD_2D);
List<Cluster<Model>> topc = clustering.getToplevelClusters();
Hierarchy<Cluster<Model>> hier = clustering.getClusterHierarchy();
Map<Object, DoubleObjPair<Polygon>> hullmap = new HashMap<>();
for (Cluster<Model> clu : topc) {
buildHullsRecursively(clu, hier, hullmap, coords);
}
{
final double projarea = 360. * 180. * .01;
// TODO: generate styles from color scheme
Iterator<Cluster<Model>> it = clusters.iterator();
for (int i = 0; it.hasNext(); i++) {
Cluster<Model> clus = it.next();
// This is a prime based magic number, to produce a colorful output
Color col = Color.getHSBColor(i / 4.294967291f, 1.f, .5f);
DoubleObjPair<Polygon> pair = hullmap.get(clus);
// Approximate area (using bounding box)
double hullarea = SpatialUtil.volume(pair.second);
final double relativeArea = Math.max(1. - (hullarea / projarea), 0.);
// final double relativeSize = pair.first / coords.size();
final double opacity = .65 * FastMath.sqrt(relativeArea) + .1;
xmlw.writeStartElement("Style");
xmlw.writeAttribute("id", "s" + i);
writeNewlineOnDebug(xmlw);
{
xmlw.writeStartElement("LineStyle");
xmlw.writeStartElement("width");
xmlw.writeCharacters("0");
// width
xmlw.writeEndElement();
// LineStyle
xmlw.writeEndElement();
}
writeNewlineOnDebug(xmlw);
{
xmlw.writeStartElement("PolyStyle");
xmlw.writeStartElement("color");
// KML uses AABBGGRR format!
xmlw.writeCharacters(String.format("%02x%02x%02x%02x", (int) (255 * Math.min(.75, opacity)), col.getBlue(), col.getGreen(), col.getRed()));
// color
xmlw.writeEndElement();
// out.writeStartElement("fill");
// out.writeCharacters("1"); // Default 1
// out.writeEndElement(); // fill
xmlw.writeStartElement("outline");
xmlw.writeCharacters("0");
// outline
xmlw.writeEndElement();
// PolyStyle
xmlw.writeEndElement();
}
writeNewlineOnDebug(xmlw);
// Style
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
}
Cluster<?> ignore = topc.size() == 1 ? topc.get(0) : null;
Iterator<Cluster<Model>> it = clusters.iterator();
for (int cnum = 0; it.hasNext(); cnum++) {
Cluster<?> c = it.next();
// Ignore sole toplevel cluster (usually: noise)
if (c == ignore) {
continue;
}
Polygon p = hullmap.get(c).second;
xmlw.writeStartElement("Placemark");
{
xmlw.writeStartElement("name");
xmlw.writeCharacters(c.getNameAutomatic());
// name
xmlw.writeEndElement();
xmlw.writeStartElement("description");
xmlw.writeCData(makeDescription(c).toString());
// description
xmlw.writeEndElement();
xmlw.writeStartElement("styleUrl");
xmlw.writeCharacters("#s" + cnum);
// styleUrl
xmlw.writeEndElement();
}
{
xmlw.writeStartElement("Polygon");
writeNewlineOnDebug(xmlw);
if (compat) {
xmlw.writeStartElement("altitudeMode");
xmlw.writeCharacters("relativeToGround");
// close altitude mode
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
{
xmlw.writeStartElement("outerBoundaryIs");
xmlw.writeStartElement("LinearRing");
xmlw.writeStartElement("coordinates");
// Reverse anti-clockwise polygons.
boolean reverse = (p.testClockwise() >= 0);
ArrayListIter<double[]> itp = p.iter();
if (reverse) {
itp.seek(p.size() - 1);
}
while (itp.valid()) {
double[] v = itp.get();
xmlw.writeCharacters(FormatUtil.format(v, ","));
if (compat && (v.length == 2)) {
xmlw.writeCharacters(",100");
}
xmlw.writeCharacters(" ");
if (!reverse) {
itp.advance();
} else {
itp.retract();
}
}
// close coordinates
xmlw.writeEndElement();
// close LinearRing
xmlw.writeEndElement();
// close *BoundaryIs
xmlw.writeEndElement();
}
writeNewlineOnDebug(xmlw);
// Polygon
xmlw.writeEndElement();
}
// Placemark
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
// Document
xmlw.writeEndElement();
// kml
xmlw.writeEndElement();
xmlw.writeEndDocument();
}
use of de.lmu.ifi.dbs.elki.data.model.Model in project elki by elki-project.
the class KMLOutputHandler method processNewResult.
@Override
public void processNewResult(ResultHierarchy hier, Result newResult) {
ArrayList<OutlierResult> ors = ResultUtil.filterResults(hier, newResult, OutlierResult.class);
ArrayList<Clustering<?>> crs = ResultUtil.filterResults(hier, newResult, Clustering.class);
if (ors.size() + crs.size() > 1) {
throw new AbortException("More than one visualizable result found. The KML writer only supports a single result!");
}
Database database = ResultUtil.findDatabase(hier);
for (OutlierResult outlierResult : ors) {
try {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(filename));
out.putNextEntry(new ZipEntry("doc.kml"));
final XMLStreamWriter xmlw = factory.createXMLStreamWriter(out);
writeOutlierResult(xmlw, outlierResult, database);
xmlw.flush();
xmlw.close();
out.closeEntry();
out.flush();
out.close();
if (autoopen) {
Desktop.getDesktop().open(filename);
}
} catch (XMLStreamException e) {
LOG.exception(e);
throw new AbortException("XML error in KML output.", e);
} catch (IOException e) {
LOG.exception(e);
throw new AbortException("IO error in KML output.", e);
}
}
for (Clustering<?> clusteringResult : crs) {
try {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(filename));
out.putNextEntry(new ZipEntry("doc.kml"));
final XMLStreamWriter xmlw = factory.createXMLStreamWriter(out);
@SuppressWarnings("unchecked") Clustering<Model> cres = (Clustering<Model>) clusteringResult;
writeClusteringResult(xmlw, cres, database);
xmlw.flush();
xmlw.close();
out.closeEntry();
out.flush();
out.close();
if (autoopen) {
Desktop.getDesktop().open(filename);
}
} catch (XMLStreamException e) {
LOG.exception(e);
throw new AbortException("XML error in KML output.", e);
} catch (IOException e) {
LOG.exception(e);
throw new AbortException("IO error in KML output.", e);
}
}
}
use of de.lmu.ifi.dbs.elki.data.model.Model in project elki by elki-project.
the class VisualizerContext method generateDefaultClustering.
/**
* Generate a default (fallback) clustering.
*
* @return generated clustering
*/
private Clustering<Model> generateDefaultClustering() {
final Database db = ResultUtil.findDatabase(hier);
Clustering<Model> c = null;
try {
// Try to cluster by labels
ByLabelHierarchicalClustering split = new ByLabelHierarchicalClustering();
c = split.run(db);
} catch (NoSupportedDataTypeException e) {
// Put everything into one
c = new TrivialAllInOne().run(db);
}
return c;
}
use of de.lmu.ifi.dbs.elki.data.model.Model in project elki by elki-project.
the class VisualizerContext method makeStyleResult.
/**
* Generate a new style result for the given style library.
*
* @param stylelib Style library
*/
protected void makeStyleResult(StyleLibrary stylelib) {
final Database db = ResultUtil.findDatabase(hier);
stylelibrary = stylelib;
List<Clustering<? extends Model>> clusterings = Clustering.getClusteringResults(db);
if (!clusterings.isEmpty()) {
stylepolicy = new ClusterStylingPolicy(clusterings.get(0), stylelib);
} else {
Clustering<Model> c = generateDefaultClustering();
stylepolicy = new ClusterStylingPolicy(c, stylelib);
}
}
use of de.lmu.ifi.dbs.elki.data.model.Model in project elki by elki-project.
the class NaiveAgglomerativeHierarchicalClustering1 method run.
/**
* Run the algorithm
*
* @param db Database
* @param relation Relation
* @return Clustering hierarchy
*/
public Result run(Database db, Relation<O> relation) {
DistanceQuery<O> dq = db.getDistanceQuery(relation, getDistanceFunction());
ArrayDBIDs ids = DBIDUtil.ensureArray(relation.getDBIDs());
final int size = ids.size();
LOG.verbose("Notice: SLINK is a much faster algorithm for single-linkage clustering!");
// Compute the initial distance matrix.
double[][] matrix = new double[size][size];
DBIDArrayIter ix = ids.iter(), iy = ids.iter();
for (int x = 0; ix.valid(); x++, ix.advance()) {
iy.seek(0);
for (int y = 0; y < x; y++, iy.advance()) {
final double dist = dq.distance(ix, iy);
matrix[x][y] = dist;
matrix[y][x] = dist;
}
}
// Initialize space for result:
double[] height = new double[size];
Arrays.fill(height, Double.POSITIVE_INFINITY);
// Parent node, to track merges
// have every object point to itself initially
ArrayModifiableDBIDs parent = DBIDUtil.newArray(ids);
// Active clusters, when not trivial.
Int2ReferenceMap<ModifiableDBIDs> clusters = new Int2ReferenceOpenHashMap<>();
// Repeat until everything merged, except the desired number of clusters:
final int stop = size - numclusters;
FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Agglomerative clustering", stop, LOG) : null;
for (int i = 0; i < stop; i++) {
double min = Double.POSITIVE_INFINITY;
int minx = -1, miny = -1;
for (int x = 0; x < size; x++) {
if (height[x] < Double.POSITIVE_INFINITY) {
continue;
}
for (int y = 0; y < x; y++) {
if (height[y] < Double.POSITIVE_INFINITY) {
continue;
}
if (matrix[x][y] < min) {
min = matrix[x][y];
minx = x;
miny = y;
}
}
}
assert (minx >= 0 && miny >= 0);
// Avoid allocating memory, by reusing existing iterators:
ix.seek(minx);
iy.seek(miny);
// Perform merge in data structure: x -> y
// Since y < x, prefer keeping y, dropping x.
height[minx] = min;
parent.set(minx, iy);
// Merge into cluster
ModifiableDBIDs cx = clusters.get(minx);
ModifiableDBIDs cy = clusters.get(miny);
if (cy == null) {
cy = DBIDUtil.newHashSet();
cy.add(iy);
}
if (cx == null) {
cy.add(ix);
} else {
cy.addDBIDs(cx);
clusters.remove(minx);
}
clusters.put(miny, cy);
// Update distance matrix for y:
for (int j = 0; j < size; j++) {
matrix[j][miny] = Math.min(matrix[j][minx], matrix[j][miny]);
matrix[miny][j] = Math.min(matrix[minx][j], matrix[miny][j]);
}
LOG.incrementProcessed(prog);
}
LOG.ensureCompleted(prog);
// Build the clustering result
final Clustering<Model> dendrogram = new Clustering<>("Hierarchical-Clustering", "hierarchical-clustering");
for (int x = 0; x < size; x++) {
if (height[x] < Double.POSITIVE_INFINITY) {
DBIDs cids = clusters.get(x);
if (cids == null) {
ix.seek(x);
cids = DBIDUtil.deref(ix);
}
Cluster<Model> cluster = new Cluster<>("Cluster", cids);
dendrogram.addToplevelCluster(cluster);
}
}
return dendrogram;
}
Aggregations