Created
September 25, 2025 13:38
-
-
Save ryancoughlin/9bfe09f667da001f5e5a3ce1994225a3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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