mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
Add map style options to location simulator (satellite, hybrid, traffic, transit stops)
- New toolbar menu on the location simulator map to switch between Standard, Satellite, and Hybrid map types - Traffic overlay toggle for Standard/Hybrid modes - Points-of-interest mode: All Places, Transit Stops (surfaces bus stops), or Hidden - Selections persist across launches via AppStorage Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CUJbpUQiRUbjiTiYA4v7UK
This commit is contained in:
parent
69d7a1d9cc
commit
f848bca707
1 changed files with 124 additions and 1 deletions
|
|
@ -103,6 +103,67 @@ enum SpeedProfile: String, CaseIterable, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MapDisplayMode: String, CaseIterable, Identifiable {
|
||||||
|
case standard
|
||||||
|
case satellite
|
||||||
|
case hybrid
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .standard: return "Standard"
|
||||||
|
case .satellite: return "Satellite"
|
||||||
|
case .hybrid: return "Hybrid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemImage: String {
|
||||||
|
switch self {
|
||||||
|
case .standard: return "map"
|
||||||
|
case .satellite: return "globe.americas.fill"
|
||||||
|
case .hybrid: return "square.2.layers.3d"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pure satellite imagery can't render traffic or labeled points of interest.
|
||||||
|
var supportsOverlays: Bool {
|
||||||
|
self != .satellite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MapPointsOfInterestMode: String, CaseIterable, Identifiable {
|
||||||
|
case all
|
||||||
|
case transit
|
||||||
|
case hidden
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "All Places"
|
||||||
|
case .transit: return "Transit Stops"
|
||||||
|
case .hidden: return "Hidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemImage: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "mappin.and.ellipse"
|
||||||
|
case .transit: return "bus.fill"
|
||||||
|
case .hidden: return "eye.slash"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var categories: PointOfInterestCategories {
|
||||||
|
switch self {
|
||||||
|
case .all: return .all
|
||||||
|
case .transit: return .including([.publicTransport])
|
||||||
|
case .hidden: return .excludingAll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct SpeedProfileSettings {
|
private struct SpeedProfileSettings {
|
||||||
let profile: SpeedProfile
|
let profile: SpeedProfile
|
||||||
let customSpeedMetersPerSecond: CLLocationSpeed
|
let customSpeedMetersPerSecond: CLLocationSpeed
|
||||||
|
|
@ -934,6 +995,11 @@ struct LocationSimulationView: View {
|
||||||
@State private var showSaveBookmark = false
|
@State private var showSaveBookmark = false
|
||||||
@State private var newBookmarkName = ""
|
@State private var newBookmarkName = ""
|
||||||
|
|
||||||
|
// Map appearance
|
||||||
|
@AppStorage("mapDisplayMode") private var mapDisplayModeRawValue: String = MapDisplayMode.standard.rawValue
|
||||||
|
@AppStorage("mapShowsTraffic") private var mapShowsTraffic: Bool = false
|
||||||
|
@AppStorage("mapPointsOfInterestMode") private var mapPointsOfInterestRawValue: String = MapPointsOfInterestMode.all.rawValue
|
||||||
|
|
||||||
// Speed profile
|
// Speed profile
|
||||||
@AppStorage("routeSpeedProfile") private var speedProfileRawValue: String = SpeedProfile.driving.rawValue
|
@AppStorage("routeSpeedProfile") private var speedProfileRawValue: String = SpeedProfile.driving.rawValue
|
||||||
@AppStorage("routeSpeedCustomKmh") private var customSpeedKmh: Double = 30
|
@AppStorage("routeSpeedCustomKmh") private var customSpeedKmh: Double = 30
|
||||||
|
|
@ -942,6 +1008,33 @@ struct LocationSimulationView: View {
|
||||||
@State private var lastFallbackSpeed: CLLocationSpeed = RouteSimulationDefaults.importedRouteFallbackSpeedMetersPerSecond
|
@State private var lastFallbackSpeed: CLLocationSpeed = RouteSimulationDefaults.importedRouteFallbackSpeedMetersPerSecond
|
||||||
@State private var isImportedRoute = false
|
@State private var isImportedRoute = false
|
||||||
|
|
||||||
|
private var mapDisplayMode: MapDisplayMode {
|
||||||
|
MapDisplayMode(rawValue: mapDisplayModeRawValue) ?? .standard
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mapPointsOfInterestMode: MapPointsOfInterestMode {
|
||||||
|
MapPointsOfInterestMode(rawValue: mapPointsOfInterestRawValue) ?? .all
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mapStyle: MapStyle {
|
||||||
|
switch mapDisplayMode {
|
||||||
|
case .standard:
|
||||||
|
return .standard(
|
||||||
|
elevation: .realistic,
|
||||||
|
pointsOfInterest: mapPointsOfInterestMode.categories,
|
||||||
|
showsTraffic: mapShowsTraffic
|
||||||
|
)
|
||||||
|
case .satellite:
|
||||||
|
return .imagery(elevation: .realistic)
|
||||||
|
case .hybrid:
|
||||||
|
return .hybrid(
|
||||||
|
elevation: .realistic,
|
||||||
|
pointsOfInterest: mapPointsOfInterestMode.categories,
|
||||||
|
showsTraffic: mapShowsTraffic
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var speedProfile: SpeedProfile {
|
private var speedProfile: SpeedProfile {
|
||||||
SpeedProfile(rawValue: speedProfileRawValue) ?? .driving
|
SpeedProfile(rawValue: speedProfileRawValue) ?? .driving
|
||||||
}
|
}
|
||||||
|
|
@ -1113,7 +1206,7 @@ struct LocationSimulationView: View {
|
||||||
.tint(.red)
|
.tint(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mapStyle(.standard(elevation: .realistic))
|
.mapStyle(mapStyle)
|
||||||
.onTapGesture { point in
|
.onTapGesture { point in
|
||||||
if let loc = proxy.convert(point, from: .local) {
|
if let loc = proxy.convert(point, from: .local) {
|
||||||
applySelection(loc)
|
applySelection(loc)
|
||||||
|
|
@ -1163,6 +1256,8 @@ struct LocationSimulationView: View {
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItemGroup(placement: .topBarLeading) {
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
mapStyleMenu
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
showBookmarks = true
|
showBookmarks = true
|
||||||
} label: {
|
} label: {
|
||||||
|
|
@ -1512,6 +1607,34 @@ struct LocationSimulationView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var mapStyleMenu: some View {
|
||||||
|
Menu {
|
||||||
|
Picker("Map Type", selection: $mapDisplayModeRawValue) {
|
||||||
|
ForEach(MapDisplayMode.allCases) { mode in
|
||||||
|
Label(mode.title, systemImage: mode.systemImage)
|
||||||
|
.tag(mode.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapDisplayMode.supportsOverlays {
|
||||||
|
Toggle(isOn: $mapShowsTraffic) {
|
||||||
|
Label("Traffic", systemImage: "car.2.fill")
|
||||||
|
}
|
||||||
|
|
||||||
|
Picker("Points of Interest", selection: $mapPointsOfInterestRawValue) {
|
||||||
|
ForEach(MapPointsOfInterestMode.allCases) { mode in
|
||||||
|
Label(mode.title, systemImage: mode.systemImage)
|
||||||
|
.tag(mode.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(.menu)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "map.fill")
|
||||||
|
}
|
||||||
|
.accessibilityLabel("Map style: \(mapDisplayMode.title)")
|
||||||
|
}
|
||||||
|
|
||||||
private var speedProfileMenu: some View {
|
private var speedProfileMenu: some View {
|
||||||
Menu {
|
Menu {
|
||||||
ForEach(SpeedProfile.allCases) { profile in
|
ForEach(SpeedProfile.allCases) { profile in
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue