高德离线地图瓦片坐标偏移纠偏
程序员文章站
2023-10-16 17:01:01
对于地图坐标偏移,以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>
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++) } } }
辅助类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()); } } }
辅助类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; } } }
写日志工具类就不贴了,可以用其它日志工具代替
处理速度大约每称300张瓦片,具体根据电脑性能不同,一个城市的瓦片大约1个小时左右能处理完。
纠偏后的地图做最佳路径分析,显示的路径和道路基本吻合,略有误差。