How to set zoom level or radius in map view? - ios

Good day. I would like to know how to set like a radius of 1km on a current location on a MapView with
mapView.showsUserLocation = true
Is that possible? Thank you.

You can set "zoom level" by MKCoordinateSpanMake.
Try with this:
mapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude), span: MKCoordinateSpanMake(0.05, 0.05)), animated: true)

Not my code. Maybe it can help you
extension MKMapView {
private var MERCATOR_OFFSET: Double {
get {
return 268435456
}
}
private var MERCATOR_RADIUS: Double {
get {
return 85445659.44705395
}
}
private var MAX_ZOOM_LEVEL: Double {
get {
return 19
}
}
// MARK: - Private functions
private func longitudeToPixelSpaceX (longitude: Double) -> Double {
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0)
}
private func latitudeToPixelSpaceY (latitude: Double) -> Double {
let a = 1 + sinf(Float(latitude * M_PI) / 180.0)
let b = 1.0 - sinf(Float(latitude * M_PI / 180.0)) / 2.0
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * Double(logf(a / b)))
}
private func pixelSpaceXToLongitude (pixelX: Double) -> Double {
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI
}
private func pixelSpaceYToLatitude (pixelY: Double) -> Double {
return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI
}
private func coordinateSpanWithMapView(mapView: MKMapView, centerCoordinate: CLLocationCoordinate2D, andZoomLevel zoomLevel:Int) -> MKCoordinateSpan {
// convert center coordiate to pixel space
let centerPixelX = self.longitudeToPixelSpaceX(centerCoordinate.longitude)
let centerPixelY = self.latitudeToPixelSpaceY(centerCoordinate.latitude)
// determine the scale value from the zoom level
let zoomExponent = 20 - zoomLevel
let zoomScale = CGFloat(pow(Double(2), Double(zoomExponent)))
// scale the map’s size in pixel space
let mapSizeInPixels = mapView.bounds.size
let scaledMapWidth = mapSizeInPixels.width * zoomScale
let scaledMapHeight = mapSizeInPixels.height * zoomScale
// figure out the position of the top-left pixel
let topLeftPixelX = CGFloat(centerPixelX) - (scaledMapWidth / 2)
let topLeftPixelY = CGFloat(centerPixelY) - (scaledMapHeight / 2)
// find delta between left and right longitudes
let minLng: CLLocationDegrees = self.pixelSpaceXToLongitude(Double(topLeftPixelX))
let maxLng: CLLocationDegrees = self.pixelSpaceXToLongitude(Double(topLeftPixelX + scaledMapWidth))
let longitudeDelta: CLLocationDegrees = maxLng - minLng
// find delta between top and bottom latitudes
let minLat: CLLocationDegrees = self.pixelSpaceYToLatitude(Double(topLeftPixelY))
let maxLat: CLLocationDegrees = self.pixelSpaceYToLatitude(Double(topLeftPixelY + scaledMapHeight))
let latitudeDelta: CLLocationDegrees = -1 * (maxLat - minLat)
// create and return the lat/lng span
let span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta)
return span
}
// MARK: - Public Functions
func setCenterCoordinate(centerCoordinate: CLLocationCoordinate2D, zoomLevel: Int, animated: Bool) {
// clamp large numbers to 28
let zoom = min(zoomLevel, 28)
// use the zoom level to compute the region
let span = self.coordinateSpanWithMapView(self, centerCoordinate:centerCoordinate, andZoomLevel:zoom)
let region = MKCoordinateRegionMake(centerCoordinate, span)
// set the region like normal
self.setRegion(region, animated:animated)
}
func getZoomLevel() -> Int {
let longitudeDelta = self.region.span.longitudeDelta
let mapWidthInPixels = self.bounds.size.width*2 //2 is for retina display
let zoomScale = longitudeDelta * MERCATOR_RADIUS * M_PI / Double((180.0 * mapWidthInPixels))
var zoomer = MAX_ZOOM_LEVEL - log2(zoomScale)
if zoomer < 0 {
zoomer = 0
}
zoomer = round(zoomer)
return Int(zoomer)
}
}
Use like
mapView.setCenterCoordinate(mapView.centerCoordinate, zoomLevel: 17, animated: true)

here i am adding circle from current location to radius of 1 km and area of map displaya about 6 km square. i think it will help you.
// adding circle as overlay
func addRadiusCircle(location: CLLocation){
self.mapView.delegate = self
//radius of 1000 meters
let circle = MKCircle(centerCoordinate: location.coordinate, radius: 1000 as CLLocationDistance)
self.mapView.addOverlay(circle)
}
//circle design and coloring
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let circle = MKCircleRenderer(overlay: overlay)
if overlay is MKCircle {
circle.strokeColor = UIColor.blackColor()
circle.fillColor = UIColor(red: 235/255, green: 174/255, blue: 13/255, alpha:0.3)
circle.lineWidth = 1
}
return circle
}
// initial area to display on map
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
6000, 6000)
mapView.setRegion(coordinateRegion, animated: true)
}

Related

Smoothy move markers on Google Map not working in Swift 3

I placed car marker on google map for smoothy move to destination location. Here I can rotate the markers but it's not getting move to the destination. Can anyone identify what's code with the below code?
override func viewDidLoad() {
super.viewDidLoad()
let centerCoordinate = CLLocationCoordinate2DMake((self.delegate?.userLocation?.coordinate.latitude)!, (self.delegate?.userLocation?.coordinate.longitude)!)
self.setUpDriverMarkerWithNewLocation(location: centerCoordinate)
}
func setUpDriverMarkerWithNewLocation(location : CLLocationCoordinate2D) {
self.sourceMarker.icon = UIImage(named: "ARCar")
let centerCoordinate = CLLocationCoordinate2DMake(13.02025636601701, 80.26621352881193)
self.sourceMarker.groundAnchor = CGPoint(x: CGFloat(0.5), y: CGFloat(0.5))
self.sourceMarker.rotation = CLLocationDegrees(getHeadingForDirection(fromCoordinate: centerCoordinate, toCoordinate: location))
self.sourceMarker.position = location
self.sourceMarker.map = self.rideMapView
CATransaction.begin()
CATransaction.setValue(Int(2.0), forKey: kCATransactionAnimationDuration)
CATransaction.setCompletionBlock({() -> Void in
self.sourceMarker.groundAnchor = CGPoint(x: CGFloat(0.5), y: CGFloat(0.5))
self.sourceMarker.rotation = self.rotation
})
self.sourceMarker.position = centerCoordinate
//this can be new position after car moved from old position to new position with animation
self.sourceMarker.map = self.rideMapView
self.sourceMarker.groundAnchor = CGPoint(x: CGFloat(0.5), y: CGFloat(0.5))
self.sourceMarker.rotation = CLLocationDegrees(getHeadingForDirection(fromCoordinate: centerCoordinate, toCoordinate: location))
CATransaction.commit()
}
func getHeadingForDirection(fromCoordinate fromLoc: CLLocationCoordinate2D, toCoordinate toLoc: CLLocationCoordinate2D) -> Float {
let fLat: Float = Float((fromLoc.latitude).degreesToRadians)
let fLng: Float = Float((fromLoc.longitude).degreesToRadians)
let tLat: Float = Float((toLoc.latitude).degreesToRadians)
let tLng: Float = Float((toLoc.longitude).degreesToRadians)
let degree: Float = (atan2(sin(tLng - fLng) * cos(tLat), cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(tLng - fLng))).radiansToDegrees
if degree >= 0 {
return degree - 180.0
}
else {
return 360 + degree - 180
}
}

How to animate MKOverlayRenderer added in Mapview iOS?

