经纬度纠偏的一些经验

  手机上报的经纬度,并不是所有的都是GPS格式的,有的是GCJ02、有的是Baidu等等,那么如何才能针对一个城市的数据进行全面的纠偏呢?首先需要建立一个纠偏库,之后使用纠偏库实现纠偏。

  以GCJ02纠偏库需要以下步骤:

一、建立纠GCJ02纠偏为GPS的偏库

1)选择一个城市的范围,找到最大最小经度、纬度的范围,设置需要纠偏的精确度,比如我选择A城市做为示例设置其纠偏范围

double leftUpLng = 121.0127;
double leftUpLat = 31.0850;
double rightDownLng = 121.392234;
double rightDownLat = 31.446334;

精确度:0.0001(以米为单位)

 long lngOffsetScope = (long)((rightDownLng - leftUpLng) / 0.0001);
 long latOffsetScope = (long)((rightDownLat - leftUpLat) / 0.0001);

3)选纠偏函数

  1         private void DoOffset(List<LatLngOffsetStruct> items)
  2         {
  3             string lats = string.Join(";", items.Select(m => m.GCJ02Lat).ToArray());
  4             string lngs = string.Join(";", items.Select(m => m.GCJ02Lng).ToArray());
  5 
  6             /**
  7 批量纠偏接口(POST)
  8 接口地址 http://api.zdoz.net/transmore.ashx
  9 接口说明 
 10 批量纠偏,一次最大可纠偏1000个坐标点
 11 参数
 12 lats:维度,多个维度用“;”隔开
 13 lngs:经度,多个经度用“;”隔开(要注意经纬度个数相等)
 14 type:转换类型 【1.WGS -> GCJ】 【2.GCJ -> WGS】 【3.GCJ -> BD】 【4.BD -> GCJ】 【5.WGS -> BD】 【6.BD -> WGS】
 15 返回值JSON
 16 根据次序返回一个json格式的数组
 17 演示
 18 参数:lats=34.123;34.332;55.231&lngs=113.123;112.213;115.321&type=1
 19 
 20 返回:[{"Lng":113.12942937312582,"Lat":34.121761850760855},{"Lng":112.21911710957568,"Lat":34.3306763095054}, {"Lng":115.33036232125529,"Lat":55.232930158541052}]
 21 */
 22             string requestUri = "http://api.zdoz.net/transmore.ashx";
 23             string parameter = string.Format("lats={0}&lngs={1}&type=2", lats, lngs);
 24             int cursor = 0;
 25 
 26             GOTO_AGAIN:
 27             try
 28             {
 29                 cursor++;
 30 
 31                 string httpContext = GetRequesetContext(requestUri, parameter);
 32                 // 返回:[{"Lng":113.12942937312582,"Lat":34.121761850760855},{"Lng":112.21911710957568,"Lat":34.3306763095054}, {"Lng":115.33036232125529,"Lat":55.232930158541052}]
 33                 string[] splitItems = httpContext.Split(new string[] { "[", "},{", "]" }, StringSplitOptions.RemoveEmptyEntries);
 34 
 35                 if (splitItems.Length < items.Count)
 36                 {
 37                     logger.Warn("出现拆分出的lat,lng的组合长度不够" + items.Count + "!!!");
 38                 }
 39 
 40                 int itemsNumber = splitItems.Length;
 41                 if (splitItems.Length > items.Count)
 42                 {
 43                     itemsNumber = items.Count;
 44                 }
 45 
 46                 for (var i = 0; i < itemsNumber; i++)
 47                 {
 48                     string[] lngLat = splitItems[i].Split(new string[] { "{", "}", "\"Lng\":", ",\"Lat\":" }, StringSplitOptions.RemoveEmptyEntries);
 49                     if (lngLat.Length != 2)
 50                     {
 51                         logger.Warn("出现" + splitItems[i] + "拆分出的lat,lng格式不正确!!!");
 52                     }
 53 
 54                     double lng = double.Parse(lngLat[0]);
 55                     double lat = double.Parse(lngLat[1]);
 56 
 57                     LatLngOffsetStruct item = items[i];
 58                     item.GpsLng = lng;
 59                     item.GpsLat = lat;
 60                     item.LatOffset = (item.GCJ02Lat - item.GpsLat).ToString();
 61                     item.LngOffset = (item.GCJ02Lng - item.GpsLng).ToString();
 62                 }
 63             }
 64             catch (Exception ex)
 65             {
 66                 if (cursor < 5)
 67                 {
 68                     goto GOTO_AGAIN;
 69                 }
 70 
 71                 logger.Error("DoOffset失败次数超过5次:\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
 72             }
 73         }
 74 
 75         private string GetRequesetContext(string requestUri, string parameter)
 76         {
 77             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
 78             request.Method = "post";
 79             request.ContentType = "application/x-www-form-urlencoded";
 80 
 81             byte[] payload = System.Text.Encoding.UTF8.GetBytes(parameter);
 82             request.ContentLength = payload.Length;
 83 
 84             Stream writer;
 85             try
 86             {
 87                 writer = request.GetRequestStream();
 88             }
 89             catch (Exception)
 90             {
 91                 writer = null;
 92                 Console.Write("连接服务器失败!");
 93             }
 94 
 95             writer.Write(payload, 0, payload.Length);
 96             writer.Close();
 97 
 98             HttpWebResponse response;
 99             try
100             {
101                 response = (HttpWebResponse)request.GetResponse();
102             }
103             catch (WebException ex)
104             {
105                 response = ex.Response as HttpWebResponse;
106             }
107 
108             string httpContext = string.Empty;
109 
110             using (Stream stream = response.GetResponseStream())
111             {
112                 using (StreamReader reader = new StreamReader(stream))
113                 {
114                     httpContext = reader.ReadToEnd();
115                 }
116             }
117 
118             response.Close();
119 
120             return httpContext;
121         }
View Code

 定义纠偏结构体类:

public class LatLngOffsetStruct
{
    public double GCJ02Lng { get; set; }
    public double GCJ02Lat { get; set; }

    public double GpsLng { get; set; }
    public double GpsLat { get; set; }

    public string LngOffset { get; set; }
    public string LatOffset { get; set; }
}

4)就行纠偏的主要业务逻辑

 1         public static void main(String[] args)
 2         {
 3             double leftUpLng = 121.0127;
 4             double leftUpLat = 31.0850;
 5             double rightDownLng = 121.392234;
 6             double rightDownLat = 31.446334;
 7 
 8             long lngOffsetScope = (long)((rightDownLng - leftUpLng) / 0.0001);
 9             long latOffsetScope = (long)((rightDownLat - leftUpLat) / 0.0001);
10             long totalCalculateNumbers = lngOffsetScope * latOffsetScope;
11             long cursor = 0;
12 
13             int previousProgress = 0;
14             List<LatLngOffsetStruct> items = new List<LatLngOffsetStruct>();
15             List<LatLngOffsetStruct> tempitems = new List<LatLngOffsetStruct>();
16             for (double lng = leftUpLng; lng < rightDownLng; lng += 0.0001)
17             {
18                 for (double lat = leftUpLat; lat < rightDownLat; lat += 0.0001)
19                 {
20                     cursor++;
21 
22                     tempitems.Add(new LatLngOffsetStruct() { GCJ02Lat = lat, GCJ02Lng = lng });
23 
24                     if (tempitems.Count == 1000)
25                     {
26                         DoOffset(tempitems);                       //批量GCJ02坐标转化为GPS坐标
27 
28                         items.AddRange(tempitems);
29 
30                         tempitems = new List<LatLngOffsetStruct>();
31 
32                         if (items.Count > 100000)
33                         {
34                             DoInsert(items);                           //批量插入纠偏结果。
35                             items = new List<LatLngOffsetStruct>();
36                         }
37                     }
38 
39                     int progress = (int)(cursor * 100 / totalCalculateNumbers);
40                     if (progress > previousProgress)
41                     {
42                         previousProgress = progress;
43                         this.backgroundWorker.ReportProgress(progress, "已经开始执行进度:" + progress + "%");
44                     }
45                 }
46             }
47 
48             if (tempitems.Count > 0)
49             {
50                 DoOffset(tempitems);             //纠偏GCJ02 to GPS
51                 items.AddRange(tempitems);
52                 DoInsert(items);                     //入库
53             }
54         }
View Code

5)入库函数

 1         private void DoInsert(List<LatLngOffsetStruct> tempitems)
 2         {
 3             try
 4             {
 5                 DataTable schema = new DataTable();
 6 
 7                 schema.Columns.Add("GCJ02Lng", typeof(string));
 8                 schema.Columns.Add("GCJ02Lat", typeof(string));
 9                 schema.Columns.Add("LngOffset", typeof(string));
10                 schema.Columns.Add("LatOffset", typeof(string));
11 
12                 foreach (var item in tempitems)
13                 {
14                     schema.Rows.Add(item.GCJ02Lng.ToString(), item.GCJ02Lat.ToString(), item.LngOffset, item.LatOffset);
15                 }
16 
17                 using (SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
18                 {
19                     connection.Open();
20                     using (SqlBulkCopy copy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, null))
21                     {
22                         copy.DestinationTableName = "dbo.Global_GCJ02_LngLatOffset";
23                         copy.BatchSize = 10000;
24                         copy.BulkCopyTimeout = 12 * 60 * 60;
25 
26                         copy.ColumnMappings.Clear();
27 
28                         copy.ColumnMappings.Add("GCJ02Lng", "GCJ02Lng");
29                         copy.ColumnMappings.Add("GCJ02Lat", "GCJ02Lat");
30                         copy.ColumnMappings.Add("LngOffset", "LngOffset");
31                         copy.ColumnMappings.Add("LatOffset", "LatOffset");
32 
33                         copy.WriteToServer(schema);
34                     }
35                 }
36             }
37             catch (Exception ex)
38             {
39                 logger.Debug("入库失败:\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
40             }
41         }
View Code

全亮代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Net;
  6 using System.IO;
  7 using System.Diagnostics;
  8 using NLog;
  9 using System.Configuration;
 10 
 11 namespace gcj02_baidu_offset_lib
 12 {
 13     public class LatLngOffsetStruct
 14     {
 15         public double GCJ02Lng { get; set; }
 16         public double GCJ02Lat { get; set; }
 17 
 18         public double GpsLng { get; set; }
 19         public double GpsLat { get; set; }
 20 
 21         public string LngOffset { get; set; }
 22         public string LatOffset { get; set; }
 23     }
 24 
 25     public class Program
 26     {
 27         static string fileWriteFilePath = string.Empty;
 28         static Logger logger = LogManager.GetCurrentClassLogger();
 29 
 30         public static void Main(String[] args)
 31         {
 32             fileWriteFilePath = ConfigurationManager.AppSettings.Get("fileWriteFilePath");// "D:\\gcj02_offset_wenzhou.csv";
 33             double leftUpLng = double.Parse(ConfigurationManager.AppSettings.Get("leftUpLng"));// 121.0127;
 34             double leftUpLat = double.Parse(ConfigurationManager.AppSettings.Get("leftUpLat"));//31.0850;
 35             double rightDownLng = double.Parse(ConfigurationManager.AppSettings.Get("rightDownLng"));// 121.392234;
 36             double rightDownLat = double.Parse(ConfigurationManager.AppSettings.Get("rightDownLat"));// 31.446334;
 37 
 38             long lngOffsetScope = (long)((rightDownLng - leftUpLng) / 0.0001);
 39             long latOffsetScope = (long)((rightDownLat - leftUpLat) / 0.0001);
 40             long totalCalculateNumbers = lngOffsetScope * latOffsetScope;
 41             long cursor = 0;
 42 
 43             int previousProgress = 0;
 44             List<LatLngOffsetStruct> items = new List<LatLngOffsetStruct>();
 45             List<LatLngOffsetStruct> tempitems = new List<LatLngOffsetStruct>();
 46 
 47             for (double lng = leftUpLng; lng < rightDownLng; lng += 0.0001)
 48             {
 49                 for (double lat = leftUpLat; lat < rightDownLat; lat += 0.0001)
 50                 {
 51                     cursor++;
 52 
 53                     tempitems.Add(new LatLngOffsetStruct() { GCJ02Lat = lat, GCJ02Lng = lng });
 54 
 55                     if (tempitems.Count == 1000)
 56                     {
 57                         DoOffset(tempitems);                            //批量GCJ02坐标转化为GPS坐标
 58 
 59                         items.AddRange(tempitems);
 60 
 61                         tempitems = new List<LatLngOffsetStruct>();
 62 
 63                         if (items.Count > 100000)
 64                         {
 65                             DoInsert(items);                           //批量插入纠偏结果。
 66                             items = new List<LatLngOffsetStruct>();
 67                         }
 68                     }
 69 
 70                     int progress = (int)(cursor * 100 / totalCalculateNumbers);
 71                     if (progress > previousProgress)
 72                     {
 73                         previousProgress = progress;
 74                         Debug.WriteLine("已经开始执行进度:" + progress + "%");
 75                     }
 76                 }
 77             }
 78 
 79             if (tempitems.Count > 0)
 80             {
 81                 DoOffset(tempitems);                                //纠偏GCJ02 to GPS
 82                 items.AddRange(tempitems);
 83                 DoInsert(items);                                    //入库
 84             }
 85         }
 86 
 87         static void DoInsert(List<LatLngOffsetStruct> tempitems)
 88         {
 89             try
 90             {
 91                 foreach (var item in tempitems)
 92                 {
 93                     using (StreamWriter writer = new StreamWriter(fileWriteFilePath, true))
 94                     {
 95                         writer.WriteLine(string.Concat(item.GCJ02Lng, ",", item.GCJ02Lat, ",", item.LngOffset, ",", item.LatOffset));
 96                     }
 97                 }
 98             }
 99             catch (Exception ex)
100             {
101                 logger.Debug("入库失败:\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
102             }
103         }
104 
105         static void DoOffset(List<LatLngOffsetStruct> items)
106         {
107             string lats = string.Join(";", items.Select(m => m.GCJ02Lat).ToArray());
108             string lngs = string.Join(";", items.Select(m => m.GCJ02Lng).ToArray());
109 
110             /**
111             批量纠偏接口(POST)
112             接口地址 http://api.zdoz.net/transmore.ashx
113             接口说明 
114             批量纠偏,一次最大可纠偏1000个坐标点
115             参数
116             lats:维度,多个维度用“;”隔开
117             lngs:经度,多个经度用“;”隔开(要注意经纬度个数相等)
118             type:转换类型 【1.WGS -> GCJ】 【2.GCJ -> WGS】 【3.GCJ -> BD】 【4.BD -> GCJ】 【5.WGS -> BD】 【6.BD -> WGS】
119             返回值JSON
120             根据次序返回一个json格式的数组
121             演示
122             参数:lats=34.123;34.332;55.231&lngs=113.123;112.213;115.321&type=1
123 
124             返回:[{"Lng":113.12942937312582,"Lat":34.121761850760855},{"Lng":112.21911710957568,"Lat":34.3306763095054}, {"Lng":115.33036232125529,"Lat":55.232930158541052}]
125             */
126             string requestUri = "http://api.zdoz.net/transmore.ashx";
127             string offsetType = ConfigurationManager.AppSettings.Get("offsetType");
128             string parameter = string.Format("lats={0}&lngs={1}&type=" + offsetType, lats, lngs);
129             int cursor = 0;
130 
131         GOTO_AGAIN:
132             try
133             {
134                 cursor++;
135 
136                 string httpContext = GetRequesetContext(requestUri, parameter);
137                 // 返回:[{"Lng":113.12942937312582,"Lat":34.121761850760855},{"Lng":112.21911710957568,"Lat":34.3306763095054}, {"Lng":115.33036232125529,"Lat":55.232930158541052}]
138                 string[] splitItems = httpContext.Split(new string[] { "[", "},{", "]" }, StringSplitOptions.RemoveEmptyEntries);
139 
140                 if (splitItems.Length < items.Count)
141                 {
142                     logger.Warn("出现拆分出的lat,lng的组合长度不够" + items.Count + "!!!");
143                 }
144 
145                 int itemsNumber = splitItems.Length;
146                 if (splitItems.Length > items.Count)
147                 {
148                     itemsNumber = items.Count;
149                 }
150 
151                 for (var i = 0; i < itemsNumber; i++)
152                 {
153                     string[] lngLat = splitItems[i].Split(new string[] { "{", "}", "\"Lng\":", ",\"Lat\":" }, StringSplitOptions.RemoveEmptyEntries);
154                     if (lngLat.Length != 2)
155                     {
156                         logger.Warn("出现" + splitItems[i] + "拆分出的lat,lng格式不正确!!!");
157                     }
158 
159                     double lng = double.Parse(lngLat[0]);
160                     double lat = double.Parse(lngLat[1]);
161 
162                     LatLngOffsetStruct item = items[i];
163                     item.GpsLng = lng;
164                     item.GpsLat = lat;
165                     item.LatOffset = (item.GCJ02Lat - item.GpsLat).ToString();
166                     item.LngOffset = (item.GCJ02Lng - item.GpsLng).ToString();
167                 }
168             }
169             catch (Exception ex)
170             {
171                 if (cursor < 5)
172                 {
173                     goto GOTO_AGAIN;
174                 }
175 
176                 logger.Error("DoOffset失败次数超过5次:\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
177             }
178         }
179 
180         static string GetRequesetContext(string requestUri, string parameter)
181         {
182             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
183             request.Method = "post";
184             request.ContentType = "application/x-www-form-urlencoded";
185 
186             byte[] payload = System.Text.Encoding.UTF8.GetBytes(parameter);
187             request.ContentLength = payload.Length;
188 
189             Stream writer;
190             try
191             {
192                 writer = request.GetRequestStream();
193             }
194             catch (Exception)
195             {
196                 writer = null;
197                 Console.Write("连接服务器失败!");
198             }
199 
200             writer.Write(payload, 0, payload.Length);
201             writer.Close();
202 
203             HttpWebResponse response;
204             try
205             {
206                 response = (HttpWebResponse)request.GetResponse();
207             }
208             catch (WebException ex)
209             {
210                 response = ex.Response as HttpWebResponse;
211             }
212 
213             string httpContext = string.Empty;
214 
215             using (Stream stream = response.GetResponseStream())
216             {
217                 using (StreamReader reader = new StreamReader(stream))
218                 {
219                     httpContext = reader.ReadToEnd();
220                 }
221             }
222 
223             response.Close();
224 
225             return httpContext;
226         }
227     }
228 }
View Code

配置文件:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" restartOnExternalChanges="false" />
  </configSections>
  <appSettings>
    <!-- 
    double leftUpLng = 121.0127;
    double leftUpLat = 31.0850;
    double rightDownLng = 121.392234;
    double rightDownLat = 31.446334;
    A经纬度范围:东经120°55'至122°16',北纬28°51'至30°33'
    B经纬度范围:东经119°37′-121°18′、北纬27°03′-28°36′
    -->
    <add key="leftUpLng" value="120.54"/>
    <add key="leftUpLat" value="28.50"/>
    <add key="rightDownLng" value="122.17"/>
    <add key="rightDownLat" value="30.34"/>
    <!--【1.WGS -> GCJ】 【2.GCJ -> WGS】 【3.GCJ -> BD】 【4.BD -> GCJ】 【5.WGS -> BD】 【6.BD -> WGS】-->
    <add key="offsetType" value="2"/>
    <add key="fileWriteFilePath" value="D:\\gcj02_offset_ningbo.csv"/>
    
  </appSettings>
  <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <targets>
      <target name="exceptionFile" type="File" fileName="${basedir}/Log/${shortdate}/${logger}_${level}.txt" layout="${longdate}|${level:uppercase=true}|${logger}${newline}${message}${newline}"/>
      <target name="console" xsi:type="ColoredConsole" layout="${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}"/>
    </targets>
    <rules>
      <logger name="*" minlevel="Debug" writeTo="exceptionFile"/>
      <logger name="*" minlevel="Info" writeTo="console"/>
      <logger name="*" minlevel="Trace" writeTo="console"/>
      <logger name="*" minlevel="Fatal" writeTo="exceptionFile"/>
      <logger name="*" minlevel="Error" writeTo="exceptionFile"/>
      <logger name="*" minlevel="Warn" writeTo="exceptionFile"/>
    </rules>
  </nlog>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
View Code

二、如何使用纠偏库实现GCJ02纠偏为GPS

 1)首先需要根据经验建立一个库来记录下哪些host上报的经纬度是gcj02格式的经纬度,哪些host上报的经纬度是baidu坐标的经纬度等。

create table global_gcj02_host(
    host string
);
insert into global_gcj02_host('lbs.amap.com');
.....
insert into global_gcj02_host('api.amap.com');
.....

2)使用host坐标系类型经验库(g_gcj02_host )、纠偏库(g_gcj02_lnglatoffset )来实现纠偏

需求:有一个库中存储的是待纠偏的数据表http_latlng

create table temp_baidu_result_for20170704 as 
select t10.begintime,t10.host,t10.base_host,
    (case when isnotnull(t11.lngoffset) then (t10.longitude-t11.lngoffset) else t10.longitude end)as longitude_offset,
    (case when isnotnull(t11.latoffset) then (t10.latitude-t11.latoffset) else t10.latitude end) as latitude_offset
from
(
    select t10.begintime,t10.endtime,t10.host,t10.longitude,t10.latitude,t11.host as base_host 
    from http_latlng t10 inner join g_gcj02_host as t11 on t10.host=t11.host
) t10 
inner join g_gcj02_lnglatoffset t11 on rpad(t10.longitude,8,'0')=rpad(t11.gcj02lng,8,'0') and rpad(t10.latitude,7,'0')=rpad(t11.gcj02lat,7,'0');

 

posted @ 2017-07-07 01:06  cctext  阅读(1999)  评论(0编辑  收藏  举报