地图选择及地图偏移解决方案

时间:2022-09-12 22:44:23

转自:http://blog.csdn.net/gjican/article/details/7914077


上一篇我们讲到了如何选择地图服务商,这一篇接着上一篇讲下一个比较重要,且大多数开发者会碰到的问题,那就是地图纠偏

  
    先来了解下为什么会存在地图偏移这么一个问题:先参考一个文档

 

    http://baike.baidu.com/view/3163334.htm (百度百科对于火星坐标系统的解释)

    http://www.kaixin001.com/repaste/16531379_3916220968.html?uid=16575561&urpid=3916329607 


     截取部分文字来看:


     国家保密插件,也叫做加密插件或者加偏或者SM模组,其实就是对真实坐标系统进行人为的加偏处理,按照几行代码的算法,将真实的坐标加密成虚假的坐标,而这个加偏并不是线性的加偏,所以各地的偏移情况都会有所不同。而加密后的坐标也常被人称为火星坐标系统


    从这个解释看以看出,为了保证国家的领土安全,国家在地图这一层上做了一个认为加偏处理,借助网上一个经典例子来描述:比如某广场的经纬度本身是固定的,但是国家为了保证安全,对其经纬度进行了一个加偏,比如原先是假设(20.0022211,110.2223455),经过处理之后得到的是(20.0022222,110.2223555),变化比较小,但其实已经可能已经不在某广场附近了,这样就达到了安全性,那么国内的地图服务商,GPS导航仪等则在出厂前会对检测到的经纬度同样有一个加偏,这样两个加偏,就凑到一块去了,一张完整的可以使用的地图就出来了。
 
   相信这个例子应该能看出来为什么会存在这个地图偏移了吧,如果还有不懂可以QQ联系  
   那从上面的描述中可以看出,基本国内的地图服务商其实是具有这一个加偏算法的,也就是说如果选择国内的地图服务商,其实表象是不存在地图偏移这么一个问题的,但是如果选择Google地图,我们知道Google 曾在10年申请牌照,但是没有得到批准,也就是说它是不具备地图服务提供资质的。但是依然可以使用啊,但是Google好像是10年那会关闭了纠偏接口,这样凡是国内使用Google地图的开发者来说基本都会发现取到的经纬度跟地图显示偏差很大,这就是地图偏移。 


  那么如何解决Google地图的偏移问题呢。 

  我当时做项目的过程中也是遇到这个问题,并且烦了好一阵子,但是后续通过查资料等最后获取到几个方式:

  1.纠偏数据库,网上有人售卖这个地图数据库,精度有0.01及0.001的,我后续买了0.001精度的,最后的效果不错,基本看不到偏差了,杭州跟深圳两地有测试过。

  2.纠偏插件,也就是一个jar包,通过这个大概1.7M的jar包,据说可以提供大致相关于0.01精度纠偏数据库的精度。不过对于移动开发者而言,或多或少会仔细考虑这1.7M 库的代价。我最后也没有购买,所以不好评价这个效果。

  3.网络上有一些纠偏接口提供商,我也测试过,效果也很好,但是由于受限于自己不能控制(服务器是国外的,可能纠偏过程会很慢,又或者别人什么时候把这个服务关闭掉了,所以不敢用)。当然我会提供给大家,供测试或者不太重视这块的开发者使用吧。

 

   


地图选择及地图偏移解决方案

 


地图选择及地图偏移解决方案

 
地图选择及地图偏移解决方案

 

 

  以下是一个免费的地图纠偏接口,本人测试过,还不错,速度包括精度都很好,建议个人开发者学习或者测试的,可以直接使用

[java] view plaincopy
  1. package *.*;//自己填写  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.InputStreamReader;  
  7. import java.net.HttpURLConnection;  
  8. import java.net.URL;  
  9. import java.util.List;  
  10.   
  11. import org.apache.http.HttpResponse;  
  12. import org.apache.http.NameValuePair;  
  13. import org.apache.http.client.entity.UrlEncodedFormEntity;  
  14. import org.apache.http.client.methods.HttpPost;  
  15. import org.apache.http.impl.client.DefaultHttpClient;  
  16. import org.apache.http.protocol.HTTP;  
  17. import org.apache.http.util.EntityUtils;  
  18.   
  19. import android.os.Bundle;  
  20.   
  21. public class HttpUtil {  
  22.     /** 
  23.      * 发送请求 
  24.      * @param url 
  25.      * @param method 
  26.      * @param params 
  27.      * @param enc 
  28.      * @return 
  29.      */  
  30.     public static String openUrl(String url, String method, Bundle params, String enc){  
  31.           
  32.         String response = null;  
  33.         //GET请求  
  34.         if(method.equals("GET")){  
  35.             if(params != null)  
  36.                 url = url + "?" + encodeUrl(params);  
  37.         }  
  38.         try {  
  39.             HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();  
  40.             conn.setRequestProperty("User-Agent", System.getProperties().getProperty("http.agent"));  
  41.             conn.setReadTimeout(10000); //设置超时时间  
  42.             if(method.equals("POST")){  
  43.                 conn.setRequestMethod("POST");  
  44.                 conn.setDoOutput(true);  
  45.                 if(params != null)  
  46.                     conn.getOutputStream().write(encodeUrl(params).getBytes("UTF-8"));  
  47.             }  
  48.             response = read(conn.getInputStream(),enc);  
  49.         } catch (Exception e) {  
  50.             throw new RuntimeException(e.getMessage(),e);  
  51.         }  
  52.         return response;  
  53.     }  
  54.     /** 
  55.      * 读取输入流,转换为String 
  56.      * @param in 
  57.      * @param enc 
  58.      * @return 
  59.      * @throws IOException 
  60.      */  
  61.     private static String read(InputStream in, String enc) throws IOException {  
  62.         StringBuilder sb = new StringBuilder();  
  63.         BufferedReader r = null;  
  64.         if(enc != null){  
  65.             //按指定的编码读入流  
  66.             r = new BufferedReader(new InputStreamReader(in,enc), 1000);  
  67.         }else{  
  68.             //按默认的编码读入  
  69.             r = new BufferedReader(new InputStreamReader(in), 1000);  
  70.         }  
  71.         for (String line = r.readLine(); line != null; line = r.readLine()) {  
  72.             sb.append(line);  
  73.         }  
  74.         in.close();  
  75.         return sb.toString();  
  76.     }  
  77.     /** 
  78.      * 按key-Value的方式组拼请求参数 
  79.      * 参数内通过“=”连接,参数间通过“&”来连接 
  80.      * @param parameters 
  81.      * @return 
  82.      */  
  83.     public static String encodeUrl(Bundle parameters) {  
  84.         if (parameters == null)  
  85.             return "";  
  86.         StringBuilder sb = new StringBuilder();  
  87.         //因为第一个参数和URL之间是通过"?"来连接的  
  88.         boolean first = true;  
  89.         for (String key : parameters.keySet()) {  
  90.             if (first)  
  91.                 first = false;  
  92.             else  
  93.                 sb.append("&");  
  94.             sb.append(key + "=" + parameters.getString(key));  
  95.         }  
  96.         return sb.toString();  
  97.     }  
  98.     /** 
  99.      * 把数据post到服务器,获取返回的结果 
  100.      *  
  101.      * @param uriAPI 
  102.      *            : API 所在的路径 
  103.      * @param params 
  104.      *            : 传递的数据 
  105.      */  
  106.     public static String postToServer(String uriAPI, List<NameValuePair> params) {  
  107.         /* 建立HTTPost对象 */  
  108.         HttpPost httpRequest = new HttpPost(uriAPI);  
  109.           
  110.         try {  
  111.             /* 添加请求参数到请求对象 */  
  112.             httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));  
  113.             /* 发送请求并等待响应 */  
  114.             HttpResponse httpResponse = new DefaultHttpClient()  
  115.                     .execute(httpRequest);  
  116.             /* 若状态码为200 ok */  
  117.             if (httpResponse.getStatusLine().getStatusCode() == 200) {  
  118.                 /* 读返回数据 */  
  119.                 return EntityUtils.toString(httpResponse.getEntity());  
  120.   
  121.             } else {  
  122.                 return "Error Response: "  
  123.                         + httpResponse.getStatusLine().toString();  
  124.             }  
  125.         } catch (Exception e) {  
  126.             return "post failure :caused by-->" + e.getMessage().toString();  
  127.         }  
  128.   
  129.     }  
  130. }  

