Correction of tile coordinate deviation in Gaud offline map

Keywords: C# xml encoding network less

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.

Posted by nic9 on Thu, 12 Dec 2019 22:42:55 -0800