利用0.01精度校正库offset.dat文件修正中国地图经纬度偏移。
gps设备获取的经纬度跟实际地图经纬度有偏移,大陆地图是这样的。google地图存在偏移是众所周知的事实,说到底就是火星坐标系和真实gps之间的转换,我国所有的地图为了安全起见都作了加偏。
具体偏移原因见: gps纠偏及大陆地图偏移原因。
总之,gps是从卫星上获取的经纬度,是准确的:GPS坐标转换经纬度及换算方法。实际的地图都在原有的基础上加偏移值,天朝说是为了安全呗。因此,gps获取出来的经纬度直接在地图上展示,那肯定是不准确的,有偏差。当然不管google map,百度地图,搜狗地图等每一个都一样,但是他们的偏移算法肯定不一样,偏移的值也不一样。
google的国内地图坐标都是加过密的,由高德提供。百度的地图数据也是加过密的,估计是四维提供。但是不同批次的数据加密后的坐标是有偏差的。总而言之,相对GPS数据,两种地图都是有偏差的。
经纬度在http://maps.google.com和google earth上可以准确对应,但是在ditu.google.cn和百度地图上得到的总是偏移的
既然有了偏移,就需要使用纠偏来解决gps定位问题,
GPS坐标转百度坐标方法:
http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=’.经度.’&y=’.纬度
google目前没有类似百度这样的,只能通过纠偏数据库进行校验。
纠偏数据库都是要钱的,0.001精度2-5k都有卖,我网上找到一个0.01精度的,是一个 offset.dat 文件, 总共74.9M,数据为9813675条,不敢独享,分享给大家.
google纠偏数据库:
http://file.yanue.net/map/offset.dat.
基于该数据库,我搞个的纠偏接口
免费纠偏接口:
http://map.yanue.net/gpsApi.php
参数:
lat: gps原始纬度,如22.502412986242,请保留小数点3位以上
lng: gps原始经度,如113.93832783228,请保留小数点3位以上
gps纠偏工具:
说正题,有了偏移数据库之后,根据算法就可以完成目的了。
下面贴出纠偏算法
php利用offset.dat修正gps经纬度的算法:
<?php /** * gps经纬度修正 * * 功能说明:利用0.01精度校正库offset.dat文件修正中国地图经纬度偏移。 * 该校正适用于 Google map China, Microsoft map china ,MapABC 等,这些地图构成方法是一样的。 * 使用方法: $gps = new GpsOffset(); echo $gps->geoLatLng($lat,$lng); * 注意: 请在服务器开启offset.dat读取权限 * @author yanue (yanue@outlook.com) * @version 1.0 * @copyright yanue.net * @time 2013-06-30 */ class GpsOffset { const datMax = 9813675;# 该文件最大数据为9813675条 private $fp = null; /* * 构造函数,打开 offset.dat 文件并初始化类中的信息 * @param string $filename * @return null */ function __construct($filename = "offset.dat") { if (($this->fp = @fopen($filename, 'rb')) !== false) { //注册析构函数,使其在程序执行结束时执行 register_shutdown_function(array(&$this, '__construct')); } } /* * 读取dat文件并查找偏移像素值 * 说明: * dat文件结构:该文件为0.01精度校正数据,并以lng和lat递增形式组合. * 其中以8个字节为一组: * lng : 2字节经度,如12151表示121.51 * lat : 2字节纬度,如3130表示31.30 * x_off : 2字节地图x轴偏移像素值 * y_off : 2字节地图y轴偏移像素值 * 因此采用二分法并以lng+lat的值作为条件 * 注意:请在服务器开启offset.dat读取权限 * */ private function fromEarthToMars($lat,$lng){ $tmpLng=intval($lng * 100); $tmpLat=intval($lat * 100); $left = 0; //开始记录 $right = self::datMax; //结束记录 $searchLngLat = $tmpLng.$tmpLat; // 采用用二分法来查找查数据 while($left <= $right){ $recordCount =(floor(($left+$right)/2))*8; // 取半 fseek ( $this->fp, $recordCount , SEEK_SET ); // 设置游标 $c = fread($this->fp,8); // 读8字节 $lng = unpack('s',substr($c,0,2)); $lat = unpack('s',substr($c,2,2)); $x = unpack('s',substr($c,4,2)); $y = unpack('s',substr($c,6,2)); $curLngLat=$lng[1].$lat[1]; if ($curLngLat==$searchLngLat){ fclose($this->fp); return array('x'=>$x[1],'y'=>$y[1]); break; }else if($curLngLat<$searchLngLat){ $left=($recordCount/8) + 1; }else if($curLngLat>$searchLngLat){ $right=($recordCount/8) - 1; } } fclose($this->fp); return false; } // 转换经纬度到 public function geoLatLng($lat,$lng){ $offset =$this->fromEarthToMars($lat,$lng); $lngPixel=$this->lngToPixel($lng,18)+$offset['x']; $latPixel=$this->latToPixel($lat,18)+$offset['y']; $mixLat = $this->pixelToLat($latPixel,18); $mixLng = $this->pixelToLng($lngPixel,18); return array('lat'=>$mixLat,'lng'=>$mixLng); } //经度到像素X值 private function lngToPixel($lng,$zoom) { return ($lng+180)*(256<<$zoom)/360; } //纬度到像素Y值 private function latToPixel($lat, $zoom) { $siny = sin($lat * pi() / 180); $y=log((1+$siny)/(1-$siny)); return (128<<$zoom)*(1-$y/(2*pi())); } //像素X到经度 private function pixelToLng($pixelX,$zoom){ return $pixelX*360/(256<<$zoom)-180; } //像素Y到纬度 private function pixelToLat($pixelY, $zoom) { $y = 2*pi()*(1-$pixelY /(128 << $zoom)); $z = pow(M_E, $y); $siny = ($z -1)/($z +1); return asin($siny) * 180/pi(); } public function __destruct(){ if($this->fp){ fclose($this->fp); } $this->fp = null; } }参考文章:http://go2log.com/2011/08/30/%E4%B8%AD%E5%9B%BD%E5%9C%B0%E5%9B%BE%E5%81%8F%E7%A7%BB%E6%A0%A1%E6%AD%A3php%E7%AE%97%E6%B3%95/(有c#算法)至此,文章完。