use of de.lmu.ifi.dbs.elki.data.spatial.Polygon in project elki by elki-project.
the class AlphaShape method compute.
public List<Polygon> compute() {
// Compute delaunay triangulation:
delaunay = (new SweepHullDelaunay2D(points)).getDelaunay();
List<Polygon> polys = new ArrayList<>();
// Working data
long[] used = BitsUtil.zero(delaunay.size());
List<double[]> cur = new ArrayList<>();
for (int i = 0; /* = used.nextClearBit(0) */
i < delaunay.size() && i >= 0; i = BitsUtil.nextClearBit(used, i + 1)) {
if (BitsUtil.get(used, i)) {
continue;
}
BitsUtil.setI(used, i);
SweepHullDelaunay2D.Triangle tri = delaunay.get(i);
if (tri.r2 <= alpha2) {
// Check neighbors
processNeighbor(cur, used, i, tri.ab, tri.b);
processNeighbor(cur, used, i, tri.bc, tri.c);
processNeighbor(cur, used, i, tri.ca, tri.a);
}
if (!cur.isEmpty()) {
polys.add(new Polygon(cur));
cur = new ArrayList<>();
}
}
return polys;
}
use of de.lmu.ifi.dbs.elki.data.spatial.Polygon in project elki by elki-project.
the class KMLOutputHandler method buildHullsRecursively.
/**
* Recursively step through the clusters to build the hulls.
*
* @param clu Current cluster
* @param hier Clustering hierarchy
* @param hulls Hull map
*/
private DoubleObjPair<Polygon> buildHullsRecursively(Cluster<Model> clu, Hierarchy<Cluster<Model>> hier, Map<Object, DoubleObjPair<Polygon>> hulls, Relation<? extends NumberVector> coords) {
final DBIDs ids = clu.getIDs();
GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
hull.add(coords.get(iter).toArray());
}
double weight = ids.size();
if (hier != null && hulls != null) {
final int numc = hier.numChildren(clu);
if (numc > 0) {
for (It<Cluster<Model>> iter = hier.iterChildren(clu); iter.valid(); iter.advance()) {
final Cluster<Model> iclu = iter.get();
DoubleObjPair<Polygon> poly = hulls.get(iclu);
if (poly == null) {
poly = buildHullsRecursively(iclu, hier, hulls, coords);
}
// Add inner convex hull to outer convex hull.
for (ArrayListIter<double[]> vi = poly.second.iter(); vi.valid(); vi.advance()) {
hull.add(vi.get());
}
weight += poly.first / numc;
}
}
}
DoubleObjPair<Polygon> pair = new DoubleObjPair<>(weight, hull.getHull());
hulls.put(clu, pair);
return pair;
}
use of de.lmu.ifi.dbs.elki.data.spatial.Polygon in project elki by elki-project.
the class AlphaShapeTest method randomConvexHull.
@Test
public void randomConvexHull() {
ArrayList<double[]> t = new ArrayList<>();
Random r = new Random(0L);
for (int i = 0; i < 1000; i++) {
t.add(new double[] { r.nextDouble(), r.nextDouble() });
}
List<Polygon> polys = new AlphaShape(t, 100.).compute();
assertEquals("Too many polys.", 1, polys.size());
assertEquals("Hull size not as expected.", 21, polys.get(0).size());
}
use of de.lmu.ifi.dbs.elki.data.spatial.Polygon in project elki by elki-project.
the class KMLOutputHandler method writeOutlierResult.
private void writeOutlierResult(XMLStreamWriter xmlw, OutlierResult outlierResult, Database database) throws XMLStreamException {
Relation<PolygonsObject> polys = database.getRelation(TypeUtil.POLYGON_TYPE);
Relation<String> labels = DatabaseUtil.guessObjectLabelRepresentation(database);
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 " + outlierResult.getLongName());
// name
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
// TODO: e.g. list the settings in the description?
xmlw.writeStartElement("description");
xmlw.writeCharacters("ELKI KML output for " + outlierResult.getLongName());
// description
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
{
// TODO: generate styles from color scheme
for (int i = 0; i < NUMSTYLES; i++) {
Color col = getColorForValue(i / (NUMSTYLES - 1.0));
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", col.getAlpha(), 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);
}
}
DoubleRelation scores = outlierResult.getScores();
Collection<Relation<?>> otherrel = new LinkedList<>(database.getRelations());
otherrel.remove(scores);
otherrel.remove(polys);
otherrel.remove(labels);
otherrel.remove(database.getRelation(TypeUtil.DBID));
ArrayModifiableDBIDs ids = DBIDUtil.newArray(scores.getDBIDs());
scaling.prepare(outlierResult);
for (DBIDIter iter = outlierResult.getOrdering().order(ids).iter(); iter.valid(); iter.advance()) {
double score = scores.doubleValue(iter);
PolygonsObject poly = polys.get(iter);
String label = labels.get(iter);
if (Double.isNaN(score)) {
LOG.warning("No score for object " + DBIDUtil.toString(iter));
}
if (poly == null) {
LOG.warning("No polygon for object " + DBIDUtil.toString(iter) + " - skipping.");
continue;
}
xmlw.writeStartElement("Placemark");
{
xmlw.writeStartElement("name");
xmlw.writeCharacters(score + " " + label);
// name
xmlw.writeEndElement();
StringBuilder buf = makeDescription(otherrel, iter);
xmlw.writeStartElement("description");
xmlw.writeCData("<div>" + buf.toString() + "</div>");
// description
xmlw.writeEndElement();
xmlw.writeStartElement("styleUrl");
int style = (int) (scaling.getScaled(score) * NUMSTYLES);
style = Math.max(0, Math.min(style, NUMSTYLES - 1));
xmlw.writeCharacters("#s" + style);
// styleUrl
xmlw.writeEndElement();
}
{
xmlw.writeStartElement("Polygon");
writeNewlineOnDebug(xmlw);
if (compat) {
xmlw.writeStartElement("altitudeMode");
xmlw.writeCharacters("relativeToGround");
// close altitude mode
xmlw.writeEndElement();
writeNewlineOnDebug(xmlw);
}
// First polygon clockwise?
boolean first = true;
for (Polygon p : poly.getPolygons()) {
if (first) {
xmlw.writeStartElement("outerBoundaryIs");
} else {
xmlw.writeStartElement("innerBoundaryIs");
}
xmlw.writeStartElement("LinearRing");
xmlw.writeStartElement("coordinates");
// Reverse anti-clockwise polygons.
boolean reverse = (p.testClockwise() >= 0);
ArrayListIter<double[]> it = p.iter();
if (reverse) {
it.seek(p.size() - 1);
}
while (it.valid()) {
double[] v = it.get();
xmlw.writeCharacters(FormatUtil.format(v, ","));
if (compat && (v.length == 2)) {
xmlw.writeCharacters(",50");
}
xmlw.writeCharacters(" ");
if (!reverse) {
it.advance();
} else {
it.retract();
}
}
// close coordinates
xmlw.writeEndElement();
// close LinearRing
xmlw.writeEndElement();
// close *BoundaryIs
xmlw.writeEndElement();
first = false;
}
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.spatial.Polygon 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();
}
Aggregations