Merge pull request #42 from meshtastic/import_mbtiles_file

Import offline map file
This commit is contained in:
Garth Vander Houwen 2022-01-28 19:47:30 -08:00 committed by GitHub
commit ed2463c085
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 42 deletions

View file

@ -10,7 +10,6 @@
C9483F6D2773017500998F6B /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9483F6C2773017500998F6B /* MapView.swift */; };
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */; };
C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; };
C9697FA72793F9FB00250207 /* MTAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697FA62793F9FB00250207 /* MTAppDelegate.swift */; };
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */; };
C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A88B54278B503C00BD810A /* MapViewModule.swift */; };
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A88B56278B559900BD810A /* apponly.pb.swift */; };
@ -78,7 +77,6 @@
/* Begin PBXFileReference section */
C9483F6C2773017500998F6B /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = "<group>"; };
C9697FA62793F9FB00250207 /* MTAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTAppDelegate.swift; sourceTree = "<group>"; };
C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = "<group>"; };
C9A88B54278B503C00BD810A /* MapViewModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewModule.swift; sourceTree = "<group>"; };
C9A88B56278B559900BD810A /* apponly.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = apponly.pb.swift; sourceTree = "<group>"; };
@ -265,7 +263,6 @@
DDC2E18826CE24EE0042C5E4 /* Model */,
DDC2E18926CE24F70042C5E4 /* Resources */,
DDC2E15726CE248E0042C5E4 /* MeshtasticClientApp.swift */,
C9697FA62793F9FB00250207 /* MTAppDelegate.swift */,
DDC2E16526CE248F0042C5E4 /* Info.plist */,
DDC2E15D26CE248F0042C5E4 /* Preview Content */,
);
@ -562,7 +559,6 @@
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
C9483F6D2773017500998F6B /* MapView.swift in Sources */,
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */,
C9697FA72793F9FB00250207 /* MTAppDelegate.swift in Sources */,
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */,
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */,
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */,
@ -738,7 +734,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 18;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
DEVELOPMENT_TEAM = 37C534H572;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = MeshtasticClient/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;

View file

@ -1,28 +0,0 @@
//
// AppDelegate.swift
// MeshtasticClient
//
// Created by Joshua Pirihi on 16/01/22.
//
import Foundation
import UIKit
class MTAppDelegate: NSObject, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplication) {
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// ...
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
print("We received a file")
return true
}
}

View file

@ -3,8 +3,6 @@ import CoreData
@main
struct MeshtasticClientApp: App {
@UIApplicationDelegateAdaptor var delegate: MTAppDelegate
let persistenceController = PersistenceController.shared
@ -19,6 +17,33 @@ struct MeshtasticClientApp: App {
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(bleManager)
.environmentObject(userSettings)
.onOpenURL(perform: { (url) in
//we are expecting a .mbtiles map file that contains raster data
//save it to the documents directory, and name it offline_map.mbtiles
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let destination = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false)
//do we need to delete an old one?
if (fileManager.fileExists(atPath: destination.path)) {
print(" Found an old map file. Deleting it")
try? fileManager.removeItem(atPath: destination.path)
}
try? fileManager.copyItem(at: url, to: destination)
if (fileManager.fileExists(atPath: destination.path)) {
print(" Saved the map file")
//need to tell the map view that it needs to update and try loading the new overlay
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastUpdatedLocalMapFile")
} else {
print("💥 Didn't save the map file")
}
}
)
}
.onChange(of: scenePhase) { (newScenePhase) in
switch newScenePhase {

View file

@ -33,6 +33,11 @@ extension MKMapRect {
}
}
enum MapTileError: Error {
case invalidFormat
case other
}
class LocalMBTileOverlay: MKTileOverlay {
var path: String!
@ -46,7 +51,7 @@ class LocalMBTileOverlay: MKTileOverlay {
}
}
init(mbTilePath path: String) {
init?(mbTilePath path: String) {
super.init(urlTemplate: nil)
self.path = path
@ -58,6 +63,12 @@ class LocalMBTileOverlay: MKTileOverlay {
let name = Expression<String>("name")
let value = Expression<String>("value")
//make sure it's raster
let formatQuery = try mb.pluck(metadata.select(value).filter(name == "format"))
if formatQuery?[value] == nil || (formatQuery![value] != "jpg" && formatQuery![value] != "png") {
throw MapTileError.invalidFormat
}
let minZQuery = try mb.pluck(metadata.select(value).filter(name == "minzoom"))
self.minimumZ = Int(minZQuery![value])!
@ -78,7 +89,8 @@ class LocalMBTileOverlay: MKTileOverlay {
} catch {
//print("MAP ERROR \(error)")
print("💥 Map tile error: \(error)")
return nil
}

View file

@ -17,6 +17,10 @@ public struct MapView: UIViewRepresentable {
//@Binding private var region: MKCoordinateRegion
//make this view dependent on the UserDefault that is updated when importing a new map file
@AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0
@State private var loadedLastUpdatedLocalMapFile = 0
private var customMapOverlay: CustomMapOverlay?
@State private var presentCustomMapOverlayHash: CustomMapOverlay?
@ -138,20 +142,31 @@ public struct MapView: UIViewRepresentable {
//mapView.region = self.region
//}
if self.customMapOverlay != self.presentCustomMapOverlayHash {
if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile {
mapView.removeOverlays(mapView.overlays)
if let customMapOverlay = self.customMapOverlay {
if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") {
let overlay = LocalMBTileOverlay(mbTilePath: tilePath)
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path
if fileManager.fileExists(atPath: tilePath) {
//if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") {
overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
print("Loading local map file")
mapView.addOverlay(overlay)
if let overlay = LocalMBTileOverlay(mbTilePath: tilePath) {
overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
mapView.addOverlay(overlay)
}
} else {
print("Couldn't find a local map file to load")
}
}
DispatchQueue.main.async {
self.presentCustomMapOverlayHash = self.customMapOverlay
self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile
}
}