1.什么是Geocoding?
Geocoding API 是一类简单的HTTP接口,用于提供从地址到经纬度坐标或者从经纬度坐标到地址的转换服务,用户可以使用C# 、C++、Java等开发语言发送HTTP请求且接收JSON、XML的返回数据。
2.功能介绍
Geocoding API包括地址解析和逆地址解析功能。
- 地理编码:即地址解析,由详细到街道的结构化地址得到百度经纬度信息,且支持名胜古迹、标志性建筑名称直接解析返回百度经纬度。例如:“北京市海淀区中关村南大街27号”地址解析的结果是“lng:116.31985,lat:39.959836”,“百度大厦”地址解析的结果是“lng:116.30815,lat:40.056885”
- 逆地理编码,即逆地址解析,由百度经纬度信息得到结构化地址信息。例如:“lat:31.325152,lng:120.558957”逆地址解析的结果是“江苏省苏州市虎丘区塔园路318号”。
3.如何使用
百度地图Geocoding API是一套免费对外开放的API,无使用次数限制。使用方法:
第一步:申请ak(即获取密钥),若无百度账号则首先需要注册百度账号。
第二步,拼写发送http请求的url,注意需使用第一步申请的ak。
第三步,接收http请求返回的数据(支持json和xml格式)。
以下是一个关于地理编码的简单示例。发送一个地址是“百度大厦”的请求,返回该地址对应的地理坐标。发送请求的url如下:
http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=E4805d16520de693a3fe707cdc962045&callback=showLocation
4.java调用测试
1 package addressToGeoTest; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileOutputStream; 8 import java.io.IOException; 9 import java.io.InputStreamReader; 10 import java.io.OutputStreamWriter; 11 import java.net.URL; 12 import java.net.URLEncoder; 13 import java.util.*; 14 15 public class GetLatAndLngByBaidu { 16 17 public static void main(String args[]){ 18 19 String cnAddress="北京市海淀区上地十街10号"; 20 Map<String,String> map = GetLatAndLngByBaidu.getLatitude(cnAddress); 21 if(null != map){ 22 System.out.println(cnAddress+" 经度:"+map.get("lng")+" 纬度:"+map.get("lat")); 23 } 24 } 25 /** 26 * 返回输入地址的经纬度坐标 27 * lng(经度),lat(纬度) 28 */ 29 //1,申请ak(即获取密钥),若无百度账号则首先需要注册百度账号。 30 public static final String AK= "VGqyTtpnqfNxkTkPBG5APrGO"; 31 public static Map<String,String> getLatitude(String address){ 32 try { 33 address = URLEncoder.encode(address, "UTF-8"); //将地址转换成utf-8的16进制 34 //2, 拼写发送http请求的url,注意需使用第一步申请的ak。 35 //3, 接收http请求返回的数据(支持json和xml格式)本次采用json形式 36 URL resjson = new URL("http://api.map.baidu.com/geocoder/v2/?address=" 37 + address +"&output=json&ak="+ AK); 38 BufferedReader in = new BufferedReader( 39 new InputStreamReader(resjson.openStream())); 40 String res; 41 StringBuilder sb = new StringBuilder(""); 42 while((res = in.readLine())!=null){ 43 sb.append(res.trim()); 44 } 45 in.close(); 46 String str = sb.toString(); 47 //System.out.println("return json:"+str); 48 Map<String,String> map = null; 49 if(str!=null){ 50 int lngStart = str.indexOf("lng":"); 51 int lngEnd = str.indexOf(","lat"); 52 int latEnd = str.indexOf("},"precise"); 53 if(lngStart > 0 && lngEnd > 0 && latEnd > 0){ 54 String lng = str.substring(lngStart+5, lngEnd); 55 String lat = str.substring(lngEnd+7, latEnd); 56 map = new HashMap<String,String>(); 57 map.put("lng", lng); 58 map.put("lat", lat); 59 return map; 60 } 61 } 62 }catch (Exception e) { 63 e.printStackTrace(); 64 } 65 return null; 66 } 67 }
运行结果:
5.简单应用
期望实现功能:
input.txt存放中文地址
从input.txt中读取中文地址,获得经纬度
将中文地址以及经纬度输出到output.txt中
package addressToGeo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLEncoder; import java.util.*; public class GetLatAndLngByBaidu { public static void main(String args[]){ long startTime=System.currentTimeMillis(); String fileSourcePath="F://Essex//AddressToGeo//source//input.txt"; String fileDestPath="F://Essex//AddressToGeo//source//output.txt"; readAddressWriteGeo(fileSourcePath,fileDestPath); } /** * 返回输入地址的经纬度坐标 * key lng(经度),lat(纬度) */ //1,申请ak(即获取密钥),若无百度账号则首先需要注册百度账号。 public static final String AK= "VGqyTtpnqfNxkTkPBG5APrGO"; public static Map<String,String> getLatitude(String address){ try { address = URLEncoder.encode(address, "UTF-8"); //将地址转换成utf-8的16进制 //2, 拼写发送http请求的url,注意需使用第一步申请的ak。 //3, 接收http请求返回的数据(支持json和xml格式)本次采用json形式 URL resjson = new URL("http://api.map.baidu.com/geocoder/v2/?address=" + address +"&output=json&ak="+ AK); BufferedReader in = new BufferedReader( new InputStreamReader(resjson.openStream())); String res; StringBuilder sb = new StringBuilder(""); while((res = in.readLine())!=null){ sb.append(res.trim()); } in.close(); String str = sb.toString(); //System.out.println("return json:"+str); Map<String,String> map = null; if(str!=null){ int lngStart = str.indexOf("lng":"); int lngEnd = str.indexOf(","lat"); int latEnd = str.indexOf("},"precise"); if(lngStart > 0 && lngEnd > 0 && latEnd > 0){ String lng = str.substring(lngStart+5, lngEnd); String lat = str.substring(lngEnd+7, latEnd); map = new HashMap<String,String>(); map.put("lng", lng); map.put("lat", lat); return map; } } }catch (Exception e) { e.printStackTrace(); } return null; } /* *void readAddressWriteGeo(String sourceFilePath,String destFilePath) 函数 *功能:从sourceFilePath下读取中文地址,获取经纬度,存放在destFilePath中 * */ public static void readAddressWriteGeo(String sourceFilePath,String destFilePath) { long startTime=System.currentTimeMillis(); try { System.out.println("读取中文地址..."); String encoding="Unicode"; File fileSource=new File(sourceFilePath);//获得输入句柄 FileInputStream fis=new FileInputStream(fileSource);//创建字节流输入对象 InputStreamReader inStream=new InputStreamReader(fis,encoding);//将字节流转换为字符流,编码格式为Unicode,创建字节流输入对象 BufferedReader input=new BufferedReader(inStream); String outputFilePath=createFile(destFilePath); File fileDest=new File(outputFilePath);//获得输出文件句柄 FileOutputStream fos=new FileOutputStream(destFilePath);//创建字节流输出对象 OutputStreamWriter outStream=new OutputStreamWriter(fos,encoding);//将字节流转换为字符流,编码格式为Unicode,创建字节流输出对象 BufferedWriter output=new BufferedWriter(outStream); String cnAddress=null; cnAddress=input.readLine(); System.out.println("通过百度api获取经纬度..."); int count=0; while(cnAddress!=null) { //System.out.println(cnAddress); Map<String,String> map = GetLatAndLngByBaidu.getLatitude(cnAddress); if(null != map){ //System.out.println("经度:"+map.get("lng")); //System.out.println("纬度:"+map.get("lat")); output.write(map.get("lng")+" "+map.get("lat")+" "+cnAddress+" "); } cnAddress=input.readLine(); count++; } input.close(); output.close(); System.out.println("获取:"+count+"条经纬度信息"); long endTime=System.currentTimeMillis(); System.out.println("花费时间: "+(endTime-startTime)/1000+"s"); System.out.println("经纬度获取成功!"); } catch(IOException e) { e.printStackTrace(); } } //创建新的output.txt文档 public static String createFile(String filePath) { File f=new File(filePath);//构造一个路径的句柄 if(f.exists())//如果文件存在则删除,不存在则生成 { f.delete(); } else { try { f.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return f.getPath(); } }
运行测试结果:
6.改进
上面的解决方案使用单线程,数据量小的情况下还可以勉强使用。但是当数据量巨大时,耗时就非常的长(测试使用15000条地址数据差不多用了1小时)。考虑到耗费的时间主要花费在获取经纬度时服务器的响应上,因此考虑使用多线程。
从input.txt读入,然后将地址信息切分为多个数组,为每一个数组开一个线程,转换后统一输出到output.txt
实现代码:
package addressToGeo; import java.io.*; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; class MyThread extends Thread{ private String[] GpsAdd; private static String AK = "NXlvvuBhTW1Bd7PaatQvyceV"; private String name = ""; private String[] Addmat = {" "}; private int MatLen=0; public MyThread(String Name, String[] AddMat){ this.name=Name; this.Addmat = AddMat; } public MyThread(){ } public String[] getGpsAdd(){ return GpsAdd; } public void run(){ MatLen = Addmat.length; GpsAdd = new String[MatLen]; for(int i=0;i<MatLen;i++){ Map<String,String> map = getLatitude(Addmat[i]); System.out.println(this.name +": " + map.get("lng") + " " + map.get("lat")+ " " + Addmat[i] ); GpsAdd[i] = map.get("lng") + " " + map.get("lat")+ " " + Addmat[i] +" "; } } public static Map<String,String> getLatitude(String address){ Map<String,String> map = new HashMap<String,String>(); try { address = URLEncoder.encode(address, "UTF-8"); URL resjson = new URL("http://api.map.baidu.com/geocoder/v2/?address=" + address +"&output=json&ak="+ AK); URLConnection uc=resjson.openConnection(); uc.setConnectTimeout(1000*100); uc.setReadTimeout(1000*100); BufferedReader in = new BufferedReader( new InputStreamReader(uc.getInputStream())); String res; StringBuilder sb = new StringBuilder(""); while((res = in.readLine())!=null){ sb.append(res.trim()); } in.close(); String str = sb.toString(); if(str!=null){ int lngStart = str.indexOf("lng":"); int lngEnd = str.indexOf(","lat"); int latEnd = str.indexOf("},"precise"); if(lngStart > 0 && lngEnd > 0 && latEnd > 0){ String lng = str.substring(lngStart+5, lngEnd); String lat = str.substring(lngEnd+7, latEnd); map.put("lng", lng); map.put("lat", lat); } } }catch (Exception e) { e.printStackTrace(); } return map; } public static String createFile(String filePath) { File f = new File(filePath); if(f.exists()) { f.delete(); } else { try { f.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return f.getPath(); } }
package addressToGeo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; import java.util.*; public class GetLatAndLngByBaidu { private static int NumOfThread = 100; static String fileSourcePath="F://Essex//AddressToGeoAll//source//input.txt"; static String fileDestPath="F://Essex//AddressToGeoAll//source//output.txt"; public static void main(String args[]) throws InterruptedException, IOException{ long startTime=System.currentTimeMillis(); threadStart(startTime); } public static void threadStart(long startTime) throws InterruptedException, IOException { System.out.println("start..."); String cnAdd =""; int cnLen = 0; cnAdd = readAdd(fileSourcePath); String[] AddStr = cnAdd.split(","); cnLen = AddStr.length; System.out.println("get "+cnLen+" chinese address successful..."); System.out.println("to get longitude and latitude ..."); int MatLen = cnLen/NumOfThread; String [][] sp=new String[NumOfThread][MatLen]; MyThread [] mt=new MyThread[NumOfThread]; for(int j=0;j<NumOfThread;j++) { sp[j]=new String[MatLen]; for(int i=0;i<MatLen;i++) { sp[j][i]=AddStr[i+j*MatLen]; } mt[j]=new MyThread("线程"+j,sp[j]); mt[j].start(); } for(int j=0;j<NumOfThread;j++) { mt[j].join(); } String[] result; FileWriter letter = new FileWriter(fileDestPath); for(int j=0;j<NumOfThread;j++) { result = mt[j].getGpsAdd(); writeFile(result,letter,MatLen); } //处理结尾由于不能被100整数引起多余的几个数据 if(NumOfThread*MatLen <= cnLen-1){ String[] LastMat = new String[cnLen-NumOfThread*MatLen]; for(int i =0;i<cnLen-NumOfThread*MatLen;i++){ LastMat[i] = AddStr[NumOfThread*MatLen+i]; } MyThread LastThread = new MyThread("线程last",LastMat); LastThread.start(); LastThread.join(); result = LastThread.getGpsAdd(); writeFile(result, letter, cnLen-NumOfThread*MatLen); } letter.close(); getExcuteTime(startTime); } //1,申请ak(即获取密钥),若无百度账号则首先需要注册百度账号。 public static final String AK= "NXlvvuBhTW1Bd7PaatQvyceV"; public static String readAdd(String sourceFilePath){ try{ String encoding="Unicode"; File fileSource=new File(sourceFilePath);//获得输入句柄 FileInputStream fis=new FileInputStream(fileSource);//创建字节流输入对象 InputStreamReader inStream=new InputStreamReader(fis,encoding);//将字节流转换为字符流,编码格式为Unicode,创建字节流输入对象 BufferedReader input=new BufferedReader(inStream); String cnAddress=null; cnAddress=input.readLine(); StringBuilder sb = new StringBuilder(""); while(cnAddress!=null){ sb.append(cnAddress.trim()); sb.append(","); cnAddress=input.readLine(); } String sbStr = sb.toString(); // System.out.println(sb.toString()); //输出所有地址 input.close(); return sbStr; } catch (IOException e){ e.printStackTrace(); } return null; } public static void getExcuteTime(long startTime) { long endTime=System.currentTimeMillis(); long time=endTime-startTime; System.out.println("take time: "+time/1000+" second"); System.out.println("about "+time/1000/60+" minute"); } public static void writeFile(String[] result, FileWriter writer, int MatLen) throws IOException{ for(int i = 0;i<MatLen;i++){ writer.write(result[i]);; } } }
7.不足
1,网速不好的情况下,开100个线程会出现 java.net.SocketTimeoutException: connect timed out错误。
虽然使用了uc.setConnectTimeout(10000); uc.setReadTimeout(10000); 但是没有起到效果。(求改进)
2,所有的数据都会放在内存上,当数据量到达千万级别时候,会内存不足。(求改进)
8.引用
http://developer.baidu.com/map/webservice-geocoding.htm
http://www.cnblogs.com/gzggyy/archive/2013/06/21/3148610.html
http://lavasoft.blog.51cto.com/62575/99150
http://www.360doc.com/content/13/0422/09/3776353_280044198.shtml