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; + } }