Meshtastic-Apple/Meshtastic/Views/MapKitMap/Custom/LocalMBTileOverlay.swift

155 lines
4.7 KiB
Swift
Raw Normal View History

2022-01-17 14:03:48 +13:00
//
// LocalMBTileOverlay.swift
// MeshtasticApple
2022-01-17 14:03:48 +13:00
//
2023-01-13 22:30:10 -08:00
// Copyright(c) Joshua Pirihi 16/01/22.
2022-01-17 14:03:48 +13:00
//
import UIKit
import MapKit
import SQLite
2024-06-03 02:17:55 -07:00
import OSLog
2022-01-17 14:03:48 +13:00
extension MKMapRect {
init(coordinates: [CLLocationCoordinate2D]) {
self = MKMapRect()
var coordinates = coordinates
if !coordinates.isEmpty {
let first = coordinates.removeFirst()
var top = first.latitude
var bottom = first.latitude
var left = first.longitude
var right = first.longitude
coordinates.forEach { coordinate in
top = max(top, coordinate.latitude)
bottom = min(bottom, coordinate.latitude)
left = min(left, coordinate.longitude)
right = max(right, coordinate.longitude)
}
2023-03-06 10:33:18 -08:00
let topLeft = MKMapPoint(CLLocationCoordinate2D(latitude: top, longitude: left))
let bottomRight = MKMapPoint(CLLocationCoordinate2D(latitude: bottom, longitude: right))
self = MKMapRect(x: topLeft.x, y: topLeft.y,
width: bottomRight.x - topLeft.x, height: bottomRight.y - topLeft.y)
}
}
}
enum MapTileError: Error {
case invalidFormat
case other
}
2022-01-17 14:03:48 +13:00
class LocalMBTileOverlay: MKTileOverlay {
var path: String!
var mb: Connection!
private var _boundingMapRect: MKMapRect!
override var boundingMapRect: MKMapRect {
2023-03-06 13:26:04 -08:00
return _boundingMapRect
}
2023-03-06 10:33:18 -08:00
init?(mbTilePath path: String) {
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
super.init(urlTemplate: nil)
self.path = path
do {
self.mb = try Connection(self.path, readonly: true)
let metadata = Table("metadata")
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
let name = Expression<String>("name")
let value = Expression<String>("value")
2023-03-06 10:33:18 -08:00
// make sure it's raster
let formatQuery = try mb.pluck(metadata.select(value).filter(name == "format"))
if formatQuery?[value] == nil || (formatQuery![value] != "jpeg" && formatQuery![value] != "jpg" && formatQuery![value] != "png") {
throw MapTileError.invalidFormat
}
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
let minZQuery = try mb.pluck(metadata.select(value).filter(name == "minzoom"))
self.minimumZ = Int(minZQuery![value])!
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
let maxZQuery = try mb.pluck(metadata.select(value).filter(name == "maxzoom"))
self.maximumZ = Int(maxZQuery![value])!
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
self.isGeometryFlipped = true
2023-03-06 10:33:18 -08:00
let boundingBoxString = try mb.pluck(metadata.select(value).filter(name == "bounds"))
let boundCoords = boundingBoxString![value].split(separator: ",")
let coords = [
CLLocationCoordinate2D(latitude: Double(boundCoords[1]) ?? 0,
longitude: Double(boundCoords[0]) ?? 0),
CLLocationCoordinate2D(latitude: Double(boundCoords[3]) ?? 0,
longitude: Double(boundCoords[2]) ?? 0)
]
self._boundingMapRect = MKMapRect(coordinates: coords)
2023-03-06 10:33:18 -08:00
2022-01-17 14:03:48 +13:00
} catch {
2024-06-03 02:17:55 -07:00
Logger.services.error("Map tile error: \(error)")
return nil
2022-01-17 14:03:48 +13:00
}
}
2023-03-06 10:33:18 -08:00
2024-06-23 17:23:57 -07:00
// override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) {
//
// let tileX = Int64(path.x)
// let tileY = Int64(path.y)
// let tileZ = Int64(path.z)
// let tileData = Expression<SQLite.Blob>("tile_data")
// let zoomLevel = Expression<Int64>("zoom_level")
// let tileColumn = Expression<Int64>("tile_column")
// let tileRow = Expression<Int64>("tile_row")
//
// if let dataQuery = try? self.mb.pluck(Table("tiles").select(tileData).filter(zoomLevel == tileZ).filter(tileColumn == tileX).filter(tileRow == tileY)) {
// let data = Data(bytes: dataQuery[tileData].bytes, count: dataQuery[tileData].bytes.count)// dataQuery![tileData].bytes
// result(data, nil)
// } else {
// Logger.services.error("No tile here: x:\(tileX) y:\(tileY) z:\(tileZ)")
// let error = NSError(domain: "LocalMBTileOverlay", code: 1, userInfo: ["reason": "no_tile"])
// result(nil, error)
// }
// }
2022-01-17 14:03:48 +13:00
}
2023-04-25 17:56:57 -07:00
2023-08-26 23:17:30 -07:00
// public class CustomMapOverlaySource: MKTileOverlay {
2023-04-25 17:56:57 -07:00
//
// // requires folder: tiles/{mapName}/z/y/y,{tileType}
// private var parent: MapViewSwiftUI
// private let mapName: String
// private let tileType: String
// private let defaultTile: DefaultTile?
//
// public init(
// parent: MapViewSwiftUI,
// mapName: String,
// tileType: String,
// defaultTile: DefaultTile?
// ) {
// self.parent = parent
// self.mapName = mapName
// self.tileType = tileType
// self.defaultTile = defaultTile
// super.init(urlTemplate: "")
// }
//
// public override func url(forTilePath path: MKTileOverlayPath) -> URL {
// if let tileUrl = Bundle.main.url(
// forResource: "\(path.y)",
// withExtension: self.tileType,
// subdirectory: "tiles/\(self.mapName)/\(path.z)/\(path.x)",
// localization: nil
// ) {
// return tileUrl
// } else if let defaultTile = self.defaultTile, let defaultTileUrl = Bundle.main.url(
// forResource: defaultTile.tileName,
// withExtension: defaultTile.tileType,
// subdirectory: "tiles/\(self.mapName)",
// localization: nil
// ) {
// return defaultTileUrl
// } else {
// let urlstring = self.mapName+"\(path.z)/\(path.x)/\(path.y).png"
// return URL(string: urlstring)!
// }
// }
2023-08-26 23:17:30 -07:00
// }