map renders streets

fix files
This commit is contained in:
Jacob Powers 2025-07-16 02:45:17 +00:00
parent 842fa1ebd1
commit f09fcd82f7
9 changed files with 3299 additions and 0 deletions

View file

@ -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" : {

View file

@ -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 */,

View file

@ -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
}

View 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)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View 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
}
}
]
}

View file

@ -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

View file

@ -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)