Skip to content

Instantly share code, notes, and snippets.

@cab404
Created May 18, 2017 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cab404/2d01b010ebd761c1b941e3a90b071bfe to your computer and use it in GitHub Desktop.
Save cab404/2d01b010ebd761c1b941e3a90b071bfe to your computer and use it in GitHub Desktop.
Simple greedy clustering algorithm supporting both directions of clustering
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author cab404
*/
public class Clusters<A> {
public List<ClusterNode> clusters = new ArrayList<>();
public static class Coord {
public double lat;
public double lon;
public Coord(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
void add(Coord coord) {
this.lat += coord.lat;
this.lon += coord.lon;
}
void div(double scalar) {
this.lat /= scalar;
this.lon /= scalar;
}
double dst2(Coord to) {
return Math.pow(lat - to.lat, 2) + Math.pow(lon - to.lon, 2);
}
double dstChess(Coord to) {
return Math.abs(lat - to.lat) + Math.abs(lon - to.lon);
}
boolean isInRange(Coord to, double range) {
return /*dstChess(to) <= range * 2 && */dst2(to) <= range * range;
}
public void set(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
}
public class ClusterNode {
public final Coord coord = new Coord(0, 0);
}
public class ClusterPoint extends ClusterNode {
public final A boundData;
public ClusterPoint(A boundData, double lat, double lon) {
this.boundData = boundData;
coord.set(lat, lon);
}
}
public class Cluster extends ClusterNode {
public List<ClusterNode> nodes = new LinkedList<>();
public Coord updateCenter() {
coord.set(0, 0);
for (int i = 0; i < nodes.size(); i++) {
ClusterNode node = nodes.get(i);
coord.add(node.coord);
}
coord.div(nodes.size());
return coord;
}
}
public ClusterPoint addCluster(double lat, double lon, A boundObject) {
final ClusterPoint node = new ClusterPoint(boundObject, lat, lon);
clusters.add(node);
return node;
}
public void repeatClusterize(List<Double> levels) {
Collections.sort(levels);
for (double layer : levels) clusterize(layer);
}
public void clusterize(double dst) {
clusters = clusterize(clusters, dst);
}
public List<ClusterNode> unwrap(List<ClusterNode> toUnwrap, int to) {
for (int i = 0; i < to; i++) toUnwrap = unwrap(toUnwrap);
return toUnwrap;
}
public List<ClusterNode> unwrap(List<ClusterNode> toUnwrap) {
final ArrayList<ClusterNode> unwrapped = new ArrayList<ClusterNode>();
for (int i = 0; i < toUnwrap.size(); i++) {
final ClusterNode node = toUnwrap.get(i);
if (Cluster.class.isInstance(node))
unwrapped.addAll(((Cluster) node).nodes);
else
unwrapped.add(node);
}
return unwrapped;
}
public void reverseClusterize(List<Double> levels) {
Collections.sort(levels);
Collections.reverse(levels);
clusters = reverseClusterize(clusters, levels, 0);
}
public List<ClusterNode> reverseClusterize(List<ClusterNode> clusters, List<Double> levels, int level) {
if (level == levels.size()) {
return clusters;
}
// System.out.println("doing cluster with size " + clusters.size() + " on level " + level);
// System.out.println("running 'clusterize' on " + levels.get(level));
final List<ClusterNode> layer = clusterize(clusters, levels.get(level));
// System.out.println("ended up with " + layer.size());
for (int i = 0; i < layer.size(); i++) {
ClusterNode clusterNode = layer.get(i);
if (Cluster.class.isInstance(clusterNode)) {
final Cluster cluster = (Cluster) clusterNode;
cluster.nodes = reverseClusterize(cluster.nodes, levels, level + 1);
cluster.updateCenter();
}
}
return layer;
}
public List<ClusterNode> clusterize(List<ClusterNode> nodes, double dst) {
List<ClusterNode> result = new ArrayList<>();
List<ClusterNode> pool = new ArrayList<>(nodes);
while (!pool.isEmpty()) {
Cluster cluster = null;
ClusterNode node = pool.remove(0);
for (int j = 0; j < pool.size(); )
if (node.coord.isInRange(pool.get(j).coord, dst)) {
if (cluster == null) cluster = new Cluster();
cluster.nodes.add(pool.remove(j));
} else
j++;
if (cluster != null) {
cluster.nodes.add(node);
cluster.updateCenter();
result.add(cluster);
} else
result.add(node);
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment