// Traffic Data Analysis // Copyright Isao Hamamoto, Sogo Clinic,Feb 2014 // 香川県医師会雑誌67巻1号掲載予定の随筆「極小規模臨床試験EASY」のために開発した。 // 一定区間の走行を、iPad アプリケーションである、KingGPSおよびZweitGPSで記録したデータを、タブ区切りテキストとして抽出し、時間vs速度、距離vs速度のグラフを描画、信号停車の判定を行い、 // 信号停車回数を算出する。 import javax.swing.*; import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; import java.awt.event.*; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import javax.swing.JOptionPane; import java.awt.Font; public class TrafficDataAnalysis extends JFrame implements ActionListener{ JLabel label; JButton button; // データファイルの読み込みボタン JButton button2; // 分析開始ボタン JTextField mytext1=new JTextField("",50); // speed threshold JTextField mytext2=new JTextField("",50); // file path JLabel mylabel1=new JLabel("File/Path"); // fileのパスを表示 JLabel mylabel2=new JLabel("Speed Threshold"); // 速度閾値を入力する。デフォルトは10km/h。画面上で変更可能。 JLabel mylabel3=new JLabel("km/h"); // 信号停車の速度閾値の設定 JLabel mylabel4=new JLabel("******"); //単純停車回数表示用ラベル JLabel mylabel5=new JLabel("******"); //信号停車回数表示用ラベル JLabel mylabel6=new JLabel("******"); //所要時間 JLabel mylabel7=new JLabel("******"); //GPSの種類を表示する。 JLabel mylabel8=new JLabel("Copyright 2014 Isao Hamamoto"); //クレジットの表示 JLabel mylabel9=new JLabel("******"); //走行距離の表示 ButtonGroup group = new ButtonGroup(); JPanel p = new JPanel(); //JRadioButton radio1,radio2; //GPS選択のためのラジオボタン graphicCanvas canvas; File file; //入力用データファイル File file_out; //output file に書き込むデータの文字列 public int i_dis=0; //v[][]における距離の要素番号。 public int i_spd=1; //v[][]における速度の要素番号。 public int i_time=2; //v[][]における時間の要素番号。 public int indexJ; //indexKは、要素の数 public int indexK; //indexJは、データセットの数 public double spd_th; //speed threshold 値 public double signal[] = new double[30]; //出発点から信号までの距離 public int stp; //停止回数 public int sig_stop; //信号での停止回数 public int gps; //0:KingGPS, 1:ZweitGPS public double duration; //所要時間 public String output_file; //出力するファイル名 public String kingExt="plist"; //KingGPSのファイルの拡張子 public String zweitExt="json"; //ZweitGPSのファイルの拡張子 public String file_name; //データのファイル名  public double v[][]=new double[1000][3]; //計測データ //0:距離 //1:速度 //2:時刻 public double sampling_rate=3; //サンプリングレート 秒 //信号の位置 出発地点からの距離(km)で示す。中央の値は、距離。その前の値は、新語エリアの開始、最後の値は終了地点の距離。 public double[][] signals={{ 0.231453476 , 0.290181818 , 0.34891016 }, { 0.429407739 , 0.482454545 , 0.535501352 }, { 0.547166884 , 0.611 , 0.674833116 }, { 0.634688181 , 0.708363636 , 0.782039092 }, { 0.833320595 , 0.897454545 , 0.961588496 }, { 1.222117918 , 1.333636364 , 1.407981994 }, { 1.479639736 , 1.551666667 , 1.623693598 }, { 1.630165598 , 1.711666667 , 1.793167736 }, { 2.012616845 , 2.093333333 , 2.174049822 }, { 2.339639736 , 2.411666667 , 2.483693598 }, { 2.463084657 , 2.591666667 , 2.720248677 }, { 3.038864145 , 3.129166667 , 3.189368348 }, { 3.388100958 , 3.475833333 , 3.563565709 }, { 3.662901052 , 3.734166667 , 3.805432282 }, { 3.870004296 , 3.950833333 , 4.031662371 }, { 4.077232324 , 4.138333333 , 4.199434343 }, { 4.317716578 , 4.395 , 4.446522281 }, { 4.475352397 , 4.568333333 , 4.630320624 }, { 4.70085896 , 4.814166667 , 4.889705138 }, { 4.98793184 , 5.205833333 , 5.29299393 }, { 5.327094672 , 5.451666667 , 5.534714663 }, { 5.537995137 , 5.6525 , 5.728836575 }, { 5.733679499 , 5.870833333 , 5.949206953 }, { 5.963642228 , 6.2675 , 6.389043109 }, { 6.394228757 , 6.511818182 , 6.590211131 }, { 6.531632899 , 6.67 , 6.762244734 }, { 6.980387905 , 7.1025 , 7.183908063 }, { 7.241708758 , 7.466666667 , 7.55664983 }, { 7.578766063 , 7.875 , 8.171233937 }, { 8.701758547 , 8.9075 , 9.195538034 }}; public int[] sig={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};// 信号で停車したどうかの結果格納。0:停車なし、1:停車あり。 public HashMap hm = new HashMap(); public HashMap dat[]=new HashMap[1000]; //タイムセクションごとのハッシュマップを格納する。計測点だけ配列サイズを要する。 public double mark[]=new double[100]; //単純停車の場所を格納 public double distance; //走行距離 /////////// コンストラクタ //////////// TrafficDataAnalysis(String title){ super(title); setSize(850,650); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocation(350,100); setLayout(null); mylabel1.setBounds(20,20,100,30); mylabel2.setBounds(20,60,140,30); mytext1.setBounds(140,60,50,30); // speed threshold mytext1.setHorizontalAlignment(JTextField.RIGHT); mytext1.setText("" +"10.0"); mytext2.setBounds(140,20,640,30); // file path mylabel3.setBounds(200,60,100,30); mylabel4.setBounds(600,80,200,30); //単純停車回数 mylabel5.setBounds(600,100,200,30); //信号停車回数 mylabel6.setBounds(400,80,200,30); //所要時間 mylabel7.setBounds(400,60,200,30); //GPSの種類表示 mylabel8.setBounds(600,590,250,30); //クレジット表示 mylabel9.setBounds(400,100,200,30); //走行距離の表示 mylabel8.setFont(new Font("Times", Font.ITALIC, 14)); add(mylabel1); add(mytext1); add(mylabel2); add(mytext2); add(mylabel3); add(mylabel4); add(mylabel5); add(mylabel6); add(mylabel7); add(mylabel8); add(mylabel9); button = new JButton("File Select"); button2 = new JButton("Analysis");//***** button.addActionListener(this); button2.addActionListener(this); button.setBounds(100,100,100,30); button2.setBounds(220,100,100,30); this.add(button); this.add(button2); label = new JLabel(); JPanel labelPanel = new JPanel(); labelPanel.add(label); JPanel cp = new JPanel(); //グラフにデフォルトの速度閾値を描画するために、spd_thを設定する。 spd_th=Double.valueOf(mytext1.getText()); // speed threshold を書毒 //レイアウトは手動 cp.setLayout(null); //フレームにコンテントペインを登録 this.add(cp); //コンテントペインの貼り付け位置・大きさを設定 cp.setBounds(50,70,680,450); //============================================================================ //キャンバスの作成 //graphicCanvas canvas = new graphicCanvas(); canvas = new graphicCanvas(); cp.add(canvas); //キャンパスの位置はコンテントペインに合わせる。 //キャンバスのサイズはコンテントペインと同じにする。 canvas.setBounds(50,70,680,450); canvas.setBackground(Color.white); } public void actionPerformed(ActionEvent e){ int index; stp=0; //単純停車回数をリセット sig_stop=0; //信号停車回数をリセット int j,k; String str,str1,str2; String str3[]=new String[1000]; String str4[][]=new String[1000][10]; String str5[]=new String[10]; str=""; String ext; //ファイル名の拡張子 ///// 行列vをクリア ///// for(j=0;j<1000;j++){ for(k=0;k<3;k++){ v[j][k]=0.0F; } } ///// 信号停止場所をクリア ///// for(j=0;j<30;j++){ sig[j]=0; } ///// 単純停車の場所をクリア ///// for(j=0;j<100;j++){ mark[j]=0; } /////////ファイル選択ボタンが押下された場合。/////////// if(e.getSource() == button){ // ファイル選択ダイアローグを表示 // JFileChooser filechooser = new JFileChooser(); int selected = filechooser.showOpenDialog(this); if (selected == JFileChooser.APPROVE_OPTION){ file = filechooser.getSelectedFile(); label.setText(file.getName()); file_name=file.getName(); file_name=file_name.substring(file_name.lastIndexOf("/")+1); mytext2.setText(file.getAbsolutePath()); }else if (selected == JFileChooser.CANCEL_OPTION){ label.setText("キャンセルされました"); }else if (selected == JFileChooser.ERROR_OPTION){ label.setText("エラー又は取消しがありました"); } // ファイル選択ダイアローグの表示を終わる // }else if(e.getSource() == button2){ ////////// 計算ボタンが押下された場合。 // 計算し、グラフを描画する spd_th=Double.valueOf(mytext1.getText());// speed threshold を書毒 ////////////////////////////////////////////////// ////////// ファイルの文字列を読み込む ////////// try{ FileReader filereader = new FileReader(file); int ch = filereader.read(); while(ch != -1){ str+=(char)ch; ch = filereader.read(); } filereader.close(); }catch(FileNotFoundException ex){ System.out.println(e); }catch(IOException ex){ System.out.println(e); } ext=file.getName(); ext=ext.substring(file.getName().indexOf(".")+1); ////////////////// ファイル文字列の読み込み終了 ////////////////// ////////// 拡張子によって、自動的に処理を振り分ける。////////// if(ext.equals(kingExt)){ //KingGPSの場合。拡張子は.plist mylabel7.setText("GPS type: King GPS"); index=str.indexOf(""); str1=str.substring(index+15); //改良コード↓ str1=str1.substring(0,str1.indexOf("/plist")-1); str1=str1.trim(); str1=str1.substring(0,str1.indexOf("/array")-1); str1=str1.trim(); str1=str1.substring(0,str1.length()-7); str1=str1.trim(); //改良コード終わり str2=str1.replace("",""); str2=str2.substring(0,str2.length()-10); str2=str2.trim(); str3=str2.split(""); indexJ=str3.length; for(j=0;j",","); str3[j]=str3[j].replace("",","); str3[j]=str3[j].replace("",""); str3[j]=str3[j].replace("",""); str3[j]=str3[j].substring(0,str3[j].length()-1); str5=str3[j].split(","); //タイムセクションごとの計測データをハッシュマップhmに格納する。 int i; hm.clear(); for(i=0;i2){ //有意なデータが選択された場合のみ、以下の処理を行う。 // //信号停車のチェックも行う。 double dist =0.0; String tdur=""; //所要時間を分秒の文字列に変換。 stp=0; while(v[indexJ-1][i_time]<=v[0][i_time]){ //v[indexJ-1][i_time]がE7になるため(原因不明)、durationが負になるのをさけるための処理。 indexJ-=1; } duration=v[indexJ-1][i_time]-v[0][i_time]; for(j=0;jsignals[i][0] & (dist/1000)<11){ sig[i]=1; } } } distance=dist; dist=0; //単純な停止回数をカウント for(j=5;jspd_th){ stp++; mark[stp]=dist/1000; } } } stp-=2; //スタートとエンドの回数2を引く int i; //信号での停止回数をカウント for(i=0;i<30;i++){ if(sig[i]==1){ sig_stop++; } } output_file="Path:\t"+file.getAbsolutePath()+"\t"; output_file+="file_name:\t"+file_name+"\t"; output_file+="所要時間(sec):\t"+duration+"\t"; String tmpstr; //一時的文字列 tmpstr=Double.toString((int)(duration/60)); tdur+=tmpstr.replace(".0", ""); tdur+="分"; tmpstr=Double.toString((int)((duration/60-(int)(duration/60))*60)); tdur+=tmpstr.replace(".0", ""); tdur+="秒\t"; tdur="所要時間="+tdur; output_file+="所要時間:\t"+tdur+"\t"; output_file+="走行距離:\t"+distance+"\t"; output_file+="信号停車回数:\t"+sig_stop+"\t"; output_file+="単純停車回数:\t"+stp+"\t"; mylabel6.setText(tdur); mylabel4.setText("単純停止回数="+stp+"回"); mylabel5.setText("信号停止回数="+sig_stop+"回"); output_file+="停止信号\t"; for(i=0;i<29;i++){ output_file+=sig[i]+"\t"; } output_file+=sig[29]+"\n"; ///// 結果ファイルを出力 //// try{ String output_file_name=""; if(ext.equals(kingExt)){ output_file_name=file.getAbsolutePath().replace("plist","txt"); } else if(ext.equals(zweitExt)){ output_file_name=file.getAbsolutePath().replace("json","txt"); } else{ System.out.print("*Extension is not identified\n"); } System.out.print("output file name="+output_file_name+"\r"); File file_out = new File(output_file_name); if(file_out.createNewFile()){ System.out.println("ファイル作成に成功"); FileWriter filewriter = new FileWriter(file_out); filewriter.write(output_file); filewriter.close(); }else{ int ret = JOptionPane.showConfirmDialog(this,"ファイルが存在します。\n上書きしますか?","確認", JOptionPane.YES_NO_OPTION); System.out.println("ファイル作成に失敗"); if(ret == JOptionPane.YES_OPTION) { FileWriter filewriter = new FileWriter(file_out); filewriter.write(output_file); System.out.println("ファイル書き込み成功"); filewriter.close(); // System.exit(0); } } }catch(IOException ex){ System.out.println(ex); } ////////////////// } } } ///// グラフキャンバスの作成 ///// class graphicCanvas extends Canvas { String s; double f; //時間軸描画路のファクター double t; //時間 graphicCanvas() { //背景色はlightGrayに設定。 setBackground(Color.white); } public void paint(Graphics g) { int i,j,k; int ix1=0; int ix2=420; int iy1=0; int iy2=420; double dist1,dist2; double spd1,spd2; double tim1,tim2; //x軸→時間の描画のためのx座標 double time; //時間  サンプリングレートXサンプル番号 double f_distance=52.27; //x軸のx座標の乗数 距離X f_distance=x座標 int tx[]=new int[3]; //信号のマーカーの座標 int ty[]=new int[3]; //信号のマーカーの座標 String tmpstr; //区間距離表示のための一時変数 g.drawLine(60, 400, 635, 400); //x軸 g.drawLine(60,190, 635, 190); //x軸 g.drawLine(60, 240, 60, 400); //y軸 g.drawLine(60, 190, 60, 30); //y軸 g.setColor(Color.black); //x軸のメモリ 距離軸 for(i=0;i<12;i++){ g.drawLine(60+(int)(i*f_distance),400,60+(int)(i*f_distance),398); g.setColor(Color.LIGHT_GRAY); g.drawLine(60+(int)(i*f_distance),400,60+(int)(i*f_distance),240); g.setColor(Color.BLACK); g.drawString(Integer.toString(i),(int)(i*f_distance)+56,420); } float x; for(x=0;x<11F;x+=0.2){ g.drawLine(60+(int)(x*f_distance),400,60+(int)(x*f_distance),395); } //x軸のメモリ2 if(duration>0){ f=575/duration; }else{ f=1; } //大メモリ g.setColor(Color.LIGHT_GRAY); for(i=0;i<(int)duration;i+=60){ g.drawLine((int)(i*f)+60,190,(int)(i*f)+60,30); } g.setColor(Color.BLACK); //数字描画 for(i=0;i<(int)duration;i+=60){ g.drawString(Integer.toString((int)(i/60)),(int)(i*f)+57,205); } //小メモリ for(i=0;i<(int)duration;i+=10){ g.drawLine((int)(i*f)+60,190,(int)(i*f)+60,187); } g.drawString("走行時間(分)",300, 220); ////////Y軸メモリ double fi; for(fi=0;fi<11.4;fi+=0.2){ g.drawLine((int)(60+fi*50),400,(int)(60+fi*50),400); } g.drawString("走行距離(km)",300, 435); // y軸のメモリ for(i=0;i<81;i+=2){ g.drawLine(60,400-2*i,63,400-2*i); if(i%5==0 & i>0){ g.setColor(Color.LIGHT_GRAY); g.drawLine(60,400-2*i,635,400-2*i); g.setColor(Color.BLACK); } } for(i=0;i<81;i+=10){ g.drawLine(60,400-2*i,65,400-2*i); g.drawString(Integer.toString(i),35,400-2*i+5); } g.drawString("速",10, 310); g.drawString("度", 10, 340); // y軸のメモリ 速度2 for(i=0;i<81;i+=2){ g.drawLine(60,190-2*i,63,190-2*i); if(i%5==0 & i>0){ g.setColor(Color.LIGHT_GRAY); g.drawLine(60,190-2*i,635,190-2*i); g.setColor(Color.BLACK); } } for(i=0;i<81;i+=10){ g.drawLine(60,190-2*i,65,190-2*i); g.drawString(Integer.toString(i),35,190-2*i+5); } g.drawString("速",10, 100); g.drawString("度", 10, 130); g.drawString("(km/h)",20,15); g.drawString("(km/h)",20,225); //信号の位置の描画 for(i=0;i<30;i++){ //信号の位置を三角のマーカーで表示する g.setColor(Color.red); tx[0]=(int)(signals[i][1]*f_distance+55); ty[0]=405; tx[1]=(int)(signals[i][1]*f_distance+60); ty[1]=400; tx[2]=(int)(signals[i][1]*f_distance+65); ty[2]=405; g.fillPolygon(tx,ty,3); g.setColor(Color.black); //信号位置のマーカー描画終了 if(sig[i]==1){ g.setColor(Color.orange); }else{ g.setColor(Color.green); } g.fillRect((int)(signals[i][0]*f_distance)+60,400-(int)(spd_th*2),(int)((signals[i][2]-signals[i][0])*f_distance),(int)(spd_th*2)); if(sig[i]==1){ g.setColor(Color.red); }else{ g.setColor(Color.blue); } g.drawRect((int)(signals[i][0]*f_distance)+60,400-(int)(spd_th*2),(int)((signals[i][2]-signals[i][0])*f_distance),(int)(spd_th*2)); } g.setColor(Color.black); //グラフの描画 dist1=0; //距離をリセット dist2=0; ///// x軸距離 y軸速度 を描画 dist1=0; dist2=0; for(j=0;j