/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.components.cluster.kmeans.latlng;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JPanel;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.components.ComponentConfigurationEditorAPI;
import com.opendoorlogistics.api.components.ComponentExecutionApi;
import com.opendoorlogistics.api.components.ODLComponent;
import com.opendoorlogistics.api.scripts.ScriptTemplatesBuilder;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
import com.opendoorlogistics.api.tables.ODLTable;
import com.opendoorlogistics.api.tables.ODLTableAlterable;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.ODLTableDefinitionAlterable;
import com.opendoorlogistics.components.cluster.BasicCluster;
import com.opendoorlogistics.components.cluster.kmeans.KMeansAlgorithm;
import com.opendoorlogistics.components.cluster.kmeans.KMeansAlgorithm.CreateMean;
import com.opendoorlogistics.components.cluster.kmeans.KMeansConfig;
import com.opendoorlogistics.components.cluster.kmeans.Mean;
import com.opendoorlogistics.core.components.ODLWizardTemplateConfig;
import com.opendoorlogistics.core.tables.ODLFactory;
import com.opendoorlogistics.core.tables.beans.BeanMapping;
import com.opendoorlogistics.utils.ui.Icons;
final public class KMeansLatLngComponent implements ODLComponent {
@Override
public Class<? extends Serializable> getConfigClass() {
return KMeansConfig.class;
}
@Override
public JPanel createConfigEditorPanel(ComponentConfigurationEditorAPI factory,int mode,Serializable config, boolean isFixedIO) {
return KMeansConfig.createConfigEditorPanel((KMeansConfig) config);
}
@Override
public String getId() {
return "com.opendoorlogistics.components.cluster.kmeans.latlng.KMeansLatLngComponent";
}
@Override
public String getName() {
return "Cluster using K-means";
}
@Override
public ODLDatastore<? extends ODLTableDefinition> getIODsDefinition(ODLApi api,Serializable configuration) {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable> ret = api.tables().createDefinitionDs();
ret.createTable("Points", -1);
ODLTableDefinitionAlterable dfn = ret.getTableAt(0);
dfn.addColumn(-1, "Latitude", ODLColumnType.DOUBLE, 0);
dfn.addColumn(-1, "Longitude", ODLColumnType.DOUBLE, 0);
dfn.addColumn(-1, "ClusterNumber", ODLColumnType.LONG, 0);
return ret;
}
@Override
public ODLDatastore<? extends ODLTableDefinition> getOutputDsDefinition(ODLApi api,int mode, Serializable configuration) {
ODLDatastoreAlterable<? extends ODLTableDefinitionAlterable>ret= BeanMapping.buildDatastore(BasicCluster.class).getDefinition();
ret.setTableName(ret.getTableAt(0).getImmutableId(), "K-means cluster result");
return ret;
}
@SuppressWarnings("unchecked")
@Override
public void execute(ComponentExecutionApi reporter,int mode,Object configuration, ODLDatastore<? extends ODLTable> input, ODLDatastoreAlterable<? extends ODLTableAlterable> output) {
ODLTable tbl = input.getTableAt(0);
int nr = tbl.getRowCount();
// convert to our internal points class
ArrayList<KMeanPointLngLat> lngLats = new ArrayList<>();
for (int row = 0; row < nr; row++) {
Double lat = (Double) tbl.getValueAt(row, 0);
Double lng = (Double) tbl.getValueAt(row, 1);
if (lat != null && lng != null) {
KMeanPointLngLat lnglat = new KMeanPointLngLat();
lnglat.id = row;
lnglat.latitude = lat;
lnglat.longitude = lng;
lngLats.add(lnglat);
}
}
reporter.postStatusMessage("Starting k-means on " + lngLats.size() + " points");
// run algorithm
KMeansConfig kc = (KMeansConfig) configuration;
KMeansAlgorithm algorithm = new KMeansAlgorithm();
List<Mean<KMeanPointLngLat>> means = algorithm.execute(kc.getK(), kc.getRandomSeed(), new CreateMean<KMeanPointLngLat>() {
@Override
public Mean<KMeanPointLngLat> createMean(KMeanPointLngLat copyThis) {
MeanLngLat mean = new MeanLngLat();
mean.getMean().latitude = copyThis.latitude;
mean.getMean().longitude = copyThis.longitude;
return mean;
}
}, lngLats, reporter);
if (!reporter.isCancelled()) {
reporter.postStatusMessage("Writing results to table");
// write results back to input table
for (KMeanPointLngLat latLng : lngLats) {
tbl.setValueAt(latLng.clusterNumber + 1, latLng.id, 2);
}
// also output cluster table
BasicCluster[] clusters = new BasicCluster[means.size()];
for (int i = 0; i < clusters.length; i++) {
clusters[i] = new BasicCluster();
BasicCluster c = clusters[i];
c.setClusterId(Integer.toString(i + 1));
c.setLatitude(means.get(i).getMean().latitude);
c.setLongitude(means.get(i).getMean().longitude);
c.setAssignedLocationsCount(0);
}
for (KMeanPointLngLat latLng : lngLats) {
int cIndex = latLng.clusterNumber;
BasicCluster c = clusters[cIndex];
c.setAssignedLocationsCount(c.getAssignedLocationsCount() + 1);
}
BeanMapping.buildDatastore(BasicCluster.class).getTableMapping(0).writeObjectsToTable(clusters, output.getTableAt(0));
reporter.postStatusMessage("Finished k-means");
}
}
@Override
public long getFlags(ODLApi api,int mode) {
return 0;
}
// @Override
// public Iterable<ODLWizardTemplateConfig> getWizardTemplateConfigs(ODLApi api) {
// return Arrays.asList(new ODLWizardTemplateConfig("K-means", getName(), "K-means clustering using latitude & longitude", null));
// }
@Override
public void registerScriptTemplates(ScriptTemplatesBuilder templatesApi) {
templatesApi.registerTemplate("K-means", getName(), "K-means clustering using latitude & longitude",getIODsDefinition(templatesApi.getApi(), new KMeansConfig()), new KMeansConfig());
}
@Override
public Icon getIcon(ODLApi api,int mode) {
return Icons.loadFromStandardPath("kmeans.png");
}
// @Override
// public ComponentType getComponentType() {
// // TODO Auto-generated method stub
// return null;
// }
@Override
public boolean isModeSupported(ODLApi api,int mode) {
return mode==ODLComponent.MODE_DEFAULT;
}
}