原帖:http://troybrant.net/blog/2010/01/mkmapview-and-zoom-levels-a-visual-guide/

So, how exactly does the code provided in the previous post work? What follows is a visual explanation of Google Maps, zoom levels, and how you go about adding support for zoom levels to the MKMapView class.

Round to Flat

This is planet Earth:

As you may know, it is round.

To create a map of the Earth, the curved surface must be projected onto a flat surface. There are many map projections that attempt to flatten the Earth. There are distortions inherent to every projection, but each map projection aims to preserve at least one quality from the original curved representation.

Some projections preserve area, such as the Mollweide projection:

Equirectangluar projections preserve distance between the meridians:

The Mercator projection stretches out the poles in order to preserve locally measured angles:

Google uses the Mercator projection to render Google Maps:

Mercator Math

The Mercator projection converts latitude (φ) and longitude (λ) coordinates to pixel values. It uses math:

You don’t have to understand the math; just know that it converts latitudes and longitudes to pixels.

But, where are these pixels? Well, it depends on your zoom level.

Zoom Levels

At zoom level 0, Google displays the world in a single 256 pixel by 256 pixel tile:

At zoom level 1, Google doubles the area of the map while keeping the tile size constant. So, the map grows to 512 pixels by 512 pixels and uses four tiles:

At zoom level 2, Google doubles the area again. The map grows to 1024 pixels by 1024 pixels and uses sixteen tiles:

The pixel area continues to double at each zoom level, and when zoom level 20 is reached, the map is 536,870,912 pixels by 536,870,912 pixels. It has so many tiles we won’t bother to count them:

Latitudes and Longitudes to Pixels

As part of the PHP Static Maps project, Mike Tuupola wrote some code that converts latitudes and longitudes to pixels at zoom level 20. The code is easily ported to Objective-C:

// Convert latitude and longitude to pixel values at zoom level 20

#define MERCATOR_OFFSET 268435456 /* (total pixels at zoom level 20) / 2 */
#define MERCATOR_RADIUS 85445659.44705395 /* MERCATOR_OFFSET / pi */

x = round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
y = round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);

To be honest, I haven’t taken the time to wrap my head around how this code works. But, knowing that it does
work, we can now take any latitude and longitude and figure out its
pixel coordinates at zoom level 20. For instance, here are the pixel
coordinates of several cities around the world:

Add an iPhone

Say we place an iPhone on top of Anchorage, Alaska at zoom level 20:

In the iPhone shown above, the map size is 320 pixels by 460 pixels.
Since we know the map dimensions and center coordinate in pixels, we can
easily compute the pixel coordinates of the top-left corner relative to
the center pixel coordinate:

We can find the relative position of the top-right and bottom-left pixel coordinates as well:

The PHP Static Maps code also provides code to go from pixels at zoom level 20 to latitudes and longitudes:

// Convert pixel values at zoom level 20 to latitude and longitude

latitude = (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
longitude = ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;

We can use this code to convert the corners from pixel coordinates to latitudes and longitudes:

As shown above, using the corner coordinates, we can compute the
latitudinal and longitudinal distances. These distances are exactly what
we need to construct an MKCoordinateSpan. That span, in turn, is used to initialize the region property of an MKMapView:

// Create an MKCoordinateSpan to initialize the map’s region

MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
[mapView setRegion:region animated:NO];

And you’re done!…That is, if you want to see zoom level 20. What do
you do when your user wants to see the map at zoom level 19 instead of
20?

Scaling using Zoom Levels

Relative to zoom level 20, zooming out one level doubles the area visible on the map.

For example, consider the image below. On the left is Anchorage at
zoom level 19, and on the right are the 4 iPhones at zoom level 20 it
would take to display the same amount of area:

If we move up another level, the area doubles again. Consider the
following image. On the left is Anchorage at zoom level 18, and on the
right are the 16 iPhones at zoom level 20 it would take to display the
same amount of area:

Since the area doubles at each zoom level, we can define the
following exponential relationship between the zoom level and the area
covered by the map:

// Compute a scaling factor that will take us from any zoom level to zoom level 20

NSInteger zoomExponent = 20 - zoomLevel;
double zoomScale = pow(2, zoomExponent);

double scaledMapWidth = mapSizeInPixels.width * zoomScale;
double scaledMapHeight = mapSizeInPixels.height * zoomScale;

For instance, here is Anchorage at zoom levels 20, 19, and 18. The map’s width and height in pixels are unaltered:

After computing the zoom scale factor, we can apply it to each map to determine its dimensions at zoom level 20:

After we compute these new dimensions, we plug them into the algorithm for finding the coordinates of the map corners.

An Example: Zoom Level 18

For instance, say we take the map at zoom level 18:

Let’s drop the matrix of phones but keep the scaled width and height:

We find the top-left corner just like we did before, except now we use the scaled width and height:

Similarly, we use the scaled width and height for finding the top-right and bottom-left corners as well:

Using the pixel and latitude and pixel and longitude helper methods,
we can compute the coordinates of the corners and the distance between
them:

These delta values are used to initialize the map’s region property, and the map zooms to the level you specify.

That’s a Wrap

Be sure to check out the previous post for the full code that adds support for zoom levels to MKMapView.

If you are interested in learning more from someone much smarter than I am, check out these posts from Charlie Savage, a programmer and cartographer extraordinaire:

Much of what I know about maps is from these articles, and I highly
recommended checking them out if you want to learn more about how Google
Maps works under the hood.

MKMapView and Zoom Levels: A Visual Guide的更多相关文章

  1. [Forward]Visual Guide: Setting up My Sites in SharePoint 2013

    from  http://blog.sharedove.com/adisjugo/index.php/2012/07/25/visual-guide-setting-up-my-sites-in-sh ...

  2. A SIMPLE LIBRARY TO BUILD A DEEP ZOOM IMAGE

    My current project requires a lot of work with Deep Zoom images. We recently received some very high ...

  3. leaflet地图库

    an open-source JavaScript libraryfor mobile-friendly interactive maps Overview Tutorials Docs Downlo ...

  4. Android Weekly Notes Issue #221

    Android Weekly Issue #221 September 4th, 2016 Android Weekly Issue #221 ARTICLES & TUTORIALS And ...

  5. less 基础+ flex

    1.less 中的变量 @ 符号 引入 /*普通变量*/ @color:pinker; .styles{ color:@color; } /*选择器变量*/ @I:img; @{I}{ width: ...

  6. Flex 布局教程:语法篇

    作者: 阮一峰 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便 ...

  7. TileJSON

    TileJSON TileJSON is an open standard for representing map metadata. License The text of this specif ...

  8. MBTiles

    MBTiles Specification MBTiles is a specification for storing tiled map data in SQLite databases for ...

  9. 【转】Flex 布局语法教程

    网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便,比如,垂直居中 ...

随机推荐

  1. 【转】IOS7 MPMoviePlayerViewController横屏显示

    在应用程序中用到MPMoviePlayerViewController时,有时需要保持应用程序为竖屏状态,而视频播放器显示为横屏,如何做呢?如果采用强制横屏的方法,应用审核的时候是不会通过的,因为该方 ...

  2. nginx 2.基本配置

    死磕nginx 2.基本配置 鉴于深入浅出的原理,我们先从一个简单的配置了解nginx的配置 1.一个典型配置 nginx的配置文件默认在nginx安装目录的conf二级目录下面,主配置文件为 ngi ...

  3. fork安全的gettid高效实现

    进程有id,可以通过getpid()获得,线程也有id,但是glibc没有提供封装.需要自己发出系统调用.在关键路径,系统调用还是对性能有影响的.因此我们可以想到类似glibc对getpid做的cac ...

  4. C#总结项目《影院售票系统》编写总结完结篇

    回顾:昨天总结了影院售票系统核心部分-售票,整个项目也就完成了2/3了,需求中也要求了对销售信息的保存,今天就继续总结销售信息的保存以及加载销售信息. 分析:退出程序时将已售出票集合中的对象循环写入到 ...

  5. 推荐一个CMMI认证查询网站

    随着软件企业的增多和意识的增强,越来越多公司开始做CMMI的认证评估,由于国内网速和CMMI官网的网站综合原因,打开速度超级慢. 所以本文推荐一个CMMI认证查询网站,认证后的结果查询可以点这里查询: ...

  6. 如何搭建DHCP及DHCP中继服务器

    当局域网中有大量的主机时,如果逐台设置ip地址.默认网关.dns服务器地址时等网络参数,显然是一个费力也未必讨好的方法,这时使用DHCP的方式分发ip地址,能够动态配置各客户机的网络地址参数,大大减轻 ...

  7. MySQL FROM 子查询

    FROM 子句中的子查询 MySQL FROM 子查询是指 FROM 的子句作为子查询语句,主查询再到子查询结果中获取需要的数据.FROM 子查询语法如下: SELECT ... FROM (subq ...

  8. ubuntu 安装 maven3

    sudo add-apt-repository ppa:natecarlson/maven3 sudo apt-get update && sudo apt-get install m ...

  9. MYSQL Model报错:指定的存储区提供程序在配置中找不到 的解决

    开了项目发现没装mysql及mysql connector/.net.下了个最新版本,结果打开vs,进入模型edmx页面就出了这个问题. 刚开始以为是ProviderManifestToken版本的问 ...

  10. 武汉科技大学ACM:1004: 华科版C语言程序设计教程(第二版)习题5.6

    Problem Description 这天老师又给小豪出了一道题目:给你三根长度分别为a,b,c的火柴,让你计算这三跟火柴能组成的三角形的面积. Input 输入每行包括三个数a,b,c. Outp ...