Ios 为什么我的自定义mktileOverlay渲染器在多个位置绘制相同的瓷砖?

Ios 为什么我的自定义mktileOverlay渲染器在多个位置绘制相同的瓷砖?,ios,swift,mapkit,overlay,mktileoverlay,Ios,Swift,Mapkit,Overlay,Mktileoverlay,因此,我正在编写一个基于MapKit的应用程序,在地图上绘制一个覆盖图。然而,许多叠加图形是动态的,所以绘制的平铺经常变化,所以我实现了一个自定义的MKTileOverlay和一个自定义的MKTileOverlay渲染器。第一个用于处理存储平铺图像的url方案,第二个用于处理自定义drawMapRect实现 我遇到的问题是,我似乎在多个位置绘制相同的瓷砖图像。这里有一个屏幕截图来帮助你形象化我的意思:(我知道瓷砖是颠倒的,我可以修复) 我已经更改了某些平铺图像,使它们具有不同的颜色,并包含它们

因此,我正在编写一个基于MapKit的应用程序,在地图上绘制一个覆盖图。然而,许多叠加图形是动态的,所以绘制的平铺经常变化,所以我实现了一个自定义的MKTileOverlay和一个自定义的MKTileOverlay渲染器。第一个用于处理存储平铺图像的url方案,第二个用于处理自定义drawMapRect实现

我遇到的问题是,我似乎在多个位置绘制相同的瓷砖图像。这里有一个屏幕截图来帮助你形象化我的意思:(我知道瓷砖是颠倒的,我可以修复)

我已经更改了某些平铺图像,使它们具有不同的颜色,并包含它们的平铺路径。您会注意到,许多平铺图像在不同区域重复出现

我一直在试图弄清楚为什么会发生这种情况,所以按照我的代码路径,覆盖的起点是非常标准的——ViewController设置addOverlay()调用,该调用调用代理的mapView(rendererForOverlay:),该调用返回我的自定义MKTileOverlayRenderer类,然后尝试调用我的drawMapRect(mapRect:,zoomScale:,context)。然后,它获取给定的映射并计算该映射所属的磁贴,调用自定义MKTileOverlay类的loadTileAtPath()然后绘制生成的平铺图像数据。这正是我的代码所做的,所以我不确定我到底出了什么问题。也就是说,如果我不尝试实现自定义绘制并使用默认的MKTileOverlayRenderer,它工作得非常好。不幸的是,这也是应用程序的关键所在,因此不是真正可行的解决方案

以下是我的自定义类中的相关代码供参考:

我的自定义MKTileOverlay类

