一.在进行根据地图坐标经纬度返回相应的地理位置之前我们应该了解下地图坐标系:
1、大地坐标系统 WGS-84
用来表述地球上点的位置的一种地区坐标系统。它采用一个十分近似于地球自然形状的参考椭球作为描述和推算地面点位置和相互关系的基准面。一个大地坐标系统必须明确定义其三个坐标轴的方向和其中心的位置。通常人们用旋转椭球的短轴与某一规定的起始子午面分别平行干地球某时刻的平均自转轴和相应的真起始子午面来确定坐标轴的方向。若使参考椭球中心与地球平均质心重合,则定义和建立了地心大地坐标系。它是航天与远程武器和空间科学中各种定位测控测轨的依据。若椭球表面与一个或几个国家的局部大地水准面吻合最好,则建立了一个国家或区域的局部大地坐标系。大地坐标系中点的位置是以其大地坐标表示的,大地坐标均以椭球面的法线来定义。其中,过某点的椭球面法线与椭球赤道面的交角为大地纬度;包含该法线和大地子午面与起始大地子午面的二面角为该点的大地经度;沿法线至椭球面的距离为该点的大地高。大地纬度、大地经度和大地高分别用大写英文字母B、L、H表示。
国内的互联网公司,都不会使用GPS坐标,因为这不符合国家政策。所以大家都会使用GCJ-02坐标系。
2、火星坐标系统 GCJ-02
国家保密插件,也叫做加密插件或者加偏或者SM模组,其实就是对真实坐标系统进行人为的加偏处理,按照几行代码的算法,将真实的坐标加密成虚假的坐标,而这个加偏并不是线性的加偏,所以各地的偏移情况都会有所不同。而加密后的坐标也常被人称为火星坐标系统。
目前使用火星坐标系的地图商:
- 腾讯搜搜地图
- 搜狐搜狗地图
- 阿里云地图
- 高德MapABC地图
- 灵图51ditu地图
所有的电子地图所有的导航设备,都需要加入国家保密插件。第一步,地图公司测绘地图,测绘完成后,送 到国家测绘局,将真实坐标的电子地图,加密成“火星坐标”,这样的地图才是可以出版和发布的,然后才可以让GPS公司处理。第二步,所有的GPS公司,只要需要汽车导航的,需要用到导航电子地图的,统统需要在软件中加入国家保密算法,将COM口读出来的真实的坐标信号,加密转换成国家要求的保密的坐标,这样,GPS导航仪和导航电子地图就可以完全匹配,GPS也就可以正常工作。
国内互联网公司,都必须使用GCJ-02坐标系。
3、百度坐标系统 BD-09
百度坐标对火星坐标系进行了一次加密,形成了百度坐标系.
目前使用百度坐标系的地图商:
- 百度Baidu地图
少部分公司会在GCJ-02的基础上再进行一次加密,但这样的坐标不通用,只适于特定公司的地图。
4、图吧
图吧是百度坐标乘以10000的结果。
目前使用图吧坐标系的地图商:
- 图吧MapBar地图
少部分公司会在GCJ-02的基础上再进行一次加密,但这样的坐标不通用,只适于特定公司的地图。
二.地图坐标转换工具类,根据项目中的实际情况将坐标进行相应转换,因为各个地图使用的坐标系不同,必须要使用对应的坐标系经纬度才能返回正确的地理位置.
坐标系转换工具类:
PositionUtil.java
package com.ydtx.utils.gps; import java.util.HashMap; import java.util.Map; import com.ydtx.moudle.controller.carMonitor.PointDouble; import com.ydtx.utils.BaiduMapUtil; /** * 各地图API坐标系统比较与转换; * WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系, * 谷歌地图采用的是WGS84地理坐标系(中国范围除外); * GCJ02坐标系:即火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。 * 谷歌中国地图和搜搜中国地图采用的是GCJ02地理坐标系; BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系; * 搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。 chenhua */ public class PositionUtil { public static final String BAIDU_LBS_TYPE = "bd09ll"; public static double pi = 3.1415926535897932384626; public static double a = 6378245.0; public static double ee = 0.00669342162296594323; /** * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System * * @param lat * @param lon * @return */ public static Gps gps84_To_Gcj02(double lat, double lon) { if (outOfChina(lat, lon)) { return null; } double dLat = transformLat(lon - 105.0, lat - 35.0); double dLon = transformLon(lon - 105.0, lat - 35.0); double radLat = lat / 180.0 * pi; double magic = Math.sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); double mgLat = lat + dLat; double mgLon = lon + dLon; return new Gps(mgLat, mgLon); } /** * * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return * */ public static Gps gcj_To_Gps84(double lat, double lon) { Gps gps = transform(lat, lon); double lontitude = lon * 2 - gps.getWgLon(); double latitude = lat * 2 - gps.getWgLat(); return new Gps(latitude, lontitude); } /** * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标 * * @param gg_lat * @param gg_lon */ public static Gps gcj02_To_Bd09(double gg_lat, double gg_lon) { double x = gg_lon, y = gg_lat; double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * pi); double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * pi); double bd_lon = z * Math.cos(theta) + 0.0065; double bd_lat = z * Math.sin(theta) + 0.006; return new Gps(bd_lat, bd_lon); } /** * * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标 * * @param * bd_lat * @param bd_lon * @return */ public static Gps bd09_To_Gcj02(double bd_lat, double bd_lon) { double x = bd_lon - 0.0065, y = bd_lat - 0.006; double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * pi); double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * pi); double gg_lon = z * Math.cos(theta); double gg_lat = z * Math.sin(theta); return new Gps(gg_lat, gg_lon); } /** * (BD-09)-->84 * @param bd_lat * @param bd_lon * @return */ public static Gps bd09_To_Gps84(double bd_lat, double bd_lon) { Gps gcj02 = PositionUtil.bd09_To_Gcj02(bd_lat, bd_lon); Gps map84 = PositionUtil.gcj_To_Gps84(gcj02.getWgLat(), gcj02.getWgLon()); return map84; } public static boolean outOfChina(double lat, double lon) { if (lon < 72.004 || lon > 137.8347) return true; if (lat < 0.8293 || lat > 55.8271) return true; return false; } public static Gps transform(double lat, double lon) { if (outOfChina(lat, lon)) { return new Gps(lat, lon); } double dLat = transformLat(lon - 105.0, lat - 35.0); double dLon = transformLon(lon - 105.0, lat - 35.0); double radLat = lat / 180.0 * pi; double magic = Math.sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); double mgLat = lat + dLat; double mgLon = lon + dLon; return new Gps(mgLat, mgLon); } public static double transformLat(double x, double y) { double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0; return ret; } public static double transformLon(double x, double y) { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0; return ret; } public static Map GPSToBaiduMap(String log, String lat){ Map map = new HashMap(); if(log!=null&&log!=""&&lat!=null&&lat!=""){ Gps gps=PositionUtil.gps84_To_Gcj02(Double.parseDouble(lat), Double.parseDouble(log)); lat=String.valueOf(gps.getWgLat()); log=String.valueOf(gps.getWgLon()); } map.put("lng", log); map.put("lat", lat); return map; } public static void main(String[] args) { // 北斗芯片获取的经纬度为WGS84地理坐标 31.426896,119.496145 Gps gps = new Gps(31.426896, 119.496145); System.out.println("gps :" + gps); Gps gcj = gps84_To_Gcj02(gps.getWgLat(), gps.getWgLon()); System.out.println("gcj :" + gcj); Gps star = gcj_To_Gps84(gcj.getWgLat(), gcj.getWgLon()); System.out.println("star:" + star); Gps bd = gcj02_To_Bd09(gcj.getWgLat(), gcj.getWgLon()); System.out.println("bd :" + bd); Gps gcj2 = bd09_To_Gcj02(bd.getWgLat(), bd.getWgLon()); System.out.println("gcj :" + gcj2); } }
根据gprs坐标经纬度返回对应的地址:
我们项目中采用的是阿里云地图进行地址返回,使用的源数据是大地坐标系统 WGS-84需要转换成火星坐标系GCJ-02,转换之后就可以通过阿里云地图接口进行地址返回
MapAddressForGprsUtil.java
package com.ydtx.utils; import java.net.URL; import java.util.Map; import com.alibaba.fastjson.JSONArray; import com.ydtx.utils.gps.PositionUtil; import net.sf.json.JSONObject; public class MapAddressForGprsUtil { public static void main(String[] args) { String add = getAdd("22.5810633333333","113.848505"); System.out.println(add); } /** * @author lizhiyuan * 定义通过经纬度返回地理位置信息 * @param lat * @param log * @return */ public static String getAdd( String lat,String log){ //lat 小 log 大 //参数解释: 经度,纬度, type 001 (100代表道路,010代表POI,001代表门址,111可以同时显示前三项) String urlString =null; String res = ""; try { //平台和终端用的百度地图需要将经纬度转换成百度地图的经纬度 Map map=PositionUtil.GPSToBaiduMap(log, lat); if(map!=null){ urlString = "http://gc.ditu.aliyun.com/regeocoding?l="+map.get("lat")+","+map.get("lng")+"&type=111"; map=null; URL url = new URL(urlString); java.net.HttpURLConnection conn = (java.net.HttpURLConnection)url.openConnection(); conn.setDoOutput(true); conn.setRequestMethod("POST"); java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(),"UTF-8")); String line; while ((line = in.readLine()) != null) { res += line+"\n"; } in.close(); } } catch (Exception e) { //System.out.println("error in wapaction,and e is " + e.getMessage()); } JSONObject jsonObject = JSONObject.fromObject(res); JSONArray jsonArray = JSONArray.parseArray(jsonObject.getString("addrList")); JSONObject j_2 = JSONObject.fromObject(jsonArray.get(0)); String area = j_2.getString("admName"); String address=null; if(area!=null&&!area.equals("")){ String arrs[] = area.split(","); String arr= j_2.getString("name"); if(arrs.length==3){ address=arrs[0]+arrs[1]+arrs[2]+arr; }else{ address=arrs[0]+arrs[1]+arr; } JSONObject j_3 = JSONObject.fromObject(jsonArray.get(1)); String name = j_3.getString("name"); if(name!=null&&!name.equals("")){ String addr= j_3.getString("addr"); address+=name+addr; } }else{ JSONObject j_3 = JSONObject.fromObject(jsonArray.get(1)); String name = j_3.getString("admName"); if(name!=null&&!name.equals("")){ String arrs[] = name.split(","); String name1= j_3.getString("name"); String arr= j_3.getString("addr"); address=arrs[0]+arrs[1]+arrs[2]+arr+name1; } } return address; } }
测试结果为:
log4j:WARN No such property [maxFileSize] in org.apache.log4j.DailyRollingFileAppender. 广东省深圳市宝安区宝源路西乡体育中心银田路18
最后总结吧:
1.一定要了解自己源数据是什么坐标
2.因为百度地图需要申请ak而且返回接口次数有限制,参考了网上的一些博客,采用的阿里云地图接口
但是在项目中发现部分地区:比如新疆 东北 等等一些偏远地区可能阿里云地图没有采集数据所以无法返回
地理位置信息