I have a mapview as shown below
The lines drawn in the map are using MKPolyline. I have used apple MapKit framework to display my map. My requirement is when the user selects the annotation on Polyline, the polyline should show direction as shown below
How can I show the animation in my map view? MKOverlayRenderer inherits from NSObject, It is not possible to animate using UIView Animation. I found some link here , its in objective C for iOS 7, IS it possible in swift to do the same?
MKOverlayRender inherits from NSObject. So it is not possible to add CAlayer to MKOverlayRenderer as explained in this sample project. The steps I followed to get the following animation effect are
Subclass MKOverlayRenderer and add a custom image as overlay on the map
class MapOverlayView: MKOverlayRenderer {
var overlayImage: UIImage
var angle: CGFloat
init(overlay: MKOverlay, overlayImage:UIImage, angle: CGFloat) {
self.overlayImage = overlayImage
self.angle = angle
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
let mapImage = overlayImage.cgImage
let mapRect = rect(for: overlay.boundingMapRect)
// Calculate centre point on which image should be rotated
let centerPoint = CGPoint(x: mapRect.midX, y: mapRect.midY)
let a = sqrt(pow(centerPoint.x, 2.0) + pow(centerPoint.y, 2.0))
let sub1 = (centerPoint.y / a) * cos(angle / 2.0)
let sub2 = (centerPoint.x / a) * sin(angle / 2.0)
let deltaX = -2 * a * sin((0 - angle) / 2.0) * (sub1 + sub2)
let sub3 = (centerPoint.x / a) * cos(angle / 2.0)
let sub4 = (centerPoint.y / a) * sin(angle / 2.0)
let deltaY = 2 * a * sin((0 - angle) / 2.0) * (sub3 - sub4)
context.translateBy(x: deltaX, y: deltaY)
context.rotate(by: angle)
context.draw(mapImage!, in: mapRect)
}
}
class MapOverlay: NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
var boundingMapRect: MKMapRect
var identifier: String?
init(identifier:String, coord: CLLocationCoordinate2D, rect: MKMapRect) {
self.coordinate = coord
self.boundingMapRect = rect
self.identifier = identifier
}
}
Call a timer to change alpha value of added overlays
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.toggle), userInfo: nil, repeats: true)
Function to change alpha of overlay renderer
func changeAlphaValue(identifier: String) {
let overlays = self.mapView.overlays
let tag: String = identifier
for overlay in overlays {
if let overlay = overlay as? MapOverlay {
let identifier = overlay.identifier
if identifier == tag {
let renderer = mapView.renderer(for: overlay)
DispatchQueue.main.async{
renderer?.alpha = 1.0
}
}
else {
let renderer = mapView.renderer(for: overlay)
DispatchQueue.main.async{
renderer?.alpha = 0.0
}
}
}
}
}
This will give the following animation on map
The project is available on GitHub - link
https://github.com/jhurray/iOS7AnimatedMapOverlay
Above link are useful for you Or below code.
- (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers
{
// Iterate through all overlayRenderers
for (MKOverlayRenderer *overlayRenderer in renderers)
{
// MKPolylineRenderer
// Animate a 'fade'
if ([overlayRenderer isKindOfClass:[MKPolylineRenderer class]])
{
// Get MKPolylineRenderer
MKPolylineRenderer *polylineRenderer = (MKPolylineRenderer *)overlayRenderer;
// Let's manually set alpha to 0
polylineRenderer.alpha = 0.0f;
// Animate it back to 1
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.4 animations:^{
polylineRenderer.alpha = 1.0f
}];
});
}
}
}
Thanks

How to make Circular audio visualizer in swift?