class ExploredTileOverlay: MKTileOverlay {

var base_path: String
//var tile_path: String?
let cache: NSCache = NSCache()
var point_buffer: ExploredSegment
var last_tile_path: MKTileOverlayPath?
var tile_buffer: ExploredTiles

init(URLTemplate: String?, startingLocation location: CLLocation, city: City) {
    let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    let documentsDirectory: AnyObject = paths[0]
    self.base_path = documentsDirectory.stringByAppendingPathComponent("/" + city.name + "_tiles")
    if (!NSFileManager.defaultManager().fileExistsAtPath(base_path)) {
        try! NSFileManager.defaultManager().createDirectoryAtPath(base_path, withIntermediateDirectories: false, attributes: nil)
    }

    let new_point = MKMapPointForCoordinate(location.coordinate)
    self.point_buffer = ExploredSegment(fromPoint: new_point, inCity: city)
    self.tile_buffer = ExploredTiles(startingPoint: ExploredPoint(mapPoint: new_point, r: 50))
    self.last_tile_path = Array(tile_buffer.edited_tiles.values).last!.path
    super.init(URLTemplate: URLTemplate)
}

override func URLForTilePath(path: MKTileOverlayPath) -> NSURL {
    let filled_template = String(format: "%d_%d_%d.png", path.z, path.x, path.y)
    let tile_path = base_path + "/" + filled_template
    //print("fetching tile " + filled_template)
    if !NSFileManager.defaultManager().fileExistsAtPath(tile_path) {
        return NSURL(fileURLWithPath: "")
    }

    return NSURL(fileURLWithPath: tile_path)
}

override func loadTileAtPath(path: MKTileOverlayPath, result: (NSData?, NSError?) -> Void) {
    let url = URLForTilePath(path)
    let filled_template = String(format: "%d_%d_%d.png", path.z, path.x, path.y)
    let tile_path = base_path + "/" + filled_template
    if (url != NSURL(fileURLWithPath: tile_path)) {
        print("creating tile at " + String(path))
        let img_data: NSData = UIImagePNGRepresentation(UIImage(named: "small")!)!
        let filled_template = String(format: "%d_%d_%d.png", path.z, path.x, path.y)
        let tile_path = base_path + "/" + filled_template
        img_data.writeToFile(tile_path, atomically: true)
        cache.setObject(img_data, forKey: url)
        result(img_data, nil)
        return
    } else if let cachedData = cache.objectForKey(url) as? NSData {
        print("using cache for " + String(path))
        result(cachedData, nil)
        return
    } else {
        print("loading " + String(path) + " from directory")
        let img_data: NSData = UIImagePNGRepresentation(UIImage(contentsOfFile: tile_path)!)!
        cache.setObject(img_data, forKey: url)
        result(img_data, nil)
        return
    }
}
我的自定义MKTileOverlayRenderer类:

class ExploredTileRenderer: MKTileOverlayRenderer {

let tile_overlay: ExploredTileOverlay
var zoom_scale: MKZoomScale?
let cache: NSCache = NSCache()

override init(overlay: MKOverlay) {
    self.tile_overlay = overlay as! ExploredTileOverlay
    super.init(overlay: overlay)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(saveEditedTiles), name: "com.Coder.Wander.reachedMaxPoints", object: nil)
}

// There's some weird cache-ing thing that requires me to recall it 
// whenever I re-draw over the tile, I don't really get it but it works
override func canDrawMapRect(mapRect: MKMapRect, zoomScale: MKZoomScale) -> Bool {
    self.setNeedsDisplayInMapRect(mapRect, zoomScale: zoomScale)
    return true
}

override func drawMapRect(mapRect: MKMapRect, zoomScale: MKZoomScale, inContext context: CGContext) {
    zoom_scale = zoomScale
    let tile_path = self.tilePathForMapRect(mapRect, andZoomScale: zoomScale)
    let tile_path_string = stringForTilePath(tile_path)
    //print("redrawing tile: " + tile_path_string)
    self.tile_overlay.loadTileAtPath(tile_path, result: {
        data, error in
        if error == nil && data != nil {
            if let image = UIImage(data: data!) {
                let draw_rect = self.rectForMapRect(mapRect)
                CGContextDrawImage(context, draw_rect, image.CGImage)
                var path: [(CGMutablePath, CGFloat)]? = nil
                self.tile_overlay.point_buffer.readPointsWithBlockAndWait({ points in
                    let total = self.getPathForPoints(points, zoomScale: zoomScale, offset: MKMapPointMake(0.0, 0.0))
                    path = total.0
                    //print("number of points: " + String(path!.count))
                })
                if ((path != nil) && (path!.count > 0)) {
                    //print("drawing path")
                    for segment in path! {
                        CGContextAddPath(context, segment.0)
                        CGContextSetBlendMode(context, .Clear)
                        CGContextSetLineJoin(context, CGLineJoin.Round)
                        CGContextSetLineCap(context, CGLineCap.Round)
                        CGContextSetLineWidth(context, segment.1)
                        CGContextStrokePath(context)
                    }
                }
            }
        }
    })
}
以及处理zoomScale、zoomLevel、平铺路径和平铺坐标之间转换的my helper函数:

func tilePathForMapRect(mapRect: MKMapRect, andZoomScale zoom: MKZoomScale) -> MKTileOverlayPath {
    let zoom_level = self.zoomLevelForZoomScale(zoom)
    let mercatorPoint = self.mercatorTileOriginForMapRect(mapRect)
    //print("mercPt: " + String(mercatorPoint))

    let tilex = Int(floor(Double(mercatorPoint.x) * self.worldTileWidthForZoomLevel(zoom_level)))
    let tiley = Int(floor(Double(mercatorPoint.y) * self.worldTileWidthForZoomLevel(zoom_level)))

    return MKTileOverlayPath(x: tilex, y: tiley, z: zoom_level, contentScaleFactor: UIScreen.mainScreen().scale)
}

func stringForTilePath(path: MKTileOverlayPath) -> String {
    return String(format: "%d_%d_%d", path.z, path.x, path.y)
}

func zoomLevelForZoomScale(zoomScale: MKZoomScale) -> Int {
    let real_scale = zoomScale / UIScreen.mainScreen().scale
    var z = Int((log2(Double(real_scale))+20.0))
    z += (Int(UIScreen.mainScreen().scale) - 1)
    return z
}

func worldTileWidthForZoomLevel(zoomLevel: Int) -> Double {
    return pow(2, Double(zoomLevel))
}

func mercatorTileOriginForMapRect(mapRect: MKMapRect) -> CGPoint {
    let map_region: MKCoordinateRegion = MKCoordinateRegionForMapRect(mapRect)

    var x : Double = map_region.center.longitude * (M_PI/180.0)
    var y : Double = map_region.center.latitude * (M_PI/180.0)
    y = log10(tan(y) + 1.0/cos(y))

    x = (1.0 + (x/M_PI)) / 2.0
    y = (1.0 - (y/M_PI)) / 2.0

    return CGPointMake(CGFloat(x), CGFloat(y))
}

我认为这是一个相当模糊的错误,因此我还没有找到其他面临类似问题的人。任何事情都会有帮助的!

你找到答案了吗?你是如何解决瓷砖倒置的问题的?