当“过度缩放”时,计算要在MapRect中显示的切片。超出叠加图块集 [英] Calculating tiles to display in a MapRect when "over-zoomed" beyond the overlay tile set

查看:107
本文介绍了当“过度缩放”时,计算要在MapRect中显示的切片。超出叠加图块集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一款使用MKOverlay视图的应用,可以在Google基本地图上叠加我自己的自定义地图。我一直在使用Apple优秀的TileMap示例代码(来自WWDC 2010)作为指南。

I am working on an app that uses MKOverlay views to layer my own custom maps on top of the Google base map. I have been using Apple's excellent TileMap sample code (from WWDC 2010) as a guide.

我的问题 - 当过度使用到比我生成的瓷砖更深的细节水平时设置,代码没有显示任何内容,因为在计算的Z级别上没有可用的区块。

My problem - when "overzoomed" to a level of detail deeper than my generated tile set, the code displays nothing because there are no tiles available at the calculated Z level.

我想要的行为 - 当过度使用应用程序应该只是放大最深处瓷砖水平。叠加层变得模糊是一种很好的用户体验 - 叠加层消失是一种非常糟糕的体验。

The behavior I want - when "overzoomed" the app should just keep magnifying the deepest level of tiles. It is a good user experience for the overlay to become blurrier - it is a very bad experience to have the overlay vanish.

这是返回要绘制的图块的代码 - 我需要弄清楚如何修改它以限制Z深度,而不会破坏为叠加图块计算的帧的缩放。任何想法???

Here is the code which returns the tiles to draw - I need to figure out how to modify this to cap the Z-depth without breaking the scaling of the frame being calculated for the overlay tile. Any thoughts???

- (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帮助函数:

FYI, here is the zoomScaleToZoomLevel helper function that someone asked about:

// 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;
}


推荐答案

想象一下叠加层是云覆盖 - 或在我们的情况下,蜂窝信号覆盖。在放大时可能不会看起来很好,但叠加层仍在向用户传达重要信息。

Imagine that the overlay is cloud cover - or in our case, cellular signal coverage. It might not "look good" while zoomed in deep, but the overlay is still conveying essential information to the user.

我通过添加OverZoom解决了这个问题模式来增强Apple的TileMap示例代码。

I've worked around the problem by adding an OverZoom mode to enhance Apple's TileMap sample code.

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

Here is the new tilesInMapRect function in TileOverlay.m:

- (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:

And here is the new drawMapRect in TileOverlayView.m:

- (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];
    }
}

现在似乎工作得很好。

BTW - 我认为TileMap示例代码缺少[图像发布]并且正在泄漏内存。请注意我在上面的代码中添加它。

BTW - I think the TileMap sample code is missing an [image release] and was leaking memory. Note where I added it in the code above.

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

I hope that this helps some others with the same problem.

干杯,


  • Chris

这篇关于当“过度缩放”时,计算要在MapRect中显示的切片。超出叠加图块集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