手机上报的经纬度,并不是所有的都是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次: {0} {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 }
定义纠偏结构体类:
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 }
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("入库失败: {0} {1}", ex.Message, ex.StackTrace); 40 } 41 }
全亮代码:
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("入库失败: {0} {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次: {0} {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 }
配置文件:
<?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>
二、如何使用纠偏库实现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');