diff --git a/src/trajstat/forms/FrmAbout.form b/src/trajstat/forms/FrmAbout.form
index 3869bcd..d3f3b36 100644
--- a/src/trajstat/forms/FrmAbout.form
+++ b/src/trajstat/forms/FrmAbout.form
@@ -144,7 +144,7 @@
-
+
diff --git a/src/trajstat/forms/FrmAbout.java b/src/trajstat/forms/FrmAbout.java
index 11d5bcf..2f6ae27 100644
--- a/src/trajstat/forms/FrmAbout.java
+++ b/src/trajstat/forms/FrmAbout.java
@@ -78,7 +78,7 @@ private void initComponents() {
jLabel6.setText("Yaqiang Wang");
- jLabel7.setText("1.4.2");
+ jLabel7.setText("1.4.3");
jLabel8.setFont(new java.awt.Font("宋体", 1, 15)); // NOI18N
jLabel8.setText("Email:");
diff --git a/src/trajstat/forms/FrmCWT.java b/src/trajstat/forms/FrmCWT.java
index bf61719..2c2616a 100644
--- a/src/trajstat/forms/FrmCWT.java
+++ b/src/trajstat/forms/FrmCWT.java
@@ -451,6 +451,8 @@ private void jButton_WeightCWTActionPerformed(java.awt.event.ActionEvent evt) {/
aLayer.editCellValue("WCWT", i, wcwt);
}
aLayer.getAttributeTable().save();
+ aLayer.updateLegendIndexes();
+ this.app.getMapView().paintLayers();
//---- Hide progressbar
this.setCursor(Cursor.getDefaultCursor());
diff --git a/src/trajstat/forms/FrmClusterCal.form b/src/trajstat/forms/FrmClusterCal.form
index 836f262..51c5c31 100644
--- a/src/trajstat/forms/FrmClusterCal.form
+++ b/src/trajstat/forms/FrmClusterCal.form
@@ -29,7 +29,10 @@
-
+
+
+
+
@@ -43,18 +46,16 @@
-
+
+
+
+
-
-
-
-
-
-
-
+
+
@@ -392,5 +393,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/trajstat/forms/FrmClusterCal.java b/src/trajstat/forms/FrmClusterCal.java
index 1e5cf7a..8725d22 100644
--- a/src/trajstat/forms/FrmClusterCal.java
+++ b/src/trajstat/forms/FrmClusterCal.java
@@ -13,8 +13,10 @@
*/
package trajstat.forms;
+import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
+import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
@@ -27,11 +29,17 @@
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
+import javax.swing.WindowConstants;
+import org.meteoinfo.chart.Chart;
+import org.meteoinfo.chart.ChartPanel;
+import org.meteoinfo.chart.plot.XY1DPlot;
+import org.meteoinfo.data.XYListDataset;
import org.meteoinfo.geoprocess.analysis.Clustering;
import org.meteoinfo.ui.CheckBoxListEntry;
import org.meteoinfo.layer.VectorLayer;
@@ -45,9 +53,11 @@
import org.meteoinfo.layer.LayerDrawType;
import org.meteoinfo.legend.LegendManage;
import org.meteoinfo.legend.LegendScheme;
+import org.meteoinfo.legend.PolylineBreak;
import org.meteoinfo.shape.PointZ;
import org.meteoinfo.shape.ShapeTypes;
import trajstat.Main;
+import trajstat.trajectory.TrajUtil;
/**
*
@@ -59,6 +69,9 @@ public class FrmClusterCal extends javax.swing.JDialog {
/**
* Creates new form FrmClusterCal
+ *
+ * @param parent
+ * @param modal
*/
public FrmClusterCal(java.awt.Frame parent, boolean modal) {
super(parent, modal);
@@ -85,9 +98,10 @@ public FrmClusterCal(java.awt.Frame parent, boolean modal) {
this.jComboBox_Distance.addItem(DistanceType.ANGLE);
this.jComboBox_MaxClusterNum.removeAllItems();
- for (int i = 9; i <= 20; i++) {
+ for (int i = 9; i <= 30; i++) {
this.jComboBox_MaxClusterNum.addItem(i);
}
+ this.jComboBox_MaxClusterNum.setSelectedItem(30);
}
/**
@@ -124,6 +138,7 @@ private void initComponents() {
jComboBox_ClusterNum = new javax.swing.JComboBox();
jButton_MeanTraj = new javax.swing.JButton();
jButton_AddToTraj = new javax.swing.JButton();
+ jButton_viewTSV = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setResizable(false);
@@ -327,6 +342,14 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addContainerGap())
);
+ jButton_viewTSV.setText("View TSV");
+ jButton_viewTSV.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ jButton_viewTSV.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton_viewTSVActionPerformed(evt);
+ }
+ });
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@@ -336,7 +359,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
- .addComponent(jButton_Calculate, javax.swing.GroupLayout.PREFERRED_SIZE, 108, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jButton_Calculate, javax.swing.GroupLayout.PREFERRED_SIZE, 108, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jButton_viewTSV, javax.swing.GroupLayout.PREFERRED_SIZE, 108, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
@@ -346,15 +371,14 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
+ .addComponent(jButton_Calculate, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(12, 12, 12))
- .addGroup(layout.createSequentialGroup()
- .addGap(18, 18, 18)
- .addComponent(jButton_Calculate, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ .addComponent(jButton_viewTSV, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGap(12, 12, 12))
);
pack();
@@ -560,7 +584,6 @@ private void jButton_MeanTrajActionPerformed(java.awt.event.ActionEvent evt) {//
shpfn = shpfn + "." + extent;
}
-
VectorLayer aLayer = new VectorLayer(ShapeTypes.PolylineZ);
aLayer.editAddField("Cluster", DataTypes.String);
aLayer.editAddField("Traj_Num", DataTypes.Integer);
@@ -678,6 +701,115 @@ private void jButton_AddToTrajActionPerformed(java.awt.event.ActionEvent evt) {/
}
}//GEN-LAST:event_jButton_AddToTrajActionPerformed
+ private void jButton_viewTSVActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton_viewTSVActionPerformed
+ BufferedReader src = null;
+ try {
+ // TODO add your handling code here:
+ String clusterFile = this.jTextField_OutputFile.getText();
+ if (!new File(clusterFile).exists()) {
+ JOptionPane.showMessageDialog(null, "File does not exist!"
+ + System.getProperty("line.separator") + clusterFile);
+ return;
+ }
+ this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ DefaultListModel listModel = (DefaultListModel) this.checkBoxList_Data.getModel();
+ List layers = new ArrayList();
+ int i;
+ for (i = 0; i < listModel.getSize(); i++) {
+ if (((CheckBoxListEntry) listModel.get(i)).isSelected()) {
+ layers.add((VectorLayer) ((CheckBoxListEntry) listModel.get(i)).getValue());
+ }
+ }
+ int maxClusterNum = (Integer) this.jComboBox_MaxClusterNum.getSelectedItem();
+ DistanceType disType = (DistanceType) this.jComboBox_Distance.getSelectedItem();
+ List> clusters = new ArrayList>();
+ for (i = 2; i <= maxClusterNum; i++) {
+ clusters.add(new ArrayList());
+ }
+ int pointNum = Integer.parseInt(this.jLabel_PointNumValue.getText());
+ int cluster;
+ src = new BufferedReader(new FileReader(new File(clusterFile)));
+ src.readLine();
+ String[] dArray;
+ String line = src.readLine();
+ while (line != null) {
+ dArray = line.split(",");
+ if (dArray.length >= maxClusterNum) {
+ for (i = 2; i <= maxClusterNum; i++) {
+ cluster = Integer.parseInt(dArray[i]);
+ clusters.get(i - 2).add(cluster);
+ }
+ }
+ line = src.readLine();
+ }
+ src.close();
+ List tsvrs = new ArrayList();
+ double tsv1 = 0.0, tsv2, r;
+ for (i = 2; i <= maxClusterNum; i++) {
+ List cls = clusters.get(i - 2);
+ tsv2 = TrajUtil.calTSV(cls, i, pointNum, layers, disType);
+ if (i == 2) {
+ tsv1 = tsv2;
+ } else {
+ r = Math.abs(tsv2 - tsv1) * 100 / tsv1;
+ tsvrs.add(r);
+ tsv1 = tsv2;
+ }
+ }
+
+ //Plot
+ XYListDataset dataset = new XYListDataset();
+ int n = maxClusterNum - 2;
+ double[] xvs = new double[n];
+ double[] yvs = new double[n];
+ List xTickValues = new ArrayList();
+ xTickValues.add(Double.valueOf(0));
+ xTickValues.add(Double.valueOf(1));
+ for (i = 0; i < n; i++) {
+ xvs[i] = i + 2;
+ xTickValues.add(Double.valueOf(i + 2));
+ yvs[i] = tsvrs.get(i);
+ }
+ xTickValues.add(Double.valueOf(n + 2));
+ dataset.addSeries("S_1", xvs, yvs);
+ XY1DPlot plot = new XY1DPlot(dataset);
+ ((PolylineBreak) plot.getLegendBreak(0)).setDrawSymbol(true);
+ plot.getGridLine().setDrawXLine(true);
+ plot.getGridLine().setDrawYLine(true);
+ plot.getXAxis().setInverse(true);
+ plot.getXAxis().setLabel("Number of clusters");
+ plot.getXAxis().setDrawLabel(true);
+ plot.getXAxis().setTickValues(xTickValues);
+ plot.getYAxis().setLabel("Percent change in TVS (%)");
+ plot.getYAxis().setDrawLabel(true);
+ Chart chart = new Chart(plot);
+ ChartPanel chartPanel = new ChartPanel(chart);
+ JFrame frame = new JFrame();
+ BufferedImage image = null;
+ try {
+ image = ImageIO.read(this.getClass().getResource("/trajstat/resources/TrajStat_Logo.png"));
+ } catch (Exception e) {
+ }
+ frame.setIconImage(image);
+ frame.getContentPane().add(chartPanel, BorderLayout.CENTER);
+ frame.setSize(600, 400);
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setLocationRelativeTo(this);
+ frame.setVisible(true);
+ } catch (FileNotFoundException ex) {
+ Logger.getLogger(FrmClusterCal.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IOException ex) {
+ Logger.getLogger(FrmClusterCal.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ this.setCursor(Cursor.getDefaultCursor());
+ try {
+ src.close();
+ } catch (IOException ex) {
+ Logger.getLogger(FrmClusterCal.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }//GEN-LAST:event_jButton_viewTSVActionPerformed
+
private void addClusterToLayers(final List cDataArray, final List layers) {
SwingWorker worker = new SwingWorker() {
@Override
@@ -732,7 +864,7 @@ protected void done() {
FrmClusterCal.this.setCursor(Cursor.getDefaultCursor());
}
};
-
+
worker.execute();
}
@@ -784,6 +916,7 @@ public void windowClosing(java.awt.event.WindowEvent e) {
private javax.swing.JButton jButton_MeanTraj;
private javax.swing.JButton jButton_OutputFile;
private javax.swing.JButton jButton_SelAll;
+ private javax.swing.JButton jButton_viewTSV;
private javax.swing.JCheckBox jCheckBox_IgnoreDataLines;
private javax.swing.JComboBox jComboBox_ClusterNum;
private javax.swing.JComboBox jComboBox_Distance;
diff --git a/src/trajstat/forms/FrmPSCF.java b/src/trajstat/forms/FrmPSCF.java
index 3dfd5ba..723164b 100644
--- a/src/trajstat/forms/FrmPSCF.java
+++ b/src/trajstat/forms/FrmPSCF.java
@@ -405,6 +405,8 @@ private void jButton_WeightPSCFActionPerformed(java.awt.event.ActionEvent evt) {
aLayer.editCellValue("WPSCF", i, wpscf);
}
aLayer.getAttributeTable().save();
+ aLayer.updateLegendIndexes();
+ this.app.getMapView().paintLayers();
//---- Hide progressbar
this.setCursor(Cursor.getDefaultCursor());
diff --git a/src/trajstat/trajectory/TrajUtil.java b/src/trajstat/trajectory/TrajUtil.java
index db71573..5b15b92 100644
--- a/src/trajstat/trajectory/TrajUtil.java
+++ b/src/trajstat/trajectory/TrajUtil.java
@@ -28,7 +28,9 @@
import java.util.Date;
import java.util.List;
import javax.swing.JOptionPane;
+import org.meteoinfo.geoprocess.analysis.DistanceType;
import org.meteoinfo.global.MIMath;
+import org.meteoinfo.global.PointD;
import org.meteoinfo.table.DataTypes;
import org.meteoinfo.global.util.GlobalUtil;
import org.meteoinfo.layer.LayerDrawType;
@@ -555,4 +557,175 @@ public static void removeTrajFiles(TrajConfig trajConfig) {
}
}
}
+
+ /**
+ * Calculate cluster mean trajectories
+ *
+ * @param clusters The cluster list
+ * @param CLev Cluster level
+ * @param pointNum Endpoint number of one trajectory
+ * @param layers Trajectory layers
+ * @return Mean trajectory data of all clusters
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public static double[][] calMeanTrajs(List clusters, int CLev, int pointNum, List layers) throws FileNotFoundException, IOException {
+ int M;
+ int cluster;
+ int i, j;
+ M = pointNum * 3;
+ double[][] trajDataArray = new double[CLev][M];
+ //---- Mean Trajectories
+ int[] trajNumArray = new int[CLev];
+ //Initialize Trajectory number of each cluster
+ for (i = 0; i < CLev; i++) {
+ trajNumArray[i] = 0;
+ for (j = 0; j < M; j++) {
+ trajDataArray[i][j] = 0.0;
+ }
+ }
+ i = 0;
+ for (VectorLayer layer : layers) {
+ for (int s = 0; s < layer.getShapeNum(); s++) {
+ PolylineZShape shape = (PolylineZShape) layer.getShapes().get(s);
+ if (shape.getPointNum() != pointNum) {
+ continue;
+ }
+
+ cluster = clusters.get(i);
+ int m = 0;
+ for (j = 0; j < pointNum; j++) {
+ PointZ point = (PointZ) shape.getPoints().get(j);
+ trajDataArray[cluster - 1][m] += point.Y;
+ m += 1;
+ trajDataArray[cluster - 1][m] += point.X;
+ m += 1;
+ trajDataArray[cluster - 1][m] += point.Z;
+ m += 1;
+ }
+ trajNumArray[cluster - 1] += 1;
+ i += 1;
+ }
+ }
+
+ for (i = 0; i < CLev; i++) {
+ for (j = 0; j < M; j++) {
+ trajDataArray[i][j] = trajDataArray[i][j] / trajNumArray[i];
+ }
+ }
+
+ return trajDataArray;
+ }
+
+ /**
+ * Calculate total spatial variance - TSV Calculate cluster spatial variance
+ * = SPVAR
+ *
+ * @param clusters The cluster list
+ * @param CLev Cluster level
+ * @param pointNum Endpoint number of one trajectory
+ * @param layers Trajectory layers
+ * @param disType Distance type
+ * @return Mean trajectory data of all clusters
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public static double calTSV(List clusters, int CLev, int pointNum, List layers,
+ DistanceType disType) throws FileNotFoundException, IOException {
+ //Calculate mean trajectories
+ double[][] trajDataArray = calMeanTrajs(clusters, CLev, pointNum, layers);
+
+ //Calculate TSV
+ double tsv = 0.0;
+ int cluster;
+ int i, j;
+ double x, y;
+ i = 0;
+ for (VectorLayer layer : layers) {
+ for (int s = 0; s < layer.getShapeNum(); s++) {
+ PolylineZShape shape = (PolylineZShape) layer.getShapes().get(s);
+ if (shape.getPointNum() != pointNum) {
+ continue;
+ }
+
+ cluster = clusters.get(i);
+ double dist;
+ int m = 0;
+ PointD[] trajA = new PointD[pointNum];
+ PointD[] trajB = new PointD[pointNum];
+ for (j = 0; j < pointNum; j++) {
+ PointZ point = (PointZ) shape.getPoints().get(j);
+ trajA[j] = new PointD(point.X, point.Y);
+ y = trajDataArray[cluster - 1][m];
+ m += 1;
+ x = trajDataArray[cluster - 1][m];
+ m += 2;
+ trajB[j] = new PointD(x, y);
+ }
+ if (disType == DistanceType.EUCLIDEAN)
+ dist = calDistance_Euclidean(trajA, trajB);
+ else
+ dist = calDistance_Angle(trajA, trajB);
+ tsv += dist;
+ i += 1;
+ }
+ }
+
+ return tsv;
+ }
+
+ /**
+ * Calculate distance - Euclidean
+ * @param trajA Trajectory A
+ * @param trajB Trajectory B
+ * @return Euclidean distance
+ */
+ public static double calDistance_Euclidean(PointD[] trajA, PointD[] trajB) {
+ double dist = 0.0;
+ int n = trajA.length;
+ PointD pA, pB;
+ for (int j = 0; j < n; j++) {
+ pA = trajA[j];
+ pB = trajB[j];
+ dist += (pA.X - pB.X) * (pA.X - pB.X) + (pA.Y - pB.Y) * (pA.Y - pB.Y);
+ }
+ dist = Math.sqrt(dist);
+
+ return dist;
+ }
+
+ /**
+ * Calculate distance - angle
+ * @param trajA Trajectory A
+ * @param trajB Trajectory B
+ * @return Angle distance
+ */
+ public static double calDistance_Angle(PointD[] trajA, PointD[] trajB) {
+ double dist = 0.0;
+ double angle;
+ int n = trajA.length;
+ PointD pA, pB;
+ double A, B, C;
+ double X0 = trajA[0].X;
+ double Y0 = trajB[0].Y;
+ for (int j = 1; j < n; j++) {
+ pA = trajA[j];
+ pB = trajB[j];
+ A = Math.pow((pA.X - X0), 2) + Math.pow((pA.Y - Y0), 2);
+ B = Math.pow((pB.X - X0), 2) + Math.pow((pB.Y - Y0), 2);
+ C = Math.pow((pB.X - pA.X), 2) + Math.pow((pB.Y - pA.Y), 2);
+ if (A == 0 | B == 0) {
+ angle = 0;
+ } else {
+ angle = 0.5 * (A + B - C) / Math.sqrt(A * B);
+ }
+ if ((Math.abs(angle) > 1.0)) {
+ angle = 1.0;
+ }
+ dist += Math.acos(angle);
+ }
+ dist = dist / n;
+
+ return dist;
+ }
}