mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
wip
map renders streets fix files
This commit is contained in:
parent
842fa1ebd1
commit
f09fcd82f7
9 changed files with 3299 additions and 0 deletions
|
|
@ -5813,6 +5813,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Burning Man Overlays" : {
|
||||
|
||||
},
|
||||
"Button GPIO" : {
|
||||
"localizations" : {
|
||||
|
|
@ -35137,6 +35140,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Street Outlines" : {
|
||||
|
||||
},
|
||||
"Subscribed" : {
|
||||
"localizations" : {
|
||||
|
|
@ -37907,6 +37913,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Toilets" : {
|
||||
|
||||
},
|
||||
"Topic: %@" : {
|
||||
"localizations" : {
|
||||
|
|
@ -38381,6 +38390,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Trash Fence" : {
|
||||
|
||||
},
|
||||
"Treat double tap on supported accelerometers as a user button press." : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@
|
|||
25F5D5C02C3F6DA6008036E3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F5D5BF2C3F6DA6008036E3 /* Router.swift */; };
|
||||
25F5D5C22C3F6E4B008036E3 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F5D5C12C3F6E4B008036E3 /* AppState.swift */; };
|
||||
25F5D5D12C4375DF008036E3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F5D5D02C4375DF008036E3 /* RouterTests.swift */; };
|
||||
3D3417B42E2730EC006A988B /* GeoJSONOverlayManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3417B32E2730EC006A988B /* GeoJSONOverlayManager.swift */; };
|
||||
3D3417BC2E273AC6006A988B /* Street_Outlines.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 3D3417BB2E273AC6006A988B /* Street_Outlines.geojson */; };
|
||||
3D3417C32E274800006A988B /* Toilets.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 3D3417C12E274800006A988B /* Toilets.geojson */; };
|
||||
3D3417C42E274800006A988B /* Trash_Fence.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 3D3417C22E274800006A988B /* Trash_Fence.geojson */; };
|
||||
6D825E622C34786C008DBEE4 /* CommonRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D825E612C34786C008DBEE4 /* CommonRegex.swift */; };
|
||||
6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */; };
|
||||
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */; };
|
||||
|
|
@ -326,6 +330,10 @@
|
|||
25F5D5C12C3F6E4B008036E3 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||
25F5D5C72C4375A8008036E3 /* MeshtasticTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeshtasticTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
25F5D5D02C4375DF008036E3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = "<group>"; };
|
||||
3D3417B32E2730EC006A988B /* GeoJSONOverlayManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoJSONOverlayManager.swift; sourceTree = "<group>"; };
|
||||
3D3417BB2E273AC6006A988B /* Street_Outlines.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = Street_Outlines.geojson; sourceTree = "<group>"; };
|
||||
3D3417C12E274800006A988B /* Toilets.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = Toilets.geojson; sourceTree = "<group>"; };
|
||||
3D3417C22E274800006A988B /* Trash_Fence.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = Trash_Fence.geojson; sourceTree = "<group>"; };
|
||||
6D825E612C34786C008DBEE4 /* CommonRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonRegex.swift; sourceTree = "<group>"; };
|
||||
6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = "<group>"; };
|
||||
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorLog.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1050,6 +1058,9 @@
|
|||
DDB75A192A05EB67006ED576 /* alpha.png */,
|
||||
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */,
|
||||
DD0E21002B8A6BC500F2D100 /* DeviceHardware.json */,
|
||||
3D3417BB2E273AC6006A988B /* Street_Outlines.geojson */,
|
||||
3D3417C12E274800006A988B /* Toilets.geojson */,
|
||||
3D3417C22E274800006A988B /* Trash_Fence.geojson */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1108,6 +1119,7 @@
|
|||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
|
||||
DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */,
|
||||
6D825E612C34786C008DBEE4 /* CommonRegex.swift */,
|
||||
3D3417B32E2730EC006A988B /* GeoJSONOverlayManager.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1360,7 +1372,10 @@
|
|||
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */,
|
||||
25AECD4F2C2F723200862C8E /* Localizable.xcstrings in Resources */,
|
||||
DDDE5A1329AFEAB900490C6C /* Assets.xcassets in Resources */,
|
||||
3D3417BC2E273AC6006A988B /* Street_Outlines.geojson in Resources */,
|
||||
DDB75A1A2A05EB67006ED576 /* alpha.png in Resources */,
|
||||
3D3417C32E274800006A988B /* Toilets.geojson in Resources */,
|
||||
3D3417C42E274800006A988B /* Trash_Fence.geojson in Resources */,
|
||||
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */,
|
||||
DD0E21012B8A6F1300F2D100 /* DeviceHardware.json in Resources */,
|
||||
DDDBC87B2BC62E4E001E8DF7 /* Settings.bundle in Resources */,
|
||||
|
|
@ -1476,6 +1491,7 @@
|
|||
DDDB445429F8AD1600EE2349 /* Data.swift in Sources */,
|
||||
DDDB26462AACC0B7003AFCB7 /* NodeInfoItem.swift in Sources */,
|
||||
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */,
|
||||
3D3417B42E2730EC006A988B /* GeoJSONOverlayManager.swift in Sources */,
|
||||
237B46962DC8F1C100B22D99 /* RateLimitedButton.swift in Sources */,
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */,
|
||||
DDDB263F2AABEE20003AFCB7 /* NodeList.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"originHash" : "2569905853aec088d5bac6b540eac77f78963f88b406e8dd95a88c40623cc8b4",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "cocoamqtt",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/emqx/CocoaMQTT",
|
||||
"state" : {
|
||||
"revision" : "22b98acc75bdca77917a1093bd3e1b45ef6e9718",
|
||||
"version" : "2.1.9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "dd-sdk-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/DataDog/dd-sdk-ios.git",
|
||||
"state" : {
|
||||
"revision" : "d0a42d8067665cb6ee86af51251ccc071f62bd54",
|
||||
"version" : "2.29.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "mqttcocoaasyncsocket",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket",
|
||||
"state" : {
|
||||
"revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73",
|
||||
"version" : "1.0.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "opentelemetry-swift-packages",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/DataDog/opentelemetry-swift-packages.git",
|
||||
"state" : {
|
||||
"revision" : "4a7295600d4ebb9525a23c11586c5fdb74ae8b7e",
|
||||
"version" : "1.13.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "plcrashreporter",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/microsoft/plcrashreporter.git",
|
||||
"state" : {
|
||||
"revision" : "8c61e5e38e9f737dd68512ed1ea5ab081244ad65",
|
||||
"version" : "1.12.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/daltoniam/Starscream.git",
|
||||
"state" : {
|
||||
"revision" : "c6bfd1af48efcc9a9ad203665db12375ba6b145a",
|
||||
"version" : "4.0.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "102a647b573f60f73afdce5613a51d71349fe507",
|
||||
"version" : "1.30.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
75
Meshtastic/Helpers/GeoJSONOverlayManager.swift
Normal file
75
Meshtastic/Helpers/GeoJSONOverlayManager.swift
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
/// Enum for supported static overlays
|
||||
enum StaticGeoJSONOverlay: String, CaseIterable {
|
||||
case streetOutlines = "Street_Outlines"
|
||||
case toilets = "Toilets"
|
||||
case trashFence = "Trash_Fence"
|
||||
|
||||
var filename: String { self.rawValue + ".geojson" }
|
||||
}
|
||||
|
||||
/// Manager for loading and adding GeoJSON overlays
|
||||
class GeoJSONOverlayManager {
|
||||
static let shared = GeoJSONOverlayManager()
|
||||
private init() {}
|
||||
|
||||
private var overlays: [StaticGeoJSONOverlay: [MKOverlay]] = [:]
|
||||
|
||||
/// Load overlays for a given type (from bundle)
|
||||
func loadOverlays(for type: StaticGeoJSONOverlay) -> [MKOverlay] {
|
||||
print("GeoJSONOverlayManager: Attempting to load overlays for \(type.rawValue)")
|
||||
if let cached = overlays[type] {
|
||||
print("GeoJSONOverlayManager: Returning cached overlays for \(type.rawValue), count: \(cached.count)")
|
||||
return cached
|
||||
}
|
||||
guard let url = Bundle.main.url(forResource: type.rawValue, withExtension: "geojson") else {
|
||||
print("GeoJSONOverlayManager: No file found for \(type.rawValue).geojson")
|
||||
return []
|
||||
}
|
||||
print("GeoJSONOverlayManager: Found file at: \(url)")
|
||||
do {
|
||||
let data = try Data(contentsOf: url)
|
||||
print("GeoJSONOverlayManager: Loaded data size: \(data.count) bytes")
|
||||
let features = try MKGeoJSONDecoder().decode(data)
|
||||
print("GeoJSONOverlayManager: Decoded \(features.count) features for \(type.rawValue)")
|
||||
|
||||
var allOverlays: [MKOverlay] = []
|
||||
for (index, feature) in features.enumerated() {
|
||||
if let mkFeature = feature as? MKGeoJSONFeature {
|
||||
print("GeoJSONOverlayManager: Feature \(index) has \(mkFeature.geometry.count) geometries")
|
||||
for (geoIndex, geometry) in mkFeature.geometry.enumerated() {
|
||||
print("GeoJSONOverlayManager: Geometry \(geoIndex): \(Swift.type(of: geometry))")
|
||||
if let overlay = geometry as? MKOverlay {
|
||||
allOverlays.append(overlay)
|
||||
print("GeoJSONOverlayManager: Added as overlay")
|
||||
} else {
|
||||
print("GeoJSONOverlayManager: Could not cast to MKOverlay")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("GeoJSONOverlayManager: Feature \(index) could not be cast to MKGeoJSONFeature")
|
||||
}
|
||||
}
|
||||
|
||||
print("GeoJSONOverlayManager: Created \(allOverlays.count) total overlays for \(type.rawValue)")
|
||||
overlays[type] = allOverlays
|
||||
return allOverlays
|
||||
} catch {
|
||||
print("Failed to load GeoJSON overlay: \(type) error: \(error)")
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/// Add overlays to the map
|
||||
func addOverlays(_ type: StaticGeoJSONOverlay, to mapView: MKMapView) {
|
||||
let overlays = loadOverlays(for: type)
|
||||
mapView.addOverlays(overlays, level: .aboveLabels)
|
||||
}
|
||||
/// Remove overlays from the map
|
||||
func removeOverlays(_ type: StaticGeoJSONOverlay, from mapView: MKMapView) {
|
||||
let overlays = self.overlays[type] ?? []
|
||||
mapView.removeOverlays(overlays)
|
||||
}
|
||||
}
|
||||
1
Meshtastic/Resources/Street_Outlines.geojson
Normal file
1
Meshtastic/Resources/Street_Outlines.geojson
Normal file
File diff suppressed because one or more lines are too long
2984
Meshtastic/Resources/Toilets.geojson
Normal file
2984
Meshtastic/Resources/Toilets.geojson
Normal file
File diff suppressed because it is too large
Load diff
44
Meshtastic/Resources/Trash_Fence.geojson
Normal file
44
Meshtastic/Resources/Trash_Fence.geojson
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"id": 0,
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
-119.18279831290056,
|
||||
40.80313545483217
|
||||
],
|
||||
[
|
||||
-119.21776730133423,
|
||||
40.80734514946494
|
||||
],
|
||||
[
|
||||
-119.23383070469475,
|
||||
40.783371453674185
|
||||
],
|
||||
[
|
||||
-119.20880237381652,
|
||||
40.76435187944433
|
||||
],
|
||||
[
|
||||
-119.17727060534106,
|
||||
40.776562577639574
|
||||
],
|
||||
[
|
||||
-119.18279831290056,
|
||||
40.80313545483217
|
||||
]
|
||||
]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"FID": 0,
|
||||
"Id": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,6 +8,11 @@
|
|||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
struct IdentifiableOverlay: Identifiable {
|
||||
let overlay: MKOverlay
|
||||
var id: ObjectIdentifier { ObjectIdentifier(overlay as AnyObject) }
|
||||
}
|
||||
|
||||
struct MeshMapContent: MapContent {
|
||||
|
||||
/// Parameters
|
||||
|
|
@ -24,6 +29,11 @@ struct MeshMapContent: MapContent {
|
|||
@AppStorage("enableMapWaypoints") private var showWaypoints = true
|
||||
@Binding var selectedWaypoint: WaypointEntity?
|
||||
|
||||
// Burning Man GeoJSON overlays
|
||||
@AppStorage("burningManShowStreets") private var showStreets = false
|
||||
@AppStorage("burningManShowToilets") private var showToilets = false
|
||||
@AppStorage("burningManShowTrashFence") private var showTrashFence = false
|
||||
|
||||
@FetchRequest(fetchRequest: PositionEntity.allPositionsFetchRequest(), animation: .easeIn)
|
||||
var positions: FetchedResults<PositionEntity>
|
||||
|
||||
|
|
@ -222,6 +232,53 @@ struct MeshMapContent: MapContent {
|
|||
.foregroundStyle(.indigo.opacity(0.4))
|
||||
}
|
||||
}
|
||||
|
||||
/// Burning Man GeoJSON Overlays
|
||||
if showStreets {
|
||||
let overlays = GeoJSONOverlayManager.shared.loadOverlays(for: StaticGeoJSONOverlay.streetOutlines)
|
||||
let identifiableOverlays = overlays.map { IdentifiableOverlay(overlay: $0) }
|
||||
ForEach(identifiableOverlays) { identifiable in
|
||||
let overlay = identifiable.overlay
|
||||
if let polygon = overlay as? MKPolygon {
|
||||
MapPolygon(polygon)
|
||||
.stroke(.yellow.opacity(0.8), lineWidth: 0.5)
|
||||
.foregroundStyle(.clear)
|
||||
} else if let polyline = overlay as? MKPolyline {
|
||||
MapPolyline(polyline)
|
||||
.stroke(.yellow.opacity(0.9), lineWidth: 0.8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if showToilets {
|
||||
let overlays = GeoJSONOverlayManager.shared.loadOverlays(for: StaticGeoJSONOverlay.toilets)
|
||||
let identifiableOverlays = overlays.map { IdentifiableOverlay(overlay: $0) }
|
||||
ForEach(identifiableOverlays) { identifiable in
|
||||
let overlay = identifiable.overlay
|
||||
if let polygon = overlay as? MKPolygon {
|
||||
MapPolygon(polygon)
|
||||
.stroke(.brown, lineWidth: 1)
|
||||
.foregroundStyle(.brown.opacity(0.3))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if showTrashFence {
|
||||
let overlays = GeoJSONOverlayManager.shared.loadOverlays(for: StaticGeoJSONOverlay.trashFence)
|
||||
let identifiableOverlays = overlays.map { IdentifiableOverlay(overlay: $0) }
|
||||
ForEach(identifiableOverlays) { identifiable in
|
||||
let overlay = identifiable.overlay
|
||||
if let polyline = overlay as? MKPolyline {
|
||||
MapPolyline(polyline)
|
||||
.stroke(.red, lineWidth: 3)
|
||||
} else if let polygon = overlay as? MKPolygon {
|
||||
MapPolygon(polygon)
|
||||
.stroke(.red, lineWidth: 3)
|
||||
.foregroundStyle(.red.opacity(0.2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
positionAnnotations
|
||||
routeAnnotations
|
||||
waypointAnnotations
|
||||
|
|
|
|||
|
|
@ -115,6 +115,47 @@ struct MapSettingsForm: View {
|
|||
UserDefaults.enableMapPointsOfInterest = self.pointsOfInterest
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Burning Man Overlays")) {
|
||||
Toggle(isOn: Binding(
|
||||
get: { UserDefaults.standard.bool(forKey: "burningManShowStreets") },
|
||||
set: { UserDefaults.standard.set($0, forKey: "burningManShowStreets") }
|
||||
)) {
|
||||
Label {
|
||||
Text("Street Outlines")
|
||||
} icon: {
|
||||
Image(systemName: "road.lanes")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
}
|
||||
.tint(.accentColor)
|
||||
|
||||
Toggle(isOn: Binding(
|
||||
get: { UserDefaults.standard.bool(forKey: "burningManShowToilets") },
|
||||
set: { UserDefaults.standard.set($0, forKey: "burningManShowToilets") }
|
||||
)) {
|
||||
Label {
|
||||
Text("Toilets")
|
||||
} icon: {
|
||||
Image(systemName: "toilet")
|
||||
.foregroundColor(.brown)
|
||||
}
|
||||
}
|
||||
.tint(.accentColor)
|
||||
|
||||
Toggle(isOn: Binding(
|
||||
get: { UserDefaults.standard.bool(forKey: "burningManShowTrashFence") },
|
||||
set: { UserDefaults.standard.set($0, forKey: "burningManShowTrashFence") }
|
||||
)) {
|
||||
Label {
|
||||
Text("Trash Fence")
|
||||
} icon: {
|
||||
Image(systemName: "fence")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
.tint(.accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue