Skip to content

Instantly share code, notes, and snippets.

@ryancoughlin
Created September 25, 2025 13:38
Show Gist options
  • Save ryancoughlin/9bfe09f667da001f5e5a3ce1994225a3 to your computer and use it in GitHub Desktop.
Save ryancoughlin/9bfe09f667da001f5e5a3ce1994225a3 to your computer and use it in GitHub Desktop.
import SwiftUI
import MapboxMaps
/// Generic contour layer - minimal, self-contained, renders from ContourLayerState
/// Works for SST, Chlorophyll, Salinity, Water Clarity, and other dataset types
struct SSTContourLayer: MapContent {
let state: ContourLayerState
var body: some MapContent {
let _ = print("🌊 [ContourLayer] Creating \(state.datasetType.rawValue) layer")
let _ = print("🌊 [ContourLayer] DatasetType: \(state.datasetType)")
// Major contour lines (whole degrees) with range filtering
let _ = print("🌊 [ContourLayer] Range filter: min=\(state.minValue), max=\(state.maxValue)")
let _ = print("🌊 [ContourLayer] Field name: \(state.fieldName)")
LineLayer(id: "\(state.layerId)-major", source: state.sourceId)
.sourceLayer("contours")
.filter(combinedFilter(
baseFilter: Exp(.eq) { Exp(.get) { "is_major" }; true },
rangeFilter: state.rangeFilter
))
.lineColor(state.styleColor)
.lineWidth(2.5)
.lineOpacity(state.opacity)
.lineCap(.round)
.lineJoin(.round)
.slot(.top)
// Decimal contour lines (0.5 degree intervals) with range filtering
LineLayer(id: "\(state.layerId)-decimal", source: state.sourceId)
.sourceLayer("contours")
.filter(combinedFilter(
baseFilter: Exp(.eq) { Exp(.get) { "is_major" }; false },
rangeFilter: state.rangeFilter
))
.lineColor(state.styleColor)
.lineWidth(1.5)
.lineOpacity(state.opacity * 0.7)
.lineCap(.round)
.lineJoin(.round)
.slot(.top)
.minZoom(9)
// Labels for major lines with range filtering
SymbolLayer(id: "\(state.layerId)-labels", source: state.sourceId)
.sourceLayer("contours")
.filter(combinedFilter(
baseFilter: Exp(.eq) { Exp(.get) { "is_major" }; true },
rangeFilter: state.rangeFilter
))
.textColor(StyleColor(.black))
.textHaloColor(StyleColor(.white))
.textHaloWidth(2.0)
.textFont(["League Mono Regular"])
.symbolPlacement(.line)
.textField(Exp(.concat) {
Exp(.get) { ContourFilterExpression.getContourLabelFieldName(for: state.datasetType) }
})
.textSize(12)
.textMaxAngle(45.0)
.textAllowOverlap(false)
.symbolSpacing(150)
.textPadding(8)
.textOpacity(state.opacity)
.slot(.top)
.minZoom(7)
}
// MARK: - Filter Helpers
/// Creates contour filter expression if filtering is active
private func createContourFilter() -> Exp? {
guard let filterState = state.filter,
filterState.effectiveIsActive else {
return nil
}
return ContourFilterExpression.createFilter(
for: state.datasetType,
filterState: filterState
)
}
/// Combines base filter with range filter
private func combinedFilter(baseFilter: Exp, rangeFilter: Exp) -> Exp {
Exp(.all) {
baseFilter
rangeFilter
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment