技術背景
KML,是標記語言(Keyhole Markup Language)的縮寫,最初由Keyhole公司開發,是一種基於XML 語法與格式的、用於描述和保存地理信息(如點、線、圖像、多邊形和模型等)的編碼規范,可以被 Google Earth 和 Google Maps 識別並顯示。
創建這個工具只是因為一次偶然的需求,因為要將一個2G的點雲數據導到Google Earth上進行顯示,手動編輯絕對是不可能的,所以臨時創建了這個小工具,為了方便使用,添加了一些簡易的操作界面,這樣便可以批量寫入經緯度三維坐標數據(含高程)至kml文件內,然后導入Google Earth進行顯示。關於kml文件的內部格式這里就不再詳細描述了,具體內容請查看參考博客1【KML地圖文件解析】。下面具體講述一下程序設計原理。
實現原理
本程序核心原理是很簡單的,首先將.csv(或.txt)文件(內部格式為B,L,H 一行一個點坐標)讀取以后,創建kml文件的頭,然后根據kml文件的固定格式,將數據寫入,最后文件寫入完成以后,添加上文件的尾部格式。文件選擇采用的是JFileChooser,具體使用方法請查看參考博客2【swing中JFileChooser的用法】。
最后是界面的簡要制作,添加兩個控件,一個是用來選擇文件的,另一個為程序退出按鈕。不再具體闡述了,原理比較簡單,直接上代碼吧。
具體實現
項目結構
核心部分
1 package main; 2 3 import java.io.BufferedWriter; 4 import java.io.File; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 import java.io.LineNumberReader; 11 import java.io.PrintStream; 12 import javax.swing.JFileChooser; 13 import javax.swing.JLabel; 14 15 public class BlhToKml { 16 17 /** 18 * @param args 19 */ 20 public static String InputFilePath; 21 public static String OutputFilePath; 22 @SuppressWarnings("static-access") 23 public static void main() throws IOException{ 24 JFileChooser jfc=new JFileChooser(); 25 jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES ); 26 jfc.showDialog(new JLabel(), "選擇文件"); 27 File file = jfc.getSelectedFile(); 28 if(file == null){ 29 return; 30 } 31 String FilePath = file.getAbsolutePath(); 32 InputFilePath=FilePath; 33 OutputFilePath=InputFilePath; 34 if(OutputFilePath.indexOf(".")>=0) 35 { 36 OutputFilePath = OutputFilePath.substring(0, OutputFilePath.lastIndexOf(".")); 37 }else{ 38 return; 39 } 40 OutputFilePath=OutputFilePath+".kml"; 41 //System.out.println(OutputFilePath); 42 WriteHeadInformationToFile1(); 43 WriteHeadInformationToFile2(); 44 ReplacePointInformation(); 45 WriteEndInformationToFile(); 46 TipFrame TF = new TipFrame(); 47 TF.tishifu("已經轉換完成!"); 48 } 49 //將標題寫入到指定路徑下的文本內 50 public static void WriteHeadInformationToFile1() { 51 String filePath = OutputFilePath; 52 try { 53 File file = new File(filePath); 54 PrintStream ps = new PrintStream(new FileOutputStream(file)); 55 ps.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 56 ps.append("<kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\" xmlns:kml=\"http://www.opengis.net/kml/2.2\"\r\n"); 57 } catch (FileNotFoundException e){ 58 e.printStackTrace(); 59 } 60 } 61 public static void WriteHeadInformationToFile2(){ 62 String filePath = OutputFilePath; 63 try { 64 FileWriter fw = new FileWriter(filePath, true); 65 BufferedWriter bw = new BufferedWriter(fw); 66 bw.write("xmlns:atom=\"http://www.w3.org/2005/Atom\">\r\n"); 67 bw.write("<Document>\r\n"); 68 bw.write("\t<name>Placemark.kml</name>\r\n"); 69 bw.write("\t<Style id=\"sh_placemark_circle_highlight\">\r\n"); 70 bw.write("\t\t<IconStyle>\r\n"); 71 bw.write("\t\t\t<color>ff0000ff</color>\r\n"); 72 bw.write("\t\t\t<scale>0.8</scale>\r\n"); 73 bw.write("\t\t\t<Icon>\r\n"); 74 bw.write("\t\t\t\t<href>http://maps.google.com/mapfiles/kml/shapes/open-diamond.png</href>\r\n"); 75 bw.write("\t\t\t</Icon>\r\n"); 76 bw.write("\t\t</IconStyle>\r\n"); 77 bw.write("\t\t<ListStyle>\r\n"); 78 bw.write("\t\t</ListStyle>\r\n"); 79 bw.write("\t</Style>\r\n"); 80 bw.write("\t<StyleMap id=\"msn_placemark_circle\">\r\n"); 81 bw.write("\t\t<Pair>\r\n"); 82 bw.write("\t\t\t<key>normal</key>\r\n"); 83 bw.write("\t\t\t<styleUrl>#sn_placemark_circle</styleUrl>\r\n"); 84 bw.write("\t\t</Pair>\r\n"); 85 bw.write("\t\t<Pair>\r\n"); 86 bw.write("\t\t\t<key>highlight</key>\r\n"); 87 bw.write("\t\t\t<styleUrl>#sh_placemark_circle_highlight</styleUrl>\r\n"); 88 bw.write("\t\t</Pair>\r\n"); 89 bw.write("\t</StyleMap>\r\n"); 90 bw.close(); 91 fw.close(); 92 } catch (Exception e) { 93 e.printStackTrace(); 94 } 95 } 96 public static void ReplacePointInformation() throws IOException { 97 String filePath = OutputFilePath; 98 File sourceFile = new File(InputFilePath); 99 int lineNum = getTotalLines(sourceFile); 100 try { 101 FileWriter fw = new FileWriter(filePath, true); 102 BufferedWriter bw = new BufferedWriter(fw); 103 for(int i = 0;i<lineNum;i++){ 104 bw.write("\t<Placemark>\r\n"); 105 bw.write("\t\t<styleUrl>#msn_placemark_circle</styleUrl>\r\n"); 106 bw.write("\t\t<Point>\r\n"); 107 String str1=readAppointedLineNumber(sourceFile, i+1); 108 bw.write("\t\t\t<coordinates>"+str1+",</coordinates>\r\n"); 109 bw.write("\t\t</Point>\r\n"); 110 bw.write("\t</Placemark>\r\n"); 111 } 112 bw.close(); 113 fw.close(); 114 } catch (Exception e) { 115 e.printStackTrace(); 116 } 117 } 118 public static void WriteEndInformationToFile() { 119 String filePath = OutputFilePath; 120 try { 121 FileWriter fw = new FileWriter(filePath, true); 122 BufferedWriter bw = new BufferedWriter(fw); 123 bw.write("</Document>\r\n"); 124 bw.write("</kml>"); 125 bw.close(); 126 fw.close(); 127 } catch (Exception e) { 128 e.printStackTrace(); 129 } 130 } 131 // 讀取文件指定行。 132 public static String readAppointedLineNumber(File sourceFile, int lineNumber) 133 throws IOException { 134 FileReader in = new FileReader(sourceFile); 135 LineNumberReader reader = new LineNumberReader(in); 136 String s = ""; 137 if (lineNumber <= 0 || lineNumber > getTotalLines(sourceFile)) { 138 System.out.println("不在文件的行數范圍(1至總行數)之內。"); 139 System.exit(0); 140 } 141 int lines = 0; 142 while (s != null) { 143 lines++; 144 s = reader.readLine(); 145 if((lines - lineNumber) == 0) { 146 //System.out.println(s); 147 return s; 148 } 149 } 150 reader.close(); 151 in.close(); 152 return s; 153 } 154 // 文件內容的總行數。 155 public static int getTotalLines(File file) throws IOException { 156 FileReader in = new FileReader(file); 157 LineNumberReader reader = new LineNumberReader(in); 158 String s = reader.readLine(); 159 int lines = 0; 160 while (s != null) { 161 lines++; 162 s = reader.readLine(); 163 if(lines>=2){ 164 if(s!=null){ 165 //System.out.println(s+"$"); 166 } 167 } 168 } 169 reader.close(); 170 in.close(); 171 //System.out.println(lines); 172 return lines; 173 } 174 }
窗體部分1-主界面
1 package main; 2 3 import java.awt.Color; 4 import java.awt.FlowLayout; 5 import java.awt.Font; 6 import java.awt.Frame; 7 import java.awt.Label; 8 import java.awt.event.ActionEvent; 9 import java.awt.event.ActionListener; 10 import java.awt.event.WindowAdapter; 11 import java.awt.event.WindowEvent; 12 import java.io.IOException; 13 import javax.swing.JButton; 14 import javax.swing.JLabel; 15 import javax.swing.JPanel; 16 17 public class Frame1 { 18 19 /** 20 * @param args 21 */ 22 public static void main(String[] args) { 23 // TODO Auto-generated method stub 24 major_Frame(); 25 } 26 public static void major_Frame(){ 27 final Frame major = new Frame("KML轉換器"); 28 major.setLayout(new FlowLayout(FlowLayout.CENTER)); 29 major.setBounds(400, 300, 430, 250); 30 major.setBackground(new Color(255,255,255)); 31 JPanel jPanel = new JPanel();//創建jPanel 32 jPanel.setBackground(new Color(255,255,255) ); 33 jPanel.add(new JLabel("<html><br>\t每一行的數據屬性為:精度,緯度,高程。每一個數據之間用英<br>" + 34 "文逗號隔開。<br><br><br></html>"));//為jPanel添加JLabel 35 Label label1 = new Label("格式提示"); 36 label1.setFont(new Font("",1,18));//字體大小 37 label1.setBounds(100,200,400,200); 38 major.add(label1); 39 major.add(jPanel); 40 JButton btn1 = new JButton("輸入"); 41 btn1.addActionListener(new ActionListener(){ 42 @SuppressWarnings("unused") 43 public void actionPerformed(ActionEvent arg0) { 44 // TODO Auto-generated method stub 45 BlhToKml blhtokml = new BlhToKml(); 46 try { 47 BlhToKml.main(); 48 } catch (IOException e) { 49 // TODO Auto-generated catch block 50 e.printStackTrace(); 51 } 52 } 53 54 }); 55 JButton btn2 = new JButton("退出"); 56 btn2.addActionListener(new ActionListener(){ 57 public void actionPerformed(ActionEvent e) { 58 major.setVisible(false); 59 System.exit(0); 60 } 61 }); 62 major.add(btn1); 63 major.add(btn2); 64 major.addWindowListener(new WindowAdapter(){ 65 public void windowClosing(WindowEvent e){ 66 //System.exit(0); 67 major.setVisible(false); 68 major.dispose(); 69 } 70 }); 71 major.setVisible(true); 72 } 73 74 }
窗體部分2-提示窗
1 package main; 2 3 import java.awt.FlowLayout; 4 import java.awt.Font; 5 import java.awt.Frame; 6 import java.awt.GridLayout; 7 import java.awt.Label; 8 import java.awt.event.MouseEvent; 9 import java.awt.event.MouseListener; 10 import java.awt.event.WindowAdapter; 11 import java.awt.event.WindowEvent; 12 import javax.swing.JButton; 13 import javax.swing.JPanel; 14 15 public class TipFrame { 16 public static void tishifu(String str){ 17 final Frame f1=new Frame("提示"); 18 f1.setBounds(500, 300, 300, 160); 19 f1.setResizable(false); 20 f1.setLayout(new GridLayout(2,1)); 21 JPanel jpanel1 = new JPanel(new FlowLayout(FlowLayout.CENTER)); 22 Label label1 = new Label(str); 23 label1.setFont(new Font("",1,15)); 24 jpanel1.add(label1); 25 f1.add(jpanel1); 26 JPanel jpanel2 = new JPanel(new FlowLayout(FlowLayout.CENTER)); 27 JButton Yes =new JButton("確定"); 28 jpanel2.add(Yes); 29 f1.add(jpanel2); 30 f1.addWindowListener(new WindowAdapter() { 31 public void windowClosing(WindowEvent e) { 32 //System.exit(0); 33 f1.setVisible(false); 34 f1.dispose(); 35 } 36 }); 37 Yes.addMouseListener(new MouseListener(){ 38 @Override 39 public void mouseClicked(MouseEvent arg0) { 40 // TODO 自動生成的方法存根 41 } 42 @Override 43 public void mouseEntered(MouseEvent arg0) { 44 // TODO 自動生成的方法存根 45 } 46 @Override 47 public void mouseExited(MouseEvent arg0) { 48 // TODO 自動生成的方法存根 49 } 50 public void mousePressed(MouseEvent arg0) { 51 // TODO 自動生成的方法存根 52 f1.setVisible(false); 53 f1.dispose(); 54 } 55 @Override 56 public void mouseReleased(MouseEvent arg0) { 57 // TODO 自動生成的方法存根 58 } 59 }); 60 f1.setVisible(true); 61 } 62 }
運行效果
主窗體
選擇窗體
提示窗
運行結果
測試數據(部分)
成果數據(部分)
接下來導入到Google Earth就可以了
至此,小工具設計完成了!
源碼我已經上傳至博客園了,下載地址:https://files.cnblogs.com/files/thyou/BLH_to_KML.zip,希望能對使用kml文件這方面的同學有一絲幫助。
致謝
感謝以下參考博客博主對於相關技術的分享交流,衷心感謝!
參考博客
1、KML地圖文件解析【https://blog.csdn.net/onlymydreams_mfc/article/details/81840232】
2、swing中JFileChooser的用法【https://www.cnblogs.com/happyPawpaw/archive/2013/04/27/3046414.html】