2010-12-11 1 views
10

Ich arbeite an einer App, die MKOverlay-Ansichten verwendet, um eigene benutzerdefinierte Karten über der Google-Basiskarte zu erstellen. Ich habe Apples exzellenten TileMap-Beispielcode (von WWDC 2010) als Leitfaden verwendet.Berechnen von Kacheln zur Anzeige in einem MapRect, wenn über den Zoom hinausgezoomt wird

Mein Problem - wenn es auf eine Detailtiefe "überzoomt" wurde, die tiefer als meine generierte Kachelmenge ist, zeigt der Code nichts an, da auf der berechneten Z-Ebene keine Kacheln verfügbar sind.

Das Verhalten, das ich will - wenn die App "overzoomed" sollte einfach nur die tiefste Ebene der Fliesen zu vergrößern. Es ist eine gute Benutzererfahrung, dass das Overlay verschwommener wird - es ist eine sehr schlechte Erfahrung, das Overlay verschwinden zu lassen.

Hier ist der Code, der die Kacheln zum Zeichnen zurückgibt - ich muss herausfinden, wie man dies ändert, um die Z-Tiefe zu überdecken, ohne die Skalierung des Rahmens zu brechen, der für die Überlagerungskachel berechnet wird. Irgendwelche Gedanken ???


- (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, hier ist die zoomScaleToZoomLevel Helferfunktion, dass jemand gefragt:

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

Antwort

12

Stellen Sie sich vor, dass die Overlay Wolkendecke ist - oder in unserem Fall, zellulären Signalabdeckung. Es sieht möglicherweise nicht gut aus, wenn es tief hineingezoomt wird, aber das Overlay liefert dem Benutzer immer noch wichtige Informationen.

Ich habe das Problem gelöst, indem ich einen OverZoom-Modus hinzugefügt habe, um den TileMap-Beispielcode von Apple zu verbessern.

Hier ist die neue tilesInMapRect Funktion 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; 
} 

Und hier ist der neue 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]; 
    } 
} 

scheint nun das Arbeiten groß zu werden.

BTW - Ich denke, der TileMap-Beispielcode fehlt eine [Bild-Release] und Speicher verloren ging. Beachten Sie, wo ich es im obigen Code hinzugefügt habe.

Ich hoffe, dass dies einigen anderen mit dem gleichen Problem hilft.

Cheers,

  • Chris
+2

Chris - du bist ein Lebensretter. Ich habe mich nicht darauf gefreut, einen Workaround zu erstellen, also habe ich eine Suche durchgeführt und Ihre Antwort ist direkt aufgetaucht. Ich habe die 'zoomScaleToZoomLevel' -Funktion und' TILE_SIZE' -Konstante in die TileOverlayView.m-Datei von TileOverlay.m kopiert, damit es keinen Fehler auslöst, aber ansonsten funktioniert es einwandfrei. Tausend Dank! –

+0

Kommentar entfernt, Code funktioniert gut – Craig

+1

Ich verwende einen anderen Weg, um meine Kacheln zu finden, wie unter https://github.com/mtigas/iOS-MapLayerDemo beschrieben, aber die Idee ist im Grunde die gleiche. Ich habe es jedoch schwer, den Over Zoom darauf zu portieren. Hat jemand das getan? –

2

Dieser Algorithmus scheint außerhalb des MapRect viele Kartenkacheln zu produzieren. Hinzufügen der folgenden innerhalb der Schleife überspringen Kacheln außerhalb der Grenzen hilft viel:

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

Ein bisschen spät zur Party, aber ... Unter iOS 7.0 und höher, können Sie die maximumZ Eigenschaft auf MKTileOverlay verwenden. Von the docs:

Wenn Sie verschiedene Overlay-Objekte verwenden, um verschiedene Kacheln in verschiedenen Zoomstufen, verwenden Sie diese Eigenschaft stellen die maximale Zoom Ebene durch dieses Overlay Fliesen unterstützt angeben.In der Zoomstufe 0 decken die Kacheln die gesamte Weltkarte ab ; Bei Zoom-Stufe 1 decken Fliesen 1/4 der Welt ab; auf Zoomstufe 2, Kacheln decken 1/16 der Welt ab, und so weiter. Die Karte versucht nie, Kacheln für eine Zoomstufe zu laden, die größer als der von dieser Eigenschaft angegebene Wert ist.

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

Das hindert die Karte nicht daran, über das Minimum hinausgezoomt zu werden - es zeigt das Overlay überhaupt nicht an, was bedeutet, dass eine zugrundeliegende Karte statt oder keine Karte angezeigt wird, wenn canReplaceMapContent angezeigt wird – earnshavian

Verwandte Themen