Skip to content

Commit f83ba5a

Browse files
committed
Initial commit.
1 parent fe472f7 commit f83ba5a

10 files changed

Lines changed: 1450 additions & 0 deletions

File tree

demo/dbscan-clustering-demo.nlogo

Lines changed: 512 additions & 0 deletions
Large diffs are not rendered by default.
6.45 MB
Binary file not shown.

pom.xml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>org.nlogo.extensions.dbscan</groupId>
6+
<artifactId>dbscan</artifactId>
7+
<version>0.1</version>
8+
<packaging>jar</packaging>
9+
10+
<repositories>
11+
<repository>
12+
<id>in-project</id>
13+
<name>In Project Repo</name>
14+
<url>file://${project.basedir}/libs</url>
15+
</repository>
16+
<repository>
17+
<id>jitpack.io</id>
18+
<url>https://jitpack.io</url>
19+
</repository>
20+
</repositories>
21+
22+
<properties>
23+
<maven.compiler.source>1.8</maven.compiler.source>
24+
<maven.compiler.target>1.8</maven.compiler.target>
25+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
26+
</properties>
27+
28+
<dependencies>
29+
<dependency>
30+
<groupId>NetLogo</groupId>
31+
<artifactId>NetLogo</artifactId>
32+
<version>5.3.1</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>com.github.chrfrantz</groupId>
36+
<artifactId>DBSCAN</artifactId>
37+
<version>8f0532d80f775f9a27e811fa6c2901adbeae3ba9</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>junit</groupId>
41+
<artifactId>junit</artifactId>
42+
<version>4.9</version>
43+
<scope>test</scope>
44+
</dependency>
45+
</dependencies>
46+
47+
<build>
48+
<finalName>${project.name}</finalName>
49+
<plugins>
50+
<plugin>
51+
<groupId>org.apache.maven.plugins</groupId>
52+
<artifactId>maven-compiler-plugin</artifactId>
53+
<executions>
54+
<!-- Prevent test compilation as of Scala dependency -->
55+
<execution>
56+
<id>default-testCompile</id>
57+
<phase>test-compile</phase>
58+
<configuration>
59+
<testExcludes>
60+
<exclude>**/TestNetLogoDBSCAN.java</exclude>
61+
</testExcludes>
62+
</configuration>
63+
<goals>
64+
<goal>testCompile</goal>
65+
</goals>
66+
</execution>
67+
</executions>
68+
</plugin>
69+
<plugin>
70+
<groupId>org.apache.maven.plugins</groupId>
71+
<artifactId>maven-jar-plugin</artifactId>
72+
<configuration>
73+
<archive>
74+
<manifestFile>src/main/resources/manifest.txt</manifestFile>
75+
</archive>
76+
</configuration>
77+
</plugin>
78+
</plugins>
79+
</build>
80+
81+
</project>
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.nlogo.extensions.dbscan;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Iterator;
6+
7+
import org.christopherfrantz.dbscan.DBSCANClusterer;
8+
import org.christopherfrantz.dbscan.DBSCANClusteringException;
9+
import org.nlogo.agent.Agent;
10+
import org.nlogo.agent.Turtle;
11+
import org.nlogo.api.AgentSet;
12+
import org.nlogo.api.Argument;
13+
import org.nlogo.api.Context;
14+
import org.nlogo.api.DefaultReporter;
15+
import org.nlogo.api.ExtensionException;
16+
import org.nlogo.api.LogoException;
17+
import org.nlogo.api.LogoListBuilder;
18+
import org.nlogo.api.PrimitiveManager;
19+
import org.nlogo.api.Syntax;
20+
import org.nlogo.extensions.dbscan.metrics.DistanceMetricNetLogoAgents;
21+
import org.nlogo.extensions.dbscan.metrics.DistanceMetricNetLogoLocation;
22+
23+
/**
24+
* NetLogo extension for clustering based on DBSCAN by agent variables or coordinates.
25+
*
26+
* @author Christopher Frantz <[email protected]>
27+
*
28+
*/
29+
public class DBSCANExtension extends org.nlogo.api.DefaultClassManager {
30+
31+
@Override
32+
public void load(PrimitiveManager primitiveManager) throws ExtensionException {
33+
primitiveManager.addPrimitive("cluster-by-variable", new DbscanNetlogoVariableClusterer());
34+
primitiveManager.addPrimitive("cluster-by-location", new DbscanNetlogoCoordinateClusterer());
35+
}
36+
37+
/**
38+
* Clusters based on agent variables
39+
* @author Christopher Frantz <[email protected]>
40+
*/
41+
public static class DbscanNetlogoVariableClusterer extends DefaultReporter {
42+
43+
@Override
44+
public Syntax getSyntax() {
45+
//Inputs: values to be clustered, property to be clustered on, minimum number of elements, maximum distance
46+
int[] input = new int[] {Syntax.AgentsetType(), Syntax.StringType(), Syntax.NumberType(), Syntax.NumberType()};
47+
int ret = Syntax.ListType();
48+
return Syntax.reporterSyntax(input, ret);
49+
}
50+
51+
@Override
52+
public Object report(Argument[] args, Context ctx)
53+
throws ExtensionException, LogoException {
54+
55+
AgentSet inputValues = null;
56+
String field = null;
57+
int minNumberOfElements = -1;
58+
double maxDistance = -1;
59+
LogoListBuilder list = new LogoListBuilder();
60+
61+
try {
62+
inputValues = args[0].getAgentSet();
63+
field = args[1].getString();
64+
minNumberOfElements = args[2].getIntValue();
65+
maxDistance = args[3].getDoubleValue();
66+
} catch (Exception e) {
67+
throw new ExtensionException(e.getMessage());
68+
}
69+
70+
if (minNumberOfElements == -1) {
71+
throw new ExtensionException("Minimum number of cluster elements has not been defined.");
72+
}
73+
74+
if (maxDistance == -1) {
75+
throw new ExtensionException("Maximum distance of cluster variable has not been defined.");
76+
}
77+
78+
// Convert input agentset to collection
79+
Collection<Agent> inputCollection = new ArrayList<>();
80+
Iterator it = inputValues.agents().iterator();
81+
while (it.hasNext()) {
82+
inputCollection.add((Agent) it.next());
83+
}
84+
85+
// Perform clustering
86+
ArrayList<ArrayList<Agent>> tmpList = null;
87+
88+
try {
89+
DBSCANClusterer<Agent> clusterer = new DBSCANClusterer<Agent>(inputCollection, minNumberOfElements, maxDistance, new DistanceMetricNetLogoAgents(field.toUpperCase()));
90+
tmpList = clusterer.performClustering();
91+
} catch (DBSCANClusteringException e) {
92+
throw new ExtensionException(e);
93+
}
94+
95+
// Convert generated lists of clusters to nested LogoList
96+
for (ArrayList<Agent> intList: tmpList) {
97+
LogoListBuilder internalBuilder = new LogoListBuilder();
98+
for (Agent agent: intList) {
99+
internalBuilder.add(agent);
100+
}
101+
list.add(internalBuilder.toLogoList());
102+
}
103+
return list.toLogoList();
104+
}
105+
}
106+
107+
/**
108+
* Clusters based on coordinates
109+
* @author Christopher Frantz <[email protected]>
110+
*
111+
*/
112+
public static class DbscanNetlogoCoordinateClusterer extends DefaultReporter {
113+
114+
@Override
115+
public Syntax getSyntax() {
116+
//Inputs: values to be clustered, minimum number of elements, maximum distance
117+
int[] input = new int[] {Syntax.AgentsetType(), Syntax.NumberType(), Syntax.NumberType()};
118+
int ret = Syntax.ListType();
119+
return Syntax.reporterSyntax(input, ret);
120+
}
121+
122+
@Override
123+
public Object report(Argument[] args, Context ctx)
124+
throws ExtensionException, LogoException {
125+
126+
AgentSet inputValues = null;
127+
int minNumberOfElements = -1;
128+
double maxDistance = -1;
129+
LogoListBuilder list = new LogoListBuilder();
130+
131+
try {
132+
inputValues = args[0].getAgentSet();
133+
minNumberOfElements = args[1].getIntValue();
134+
maxDistance = args[2].getDoubleValue();
135+
} catch (Exception e) {
136+
throw new ExtensionException(e.getMessage());
137+
}
138+
139+
if (minNumberOfElements == -1) {
140+
throw new ExtensionException("Minimum number of cluster elements has not been defined.");
141+
}
142+
143+
if (maxDistance == -1) {
144+
throw new ExtensionException("Maximum distance of cluster variable has not been defined.");
145+
}
146+
147+
// Convert input agentset to collection
148+
Collection<Turtle> inputCollection = new ArrayList<>();
149+
Iterator it = inputValues.agents().iterator();
150+
while (it.hasNext()) {
151+
inputCollection.add((Turtle) it.next());
152+
}
153+
154+
// Perform clustering
155+
ArrayList<ArrayList<Turtle>> tmpList = null;
156+
157+
try {
158+
DBSCANClusterer<Turtle> clusterer = new DBSCANClusterer<Turtle>(inputCollection, minNumberOfElements, maxDistance, new DistanceMetricNetLogoLocation());
159+
tmpList = clusterer.performClustering();
160+
} catch (DBSCANClusteringException e) {
161+
throw new ExtensionException(e);
162+
}
163+
164+
// Convert generated lists of clusters to nested LogoList
165+
for (ArrayList<Turtle> intList: tmpList) {
166+
LogoListBuilder internalBuilder = new LogoListBuilder();
167+
for (Turtle agent: intList) {
168+
internalBuilder.add(agent);
169+
}
170+
list.add(internalBuilder.toLogoList());
171+
}
172+
return list.toLogoList();
173+
}
174+
}
175+
176+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.nlogo.extensions.dbscan.metrics;
2+
3+
import org.christopherfrantz.dbscan.DBSCANClusteringException;
4+
import org.christopherfrantz.dbscan.DistanceMetric;
5+
import org.nlogo.agent.Agent;
6+
import org.nlogo.api.AgentException;
7+
8+
public class DistanceMetricNetLogoAgents implements DistanceMetric<Agent>{
9+
10+
private String field = null;
11+
12+
public DistanceMetricNetLogoAgents(String field) {
13+
this.field = field;
14+
}
15+
16+
@Override
17+
public double calculateDistance(Agent val1, Agent val2) throws DBSCANClusteringException {
18+
try {
19+
// Check on turtle level first ...
20+
Double val1Num = Double.parseDouble(val1.getTurtleOrLinkVariable(field).toString());
21+
Double val2Num = Double.parseDouble(val2.getTurtleOrLinkVariable(field).toString());
22+
return Math.abs(val1Num - val2Num);
23+
} catch (ArrayIndexOutOfBoundsException e) {
24+
try {
25+
// ... before looking at breed level
26+
Double val1Num = Double.parseDouble(val1.getBreedVariable(field).toString());
27+
Double val2Num = Double.parseDouble(val2.getBreedVariable(field).toString());
28+
return Math.abs(val1Num - val2Num);
29+
} catch (NumberFormatException | AgentException e1) {
30+
throw new DBSCANClusteringException(e1.getMessage());
31+
}
32+
} catch (NumberFormatException | AgentException e) {
33+
throw new DBSCANClusteringException(e.getMessage());
34+
}
35+
}
36+
37+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.nlogo.extensions.dbscan.metrics;
2+
3+
import org.christopherfrantz.dbscan.DBSCANClusteringException;
4+
import org.christopherfrantz.dbscan.DistanceMetric;
5+
import org.nlogo.agent.Agent;
6+
import org.nlogo.agent.Turtle;
7+
import org.nlogo.api.AgentException;
8+
9+
public class DistanceMetricNetLogoLocation implements DistanceMetric<Turtle>{
10+
11+
private static final String X_COORDINATE = "XCOR";
12+
private static final String Y_COORDINATE = "YCOR";
13+
14+
public DistanceMetricNetLogoLocation() {
15+
}
16+
17+
@Override
18+
public double calculateDistance(Turtle val1, Turtle val2) throws DBSCANClusteringException {
19+
20+
try {
21+
Double val1XNum = Double.parseDouble(val1.getTurtleOrLinkVariable(X_COORDINATE).toString());
22+
Double val1YNum = Double.parseDouble(val1.getTurtleOrLinkVariable(Y_COORDINATE).toString());
23+
Double val2XNum = Double.parseDouble(val2.getTurtleOrLinkVariable(X_COORDINATE).toString());
24+
Double val2YNum = Double.parseDouble(val2.getTurtleOrLinkVariable(Y_COORDINATE).toString());
25+
26+
return StrictMath.sqrt(StrictMath.pow(val1YNum - val2YNum, 2.0) + StrictMath.pow(val1XNum - val2XNum, 2.0));
27+
} catch (NumberFormatException e) {
28+
throw new DBSCANClusteringException(e.getMessage());
29+
}
30+
}
31+
32+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.nlogo.extensions.dbscan.metrics;
2+
3+
import org.christopherfrantz.dbscan.DBSCANClusteringException;
4+
import org.christopherfrantz.dbscan.DistanceMetric;
5+
import org.nlogo.agent.Turtle;
6+
import org.nlogo.api.AgentException;
7+
8+
public class DistanceMetricNetLogoTurtles implements DistanceMetric<Turtle>{
9+
10+
private String field = null;
11+
12+
public DistanceMetricNetLogoTurtles(String field) {
13+
this.field = field;
14+
}
15+
16+
@Override
17+
public double calculateDistance(Turtle val1, Turtle val2) throws DBSCANClusteringException {
18+
try {
19+
// Check on turtle level first ...
20+
Double val1Num = Double.parseDouble(val1.getTurtleOrLinkVariable(field).toString());
21+
Double val2Num = Double.parseDouble(val2.getTurtleOrLinkVariable(field).toString());
22+
return Math.abs(val1Num - val2Num);
23+
} catch (ArrayIndexOutOfBoundsException e) {
24+
try {
25+
// ... before looking at breed level
26+
Double val1Num = Double.parseDouble(val1.getBreedVariable(field).toString());
27+
Double val2Num = Double.parseDouble(val2.getBreedVariable(field).toString());
28+
return Math.abs(val1Num - val2Num);
29+
} catch (NumberFormatException | AgentException e1) {
30+
throw new DBSCANClusteringException(e1.getMessage());
31+
}
32+
} catch (NumberFormatException e) {
33+
throw new DBSCANClusteringException(e.getMessage());
34+
}
35+
}
36+
37+
}

src/main/resources/manifest.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Manifest-Version: 1.0
2+
Extension-Name: dbscan
3+
Class-Manager: org.nlogo.extensions.dbscan.DBSCANExtension
4+
NetLogo-Extension-API-Version: 5.0

0 commit comments

Comments
 (0)