2010-12-11 25 views
10

Tôi đang làm việc trên một ứng dụng sử dụng chế độ xem MKOverlay để lớp bản đồ tùy chỉnh của riêng tôi trên bản đồ cơ sở của Google. Tôi đã sử dụng mã mẫu TileMap tuyệt vời của Apple (từ WWDC 2010) làm hướng dẫn.Tính gạch để hiển thị trong MapRect khi "phóng to" vượt ra ngoài bộ lớp phủ phủ

Vấn đề của tôi - khi "bị quá tải" đến mức chi tiết sâu hơn bộ gạch được tạo của tôi, mã sẽ không hiển thị gì vì không có ô có sẵn ở cấp Z được tính.

Hành vi tôi muốn - khi "quá tải", ứng dụng sẽ chỉ giữ cho việc phóng to mức độ sâu nhất của ô. Đó là trải nghiệm người dùng tốt cho lớp phủ để trở nên mờ hơn - đó là một trải nghiệm rất xấu để lớp phủ biến mất.

Đây là mã trả về các ô để vẽ - tôi cần tìm ra cách sửa đổi số này để giới hạn chiều sâu Z mà không làm vỡ tỷ lệ khung được tính cho lớp phủ. Bất kỳ suy nghĩ nào ???


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

FYI, đây là chức năng helper zoomScaleToZoomLevel rằng ai đó hỏi về:

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

Trả lời

12

Hãy tưởng tượng rằng lớp phủ là mây che phủ - hoặc trong trường hợp của chúng tôi, vùng phủ sóng di động. Nó có thể không "nhìn tốt" trong khi phóng to sâu, nhưng lớp phủ vẫn truyền đạt thông tin cần thiết cho người dùng.

Tôi đã giải quyết vấn đề bằng cách thêm chế độ OverZoom để tăng cường mã mẫu TileMap của Apple.

Dưới đây là chức năng tilesInMapRect mới trong 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; 
} 

Và đây là drawMapRect mới trong 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]; 
    } 
} 

vẻ khi được làm việc vĩ đại bây giờ.

BTW - Tôi nghĩ mã mẫu TileMap thiếu [hình ảnh phát hành] và đã bị rò rỉ bộ nhớ. Lưu ý nơi tôi đã thêm nó vào mã ở trên.

Tôi hy vọng điều này sẽ giúp một số người khác có cùng vấn đề.

Chúc mừng,

  • Chris
+2

Chris - bạn là một phao cứu sinh. Tôi không mong muốn tạo một giải pháp thay thế, vì vậy tôi đã thực hiện tìm kiếm và câu trả lời của bạn xuất hiện ngay lập tức. Tôi đã sao chép hàm 'zoomScaleToZoomLevel' và' TILE_SIZE' không đổi thành tệp TileOverlayView.m từ TileOverlay.m nên nó sẽ không gây ra lỗi, nhưng khác với nó hoạt động hoàn hảo. Cảm ơn rất nhiều! –

+0

nhận xét đã bị xóa, mã hoạt động tốt – Craig

+1

Tôi đang sử dụng một cách khác để tìm gạch của mình như được mô tả tại https://github.com/mtigas/iOS-MapLayerDemo, nhưng ý tưởng là về cơ bản giống nhau. Tôi đang gặp khó khăn trong việc chuyển đổi zoom sang nó. Bất cứ ai làm điều này? –

2

thuật toán này dường như tạo ra rất nhiều các ô bản đồ bên ngoài của MapRect. Thêm sau bên trong vòng lặp để bỏ gạch bên ngoài ranh giới sẽ giúp rất nhiều:

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

Một chút muộn để đảng, nhưng ... Theo iOS 7.0 và cao hơn, bạn có thể sử dụng tài sản trên maximumZMKTileOverlay. Từ the docs:

Nếu bạn sử dụng đối tượng lớp phủ khác nhau để đại diện cho gạch khác nhau tại mức zoom khác nhau, sử dụng tài sản này để xác định tối đa zoom mức hỗ trợ bởi gạch của lớp phủ này.Ở mức thu phóng 0, các ô bao gồm toàn bộ bản đồ thế giới; ở mức thu phóng 1, gạch che 1/4 của thế giới; ở mức thu phóng 2, các ô bao gồm 1/16 thế giới, v.v. Bản đồ không bao giờ cố gắng tải hình xếp cho mức thu phóng lớn hơn giá trị được chỉ định bởi thuộc tính này.

+1

Điều đó không ngăn bản đồ bị thu nhỏ quá mức tối thiểu - nó không chỉ hiển thị lớp phủ ở tất cả nghĩa là bản đồ bên dưới xuất hiện thay vào đó hoặc không có bản đồ nếu canReplaceMapContent được hiển thị – earnshavian

Các vấn đề liên quan