For map coordinate migration, take leaflet as an example, there are the following solutions
Method 1. Modify the source code of leaflet to solve the problem of map coordinate migration
Method 2. Add the true latitude and longitude of the point to the encrypted map through the offset algorithm
Method 3. Rectify the off-line map tile directly
Method 1 needs to modify the source code
Method 2 is defective. The map is still offset. If the latitude and longitude of the map are displayed, the latitude and longitude are also wrong
I use method 3, the principle is: Although the offset is not linear, I do not know the correction algorithm, but in the city or county-level area, the offset is approximately linear, so for the city or county-level map application, you can simply correct the map tiles, the advantage is that the map tiles obtained can be considered as no offset.
I use C Chen to write a gaud map tile correction program, the code is as follows:
1. Configuration file
<?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:\_Temporary file\GISMap\1818940751"/> <add key="outputPath" value="D:\_Temporary file\GISMapOutput\1818940751"/> <add key="deltaPixcelX" value="1031"/> <add key="deltaPixcelY" value="421"/> <add key="fromMapZoom" value="1"/> <add key="toMapZoom" value="16"/> </appSettings> </configuration>
Deltapixcellx and deltapixcelly are different according to different cities, and the unit is pixel. On the map downloader of Taile, turn on the network, zoom in to level 18, use the correction calculation of Downloader, locate the point and the corrected point. It can be seen basically with your eyes that the difference is 256 pixels. If you have less than one grid, calculate the parameters of deltapixcellx and deltapixcelly Now.
2. Correction code
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("Start processing"); 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("Processed {0} Tile Map", _count); if (_count - _lastCount > 0) { lblSpeed.Text = string.Format("Current speed:{0} Zhang/Per second, average speed:{1} Zhang/Per second", (_count - _lastCount) * 1000.0 / sleepInterval, avg); } _lastCount = _count; })); } })); thread.IsBackground = true; thread.Start(); } /// <summary> /// Correction of tile deviation /// </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; //Definition of temporary variable string pathSource = null; FileStream fs = null; byte[] bArr = null; MemoryStream ms = null; Bitmap bmpSource = null; //Start 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; } //right 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; } //lower 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; } //lower right 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:\\_Temporary file\\1234.png"); //Test use 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 = "Error:" + ex.Message + "\r\n" + ex.StackTrace; LogUtil.LogError(ex, "error"); })); }); //end ThreadUtil.Run } //end foreach (string file in fileArr) } //end for (int i = _fromMapZoom; i <= _toMapZoom; i++) } } }
Auxiliary class ThreadUtil:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Utils { /// <summary> /// Thread tool class /// </summary> public class ThreadUtil { /// <summary> /// Number of logical processors used /// </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; //Number of logical processors used 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()); } } }
Auxiliary class 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; } } }
The logging tool class will not be pasted. You can use other logging tools instead
The processing speed is about 300 tiles. According to different computer performance, tiles in a city can be processed in about one hour.
The best path analysis of the corrected map shows that the path basically coincides with the road, with slight error.