技术背景
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" "); 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"> "); 67 bw.write("<Document> "); 68 bw.write(" <name>Placemark.kml</name> "); 69 bw.write(" <Style id="sh_placemark_circle_highlight"> "); 70 bw.write(" <IconStyle> "); 71 bw.write(" <color>ff0000ff</color> "); 72 bw.write(" <scale>0.8</scale> "); 73 bw.write(" <Icon> "); 74 bw.write(" <href>http://maps.google.com/mapfiles/kml/shapes/open-diamond.png</href> "); 75 bw.write(" </Icon> "); 76 bw.write(" </IconStyle> "); 77 bw.write(" <ListStyle> "); 78 bw.write(" </ListStyle> "); 79 bw.write(" </Style> "); 80 bw.write(" <StyleMap id="msn_placemark_circle"> "); 81 bw.write(" <Pair> "); 82 bw.write(" <key>normal</key> "); 83 bw.write(" <styleUrl>#sn_placemark_circle</styleUrl> "); 84 bw.write(" </Pair> "); 85 bw.write(" <Pair> "); 86 bw.write(" <key>highlight</key> "); 87 bw.write(" <styleUrl>#sh_placemark_circle_highlight</styleUrl> "); 88 bw.write(" </Pair> "); 89 bw.write(" </StyleMap> "); 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(" <Placemark> "); 105 bw.write(" <styleUrl>#msn_placemark_circle</styleUrl> "); 106 bw.write(" <Point> "); 107 String str1=readAppointedLineNumber(sourceFile, i+1); 108 bw.write(" <coordinates>"+str1+",</coordinates> "); 109 bw.write(" </Point> "); 110 bw.write(" </Placemark> "); 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> "); 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> 每一行的数据属性为:精度,纬度,高程。每一个数据之间用英<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】