I want to make a visualizer like this Circular visualizer, click the green flag to see the animation.
In my project first I draw a circle, I calculate the points on the circle to draw the visualizer bars, I rotate the view to make the bars feels like circle. I use StreamingKit to stream live radio. StreamingKit provides the live audio power in decibels. Then I animate the visualizer bars. But when I rotate the view the height and width changes according to the angle I rotate. But the bounds value not change (I know the frame depends on superViews).
audioSpectrom Class
class audioSpectrom: UIView {
let animateDuration = 0.15
let visualizerColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
var barsNumber = 0
let barWidth = 4 // width of bar
let radius: CGFloat = 40
var radians = [CGFloat]()
var barPoints = [CGPoint]()
private var rectArray = [CustomView]()
private var waveFormArray = [Int]()
private var initialBarHeight: CGFloat = 0.0
private let mainLayer: CALayer = CALayer()
// draw circle
var midViewX: CGFloat!
var midViewY: CGFloat!
var circlePath = UIBezierPath()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
convenience init() {
self.init(frame: CGRect.zero)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
self.layer.addSublayer(mainLayer)
barsNumber = 10
}
override func layoutSubviews() {
mainLayer.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
drawVisualizer()
}
//-----------------------------------------------------------------
// MARK: - Drawing Section
//-----------------------------------------------------------------
func drawVisualizer() {
midViewX = self.mainLayer.frame.midX
midViewY = self.mainLayer.frame.midY
// Draw Circle
let arcCenter = CGPoint(x: midViewX, y: midViewY)
let circlePath = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
let circleShapeLayer = CAShapeLayer()
circleShapeLayer.path = circlePath.cgPath
circleShapeLayer.fillColor = UIColor.blue.cgColor
circleShapeLayer.strokeColor = UIColor.clear.cgColor
circleShapeLayer.lineWidth = 1.0
mainLayer.addSublayer(circleShapeLayer)
// Draw Bars
rectArray = [CustomView]()
for i in 0..<barsNumber {
let angle = ((360 / barsNumber) * i) - 90
let point = calculatePoints(angle: angle, radius: radius)
let radian = angle.degreesToRadians
radians.append(radian)
barPoints.append(point)
let rectangle = CustomView(frame: CGRect(x: barPoints[i].x, y: barPoints[i].y, width: CGFloat(barWidth), height: CGFloat(barWidth)))
initialBarHeight = CGFloat(self.barWidth)
rectangle.setAnchorPoint(anchorPoint: CGPoint.zero)
let rotationAngle = (CGFloat(( 360/barsNumber) * i)).degreesToRadians + 180.degreesToRadians
rectangle.transform = CGAffineTransform(rotationAngle: rotationAngle)
rectangle.backgroundColor = visualizerColor
rectangle.layer.cornerRadius = CGFloat(rectangle.bounds.width / 2)
rectangle.tag = i
self.addSubview(rectangle)
rectArray.append(rectangle)
var values = [5, 10, 15, 10, 5, 1]
waveFormArray = [Int]()
var j: Int = 0
for _ in 0..<barsNumber {
waveFormArray.append(values[j])
j += 1
if j == values.count {
j = 0
}
}
}
}
//-----------------------------------------------------------------
// MARK: - Animation Section
//-----------------------------------------------------------------
func animateAudioVisualizerWithChannel(level0: Float, level1: Float ) {
DispatchQueue.main.async {
UIView.animateKeyframes(withDuration: self.animateDuration, delay: 0, options: .beginFromCurrentState, animations: {
for i in 0..<self.barsNumber {
let channelValue: Int = Int(arc4random_uniform(2))
let wavePeak: Int = Int(arc4random_uniform(UInt32(self.waveFormArray[i])))
let barView = self.rectArray[i] as? CustomView
guard var barFrame = barView?.frame else { return }
// calculate the bar height
let barH = (self.frame.height / 2 ) - self.radius
// scale the value to 40, input value of this func range from 0-60, 60 is low and 0 is high. Then calculate the height by minimise the scaled height from bar height.
let scaled0 = (CGFloat(level0) * barH) / 60
let scaled1 = (CGFloat(level1) * barH) / 60
let calc0 = barH - scaled0
let calc1 = barH - scaled1
if channelValue == 0 {
barFrame.size.height = calc0
} else {
barFrame.size.height = calc1
}
if barFrame.size.height < 4 || barFrame.size.height > ((self.frame.size.height / 2) - self.radius) {
barFrame.size.height = self.initialBarHeight + CGFloat(wavePeak)
}
barView?.frame = barFrame
}
}, completion: nil)
}
}
func calculatePoints(angle: Int, radius: CGFloat) -> CGPoint {
let barX = midViewX + cos((angle).degreesToRadians) * radius
let barY = midViewY + sin((angle).degreesToRadians) * radius
return CGPoint(x: barX, y: barY)
}
}
extension BinaryInteger {
var degreesToRadians: CGFloat { return CGFloat(Int(self)) * .pi / 180 }
}
extension FloatingPoint {
var degreesToRadians: Self { return self * .pi / 180 }
var radiansToDegrees: Self { return self * 180 / .pi }
}
extension UIView{
func setAnchorPoint(anchorPoint: CGPoint) {
var newPoint = CGPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: self.bounds.size.width * self.layer.anchorPoint.x, y: self.bounds.size.height * self.layer.anchorPoint.y)
newPoint = newPoint.applying(self.transform)
oldPoint = oldPoint.applying(self.transform)
var position : CGPoint = self.layer.position
position.x -= oldPoint.x
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
self.layer.position = position;
self.layer.anchorPoint = anchorPoint;
}
}
I drag a empty view to storyBoard and give custom class as audioSpectrom.
ViewController
func startAudioVisualizer() {
visualizerTimer?.invalidate()
visualizerTimer = nil
visualizerTimer = Timer.scheduledTimer(timeInterval: visualizerAnimationDuration, target: self, selector: #selector(self.visualizerTimerFunc), userInfo: nil, repeats: true)
}
#objc func visualizerTimerFunc(_ timer: CADisplayLink) {
let lowResults = self.audioPlayer!.averagePowerInDecibels(forChannel: 0)
let lowResults1 = self.audioPlayer!.averagePowerInDecibels(forChannel: 1)
audioSpectrom.animateAudioVisualizerWithChannel(level0: -lowResults, level1: -lowResults1)
}
OUTPUT
Without animation
With animation
In my observation, the height value and width value of frame changed when rotates. Means when I give CGSize(width: 4, height: 4) to bar, then when I rotate using some angle it changes the size of frame like CGSize(width: 3.563456, height: 5.67849) (not sure for the value, it's an assumption).
How to resolve this problem?
Any suggestions or answers will be appreciated.
Edit
func animateAudioVisualizerWithChannel(level0: Float, level1: Float ) {
DispatchQueue.main.async {
UIView.animateKeyframes(withDuration: self.animateDuration, delay: 0, options: .beginFromCurrentState, animations: {
for i in 0..<self.barsNumber {
let channelValue: Int = Int(arc4random_uniform(2))
let wavePeak: Int = Int(arc4random_uniform(UInt32(self.waveFormArray[i])))
var barView = self.rectArray[i] as? CustomView
guard let barViewUn = barView else { return }
let barH = (self.frame.height / 2 ) - self.radius
let scaled0 = (CGFloat(level0) * barH) / 60
let scaled1 = (CGFloat(level1) * barH) / 60
let calc0 = barH - scaled0
let calc1 = barH - scaled1
let kSavedTransform = barViewUn.transform
barViewUn.transform = .identity
if channelValue == 0 {
barViewUn.frame.size.height = calc0
} else {
barViewUn.frame.size.height = calc1
}
if barViewUn.frame.height < CGFloat(4) || barViewUn.frame.height > ((self.frame.size.height / 2) - self.radius) {
barViewUn.frame.size.height = self.initialBarHeight + CGFloat(wavePeak)
}
barViewUn.transform = kSavedTransform
barView = barViewUn
}
}, completion: nil)
}
}
Output
Run the below code snippet show the output
<img src="https://i.imgflip.com/227xsa.gif" title="made at imgflip.com"/>
GOT IT!!
circular-visualizer
There are two (maybe three) issues in your code:
1. audioSpectrom.layoutSubviews()
You create new views in layoutSubviews and add them to the view hierarchy. This is not what you are intened to do, because layoutSubviews is called multiple times and you should use it only for layouting purposes.
As a dirty work-around, I modified the code in the func drawVisualizer to only add the bars once:
func drawVisualizer() {
// ... some code here
// ...
mainLayer.addSublayer(circleShapeLayer)
// This will ensure to only add the bars once:
guard rectArray.count == 0 else { return } // If we already have bars, just return
// Draw Bars
rectArray = [CustomView]()
// ... Rest of the func
}
Now, it almost looks good, but there are still some dirt effects with the topmost bar. So you'll have to change
2. audioSectrom.animateAudioVisualizerWithChannel(level0:level1:)
Here, you want to recalculate the frame of the bars. Since they are rotated, the frame also is rotated, and you'd have to apply some mathematical tricks. To avoid this adn make your life more easy, you save the rotated transform, set it to .identity, modify the frame, and then restore the original rotated transform. Unfortunately, this causes some dirt effects with rotations of 0 or 2pi, maybe caused by some rounding issues. Never mind, there is a much more simple solution:
Instead of modifiying the frame, you better modify the bounds.
frame is measured in the outer (in your case: rotated) coordinate system
bounds is measured in the inner (non-transformed) coordinate system
So I simply replaced all the frames with bounds in the function animateAudioVisualizerWithChannel and also removed the saving and restoring of the transformation matrix:
func animateAudioVisualizerWithChannel(level0: Float, level1: Float ) {
// some code before
guard let barViewUn = barView else { return }
let barH = (self.bounds.height / 2 ) - self.radius
let scaled0 = (CGFloat(level0) * barH) / 60
let scaled1 = (CGFloat(level1) * barH) / 60
let calc0 = barH - scaled0
let calc1 = barH - scaled1
if channelValue == 0 {
barViewUn.bounds.size.height = calc0
} else {
barViewUn.bounds.size.height = calc1
}
if barViewUn.bounds.height < CGFloat(4) || barViewUn.bounds.height > ((self.bounds.height / 2) - self.radius) {
barViewUn.bounds.size.height = self.initialBarHeight + CGFloat(wavePeak)
}
barView = barViewUn
// some code after
}
3. Warnings
By the way, you should get rid of all the warnings in your code. I didn't clean up my answer code to keep it comparable with the orginal code.
For example, in var barView = self.rectArray[i] as? CustomView you don't need the conditional cast, because the array already contains CustomView objects.
So, all the barViewUn stuff is unnecessary.
Much more to find and to clean up.

MKPolyline draws strange relics when zooming

I draw MKPolyline on map. Sometimes when I zoom a couple of times, something like this is left on the map:
Is it something specific for iOS maps that I cannot improve?
Or maybe with my drawing method something is wrong?
my MKPolyline class:
class MulticolorPolylineSegment: MKPolyline {
var color: UIColor?
class func colorSegments(forLocations locations: [Location]) -> [MulticolorPolylineSegment] {
var colorSegments = [MulticolorPolylineSegment]()
let red = (r: 254.0/255.0, g: 200.0 / 255.0, b: 20.0/255.0)
// RGB for Yellow (middle)
let yellow = (r: 254.0/255.0, g: 200.0 / 255.0, b: 20.0/255.0)
// RGB for Green (fastest)
let green = (r: 254.0/255.0, g: 200.0 / 255.0, b: 20.0/255.0)
let (speeds, minSpeed, maxSpeed) = allSpeeds(forLocations: locations)
// now knowing the slowest+fastest, we can get mean too
let meanSpeed = (minSpeed + maxSpeed)/2
for i in 1..<locations.count {
let l1 = locations[i-1]
let l2 = locations[i]
print("dsd")
var coords = [CLLocationCoordinate2D]()
coords.append(CLLocationCoordinate2D(latitude: l1.latitude, longitude: l1.longitude))
coords.append(CLLocationCoordinate2D(latitude: l2.latitude, longitude: l2.longitude))
let speed = speeds[i-1]
var color = UIColor.black
if speed < minSpeed { // Between Red & Yellow
let ratio = (speed - minSpeed) / (meanSpeed - minSpeed)
let r = CGFloat(red.r + ratio * (yellow.r - red.r))
let g = CGFloat(red.g + ratio * (yellow.g - red.g))
let b = CGFloat(red.r + ratio * (yellow.r - red.r))
color = UIColor(red: r, green: g, blue: b, alpha: 1)
}
else { // Between Yellow & Green
let ratio = (speed - meanSpeed) / (maxSpeed - meanSpeed)
let r = CGFloat(yellow.r + ratio * (green.r - yellow.r))
let g = CGFloat(yellow.g + ratio * (green.g - yellow.g))
let b = CGFloat(yellow.b + ratio * (green.b - yellow.b))
color = UIColor(red: r, green: g, blue: b, alpha: 1)
}
let segment = MulticolorPolylineSegment(coordinates: &coords, count: coords.count)
segment.color = color
colorSegments.append(segment)
}
return colorSegments
}
fileprivate class func allSpeeds(forLocations locations: [Location]) -> (speeds: [Double], minSpeed: Double, maxSpeed: Double) {
// Make Array of all speeds. Find slowest and fastest
var speeds = [Double]()
var minSpeed = DBL_MAX
var maxSpeed = 0.0
for i in 1..<locations.count {
let l1 = locations[i-1]
let l2 = locations[i]
let cl1 = CLLocation(latitude: l1.latitude, longitude: l1.longitude)
let cl2 = CLLocation(latitude: l2.latitude, longitude: l2.longitude)
let distance = cl2.distance(from: cl1)
let time = l2.timestamp.timeIntervalSince(l1.timestamp as Date)
let speed = distance/time
minSpeed = min(minSpeed, speed)
maxSpeed = max(maxSpeed, speed)
speeds.append(speed)
}
return (speeds, minSpeed, maxSpeed)
}
}
View Controller methods implementation
#objc(mapView:rendererForOverlay:) func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let polyline = overlay as! MulticolorPolylineSegment
let renderer = MKPolylineRenderer(polyline: polyline)
renderer.strokeColor = polyline.color
renderer.lineWidth = 10
return renderer
}
func polyline() -> MKPolyline {
var coords = [CLLocationCoordinate2D]()
let locations = self.locations
for location in locations {
coords.append(CLLocationCoordinate2D(latitude: location.latitude,
longitude: location.longitude))
print("dodalo")
}
return MKPolyline(coordinates: &coords, count: run.locations.count)
}

swift: how to draw an arc on map via MapKit?

I have to draw an arc having two angles, center point and radius as input. I use UIBezierPath, but overlay isn't added to the map. Here's my code:
func calculateByArc()
{
let startingAzimuth = Double(upperLimitTextBox.text!)
let endingAzimuth = Double(upperLimitUomTextBox.text!)
let radius = Double(lowerLimitTextBox.text!)
let latitude = Double(lowerLimitUomTextBox.text!)
let longitude = Double(textField5.text!)
let clockwise = clockwiseSwitch.isOn ? true : false
let qwe = CLLocationCoordinate2DMake(latitude!,longitude!)
let asd = Map.convert(qwe, toPointTo: Map)
let test = Map.convert(asd, toCoordinateFrom: Map)
print("test lat: " + String(test.latitude))
print("test lon: " + String(test.longitude))
let path = UIBezierPath(arcCenter: Map.convert (qwe, toPointTo: mapController.Map), radius: CGFloat(radius!), startAngle: CGFloat(Helper.toRad(startingAzimuth!)), endAngle: CGFloat(Helper.toRad(endingAzimuth!)) , clockwise: clockwise)
let arc = MKCircle(center: CLLocationCoordinate2DMake(latitude!, longitude!), radius: radius!)
arc.accessibilityPath = path
Map.add(arc)
}
and my mapView:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if overlay is MKCircle
{
print("overlay latitude: "+String(overlay.coordinate.latitude))
print("overlay longitude: "+String(overlay.coordinate.longitude))
let circleOverlay = overlay as! MKCircle
if(circleOverlay.accessibilityPath != nil)
{
let arcRenderer = MKOverlayPathRenderer()
arcRenderer.path = circleOverlay.accessibilityPath?.cgPath
arcRenderer.strokeColor = UIColor.red
arcRenderer.lineWidth = 10
arcRenderer.alpha = 0.3
return arcRenderer
}
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.black
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
circle.alpha = 0.3
return circle
}
}
I guess it's because i don't properly convert CLLocationCoordinate2D to CGPoint, because a couple of weeks ago i managed to draw an arc, but with wrong coordinates(can't remember how i did that).
Is this what you are looking for http://nshipster.com/mkgeodesicpolyline/ ?

Resources