对于地图坐标偏移,以leaflet为例,有如下解决办法
方法1、修改leaflet源码,解决地图坐标偏移问题
方法2、将点位真实的经纬度经过偏移算法,添加到加密的地图上
方法3、直接对离线地图瓦片进行纠偏
方法1需要修改源码
方法2有缺陷,地图依然是偏移的,如果把地图经纬度显示出来,经纬度也是不对的
我使用的是方法3,原理是:虽然偏移不是线性的,我也不知道纠偏算法,但是在市级或者县级区域,偏移近似线性的,所以对于市级或县级地图应用,可以对地图瓦片进行简单的纠编,优点是得到的地图瓦片可以认为是没有偏移的。
我用C#写了一个高德地图瓦片纠偏程序,代码如下:
1、配置文件
<?xml version="1.0" encoding="utf-8" ?><configuration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup><appSettings><add key="inputPath" value="D:\_临时文件\GISMap\1818940751"/><add key="outputPath" value="D:\_临时文件\GISMapOutput\1818940751"/><add key="deltaPixcelX" value="1031"/><add key="deltaPixcelY" value="421"/><add key="fromMapZoom" value="1"/><add key="toMapZoom" value="16"/></appSettings></configuration>
View Code
deltaPixcelX和deltaPixcelY根据不同城市而不同,单位是像素,在太乐地图下载器上,开启网络,放大到18级,使用下载器的纠偏计算,定位点位和纠偏后的点位,基本用眼可以看出来,差一个格就是256像素,不足一格的自己算一下,就把deltaPixcelX和deltaPixcelY参数算出来了。
2、纠偏代码
using System;using System.Collections.Generic;using ponentModel;using System.Configuration;using System.Data;using System.Drawing;using System.Drawing.Imaging;using System.IO;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;using Utils;namespace TileProcess{public partial class Form1 : Form{private int _count = 0;private int _deltaPixcelX;private int _deltaPixcelY;private string _inputPath;private string _outputPath;private int _fromMapZoom;private int _toMapZoom;private DateTime _startTime;private int _lastCount;private object _lock = new object();public Form1(){InitializeComponent();_deltaPixcelX = Convert.ToInt32(ConfigurationManager.AppSettings["deltaPixcelX"]);_deltaPixcelY = Convert.ToInt32(ConfigurationManager.AppSettings["deltaPixcelY"]);_inputPath = ConfigurationManager.AppSettings["inputPath"];_outputPath = ConfigurationManager.AppSettings["outputPath"];_fromMapZoom = Convert.ToInt32(ConfigurationManager.AppSettings["fromMapZoom"]);_toMapZoom = Convert.ToInt32(ConfigurationManager.AppSettings["toMapZoom"]);}private void btnTileProcess_Click(object sender, EventArgs e){this.btnTileProcess.Enabled = false;Task.Factory.StartNew(() =>{LogUtil.Log("开始处理");Process();});Thread thread = new Thread(new ThreadStart(() =>{int sleepInterval = 1000;while (true){Thread.Sleep(sleepInterval);this.BeginInvoke(new Action(() =>{double totalSeconds = DateTime.Now.Subtract(_startTime).TotalSeconds;int avg = (int)(_count / totalSeconds);lblMsg.Text = string.Format("已处理 {0} 张瓦片图", _count);if (_count - _lastCount > 0){lblSpeed.Text = string.Format("当前速度:{0} 张/每秒,平均速度:{1} 张/每秒", (_count - _lastCount) * 1000.0 / sleepInterval, avg);}_lastCount = _count;}));}}));thread.IsBackground = true;thread.Start();}/// <summary>/// 瓦片纠偏处理/// </summary>private void Process(){_startTime = DateTime.Now;Regex regex = new Regex(@"\\(\d+)\\(\d+).png", RegexOptions.IgnoreCase);for (int i = _fromMapZoom; i <= _toMapZoom; i++){int deltaPixcelX = (int)Math.Round(_deltaPixcelX / Math.Round(Math.Pow(2, 18 - i)));int deltaPixcelY = (int)Math.Round(_deltaPixcelY / Math.Round(Math.Pow(2, 18 - i)));string[] fileArr = Directory.GetFiles(_inputPath + "\\" + i, "*.*", SearchOption.AllDirectories);foreach (string file in fileArr){ThreadData data = new ThreadData();data.File = file;data.I = i;data.DeltaPixcelX = deltaPixcelX;data.DeltaPixcelY = deltaPixcelY;ThreadUtil.Run((obj) =>{ThreadData d = obj as ThreadData;Match match = regex.Match(d.File);if (match.Success){int x = Convert.ToInt32(match.Groups[1].Value);int y = Convert.ToInt32(match.Groups[2].Value);string pathTarget = string.Format(string.Format(@"{0}\{1}\{2}\{3}.png", _outputPath, d.I, x, y));if (!File.Exists(pathTarget)){if (!Directory.Exists(Path.GetDirectoryName(pathTarget))){Directory.CreateDirectory(Path.GetDirectoryName(pathTarget));}Bitmap bmpNew = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format32bppArgb);bmpNew.SetResolution(96, 96);Graphics graph = Graphics.FromImage(bmpNew);int deltaX = data.DeltaPixcelX / 256;int deltaY = data.DeltaPixcelY / 256;//临时变量定义string pathSource = null;FileStream fs = null;byte[] bArr = null;MemoryStream ms = null;Bitmap bmpSource = null;//起始pathSource = string.Format(@"{0}\{1}\{2}\{3}.png", _inputPath, d.I, x + deltaX, y + deltaY);if (File.Exists(pathSource)){fs = new FileStream(pathSource, FileMode.Open, FileAccess.Read);bArr = new byte[fs.Length];int readCount = fs.Read(bArr, 0, bArr.Length);ms = new MemoryStream(bArr, 0, readCount);bmpSource = new Bitmap(ms);bmpSource.SetResolution(96, 96);graph.DrawImage(bmpSource, 0, 0, new RectangleF(data.DeltaPixcelX % 256, data.DeltaPixcelY % 256, 256 - data.DeltaPixcelX % 256, 256 - data.DeltaPixcelY % 256), GraphicsUnit.Pixel);graph.Flush();fs.Close();fs = null;ms.Close();ms = null;bmpSource.Dispose();bmpSource = null;}//右pathSource = string.Format(@"{0}\{1}\{2}\{3}.png", _inputPath, d.I, x + deltaX + 1, y + deltaY);if (File.Exists(pathSource) && (data.DeltaPixcelX > 0 || data.DeltaPixcelY > 0)){fs = new FileStream(pathSource, FileMode.Open, FileAccess.Read);bArr = new byte[fs.Length];int readCount = fs.Read(bArr, 0, bArr.Length);ms = new MemoryStream(bArr, 0, readCount);bmpSource = new Bitmap(ms);bmpSource.SetResolution(96, 96);graph.DrawImage(bmpSource, 256 - data.DeltaPixcelX % 256, 0, new RectangleF(0, data.DeltaPixcelY % 256, data.DeltaPixcelX % 256, 256 - data.DeltaPixcelY % 256), GraphicsUnit.Pixel);graph.Flush();fs.Close();fs = null;ms.Close();ms = null;bmpSource.Dispose();bmpSource = null;}//下pathSource = string.Format(@"{0}\{1}\{2}\{3}.png", _inputPath, d.I, x + deltaX, y + deltaY + 1);if (File.Exists(pathSource) && (data.DeltaPixcelX > 0 || data.DeltaPixcelY > 0)){fs = new FileStream(pathSource, FileMode.Open, FileAccess.Read);bArr = new byte[fs.Length];int readCount = fs.Read(bArr, 0, bArr.Length);ms = new MemoryStream(bArr, 0, readCount);bmpSource = new Bitmap(ms);bmpSource.SetResolution(96, 96);graph.DrawImage(bmpSource, 0, 256 - data.DeltaPixcelY % 256, new RectangleF(data.DeltaPixcelX % 256, 0, 256 - data.DeltaPixcelX % 256, data.DeltaPixcelY % 256), GraphicsUnit.Pixel);graph.Flush();fs.Close();fs = null;ms.Close();ms = null;bmpSource.Dispose();bmpSource = null;}//右下pathSource = string.Format(@"{0}\{1}\{2}\{3}.png", _inputPath, d.I, x + deltaX + 1, y + deltaY + 1);if (File.Exists(pathSource) && (data.DeltaPixcelX > 0 || data.DeltaPixcelY > 0)){fs = new FileStream(pathSource, FileMode.Open, FileAccess.Read);bArr = new byte[fs.Length];int readCount = fs.Read(bArr, 0, bArr.Length);ms = new MemoryStream(bArr, 0, readCount);bmpSource = new Bitmap(ms);bmpSource.SetResolution(96, 96);graph.DrawImage(bmpSource, 256 - data.DeltaPixcelX % 256, 256 - data.DeltaPixcelY % 256, new RectangleF(0, 0, data.DeltaPixcelX % 256, data.DeltaPixcelY % 256), GraphicsUnit.Pixel);graph.Flush();fs.Close();fs = null;ms.Close();ms = null;bmpSource.Dispose();bmpSource = null;}bmpNew.Save(pathTarget);//bmpNew.Save("d:\\_临时文件\\1234.png"); //测试用bmpNew.Dispose();bmpNew = null;graph.Dispose();graph = null;} //end if (!File.Exists(pathTarget))lock (_lock){_count++;}} //end if (match.Success)}, data, (ex) =>{this.BeginInvoke(new Action(() =>{lblErrorMsg.Text = "出错:" + ex.Message + "\r\n" + ex.StackTrace;LogUtil.LogError(ex, "出错");}));}); //end ThreadUtil.Run} //end foreach (string file in fileArr)} //end for (int i = _fromMapZoom; i <= _toMapZoom; i++) }}}
View Code
辅助类ThreadUtil:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace Utils{/// <summary>/// 线程工具类/// </summary>public class ThreadUtil{/// <summary>/// 使用的逻辑处理器数/// </summary>private static int _ProcessorCount;private static Semaphore _semaphore;private static List<Task> _TaskList = new List<Task>();private static object _lock = new object();static ThreadUtil(){_ProcessorCount = Environment.ProcessorCount * 2 / 4; //使用的逻辑处理器数if (_ProcessorCount < 1) _ProcessorCount = 1;_semaphore = new Semaphore(_ProcessorCount, _ProcessorCount);}public static void Run(Action<object> doWork, object arg, Action<Exception> errorAction){Task task = null;task = Task.Factory.StartNew((obj) =>{_semaphore.WaitOne();try{doWork(obj);}catch (Exception ex){errorAction(ex);}_semaphore.Release();lock (_lock){_TaskList.Remove(task);}}, arg);lock (_lock){_TaskList.Add(task);}}public static void WaitAll(){Task.WaitAll(_TaskList.ToArray());}}}
View Code
辅助类ThreadData:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace TileProcess{public class ThreadData{public int I { get; set; }public string File { get; set; }public int DeltaPixcelX { get; set; }public int DeltaPixcelY { get; set; }}}
View Code
写日志工具类就不贴了,可以用其它日志工具代替
处理速度大约每称300张瓦片,具体根据电脑性能不同,一个城市的瓦片大约1个小时左右能处理完。
纠偏后的地图做最佳路径分析,显示的路径和道路基本吻合,略有误差。
还有另一种纠偏方法,通过修改leaflet源码进行纠偏:/s0611163/p/13396622.html