harry lakins - 1 year ago 67

Swift Question

**My aim**

have a line drawn based on three bits of data: given coordinates, a distance, a bearing.

I want a line to be drawn from my current location in the direction of the bearing, at and end point of the distance.

**Bare in mind**

I do get a line on the map, updated every time current location is, so there isn't a problem in that sense (the code i've shown may not include all definitions - but assume there is no compile error, etc)

**The problem**

My line appears, but only always in the same direction, at a distance across the whole world (instead of 1km!). I tried changing the heading input but still get the same line.

**The code**

`func drawHeadingLine(currentLocation: [Double], heading: Double){`

//to radians

let headingR = heading * 0.0174533;

if viewLine != nil{

mapView.removeOverlay(viewLine);

}

// setup data for polar formulae

let pi = M_PI;

let currentLONG = currentLocation[0];

let currentLAT = currentLocation[1];

let currentLongRadian = currentLocation[0] * (pi * 180);

let currentLatRadian = currentLocation[1] * (pi * 180);

let lineDistance: Double = 1; //km

let earthRadius: Double = 6378.1; //km

let angularDistance: Double = lineDistance / earthRadius;

//use polar formulae (given point, distance, bearing) for line end point

let latitude2 = asin( sin(currentLatRadian) * cos(angularDistance)

+ cos(currentLatRadian) * sin(angularDistance) * cos(headingR) );

let longitude2 = currentLongRadian + atan2( cos(angularDistance) - sin(currentLatRadian) * sin(latitude2),

sin(headingR) * sin(angularDistance) * cos(currentLatRadian));

var coordinates1 = CLLocationCoordinate2D();

coordinates1.longitude = currentLONG;

coordinates1.latitude = currentLAT;

var coordinates2 = CLLocationCoordinate2D();

coordinates2.longitude = longitude2;

coordinates2.latitude = latitude2;

var lineCoords = [ coordinates1,coordinates2];

viewLine = MKPolyline(coordinates: &lineCoords, count: lineCoords.count);

self.mapView.addOverlay(viewLine);

}

func mapView(mapView: MKMapView!, viewForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {

if (overlay is MKPolyline) {

viewLine = overlay;

let line = MKPolylineRenderer(overlay: viewLine);

line.strokeColor = UIColor.redColor().colorWithAlphaComponent(0.5);

line.lineWidth = 5;

return line;

}

return nil

}

Please say if you need more information. Thanks for any help.

Answer Source

Your math was off. Try this:

```
func drawHeadingLine(currentLocation: [Double], heading: Double){
if viewLine != nil {
mapView.removeOverlay(viewLine!)
}
let (lat, long) = (currentLocation[0], currentLocation[1])
let headingR = heading * M_PI / 180.0
let lineDistance = 1.0 //km
let latDelta = cos(headingR) * lineDistance / EarthMeasurement.lengthOfLatitude(at: lat)
let longDelta = sin(headingR) * lineDistance / EarthMeasurement.lengthOfLongitude(at: lat)
let pointA = CLLocationCoordinate2D(latitude: lat, longitude: long)
let pointB = CLLocationCoordinate2D(latitude: lat + latDelta, longitude: long + longDelta)
// For debugging, let's calculate a geodesic distance between these 2 points
// Results are in meters
let locationA = CLLocation(latitude: lat, longitude: long)
let locationB = CLLocation(latitude: lat + latDelta, longitude: long + longDelta)
print("heading = \(heading), distance = \(locationA.distanceFromLocation(locationB)")
var coordinates = [pointA, pointB]
let polyline = MKPolyline(coordinates: &coordinates, count: 2)
self.mapView.addOverlay(polyline)
}
```

And the `EarthMeasurement`

class:

```
final class EarthMeasurement {
static func lengthOfLatitude(at latitude: Double) -> Double {
return 110.574
}
static func lengthOfLongitude(at latitude: Double) -> Double {
let latitudeR = latitude * M_PI / 180.0
return cos(latitudeR) * 111.320
}
}
```

The idea is that Map Kit deals in latitudes and longitude, not in kilometer, so we need to know how many kilometer are there in a latitude / longitude. This is far from simple as the Earth is not a perfect sphere. but we can make approximations:

- The length of 1 degree of latitude does become longer as you approach the poles, but the difference is small: about 1km (or 1%) between the equator and the poles. We can ignore that and use the length at the equator, which according to Wikipedia is 111.574km.
- The formula for the length of 1 degree of latitude is taken from this question.

This does not give a point that is exactly 1km away due to the approximations we used. The error is smaller at latitudes near the equator and progressively increases as you approach the poles (~10m). If you want more accurate result, adjust the `EarthMeasurement`

class accordingly.