[java] view plaincopy
  1. import android.os.Bundle;  
  2.   
  3. import com.google.android.maps.GeoPoint;  
  4.   
  5. /** 
  6.  * 主要解决GPS偏移的问题 
  7.  * @author Administrator 
  8.  * 
  9.  */  
  10. public class GPSUtil {  
  11.     private static String QUERY_URL = "http://www.mapdigit.com/guidebeemap/offsetinchina.php";  
  12.     private static String METHOD_GET = "GET";  
  13.       
  14.     /** 
  15.      * 获取偏移值 
  16.      * @param longitude 
  17.      * @param latitude 
  18.      * @param path 
  19.      * @return 
  20.      */  
  21.     public static GeoPoint adjustLoction(double longitude, double latitude) {  
  22.   
  23.         Bundle params = new Bundle();  
  24.         params.putString("lng", String.valueOf(longitude));  
  25.         params.putString("lat", String.valueOf(latitude));  
  26.         String offsetString = HttpUtil.openUrl(QUERY_URL, METHOD_GET, params,  
  27.                 CustomParameter.ENCODE);  
  28.         int index = offsetString.indexOf(",");  
  29.         if (index > 0) {  
  30.             // 将坐标值转为18级相应的像素值  
  31.             double lngPixel = lonToPixel(longitude, 18);  
  32.             double latPixel = latToPixel(latitude, 18);  
  33.             // 获取偏移值  
  34.             String OffsetX = offsetString.substring(0, index).trim();  
  35.             String OffsetY = offsetString.substring(index + 1).trim();  
  36.             //加上偏移值  
  37.             double adjustLngPixel = lngPixel + Double.valueOf(OffsetX);  
  38.             double adjustLatPixel = latPixel + Double.valueOf(OffsetY);  
  39.             //由像素值再转为经纬度  
  40.             double adjustLng = pixelToLon(adjustLngPixel, 18);  
  41.             double adjustLat = pixelToLat(adjustLatPixel, 18);  
  42.   
  43.             return new GeoPoint((int) (adjustLng * 1000000),  
  44.                     (int) (adjustLat * 1000000));  
  45.         }  
  46.         //经验公式  
  47.         return new GeoPoint((int) ((latitude - 0.0025) * 1000000),  
  48.                 (int) ((longitude + 0.0045) * 1000000));  
  49.     }  
  50.   
  51.     /** 
  52.      * 经度到像素X值 
  53.      *  
  54.      * @param lng 
  55.      * @param zoom 
  56.      * @return 
  57.      */  
  58.     public static double lonToPixel(double lng, int zoom) {  
  59.         return (lng + 180) * (256L << zoom) / 360;  
  60.     }  
  61.   
  62.     /** 
  63.      * 像素X到经度 
  64.      *  
  65.      * @param pixelX 
  66.      * @param zoom 
  67.      * @return 
  68.      */  
  69.     public static double pixelToLon(double pixelX, int zoom) {  
  70.         return pixelX * 360 / (256L << zoom) - 180;  
  71.     }  
  72.   
  73.     /** 
  74.      * 纬度到像素Y 
  75.      *  
  76.      * @param lat 
  77.      * @param zoom 
  78.      * @return 
  79.      */  
  80.     public static double latToPixel(double lat, int zoom) {  
  81.         double siny = Math.sin(lat * Math.PI / 180);  
  82.         double y = Math.log((1 + siny) / (1 - siny));  
  83.         return (128 << zoom) * (1 - y / (2 * Math.PI));  
  84.     }  
  85.   
  86.     /** 
  87.      * 像素Y到纬度 
  88.      *  
  89.      * @param pixelY 
  90.      * @param zoom 
  91.      * @return 
  92.      */  
  93.     public static double pixelToLat(double pixelY, int zoom) {  
  94.         double y = 2 * Math.PI * (1 - pixelY / (128 << zoom));  
  95.         double z = Math.pow(Math.E, y);  
  96.         double siny = (z - 1) / (z + 1);  
  97.         return Math.asin(siny) * 180 / Math.PI;  
  98.     }  
  99.       
  100.       
  101. }