對於地圖坐標偏移,以leaflet為例,有如下解決辦法 方法1、修改leaflet源碼,解決地圖坐標偏移問題 方法2、將點位真實的經緯度經過偏移演算法,添加到加密的地圖上 方法3、直接對離線地圖瓦片進行糾偏 方法1需要修改源碼 方法2有缺陷,地圖依然是偏移的,如果把地圖經緯度顯示出來,經緯度也是不對的 ...
對於地圖坐標偏移,以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 System.ComponentModel; 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個小時左右能處理完。
糾偏後的地圖做最佳路徑分析,顯示的路徑和道路基本吻合,略有誤差。