平台:android web服务器
需求:
1:持续定位服务
2:支持多家地图,可扩展
API :
1:高德定位sdk : http://lbs.amap.com/api/android-location-sdk/locationsummary/
2:高德poi检索服务:http://lbs.amap.com/api/webservice/reference/search/
3:百度poi检索服务:http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi
URI :
1:高德URI : http://lbs.amap.com/api/uri-api/web-uri-explain/
2:百度URI : http://lbsyun.baidu.com/index.php?title=uri
实现方案:
实现上面的需求最大的一个问题是支持多家地图厂商(APP),目前国内现在几家地图厂商真是一片混乱,简单分一下:
1:开放平台系:高德 百度 腾讯
2:授权收费系:图吧 凯立德 美行
3:找不到定位系:搜狗地图
上面基本罗列了国内的各家地图,等下挨个来揭他们的老底,说这个之前先普及两个姿势,一个是地理数据,一个是坐标系。
数据:生在大天朝,很多事情你永远都是不能理解,所以,在我们大天朝不是所有的地图厂商都能采集地图数据的,所以我们来看看各家地图厂商都是怎么弄来的数据。因为数据采集需要一个东西,叫做甲级数据采集资质,所以各大厂家都想法设法的搞来数据。
高德:甲级测绘资质,目前是国内实力最强地图厂家,他把国内多家厂商搞的要死不活,因为他从数据到引擎到产品到开放平台都是自己的,关键是现在抱上了阿里的腿,阿里又在积极布局车联网,未来高德可能是阿里系最挣钱的企业之一。
百度:百度最开始的数据应该是从四维图新买来的,本来百度是没有甲级测绘资质的,也就是说他没有数据,但是奈何百度有钱啊,收购了长地万方,拿到了这个测绘资质,所以现在可以在路面上看到百度的地图采集小车到处跑。百度和高德基本是死磕,你有的我一定要有,你免费我也免费。
腾讯:这位哥们数据是四维图新的,和四维图新绑的比较死,入股思维,也是开放平台系,不过布局比较晚,地图确实做的也不咋滴。
图吧:图吧的数据也是四维图新的,是每年四维图新的付费用户。这位哥们的地图引擎做的不咋滴,产品做的也不咋滴,基本上是靠地图定制还活着,也就是说,各大车机厂商如果比较有钱的,可以定位他们的地图,类似的收授权,可以按照方案商rom需求修改地图App,吐槽一下,他们的App做的也很挫。
凯立德:这个家伙是一开始就有甲级的测绘资质的,但是这位哥们最开始的数据好像有部分是弄的四维的,主要是自己采集吧。他也是卖授权的,并且依靠这种方式他还活的挺好的,好多车机上面都是凯立德的地图,基本上一个授权50快的样子,不过最近高德出了了车机版的地图,估计最头疼的就是凯立德了。
美行:只知道数据是思维的,据说地图引擎做的挺好的,也是靠授权活着。
搜狗:数据是自己采集的,不知道是怎么活着的。
坐标系:
火星坐标系:生活在天朝,你就得按照天朝的方式去理解。这个就是国家测绘局在原始的GPS坐标系的基础上加上了一套经纬度转换算法和写入了一套随机偏差值出来的一个坐标系,简单来说,就是一套加密过后的GPS坐标系,用这些坐标系的地图厂商很多,高德,腾讯都是此类。
BD09坐标系:百度基于火星坐标系又进行加密后的产物,我觉得这个完全是自闭门户的做法。
搜狗坐标系:和BD09坐标系一样,都是属于自闭门户的产物。
图吧坐标系:如上,其实我是最鄙视这个公司的,数据都不是的,还学人搞一套自己的坐标系,在实际和他们地图对接的时候,和他们的人确认过,其实就是火星坐标系。
好了,介绍了这么多,终于差不多把目前国内几家地图厂商给分析差不多了,终于能好好说方案和实现了。
定位实现:
定位我是用的高德的定位SDK,目前使用的是2.3版本的定位sdk,说一个实际遇到的问题,2.3之前的定位sdk是有bug的,在没有网的情况下,设定周期定位,没有回调。因为个人需求不一样,高德的开放平台上面也有代码实例。本着自己动手丰衣足食的原则,这里就不把源码写出来了。读到这里,大家是不是会有一个疑问,使用高德的定位SDK,出来的定位数据,是不是就应该是GCJ02的坐标系了,那如果是需要BD09的坐标系呢,原始坐标系呢?下面我把我在项目中使用经纬度转换源码贴出来,大家可以直接拿去使用,实际测试下来,BD09和GCJ02坐标系互转误差可以控制在50米以内。
public class TransformPositionUtil { private static final String TAG = "AIMap-TransformPositionUtil"; public static PoiBean b=new PoiBean(); public static final double pi = 3.1415926535897932384626; public static final double a = 6378245.0; public static final double ee = 0.00669342162296594323; /** * to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System * * @return */ public static PoiBean gps84_To_Gcj02(PoiBean lb) { if (outOfChina(lb)) { return null; } double dLat = transformLat(lb.getLongitude() - 105.0, lb.getLatitude() - 35.0); double dLon = transformLon(lb.getLongitude() - 105.0, lb.getLatitude() - 35.0); double radLat = lb.getLatitude() / 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 = lb.getLatitude() + dLat; double mgLon = lb.getLongitude() + dLon; b.setLatitude(mgLat); b.setLongitude(mgLon); return b; } /** * 火星坐标系 (GCJ-02) To 百度坐标系 (BD-09) 的转换算法 * 将 GCJ-02 坐标转换成 BD-09 坐标 */ public static PoiBean gcj02_To_Bd09(PoiBean lb) { double x = lb.getLongitude(), y = lb.getLatitude(); double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * (pi * 3000.0 / 180.0)); double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * (pi * 3000.0 / 180.0)); double bd_lon = z * Math.cos(theta) + 0.0065; double bd_lat = z * Math.sin(theta) + 0.006; b.setLatitude(bd_lat); b.setLongitude(bd_lon); return b; } /** * 火星坐标系 (GCJ-02) To 百度坐标系 (BD-09) 的转换算法 * 将 GCJ-02 坐标转换成 BD-09 坐标 */ public static PoiBean gcj02_To_Bd09(double lat, double lng) { double x = lng, y = lat; double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * (pi * 3000.0 / 180.0)); double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * (pi * 3000.0 / 180.0)); double bd_lon = z * Math.cos(theta) + 0.0065; double bd_lat = z * Math.sin(theta) + 0.006; b.setLatitude(bd_lat); b.setLongitude(bd_lon); return b; } /** * 火星坐标系 (GCJ-02) To 百度坐标系 (BD-09) 的转换算法 * 将 BD-09 坐标转换成GCJ-02 坐标 * * @return */ public static PoiBean bd09_To_Gcj02(PoiBean lb) { double x = lb.getLongitude() - 0.0065, y = lb.getLatitude() - 0.006; double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * (pi * 3000.0 / 180.0)); double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * (pi * 3000.0 / 180.0)); double gg_lon = z * Math.cos(theta); double gg_lat = z * Math.sin(theta); b.setLatitude(gg_lat); b.setLongitude(gg_lon); return b; } /** * 火星坐标系 (GCJ-02) To 百度坐标系 (BD-09) 的转换算法 * 将 BD-09 坐标转换成GCJ-02 坐标 * * @return */ public static PoiBean bd09_To_Gcj02(double lat, double lng) { double x = lng - 0.0065, y = lat - 0.006; double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * (pi * 3000.0 / 180.0)); double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * (pi * 3000.0 / 180.0)); double gg_lon = z * Math.cos(theta); double gg_lat = z * Math.sin(theta); b.setLatitude(gg_lat); b.setLongitude(gg_lon); return b; } public static boolean outOfChina(PoiBean lb) { if (lb.getLongitude() < 72.004 || lb.getLongitude() > 137.8347) return true; if (lb.getLatitude() < 0.8293 || lb.getLatitude() > 55.8271) return true; return false; } public static PoiBean transform(PoiBean lb) { if (outOfChina(lb)) { b.setLatitude(lb.getLatitude()); b.setLongitude(lb.getLongitude()); return b; } double dLat = transformLat(lb.getLongitude() - 105.0, lb.getLatitude() - 35.0); double dLon = transformLon(lb.getLongitude() - 105.0, lb.getLatitude() - 35.0); double radLat = lb.getLatitude() / 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 = lb.getLatitude() + dLat; double mgLon = lb.getLongitude() + dLon; b.setLatitude(mgLat); b.setLongitude(mgLon); return b; } 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; } }
poi检索实现:
有了上面的内容铺垫,其实这部分已经没有多少值得说的信息了,这部分的实现,我们是用的web url服务来做的,公司自己搭建的web服务器,做了一层转发。在文章的最开始位置,各家的开放平台上路径都很清楚。基本上各家都需要有一个企业级用户认证的过程,服务端只要做一边请求转发就行。说一个比较有意思的地方,就能体现出来百度和高德的区别,如上,每一家在检索poi的时候都需要传入一个城市名,和检索关键字。百度和高德区别就是,key = “天安门”,city = "深圳市" 百度的结果:深圳天安门诊部 高德的结果:北京天安门 所以,百度的检索要检索 key = "天安门" city = "" 才能检索出来北京天安门,所以我个人觉得高德还是要比百度要强的。
URI导航:
各家都差不多,目前已知高德地图,高德地图车机版,百度地图,百度导航都是支持URI这种方式的,腾讯地图的官网还写了个即将支持。。。其余其他家企图,诸如凯立德,图吧,美行等都是需要定制,但是也都基本有各自的标准协议。