计算贴图时,如果在“叠加”贴图集之外的情况下显示在MapRect中

我正在研究一个使用MKOverlay视图的应用程序,将我自己的自定义地图放在Google底图上。 我一直在使用苹果公司出色的TileMap示例代码(来自WWDC 2010)作为指导。

我的问题 – 当“overzoomed”到比我生成的tile组更深的细节级别时,代码不显示任何内容,因为在计算的Z级没有可用的tile。

我想要的行为 – 当“超越”应用程序应该不断放大最深的瓷砖水平。 覆盖层变得模糊是一种很好的用户体验 – 覆盖层消失是一个非常糟糕的体验。

这里是返回要绘制的瓷砖的代码 – 我需要弄清楚如何修改这个来限制Z-深度,而不会破坏为叠加瓷砖计算的框架的缩放比例。 有什么想法吗???


- (NSArray *)tilesInMapRect:(MKMapRect)rect zoomScale:(MKZoomScale)scale { NSInteger z = zoomScaleToZoomLevel(scale); // PROBLEM: I need to find a way to cap z at my maximum tile directory depth. // Number of tiles wide or high (but not wide * high) NSInteger tilesAtZ = pow(2, z); NSInteger minX = floor((MKMapRectGetMinX(rect) * scale) / TILE_SIZE); NSInteger maxX = floor((MKMapRectGetMaxX(rect) * scale) / TILE_SIZE); NSInteger minY = floor((MKMapRectGetMinY(rect) * scale) / TILE_SIZE); NSInteger maxY = floor((MKMapRectGetMaxY(rect) * scale) / TILE_SIZE); NSMutableArray *tiles = nil; for (NSInteger x = minX; x <= maxX; x++) { for (NSInteger y = minY; y <= maxY; y++) { // As in initWithTilePath, need to flip y index // to match the gdal2tiles.py convention. NSInteger flippedY = abs(y + 1 - tilesAtZ); NSString *tileKey = [[NSString alloc] initWithFormat:@"%d/%d/%d", z, x, flippedY]; if ([tilePaths containsObject:tileKey]) { if (!tiles) { tiles = [NSMutableArray array]; } MKMapRect frame = MKMapRectMake((double)(x * TILE_SIZE) / scale, (double)(y * TILE_SIZE) / scale, TILE_SIZE / scale, TILE_SIZE / scale); NSString *path = [[NSString alloc] initWithFormat:@"%@/%@.png", tileBase, tileKey]; ImageTile *tile = [[ImageTile alloc] initWithFrame:frame path:path]; [path release]; [tiles addObject:tile]; [tile release]; } [tileKey release]; } } return tiles; } 

仅供参考,以下是有人询问的zoomScaleToZoomLevel帮助函数:

 // Convert an MKZoomScale to a zoom level where level 0 contains 4 256px square tiles, // which is the convention used by gdal2tiles.py. static NSInteger zoomScaleToZoomLevel(MKZoomScale scale) { double numTilesAt1_0 = MKMapSizeWorld.width / TILE_SIZE; NSInteger zoomLevelAt1_0 = log2(numTilesAt1_0); // add 1 because the convention skips a virtual level with 1 tile. NSInteger zoomLevel = MAX(0, zoomLevelAt1_0 + floor(log2f(scale) + 0.5)); return zoomLevel; } 

想象一下覆盖层是云覆盖 – 在我们的例子中是蜂窝信号覆盖。 在放大深度时,它可能不是“看起来不错”,但叠加层仍然向用户传递重要信息。

我已经解决了这个问题,增加了一个OverZoom模式来增强Apple的TileMap示例代码。

这里是TileOverlay.m中新的tilesInMapRect函数:

 - (NSArray *)tilesInMapRect:(MKMapRect)rect zoomScale:(MKZoomScale)scale { NSInteger z = zoomScaleToZoomLevel(scale); // OverZoom Mode - Detect when we are zoomed beyond the tile set. NSInteger overZoom = 1; NSInteger zoomCap = MAX_ZOOM; // A constant set to the max tile set depth. if (z > zoomCap) { // overZoom progression: 1, 2, 4, 8, etc... overZoom = pow(2, (z - zoomCap)); z = zoomCap; } // When we are zoomed in beyond the tile set, use the tiles // from the maximum z-depth, but render them larger. NSInteger adjustedTileSize = overZoom * TILE_SIZE; // Number of tiles wide or high (but not wide * high) NSInteger tilesAtZ = pow(2, z); NSInteger minX = floor((MKMapRectGetMinX(rect) * scale) / adjustedTileSize); NSInteger maxX = floor((MKMapRectGetMaxX(rect) * scale) / adjustedTileSize); NSInteger minY = floor((MKMapRectGetMinY(rect) * scale) / adjustedTileSize); NSInteger maxY = floor((MKMapRectGetMaxY(rect) * scale) / adjustedTileSize); NSMutableArray *tiles = nil; for (NSInteger x = minX; x <= maxX; x++) { for (NSInteger y = minY; y <= maxY; y++) { // As in initWithTilePath, need to flip y index to match the gdal2tiles.py convention. NSInteger flippedY = abs(y + 1 - tilesAtZ); NSString *tileKey = [[NSString alloc] initWithFormat:@"%d/%d/%d", z, x, flippedY]; if ([tilePaths containsObject:tileKey]) { if (!tiles) { tiles = [NSMutableArray array]; } MKMapRect frame = MKMapRectMake((double)(x * adjustedTileSize) / scale, (double)(y * adjustedTileSize) / scale, adjustedTileSize / scale, adjustedTileSize / scale); NSString *path = [[NSString alloc] initWithFormat:@"%@/%@.png", tileBase, tileKey]; ImageTile *tile = [[ImageTile alloc] initWithFrame:frame path:path]; [path release]; [tiles addObject:tile]; [tile release]; } [tileKey release]; } } return tiles; } 

这里是TileOverlayView.m中的新的drawMapRect:

 - (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { // OverZoom Mode - Detect when we are zoomed beyond the tile set. NSInteger z = zoomScaleToZoomLevel(zoomScale); NSInteger overZoom = 1; NSInteger zoomCap = MAX_ZOOM; if (z > zoomCap) { // overZoom progression: 1, 2, 4, 8, etc... overZoom = pow(2, (z - zoomCap)); } TileOverlay *tileOverlay = (TileOverlay *)self.overlay; // Get the list of tile images from the model object for this mapRect. The // list may be 1 or more images (but not 0 because canDrawMapRect would have // returned NO in that case). NSArray *tilesInRect = [tileOverlay tilesInMapRect:mapRect zoomScale:zoomScale]; CGContextSetAlpha(context, tileAlpha); for (ImageTile *tile in tilesInRect) { // For each image tile, draw it in its corresponding MKMapRect frame CGRect rect = [self rectForMapRect:tile.frame]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:tile.imagePath]; CGContextSaveGState(context); CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); // OverZoom mode - 1 when using tiles as is, 2, 4, 8 etc when overzoomed. CGContextScaleCTM(context, overZoom/zoomScale, overZoom/zoomScale); CGContextTranslateCTM(context, 0, image.size.height); CGContextScaleCTM(context, 1, -1); CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), [image CGImage]); CGContextRestoreGState(context); // Added release here because "Analyze" was reporting a potential leak. Bug in Apple's sample code? [image release]; } } 

似乎现在工作很好。

顺便说一句 – 我认为TileMap示例代码缺less[图像发布],并泄漏内存。 注意我在上面的代码中添加了它。

我希望这可以帮助其他人解决同样的问题。

干杯,

  • 克里斯

这个algorithm似乎会在MapRect之外产生大量的地图块。 在循环内添加以下内容以跳过边界外的图块会有所帮助:

 if (! MKMapRectIntersectsRect(rect, tileMapRect)) continue; 

晚了一点晚了,但…在iOS 7.0及更高版本中,您可以使用maximumZ上的maximumZ属性。 从文档 :

如果使用不同的叠加对象以不同的缩放级别表示不同的拼贴,请使用此属性指定此叠加拼贴所支持的最大缩放级别。 在缩放级别0,瓦片覆盖整个世界地图; 在缩放级别1,瓷砖覆盖世界的1/4; 在缩放级别2,瓦片覆盖世界的1/16,依此类推。 地图不会尝试加载大于此属性指定的值的缩放级别的图块。

 - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay { if ([overlay isKindOfClass:[MKTileOverlay class]]) { MKTileOverlay *ovrly = (MKTileOverlay *)overlay; ovrly.maximumZ = 9; // Set your maximum zoom level here MKTileOverlayRenderer *rndr = [[MKTileOverlayRenderer alloc] initWithTileOverlay:ovrly]; return rndr; } return nil; } 
    Interesting Posts