Skip to content

Instantly share code, notes, and snippets.

@mpiannucci
Last active April 6, 2020 02:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpiannucci/9dadb9d17cb920ee53a81f9d3c6b68db to your computer and use it in GitHub Desktop.
Save mpiannucci/9dadb9d17cb920ee53a81f9d3c6b68db to your computer and use it in GitHub Desktop.
BuoyFinder Map Overlays
import SwiftUI
import MapKit
import PlaygroundSupport
class SwellDataOverlay : NSObject, MKOverlay {
let coordinate: CLLocationCoordinate2D
let boundingMapRect: MKMapRect
let magnitude: Double
let angle: Double
let color: UIColor
init(stationLocation: CLLocationCoordinate2D, magnitude: Double, angle: Double, color: UIColor) {
coordinate = stationLocation
boundingMapRect = MKMapRect(origin: MKMapPoint(coordinate), size: MKMapSize(width: 200, height: 200))
self.magnitude = magnitude
self.color = color
self.angle = angle
super.init()
}
}
class SwellDataOverlayRenderer : MKOverlayPathRenderer {
override func createPath() {
guard let swellDataOverlay = overlay as? SwellDataOverlay else {
return
}
let radius: CGFloat = CGFloat(6e5 * swellDataOverlay.magnitude)
let angle: CGFloat = CGFloat(swellDataOverlay.angle)
let angleWidth: CGFloat = 40.0
let startAngle = (angle - (angleWidth * 0.5) - 90) * CGFloat.pi / 180.0
let endAngle = (angle + (angleWidth * 0.5) - 90) * CGFloat.pi / 180.0
let path = CGMutablePath()
let originMapPoint = MKMapPoint(swellDataOverlay.coordinate)
let origin = point(for: originMapPoint)
path.move(to: origin)
path.addArc(center: origin, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
path.addLine(to: origin)
self.path = path
}
}
class MapDelegate : NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "stationAnnotationView"
var stationAnnotationView: MKMarkerAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier, for: annotation) as? MKMarkerAnnotationView
if (stationAnnotationView == nil) {
stationAnnotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
stationAnnotationView?.markerTintColor = .systemGreen
stationAnnotationView?.glyphTintColor = .black
stationAnnotationView?.glyphImage = UIImage(systemName: "antenna.radiowaves.left.and.right")
return stationAnnotationView
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let swellDataOverlay = overlay as? SwellDataOverlay else {
return MKOverlayRenderer(overlay: overlay)
}
let renderer = SwellDataOverlayRenderer(overlay: swellDataOverlay)
renderer.lineCap = .round
renderer.lineWidth = 1.0
renderer.strokeColor = swellDataOverlay.color.withAlphaComponent(0.5)
renderer.fillColor = swellDataOverlay.color.withAlphaComponent(0.3)
return renderer
}
}
struct StationMapView : UIViewRepresentable {
let station: Station
let mapDelegate = MapDelegate()
typealias UIViewType = MKMapView
func makeUIView(context: UIViewRepresentableContext<StationMapView>) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = mapDelegate
let delta = 1.5
mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: "stationAnnotationView")
var region = MKCoordinateRegion()
region.center.latitude = station.location.latitude
region.center.longitude = station.location.longitude
region.span.latitudeDelta = delta
region.span.longitudeDelta = delta
mapView.setRegion( region, animated: true )
let stationAnnotation = MKPointAnnotation()
stationAnnotation.coordinate = station.location
//stationAnnotation.title = station.id
stationAnnotation.subtitle = "\(station.id) • NDBC • Active"
mapView.addAnnotation(stationAnnotation)
let stationSecondSwellOverlay = SwellDataOverlay(stationLocation: station.location, magnitude: 0.5, angle: 165.0, color: .systemYellow)
mapView.addOverlay(stationSecondSwellOverlay)
let stationThirdSwellOverlay = SwellDataOverlay(stationLocation: station.location, magnitude: 0.8, angle: 205, color: .systemOrange)
mapView.addOverlay(stationThirdSwellOverlay)
let stationFirstSwellOverlay = SwellDataOverlay(stationLocation: station.location, magnitude: 1.0, angle: 180.0, color: .systemRed)
mapView.addOverlay(stationFirstSwellOverlay)
mapView.isScrollEnabled = false
mapView.isZoomEnabled = false
mapView.isPitchEnabled = false
return mapView
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<StationMapView>) {
//
}
}
struct ConditionTile : View {
let iconName: String
let title: String
let subtitle: String?
var body: some View {
VStack {
Image(systemName: iconName)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
Text(title).allowsTightening(true).minimumScaleFactor(0.5).lineLimit(1)
Text(subtitle ?? "")
.allowsTightening(true)
.minimumScaleFactor(0.5)
.lineLimit(1)
}
.frame(width: 56, height: 56)
.padding()
.background(Color(.systemGray6))
.cornerRadius(20.0)
}
}
struct StationView : View {
let station: Station
var body: some View {
return NavigationView {
ScrollView{
HStack {
Image(systemName: "location.north.fill")
.resizable()
.rotationEffect(.degrees(170 - 180))
.frame(width: 36, height: 36)
.padding()
VStack (alignment: .leading ) {
Text("4.8 ft @ 7 s SSE")
.font(Font.system(size: 22.0))
.bold()
Text("Significant Wave Height")
.font(.subheadline)
.foregroundColor(Color(.systemGray))
.minimumScaleFactor(0.5)
Text("Reported 12:30 PM")
.font(.subheadline)
.foregroundColor(Color(.systemGray))
.minimumScaleFactor(0.5)
}
}.padding()
StationMapView(station: station)
.frame(height: 150.0)
.cornerRadius(8.0)
.padding()
VStack {
HStack {
Image(systemName: "1.circle.fill")
.foregroundColor(Color(.systemRed).opacity(0.7))
Text("3.8 ft @ 7 s 165° SSE")
.font(.callout)
}
HStack {
Image(systemName: "2.circle.fill")
.foregroundColor(Color(.systemOrange).opacity(0.7))
Text("2 ft @ 9 s 205° SSW")
.font(.callout)
}
}
HStack {
ConditionTile(iconName: "thermometer", title: "42° F", subtitle: "Water")
ConditionTile(iconName: "wind", title: "14 mph", subtitle: "WNW")
ConditionTile(iconName: "gauge.badge.plus", title: "29.10 in", subtitle: "Rising")
}.padding()
Text("Previous Observations").font(.headline).padding()
}
.navigationBarTitle(station.name)
.navigationBarItems(trailing: Button (action: {
}) {
Image(systemName: "magnifyingglass")
})
}
}
}
let blockIslandStation = Station(id: "44097", name: "Block Island, RI", latitude: 40.969, longitude: -71.127)
PlaygroundPage.current.setLiveView(StationView(station: blockIslandStation))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment