MKMapView setRegion“捕捉”预定义的缩放级别?
任何人都可以确认 setRegion “捕捉”到预定义的缩放级别以及此行为是否符合设计(尽管未记录)或已知的错误?具体来说,setRegion 似乎会捕捉到与用户双击地图时使用的缩放级别相对应的相同缩放级别。
我正在尝试恢复以前保存的区域,但如果保存的区域是通过捏缩放而不是双击缩放设置的,则此行为将变得不可能。
对我来说,Mapkit 方面出现问题的一个重要线索是,如果我在地图的当前区域上调用regionThatFits,会发生什么情况。它应该返回相同的区域(因为它显然适合地图的框架),但它返回与下一个更高的预定义缩放级别相对应的区域。
setVisibleMapRect 的行为类似。
任何进一步的见解或信息将不胜感激。
我找到了这些相关的帖子,但都没有包含解决方案或明确确认这实际上是一个 Mapkit 错误:
编辑:
这是一个演示该问题的示例。所有值对于我的地图视图的纵横比均有效:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = 47.700200f;
initialRegion.center.longitude = -122.367109f;
initialRegion.span.latitudeDelta = 0.065189f;
initialRegion.span.longitudeDelta = 0.067318f;
[map setRegion:initialRegion animated:NO];
NSLog(@"DEBUG initialRegion: %f %f %f %f", initialRegion.center.latitude, initialRegion.center.longitude, initialRegion.span.latitudeDelta, initialRegion.span.longitudeDelta);
NSLog(@"DEBUG map.region: %f %f %f %f", map.region.center.latitude, map.region.center.longitude, map.region.span.latitudeDelta, map.region.span.longitudeDelta);
输出:
DEBUG initialRegion: 47.700199 -122.367111 0.065189 0.067318
DEBUG map.region: 47.700289 -122.367096 0.106287 0.109863
注意纬度/经度增量值的差异。地图的值几乎是我要求的两倍。较大的值对应于用户双击地图时使用的缩放级别之一。
Can anyone confirm that setRegion "snaps" to predefined zoom levels and whether or not this behavior is as designed (although undocumented) or a known bug? Specifically, it appears that setRegion snaps to the same zoom levels that correspond to the zoom levels used when the user double-taps the map.
I'm trying to restore a previously saved region but this behavior makes it impossible if the saved region was set via a pinch zoom and not a double-tap zoom.
A big clue to me that things are broken on the mapkit side is what occurs if I call regionThatFits on the map's current region. It should return the same region (since it obviously fits the map's frame) but it returns the region that corresponds to the next higher predefined zoom level instead.
setVisibleMapRect behaves similarly.
Any further insight or information would be appreciated.
I found these related posts but neither included a solution or definitive confirmation that this is in fact a mapkit bug:
MKMapView setRegion: odd behavior?
MKMapView show incorrectly saved region
EDIT:
Here is an example that demonstrates the problem. All values are valid for my map view's aspect ratio:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = 47.700200f;
initialRegion.center.longitude = -122.367109f;
initialRegion.span.latitudeDelta = 0.065189f;
initialRegion.span.longitudeDelta = 0.067318f;
[map setRegion:initialRegion animated:NO];
NSLog(@"DEBUG initialRegion: %f %f %f %f", initialRegion.center.latitude, initialRegion.center.longitude, initialRegion.span.latitudeDelta, initialRegion.span.longitudeDelta);
NSLog(@"DEBUG map.region: %f %f %f %f", map.region.center.latitude, map.region.center.longitude, map.region.span.latitudeDelta, map.region.span.longitudeDelta);
OUTPUT:
DEBUG initialRegion: 47.700199 -122.367111 0.065189 0.067318
DEBUG map.region: 47.700289 -122.367096 0.106287 0.109863
Note the discrepancy in the latitude/longitude delta values. The map's values are almost double what I requested. The larger values correspond to one of the zoom levels used when the user double-taps the map.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
是的,它会捕捉到离散的级别。我做了相当多的实验,似乎喜欢每个像素经度 2.68220906e-6 度的倍数。
因此,如果您的地图填满屏幕的整个宽度,则第一个级别的跨度为 0.0008583 度,那么您可以获得的下一个级别是该级别的两倍,即 0.001717,然后下一个级别的跨度是该级别的两倍,即 0.003433,依此类推。我不确定他们为什么选择按经度标准化,这意味着修复缩放级别会根据您正在查看的世界的哪个部分而有所不同。
我还花了很多时间试图理解 0.68220906e-6 度这个数字的意义。赤道处的距离约为 30 厘米,这是有道理的,因为 Google 地图使用的高分辨率照片具有 30 厘米的分辨率,但我希望他们使用纬度而不是经度来确定缩放级别。这样,在最大变焦时,您始终是卫星图像的原始分辨率,但谁知道呢,他们可能有一些聪明人的理由让它这样工作。
在我的应用程序中,我需要显示一定范围的纬度。我将编写一些代码来尝试将地图缩放到尽可能接近的程度。如果有人有兴趣,请联系我。
Yes, it snaps to discrete levels. I've done quite a bit of experimentation, and it seems to like multiples of 2.68220906e-6 degrees of longitude per pixel.
So if your map fills the whole width of the screen, the first level spans .0008583 degrees, then the next level up you can get is twice that, .001717, and then the next one is twice that, .003433, and so on. I'm not sure why they chose to normalize by longitude, it means that fixes zoom levels vary depending on what part of the world you are looking at.
I've also spent a lot of time trying to understand the significance of that number .68220906e-6 degrees. It comes out to about 30cm at the equator, which kind of makes sense since the high resolution photos used by Google Maps have a 30cm resolution, but I would have expected them to use latitude instead of longitude to establish the zoom levels. That way, at maximum zoom, you always the native resolution of the satellite images, but who knows, they probably have some smart-people reason for making it work like that.
In my application I need to display a certain range of latitude. I'm gonna work on some code to try to zoom the map as close as possible to that. If anyone is interested, contact me.
我找到了解决方案。
如果接收到的捕捉缩放级别比所需的缩放级别大 1.2:
使用这个算法来纠正:
假设:您希望将地图视图设置为从左到右精确显示“longitudinalMeters”
1)计算校正比例:
计算您收到的纵向跨度与您拥有的纵向跨度之间的关系。
2) 创建变换并将其应用到地图
3) 将逆变换应用到地图图钉,以使其保持原始大小
I found a solution.
If the received snapped zoom level, is, lets say a factor of 1.2 bigger than the desired one:
use this algorithm to correct:
Asumption: you want to set the map view to exactly show "longitudinalMeters" from left to right
1) Calculate the correction scale:
Calculate the relation between longitudinal span you received, to that one you have got.
2) Create the transformation and apply it to the map
3) Apply the inverse transformation to the Map pins, to keep them at original size
这种奇怪的行为似乎是由于这样一个事实:当请求特定区域或视图大小时,对 google 的实际 API 调用是使用中心点和缩放级别来调用的。 EG:
现在苹果可以请求适当的缩放级别,然后调整视图的大小以适应实际的区域请求,但他们似乎没有这样做。我正在地图上绘制公交路线,其中一条路线几乎无法触发更大的缩放级别,因此比例太小(缩放不足)并且看起来丑陋且破碎。
@pseudopeach,请向我通报您尝试解决此问题的最新进展。如果可以检测到缩放级别的边界,则可以故意缩小区域请求以避免缩放不足。既然您对此感兴趣,我有兴趣在我自己尝试之前查看您的代码。
博客 Backspace Prolog 的作者编写了一个有趣的类别,通过模拟 setCenter(centerPoint,ZoomLevel) 调用签名来实现对 Google Maps API 的直接操作。您可以在此处找到它。我还没有花时间,但是可以对数学进行逆向工程,以产生计算给定区域或 MapRect 的缩放级别的方法。根据它在缩放级别范围内的距离(即超出触发较低缩放级别的阈值的距离),它可以决定是转到较低级别还是通过请求不足保持较高级别。
这显然是一个行为错误,需要修复,以便 MKMapView 能够以更精细的方式使用。
The weird behavior seems to be due to the fact that while one requests a particular region or view size, the actual API call to google is invoked with a center point and a zoom level. E.G.:
Now it would be possible for Apple to request the appropriate zoom level and then adjust the sizing of the view to accommodate the actual region request, but it seems they fail to do so. I am drawing bus routes on a map, and one of my routes barely triggers a larger zoom level and thus scales too small (under-zooms) and looks ugly and smashed.
@pseudopeach, Please update me on the progress of your attempts to work around this issue. If one could detect the boundaries of a zoom level, the region request could then be deliberately underscaled to avoid the under-zoom. Since you are onto this I would be interested in seeing your code before I have to make an attempt at it myself.
There is an interesting category that the author of the blog Backspace Prolog has written to enable the direct manipulation of the Google Maps API by emulating their setCenter(centerPoint,ZoomLevel) call signature. You can find it here. I haven't spent the time yet, but the math can probably be reverse engineered to yield a means of calculating the zoom level for a given Region or MapRect. Depending on how far it is within the zoom level's range - i.e. how far it is over the threshold that triggers the lower zoom level - it could decide whether to go to the lower level or keep to higher one by under-requesting.
This is clearly a behavioral bug that needs to be fixed so that MKMapView can be used in a more refined manner.
这是一个老问题,但我最近详细研究了谷歌地图,并且可以分享一些见解。我不知道这是否也适用于当前的苹果地图。
分辨率捕捉到预定义缩放级别的原因是因为从 Google 服务器获取的原始地图是使用这些缩放级别绘制的。这些地图上要素的大小是按照一定的分辨率绘制的。例如,这些地图上道路的宽度(以像素为单位)始终相同。在更高分辨率的地图上,会绘制更多的次要道路,但它们的宽度始终相同。分辨率捕捉到预定义的级别,以确保这些要素始终以相同的尺寸描绘。也就是说,它不是一个错误而是一个功能。
由于地图的墨卡托投影,这些预定义的分辨率随纬度变化。墨卡托投影很容易使用,因为纬线被描绘为笔直且水平的,而经度线被描绘为笔直且垂直的。但使用墨卡托投影时,地图顶部的分辨率略高于底部(在北半球)。这会对将地图在北部和南部边缘拟合在一起产生影响。
也就是说,当您从赤道开始并向北行驶时,您行驶过的墨卡托地图的分辨率将逐渐增加。经度线保持垂直,因此经度跨度保持不变。但分辨率增加,因此纬度跨度减小。尽管如此,在所有这些地图上,道路都具有相同的像素宽度,并且文本以相同的字体大小描绘,等等。
谷歌使用墨卡托投影,其中赤道周长在缩放级别 0 时为 256 像素。每个下一个缩放级别都会将该数量加倍。也就是说,在缩放级别 1 时,赤道长 512 像素,在缩放级别 2 时,赤道长 1024 像素,等等。他们使用的地球模型是 FAI 地球仪,半径恰好为 6371 公里,周长为 40030公里。
因此,赤道缩放级别 0 的分辨率为 156.37 公里/像素,缩放级别 1 的分辨率为 78.19 公里/像素,依此类推。这些分辨率会随着地球上其他任何地方的纬度余弦而变化。
This is an old question, but I recently investigated Google maps in detail, and can share some insight. I don't know whether this is also valid for the current Apple maps.
The reason that the resolution snaps to predefined zoomlevels is because the original maps fetched from Google's servers are drawn with those zoomlevels. The size of the features on those maps are drawn with a certain resolution in mind. For example, the width (in pixels) of a road on those maps is always the same. On higher resolution maps, more secundary roads are drawn, but their width is always the same. The resolution snaps to predefined levels to make sure those features are always depicted with the same size. That is, it is not a bug but a feature.
Those predefined resolutions vary with latitude because of the Mercator projection of the maps. Mercator projection is easy to work with because latitude lines are depicted straight and horizontal and longitude lines are straight and vertical. But with Mercator projection the top of the map has a slightly higher resolution than the bottom (on the Northern hemisphere). That has consequences for fitting maps together at the northern and sourthern edges.
That is, when you start on the equator and drive north, then the resolution of the Mercator maps you drive over will gradually increase. The longitude lines remain vertical, and therefore the longitude spans remains the same. But the resolution increases, and therefore the latitude span decreases. Still, on all those maps the roads have the same width in pixels, and texts are depicted in the same font size, etc.
Google uses a Mercator projection where the equator circumference is 256 pixels at zoomlevel 0. Each next zoomlevel doubles that amount. That is, at zoomlevel 1, the equator is 512 pixels long, at zoomlevel 2, the equator is 1024 pixels long, etc. The model for the earth they use is a FAI globe with a radius of exactly 6371 km, or circumference of 40030 km.
Therefore, resolution for zoomLevel 0 at the equator is 156.37 km/pixel, at zoomlevel 1 it is 78.19 km/pixel, etc. Those resolutions then vary with the cosinus of the latitude anywhere else on the earth.
MKCooperativeRegion区域;
MKCoordinateRegion region;
正如您所描述的,我恢复了该区域,没有任何问题,也没有任何差异。如果没有一些代码可供查看,实际上不可能判断出您的情况具体出了什么问题,但这对我有用:
将中心值和跨度值保存在某处。当您恢复它们时,请特别设置中心和跨度。
恢复应该如下所示:
另请记住,此方法在 4.0 中可用: `mapRectThatFits:edgePadding: MapRectThatFits 有助于添加合理的边框,以确保边缘上的地图注释不会被遮挡,并且您尝试的矩形不会被遮挡显示完全可见。如果您想控制边框,请使用允许您设置 edgePadding 的调用。
I restore the region with no problem and with no variance as you describe. It is really impossible to tell what is specifically wrong in your case without some code to look at but here's what works for me:
Save both the center and span values somewhere. When you are restoring them specifically set both the center and span.
Restoring should look like this:
Also remember that this method is available in 4.0: `mapRectThatFits:edgePadding: MapRectThatFits helpfully adds a reasonable border to ensure that say a map annotation on the edge is not obscured and the the rect that you're attempting to display is fully visible. If you want to control the border use the call that gives you access to set edgePadding as well.
如果您在 InterfaceBuilder 中设置 MapView,请确保不要这样做:
_mapView = [[MKMapView alloc] init];
当我删除此初始化行后,我的地图视图突然开始正确响应我发送的所有更新。我怀疑发生的情况是,如果您执行 alloc init,它实际上会创建另一个未在任何地方显示的视图。您在屏幕上看到的就是由您的笔尖初始化的。但是如果你分配了一个新的 init ,那么它就在其他地方并且不会做任何事情。
If you set up the MapView in InterfaceBuilder, make sure you don't do this:
_mapView = [[MKMapView alloc] init];
As soon as I removed this init line, my map view suddenly began responding properly to all the updates I sent it. I suspect that what happens is that if you do the alloc init, it's actually creating another view that's not being shown anywhere. The one you see on the screen is the one initialized by your nib. But if you alloc init a new one, then that's something somewhere else and it's not going to do anything.