Local MBTiles source

This commit is contained in:
Joshua Pirihi 2022-01-17 14:03:48 +13:00
parent f2a333b5df
commit fae46080c7
9 changed files with 188 additions and 17 deletions

View file

@ -8,6 +8,10 @@
/* Begin PBXBuildFile section */
C9483F6D2773017500998F6B /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9483F6C2773017500998F6B /* MapView.swift */; };
C9697F9B2793366500250207 /* offline_map.mbtiles in Resources */ = {isa = PBXBuildFile; fileRef = C9697F9A2793366500250207 /* offline_map.mbtiles */; };
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 */; };
@ -74,6 +78,9 @@
/* Begin PBXFileReference section */
C9483F6C2773017500998F6B /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
C9697F9A2793366500250207 /* offline_map.mbtiles */ = {isa = PBXFileReference; lastKnownFileType = file; path = offline_map.mbtiles; 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>"; };
@ -132,6 +139,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C9697FA527933B8C00250207 /* SQLite in Frameworks */,
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -159,6 +167,7 @@
C9A7BC0E27759A6800760B50 /* Custom */,
C9483F6C2773017500998F6B /* MapView.swift */,
C9A88B54278B503C00BD810A /* MapViewModule.swift */,
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */,
);
path = Map;
sourceTree = "<group>";
@ -258,6 +267,7 @@
DDC2E18826CE24EE0042C5E4 /* Model */,
DDC2E18926CE24F70042C5E4 /* Resources */,
DDC2E15726CE248E0042C5E4 /* MeshtasticClientApp.swift */,
C9697FA62793F9FB00250207 /* MTAppDelegate.swift */,
DDC2E16526CE248F0042C5E4 /* Info.plist */,
DDC2E15D26CE248F0042C5E4 /* Preview Content */,
);
@ -316,6 +326,7 @@
DDC2E18926CE24F70042C5E4 /* Resources */ = {
isa = PBXGroup;
children = (
C9697F9A2793366500250207 /* offline_map.mbtiles */,
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */,
);
path = Resources;
@ -383,6 +394,7 @@
name = MeshtasticClient;
packageProductDependencies = (
DD5394FB276993AD00AD86B1 /* SwiftProtobuf */,
C9697FA427933B8C00250207 /* SQLite */,
);
productName = MeshtasticClient;
productReference = DDC2E15426CE248E0042C5E4 /* MeshtasticClient.app */;
@ -435,6 +447,7 @@
TargetAttributes = {
DDC2E15326CE248E0042C5E4 = {
CreatedOnToolsVersion = 12.5.1;
LastSwiftMigration = 1320;
};
DDC2E16926CE248F0042C5E4 = {
CreatedOnToolsVersion = 12.5.1;
@ -457,6 +470,7 @@
mainGroup = DDC2E14B26CE248E0042C5E4;
packageReferences = (
DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */,
);
productRefGroup = DDC2E15526CE248E0042C5E4 /* Products */;
projectDirPath = "";
@ -476,6 +490,7 @@
files = (
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */,
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */,
C9697F9B2793366500250207 /* offline_map.mbtiles in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -551,10 +566,12 @@
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 */,
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */,
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */,
DD539502276DAA6A00AD86B1 /* MapLocation.swift in Sources */,
DDAF8C6726ED0C8C0058C060 /* remote_hardware.pb.swift in Sources */,
@ -720,6 +737,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 15;
@ -737,6 +755,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,6";
};
@ -748,6 +767,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 15;
@ -896,6 +916,14 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/stephencelis/SQLite.swift.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.9.2;
};
};
DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
@ -907,6 +935,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C9697FA427933B8C00250207 /* SQLite */ = {
isa = XCSwiftPackageProductDependency;
package = C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */;
productName = SQLite;
};
DD5394FB276993AD00AD86B1 /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */;

View file

@ -1,6 +1,15 @@
{
"object": {
"pins": [
{
"package": "SQLite.swift",
"repositoryURL": "https://github.com/stephencelis/SQLite.swift.git",
"state": {
"branch": null,
"revision": "60a65015f6402b7c34b9a924f755ca0a73afeeaa",
"version": "0.13.1"
}
},
{
"package": "SwiftProtobuf",
"repositoryURL": "https://github.com/apple/swift-protobuf.git",

View file

@ -2,10 +2,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UISupportsDocumentBrowser</key>
<false/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Meshtastic</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>MBTiles Map</string>
<key>LSHandlerRank</key>
<string>Default</string>
<key>LSItemContentTypes</key>
<array>
<string>gvh.MeshtasticClient.mbtiles</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -29,7 +46,7 @@
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
<string>Bluetooth is used to connect an iPhone to a user&apos;s meshtastic device to allow text messaging and location data for the mesh network.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to center maps of the mesh</string>
<key>Privacy Bluetooth Always Usage Description</key>
@ -65,5 +82,27 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>MBTiles Map</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>gvh.MeshtasticClient.mbtiles</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mbtiles</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,28 @@
//
// 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

@ -4,6 +4,8 @@ import CoreData
@main
struct MeshtasticClientApp: App {
@UIApplicationDelegateAdaptor var delegate: MTAppDelegate
let persistenceController = PersistenceController.shared
@ObservedObject private var bleManager: BLEManager = BLEManager.shared

View file

@ -0,0 +1,72 @@
//
// LocalMBTileOverlay.swift
// MeshtasticClient
//
// Created by Joshua Pirihi on 16/01/22.
//
import UIKit
import MapKit
import SQLite
class LocalMBTileOverlay: MKTileOverlay {
var path: String!
var mb: Connection!
init(mbTilePath path: String) {
super.init(urlTemplate: nil)
self.path = path
do {
self.mb = try Connection(self.path, readonly: true)
let metadata = Table("metadata")
let name = Expression<String>("name")
let value = Expression<String>("value")
let minZQuery = try mb.pluck(metadata.select(value).filter(name == "minzoom"))
self.minimumZ = Int(minZQuery![value])!
let maxZQuery = try mb.pluck(metadata.select(value).filter(name == "maxzoom"))
self.maximumZ = Int(maxZQuery![value])!
self.isGeometryFlipped = true
//let boundingBoxString = try mb.pluck(metadata.select(value).filter(name == "bounds"))
//let boundCoords = boundingBoxString![value].split(separator: ",")
//self.boundingMapRect = MKMapRect(coordinates: [CLLocationCoordinate2D(latitude: Double(boundCoords[0]) ?? 0, longitude: Double(boundCoords[1]) ?? 0), CLLocationCoordinate2D(latitude: Double(boundCoords[2]) ?? 0, longitude: Double(boundCoords[3]) ?? 0)])
} catch {
}
}
override func loadTile(at path: MKTileOverlayPath) async throws -> Data {
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
return data
} else {
return Data()
}
}
}

View file

@ -97,7 +97,7 @@ public struct MapView: UIViewRepresentable {
//self.annotations = annotations
self.locationNodes = locationNodes
//self.locationNodes = locationNodes
self.overlays = overlays
@ -135,22 +135,10 @@ public struct MapView: UIViewRepresentable {
if self.customMapOverlay != self.presentCustomMapOverlayHash {
mapView.removeOverlays(mapView.overlays)
if let customMapOverlay = self.customMapOverlay {
let overlay = CustomMapOverlaySource(
parent: self,
mapName: customMapOverlay.mapName,
tileType: customMapOverlay.tileType,
defaultTile: customMapOverlay.defaultTile
)
if let minZ = customMapOverlay.minimumZoomLevel {
overlay.minimumZ = minZ
}
let overlay = LocalMBTileOverlay(mbTilePath: Bundle.main.path(forResource: "offline_map", ofType: "mbtiles")!)
if let maxZ = customMapOverlay.maximumZoomLevel {
overlay.maximumZ = maxZ
}
overlay.canReplaceMapContent = customMapOverlay.canReplaceMapContent
overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
mapView.addOverlay(overlay)
}

View file

@ -49,7 +49,7 @@ struct NodeMap: View {
)*/
@State private var customMapOverlay: MapView.CustomMapOverlay? = MapView.CustomMapOverlay(
mapName: UserDefaults.standard.string(forKey: "meshMapCustomTileServer"),
mapName: "offlinemap",
tileType: "png",
canReplaceMapContent: true
)