Created
May 18, 2017 11:39
-
-
Save cab404/2d01b010ebd761c1b941e3a90b071bfe to your computer and use it in GitHub Desktop.
Simple greedy clustering algorithm supporting both directions of clustering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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