文档章节

地图/路况图Tile计算和拼装方法

小云1983
 小云1983
发布于 2016/09/23 14:20
字数 1354
阅读 36
收藏 0

1. 基础介绍

在线地图服务提供的图片下载和组织方式与google map基本相同。下载图片时,客户端需要指定缩放级别和下载图片的 tile 编号(即

tile所在的行号和列号),下载完成后自行拼装成前台显示的地图。

Tile与显示窗口的关系如下图所示:

   黄色小方块表示一个tile,大小固定,一般取256*256。

  红色方框表示前端的地图显示窗口。

  紫色方框表示为了完成地图显示窗口的地图拼装所需要下载的 tile组。

 

2.        坐标转换方法

2.1 基本换算

1)       墨卡托正变换  通过墨卡托投影法将球面经纬度坐标变换成平面坐标:

 public static Point2D convertLonLat2MercXY(LonLat lonlat)

         {

                       Point2D result = new Point2D();

                 

                       result.X = lonlat.lon * DEG2RAD;

                 

                 double phi = lonlat.lat * DEG2RAD;                

                 result.Y = 0.5 * Math.log((1 + Math.sin(phi)) / (1 - Math.sin(phi)));

                 return (result);

         }

 其中的数据结构和常量定义如下:

public class LonLat { 

      public double lon; 

      public double lat;

}

public class Point2D { 

      public double X; 

      public double Y;

}

// 度换算成弧度

public static double DEG2RAD = 0.017453293;

 

2)         墨卡托逆变换将墨卡托平面坐标变换成经纬度坐标:

   public static LonLat convertMercXY2LonLat(Point2D mercXY){

                        LonLat result = new LonLat();

                       result.lon = mercXY.X / DEG2RAD;

                        result.lat = Math.atan(Math.sinh(mercXY.Y)) / DEG2RAD;

                      return (result);

}

 

3)         墨卡托坐标到pixel坐标的变换根据显示级别和墨卡托坐标,取得像素坐标:

public static PixelXY convertMercXY2PixelXY(Point2D mercXY, int zoom) {

                         PixelXY result = new PixelXY();

                 

                 Point2D ltPoint = new Point2D();            

                ltPoint.X = -Math.PI;                  

                ltPoint.Y = Math.PI;

                 Point2D point = new Point2D();              

                 point.X = mercXY.X - ltPoint.X;                  

                 point.Y = ltPoint.Y - mercXY.Y;

                 double res = (256 * Math.pow(2, (zoom-1))) / Math.PI; 

                 result.X = (long)(point.X * res);               

                 result.Y = (long)(point.Y * res);

                 return result;

        } 

其中的数据结构定义如下所示:

public class PixelXY {          

        long X;   

        long Y;

}

 

4) pixel坐标到墨卡托坐标的变换根据pixel坐标和显示级别,取得墨卡托坐标。

public static Point2D convertPixelXY2MercXY(PixelXY pixelXY, int zoom) {

                       Point2D result = new Point2D();

                 

                         Point2D ltPoint = new Point2D();            

                        ltPoint.X = -Math.PI;                  

                        ltPoint.Y = Math.PI;

                        double res = (256 * Math.pow(2, (zoom-1))) / Math.PI;

                        Point2D point = new Point2D(); point.X = ((double)pixelXY.X) / res; point.Y = ((double)pixelXY.Y) / res;

 

result.X = point.X + ltPoint.X; result.Y = ltPoint.Y - point.Y;         return result;

}

 

5) 经纬度坐标所在tile编号获取根据经纬度坐标和显示级别,取得该坐标所在的tile编号。

                       public static TileXY getTileXY(LonLat lonlat, int zoom)

         {

                         TileXY result = new TileXY();

                  // 墨卡托正变换

                       Point2D mercXY = convertLonLat2MercXY(lonlat);

                 

                      double maxlonlat = Math.PI;

                   // 每行列的总tile数

                       double maxTileXY = Math.pow(2, zoom);

                    // 指定经纬度所在的tile

                   result.X = (long)(((1 + mercXY.X / maxlonlat) / 2) * (maxTileXY));              

                  result.Y = (long)(((1 - mercXY.Y / maxlonlat) / 2) * (maxTileXY));

                 

                      return (result);

        }  其中的数据结构定义如下所示: public class TileXY {           long X;    long Y;

}

 

6) 指定tile左上角pixel坐标取得

                        public static PixelXY convertTileXY2PixelXY(TileXY tileXY) {

                         PixelXY result = new PixelXY();

                 

                     result.X = tileXY.X * 256;                  

                     result.Y = tileXY.Y * 256;

                 

                     return result;

         }

 

7) 指定Pixel坐标所在Tile编号取得

public static TileXY convertPixelXY2TileXY(PixelXY pixelxy) {

TileXY result = new TileXY();

 

result.X = pixelxy.X / 256;

result.Y = pixelxy.Y / 256;

return result;

}

 

2.2 应用

利用以上基本变换可以进行tile图片下载和拼装。以下是常用的应用变换的实例,请参考。

 

1) 根据显示窗口参数,取得需要下载的tile列表。

由参数可知,当视窗内的地图发生平移,缩放,视窗大小变更等事件时,需要重新计算需要下载的tile列表。

         /**

*   取得需要下载的tile图片列表

*   @param center :视窗中心点经纬度坐标(单位:度)

*   @param zoom   :显示级别(0 - 17)

*   @param width  :视窗宽度(单位:pixel)

*   @param height :视窗高度(单位:pixel)

*   @return tile列表

          */

 public static List<TileXY> getTileList(LonLat center, int zoom, int width, int height) {

                            List<TileXY> result = new ArrayList<TileXY>();

                   // 取得中心点pixel坐标

                                      PixelXY cPixel = convertMercXY2PixelXY(convertLonLat2MercXY(center), zoom);

                   // 计算左上角pixel坐标

                 PixelXY ltPixel = new PixelXY();                ltPixel.X = cPixel.X - width / 2;                    ltPixel.Y = cPixel.Y - height / 2;

                   // 计算右下角pixel坐标

                 PixelXY rbPixel = new PixelXY();               rbPixel.X = cPixel.X + width / 2;                 rbPixel.Y = cPixel.Y + height / 2;

                   // 左上角所在tile编号取得

                             TileXY ltTile = convertPixelXY2TileXY(ltPixel);

                   // 右下角所在tile编号取得

                            TileXY rbTile = convertPixelXY2TileXY(rbPixel);

  // 需要下载的Tile满足以下条件:ltTile.X <= tile.X <= rbTile.X并且ltTile.Y <= tile.Y <= rbTile.Y

                 for (long i = ltTile.X; i <= rbTile.X; i++) {                    for (long j = ltTile.Y; j <= rbTile.Y; j++) {

                        TileXY tile = new TileXY();                 tile.X = i;                    tile.Y = j;

                 

                                         result.add(tile);

                            }

}      return result;

}

 

2)    Tile图片在视窗内的描画位置计算通过计算tile图片在视窗内的描画位置,就可以对获取的图片在视窗内进行正确拼装。步骤如下:

a)       取得视窗左上角的pixel坐标方法如以上代码所示:

// 取得中心点pixel坐标

                                       PixelXY cPixel = convertMercXY2PixelXY(convertLonLat2MercXY(center), zoom);

                   // 计算左上角pixel坐标

                 PixelXY ltPixel = new PixelXY();                ltPixel.X = cPixel.X - width / 2;                    ltPixel.Y = cPixel.Y - height / 2;

 

b)       根据tile编号取得tile左上角的pixel坐标

PixelXY pixelxy = convertTileXY2PixelXY(tileXY);

 

c)       取得tile的画面坐标(即相对于视窗左上角的相对坐标)

PixelXY mapxy = new PixelXY(); mapxy.X = pixelxy.X – ltPixel.X; mapxy.Y = pixelxy.Y – ltPixel.Y;

 

