我想开发一个离线显示开放街道地图的iOS应用程序

问题描述 投票:0回答:1

我想开发一个离线显示开放街道地图的应用程序。我通过以下方式保存开放街道地图图块。

class HomeViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate, DistanceCalculationDelegate{
func startMonitoringNetwork() {
        if let currentLatString = UserDefaults.standard.string(forKey: "myLat"),
           let currentLonString = UserDefaults.standard.string(forKey: "myLon"),
           let currentLat = Double(currentLatString),
           let currentLon = Double(currentLonString) {
            let zoomLevel = 15
            let centerTileCoords = tileCoordinates(fromLatitude: currentLat, longitude: currentLon, zoomLevel: zoomLevel)
            
            let range = 4  
            for dx in -range...range {
                for dy in -range...range {
                    let tileX = centerTileCoords.x + dx
                    let tileY = centerTileCoords.y + dy
                    downloadTile(zoom: zoomLevel, x: tileX, y: tileY)
                }
            }
        }
    }

func downloadTile(zoom: Int, x: Int, y: Int) {
        let urlString = "https://tile.openstreetmap.org/\(zoom)/\(x)/\(y).png"
        guard let url = URL(string: urlString) else { return }
        print(url)
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else { return }
            
            self.saveTileData(data, zoom: zoom, x: x, y: y)
        }
        
        task.resume()
    }
    
    func saveTileData(_ data: Data, zoom: Int, x: Int, y: Int) {
        let fileManager = FileManager.default
        guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return }

        
        let zoomDir = documentsDirectory.appendingPathComponent("tiles/\(zoom)")
        let xDir = zoomDir.appendingPathComponent("\(x)")

      
        if !fileManager.fileExists(atPath: xDir.path) {
            try? fileManager.createDirectory(at: xDir, withIntermediateDirectories: true)
        }

        let tileFilePath = xDir.appendingPathComponent("\(y).png")
        do {
            try data.write(to: tileFilePath)
            print("Tile saved: \(tileFilePath.path)")
        } catch {
            print("Error saving tile: \(error)")
        }
    }

}

以下数据都是这样保存的。

`文件:///var/mobile/Containers/Data/Application/2316925A-8954-4407-A286-AF81F862F2D2/Documents/tiles/15/29096/12903.png

文件:///var/mobile/Containers/Data/Application/2316925A-8954-4407-A286-AF81F862F2D2/Documents/tiles/15/29096/12903.png

文件:///var/mobile/Containers/Data/Application/2316925A-8954-4407-A286-AF81F862F2D2/Documents/tiles/15/29096/12903.png

文件:///var/mobile/Containers/Data/Application/2316925A-8954-4407-A286-AF81F862F2D2/Documents/tiles/15/29096/12903.png`

但是取出来的数据是下面代码的这些。

class OpenStreetMapController: UIViewController, MKMapViewDelegate {
    
    
    @IBOutlet weak var openStreetMap: MKMapView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let overlay = CustomTileOverlay(urlTemplate: nil)
        let tilePath = MKTileOverlayPath(x: 35, y: 139, z: 17, contentScaleFactor: 1.0)
        overlay.loadTile(at: tilePath) { (data, error) in
        }
        overlay.canReplaceMapContent = true
        openStreetMap.addOverlay(overlay)
        openStreetMap.delegate = self

    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let tileOverlay = overlay as? MKTileOverlay {
            let renderer = MKTileOverlayRenderer(tileOverlay: tileOverlay)
            return renderer
        }
        return MKOverlayRenderer()
    }
    
    class CustomTileOverlay: MKTileOverlay {
        
        override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) {
            let fileManager = FileManager.default
            guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
                result(nil, NSError(domain: "TileErrorDomain", code: 0, userInfo: nil))
                return
            }

            let tileFilePath = documentsDirectory
                                .appendingPathComponent("tiles/\(path.z)")
                                .appendingPathComponent("\(path.x)")
                                .appendingPathComponent("\(path.y).png")
            print(tileFilePath)

            if let tileData = try? Data(contentsOf: tileFilePath) {
                result(tileData, nil)
            } else {
                result(nil, NSError(domain: "TileErrorDomain", code: 1, userInfo: nil))
            }
        }

`文件:///var/mobile/Containers/Data/Application/7C2AB696-8425-4DA7-A2AA-0856C4B4C677/Documents/tiles/17/35/139.png

文件:///var/mobile/Containers/Data/Application/7C2AB696-8425-4DA7-A2AA-0856C4B4C677/Documents/tiles/6/54/24.png

文件:///var/mobile/Containers/Data/Application/7C2AB696-8425-4DA7-A2AA-0856C4B4C677/Documents/tiles/6/57/26.png

文件:///var/mobile/Containers/Data/Application/7C2AB696-8425-4DA7-A2AA-0856C4B4C677/Documents/tiles/6/57/20.png `

请告诉我原因。

我使用 ChatGPT 开发这些代码。但它不正确。我不知道如何解决这个问题。

ios swift xcode chat openstreetmap
1个回答
0
投票

您遇到的问题似乎来自您保存的图块坐标与您尝试检索的图块坐标之间的差异。具体来说,您引用

x
y
坐标以及可能的缩放级别 (
z
) 的方式似乎存在混淆。

问题领域

  1. 缩放级别不匹配:您保存的图块处于缩放级别 15 (

    tiles/15/...
    ),但您尝试加载图块的路径包括缩放级别 17 和其他级别 (
    tiles/17/...
    tiles/6/...) 
    )。确保您尝试访问的缩放级别与已保存图块的缩放级别相匹配。

  2. 坐标混乱:您正在创建的

    MKTileOverlayPath
    使用
    (x: 35, y: 139, z: 17)
    ,根据大多数图块服务器(包括OpenStreetMap)使用的标准图块坐标系,这似乎是不正确的。通常,
    x
    y
    值源自纬度和经度,并且随缩放级别的变化而显着变化。值
    (x: 35, y: 139)
    看起来更像是经度/纬度对,而不是平铺坐标。确保根据纬度和经度值正确计算您的图块
    x
    y
    坐标。

调试步骤

  1. 验证坐标转换:仔细检查用于将纬度和经度转换为平铺坐标的函数

    tileCoordinates(fromLatitude:longitude:zoomLevel:)
    。确保它正确计算图块的
    x
    y
    值。

  2. 缩放级别一致性:确保在保存和加载图块时始终使用正确的缩放级别。如果您要下载缩放级别 15 的图块,则应仅尝试加载缩放级别 15 的图块,除非您还下载了其他缩放级别的图块。

  3. 坐标匹配:加载图块时,确保用于生成文件路径的坐标(

    x
    y
    zoom level
    )与保存图块时使用的坐标相匹配。此处任何不匹配都会导致无法找到已保存的图块。

更正图块加载逻辑

加载图块时,您的路径生成必须准确反映您保存时使用的结构。假设您的保存逻辑是正确的,并且您已将图块保存在

tiles/{z}/{x}/{y}.png
中,则您的加载逻辑应如下所示:

override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) {
    let fileManager = FileManager.default
    guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
        result(nil, NSError(domain: "TileErrorDomain", code: 0, userInfo: nil))
        return
    }

    let tileFilePath = documentsDirectory
                        .appendingPathComponent("tiles/\(path.z)")
                        .appendingPathComponent("\(path.x)")
                        .appendingPathComponent("\(path.y).png")

    if let tileData = try? Data(contentsOf: tileFilePath) {
        result(tileData, nil)
    } else {
        // Consider fetching the tile from the web or a placeholder if not found
        result(nil, NSError(domain: "TileErrorDomain", code: 1, userInfo: nil))
    }
}

确保

MKTileOverlayPath
中使用的
loadTile(at:result:)
值准确反映基于您保存图块方式的预期值。这不仅涉及固定缩放级别,还涉及确保根据所需的纬度和经度正确计算
x
y

© www.soinside.com 2019 - 2024. All rights reserved.