You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mapbox SDK升级后离线模式下道路等地图细节缺失问题

Mapbox SDK 10.7 离线地图道路细节缺失问题排查与解决

问题描述

我正在开发离线地图,已将Mapbox SDK从6.2版本升级至10.7版本,Style Pack和Tile Region均可成功下载。

操作步骤:

  • 在首页下载样式及瓦片源
  • 关闭网络
  • 返回首页

实际结果:离线时Mapbox地图可显示,但道路、街道等细节缺失
预期结果:离线状态下地图应正常显示道路、街道等细节

相关代码

下载Tile Region和Style Pack的代码

let dispatchGroup = DispatchGroup()
// 1. Create style package with loadStylePack() call.
let stylePackLoadOptions = StylePackLoadOptions(glyphsRasterizationMode: .ideographsRasterizedLocally,
                                                metadata: ["tag": "my-outdoors-style-pack"],
                                                acceptExpired: true)!
let tileStore = TileStore.default
let accessToken = ResourceOptionsManager.default.resourceOptions.accessToken
tileStore.setOptionForKey(TileStoreOptions.mapboxAccessToken, value: accessToken)
self.tileStore = tileStore
let accessToken = ResourceOptionsManager.default.resourceOptions.accessToken
offlineManager = OfflineManager(resourceOptions: ResourceOptions(accessToken: accessToken, tileStore: tileStore))

dispatchGroup.enter()
_ = offlineManager?.loadStylePack(for: .outdoors, loadOptions: stylePackLoadOptions) { [weak self] progress in
    // These closures do not get called from the main thread. In this case
    DispatchQueue.main.async {
        print(" progress completedResourceCount/ - requiredResourceCount \(progress.completedResourceCount) / \(progress.requiredResourceCount)")
        print("StylePack = \(progress)")
    }

} completion: { [weak self] result in
    // getting result success
    DispatchQueue.main.async {
        defer {
            dispatchGroup.leave()
        }

        switch result {
        case let .success(stylePack):
            print("StylePack = \(stylePack)")

        case let .failure(error):
            print("stylePack download Error = \(error)")
        }
    }
}

// 2. Create an offline region with tiles for the outdoors style
let outdoorsOptions = TilesetDescriptorOptions(styleURI: .outdoors, zoomRange: UInt8(minZoom)...UInt8(maxZoom))
guard let outdoorsDescriptor = offlineManager?.createTilesetDescriptor(for: outdoorsOptions) else {
    print("outdoorsDescriptor -> missing")
    return
}
// Load the tile region
let tileRegionLoadOptions = TileRegionLoadOptions(
    geometry: .point(Point(coord)),
    descriptors: [outdoorsDescriptor],
    metadata: userInfo,
    acceptExpired: true)!

// Use the the default TileStore to load this region. You can create
// custom TileStores are are unique for a particular file path, i.e.
// there is only ever one TileStore per unique path.
dispatchGroup.enter()
_ = tileStore.loadTileRegion(forId: tileRegionId, loadOptions: tileRegionLoadOptions) { progress in
    // These closures do not get called from the main thread. In this case
    // we're updating the UI, so it's important to dispatch to the main
    // queue.
    DispatchQueue.main.async {
        // Update the progress bar
        print("Download progress : ")
        print(Float(progress.completedResourceCount) / Float(progress.requiredResourceCount))
    }
} completion: { result in
    //getting result success
    DispatchQueue.main.async {
        defer {
            dispatchGroup.leave()
        }

        switch result {
        case let .success(tileRegion):
            print("tileRegion = \(tileRegion)")

            print(" tileRegion progress completedResourceCount/ - requiredResourceCount \(tileRegion.completedResourceCount) / \(tileRegion.requiredResourceCount)")

        case let .failure(error):
            print("tileRegion download Error = \(error)")
        }
    }
}
// Wait for both downloads before moving to the next state
dispatchGroup.notify(queue: .main) {
    print("notify download complete")
}

地图加载代码

let mapView = MapView(frame: view.bounds)
mapView.mapboxMap.loadStyleURI(.outdoors)
self.view.addSubview(mapView)

效果对比

离线状态地图效果:
离线状态地图效果

联网状态地图效果:
联网状态地图效果

问题排查与解决方案

  1. 确认瓦片下载的缩放范围覆盖足够层级
    代码中使用UInt8(minZoom)...UInt8(maxZoom)定义缩放范围,需确保maxZoom值足够高(建议至少设置到14-16级)。道路等细节仅在较高缩放层级才会加载,若maxZoom过低,离线瓦片不会包含这些内容。

  2. 加载离线Style Pack而非在线样式URI
    SDK 10.x中直接调用loadStyleURI(.outdoors)会优先尝试从网络加载样式,即使本地已有离线Style Pack。需改为加载本地已下载的Style Pack:

    // 通过metadata标签筛选目标Style Pack并加载
    offlineManager?.allStylePacks { result in
        switch result {
        case .success(let stylePacks):
            if let targetPack = stylePacks.first(where: { $0.metadata?["tag"] as? String == "my-outdoors-style-pack" }) {
                mapView.mapboxMap.loadStyle(stylePack: targetPack) { loadResult in
                    switch loadResult {
                    case .success:
                        print("离线样式加载成功")
                    case .failure(let error):
                        print("离线样式加载失败:\(error)")
                    }
                }
            }
        case .failure(let error):
            print("获取Style Pack列表失败:\(error)")
        }
    }
    
  3. 调整Tile Region的几何范围
    当前使用.point(Point(coord))仅会下载该点极小范围的瓦片,若需要更大区域的离线地图,建议使用边界框(BoundingBox)或多边形定义下载范围:

    // 示例:使用边界框定义下载区域
    let bounds = BoundingBox(
        sw: CLLocationCoordinate2D(latitude: 39.0, longitude: -105.0),
        ne: CLLocationCoordinate2D(latitude: 40.0, longitude: -104.0)
    )
    let tileRegionLoadOptions = TileRegionLoadOptions(
        geometry: .bounds(bounds),
        descriptors: [outdoorsDescriptor],
        metadata: userInfo,
        acceptExpired: true)!
    
  4. 强制启用离线模式
    在加载地图前设置离线模式,强制SDK优先使用本地资源:

    mapView.mapboxMap.setConnected(false)
    
  5. 验证Tile Region的有效性
    通过以下代码确认已下载的Tile Region是否存在且范围正确:

    tileStore.getTileRegion(forId: tileRegionId) { result in
        switch result {
        case .success(let region):
            print("已存在Tile Region:\(region.id),覆盖范围:\(region.geometry)")
        case .failure(let error):
            print("Tile Region不存在:\(error)")
        }
    }
    

内容的提问来源于stack exchange,提问作者Bhargav Nakrani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:30:55