3)    地图平移时视窗中心经纬度坐标的重新计算地图平移时需要根据平移距离重新计算中心经纬度坐标,然后重新计算需要下载的 tile图片列表。

 /**

*   计算平移后新的视窗中心点经纬度

*   @param oriCenter :原视窗中心经纬度坐标(单位:度)

*   @param zoom      : 缩放级别(0 - 17)

*   @param offsetX   : 横向平移(单位:pixel,向右平移为正,反之为负。)

*   @param offsetY   : 纵向平移(单位:pixel,向下平移为正,反之为负。)

*   @return

          */

 public static LonLat getNewCenter(LonLat oriCenter, int zoom, int offsetX, int offsetY) {

                        LonLat result = new LonLat();

                 

                   // 取得中心点pixel坐标

                 PixelXY cPixel = convertMercXY2PixelXY(convertLonLat2MercXY(oriCenter), zoom);

// 新中心点的pixel坐标

PixelXY newCPixel = new PixelXY();

newCPixel.X = cPixel.X - offsetX;      newCPixel.Y = cPixel.Y - offsetY;

                   // 新中心点的经纬度坐标取得

                         result = convertMercXY2LonLat(convertPixelXY2MercXY(newCPixel, zoom));

                 

                    return result;

         }

 

 

 

 

© 著作权归作者所有

小云1983
粉丝 1
博文 1
码字总数 1354
作品 0
朝阳
架构师
私信 提问
基于html5绘制上海地铁图 - 路况信息展示

前面介绍了上海地铁图的绘制,最近有客户提出了新的需求:双车道,并显示路网状态信息。经过一番研究,在原地铁图基础上做了扩展实现 交通图介绍 路况状态在GIS系统中广泛应用,谷歌地图,百...

nosand
2014/05/13
6.4K
19
2D横纵版与斜视角游戏地图开发原理

一个学生问的问题,借机做了个文档。发到博客。 2D横纵版与斜视角游戏地图 开发原理 作者 Honghaier QQ:285421210 日期:2009-12-8 开发前提: 1.假设您已经常握了C++语言,并能够熟练使用V...

长平狐
2013/03/19
757
0
2D横纵版与斜视角游戏地图开发原理

一个学生问的问题,借机做了个文档。发到博客。 2D横纵版与斜视角游戏地图 开发原理 作者 Honghaier QQ:285421210 日期:2009-12-8 开发前提: 1.假设您已经常握了C++语言,并能够熟练使用V...

长平狐
2012/11/19
141
0
(译)如何使用cocos2d制作基于tile地图的游戏教程:第一部分

 免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿...

孙启超
2013/08/27
127
0
Android游戏开发之地图编辑器的使用以及绘制地图 (四)

雨松MOMO带你走进游戏开发的世界之地图编辑器的使用以及绘制地图 雨松MOMO原创文章如转载,请注明:转载自雨松MOMO的博客原文地址:http://blog.csdn.net/xys289187120/article/details/6615...

晨曦之光
2012/03/07
342
0

没有更多内容

加载失败,请刷新页面

加载更多

golang-字符串-地址分析

demo package mainimport "fmt"func main() {str := "map.baidu.com"fmt.Println(&str, str)str = str[0:5]fmt.Println(&str, str)str = "abc"fmt.Println(&s......

李琼涛
54分钟前
4
0
Spring Boot WebFlux 增删改查完整实战 demo

03:WebFlux Web CRUD 实践 前言 上一篇基于功能性端点去创建一个简单服务,实现了 Hello 。这一篇用 Spring Boot WebFlux 的注解控制层技术创建一个 CRUD WebFlux 应用,让开发更方便。这里...

泥瓦匠BYSocket
今天
6
0
从0开始学FreeRTOS-(列表与列表项)-3

FreeRTOS列表&列表项的源码解读 第一次看列表与列表项的时候,感觉很像是链表,虽然我自己的链表也不太会,但是就是感觉很像。 在FreeRTOS中,列表与列表项使用得非常多,是FreeRTOS的一个数...

杰杰1号
今天
4
0
Java反射

Java 反射 反射是框架设计的灵魂(使用的前提条件:必须先得到代表的字节码的 Class,Class 类 用于表示.class 文件(字节码)) 一、反射的概述 定义:JAVA 反射机制是在运行状态中,对于任...

zzz1122334
今天
5
0
聊聊nacos的LocalConfigInfoProcessor

序 本文主要研究一下nacos的LocalConfigInfoProcessor LocalConfigInfoProcessor nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java p......

go4it
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部