如何在iOS上的Google地图上绘制圆弧?

时间:2017-10-25 13:52:05

标签: ios iphone swift google-maps

     如何在Google地图中的两个坐标点之间绘制一个圆弧,就像在此图像中一样,像iOS中的facebook post一样?

6 个答案:

答案 0 :(得分:5)

在使用以下功能之前,请不要忘记导入 GoogleMaps 积分:xomena

func drawArcPolyline(startLocation: CLLocationCoordinate2D?, endLocation: CLLocationCoordinate2D?) {
    if let _ = startLocation, let _ = endLocation {
        //swap the startLocation & endLocation if you want to reverse the direction of polyline arc formed.
        let mapView = GMSMapView()
        let path = GMSMutablePath()
        path.add(startLocation!)
        path.add(endLocation!)
        // Curve Line
        let k: Double = 0.2 //try between 0.5 to 0.2 for better results that suits you
        let d = GMSGeometryDistance(startLocation!, endLocation!)
        let h = GMSGeometryHeading(startLocation!, endLocation!)
        //Midpoint position
        let p = GMSGeometryOffset(startLocation!, d * 0.5, h)
        //Apply some mathematics to calculate position of the circle center
        let x = (1-k*k)*d*0.5/(2*k);
        let r = (1+k*k)*d*0.5/(2*k);
        let c = GMSGeometryOffset(p, x, h + 90.0)
        //Polyline options
        //Calculate heading between circle center and two points
        let h1 =  GMSGeometryHeading(c, startLocation!)
        let h2 = GMSGeometryHeading(c, endLocation!)
        //Calculate positions of points on circle border and add them to polyline options
        let numpoints = 100.0
        let step = ((h2 - h1) / Double(numpoints))
        for i in stride(from: 0.0, to: numpoints, by: 1) {
            let pi = GMSGeometryOffset(c, r, h1 + i * step)
            path.add(pi)
        }
        //Draw polyline
        let polyline = GMSPolyline(path: path)
        polyline.map = mapView // Assign GMSMapView as map
        polyline.strokeWidth = 3.0
        let styles = [GMSStrokeStyle.solidColor(UIColor.black), GMSStrokeStyle.solidColor(UIColor.clear)]
        let lengths = [20, 20] // Play with this for dotted line
        polyline.spans = GMSStyleSpans(polyline.path!, styles, lengths as [NSNumber], .rhumb)

        let bounds = GMSCoordinateBounds(coordinate: startLocation!, coordinate: endLocation!)
        let insets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
        let camera = mapView.camera(for: bounds, insets: insets)!
        mapView.animate(to: camera)
    }
}

答案 1 :(得分:1)

我用Bezier二次方程画曲线。 You can have a look on to the implementation。这是示例代码。

func bezierPath(from startLocation: CLLocationCoordinate2D, to endLocation: CLLocationCoordinate2D) -> GMSMutablePath {

        let distance = GMSGeometryDistance(startLocation, endLocation)
        let midPoint = GMSGeometryInterpolate(startLocation, endLocation, 0.5)

        let midToStartLocHeading = GMSGeometryHeading(midPoint, startLocation)

        let controlPointAngle = 360.0 - (90.0 - midToStartLocHeading)
        let controlPoint = GMSGeometryOffset(midPoint, distance / 2.0 , controlPointAngle)
        
        let path = GMSMutablePath()
        
        let stepper = 0.05
        let range = stride(from: 0.0, through: 1.0, by: stepper)// t = [0,1]
        
        func calculatePoint(when t: Double) -> CLLocationCoordinate2D {
            let t1 = (1.0 - t)
            let latitude = t1 * t1 * startLocation.latitude + 2 * t1 * t * controlPoint.latitude + t * t * endLocation.latitude
            let longitude = t1 * t1 * startLocation.longitude + 2 * t1 * t * controlPoint.longitude + t * t * endLocation.longitude
            let point = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            return point
        }
        
        range.map { calculatePoint(when: $0) }.forEach { path.add($0) }
        return path
 }

答案 2 :(得分:0)

Objective-C版本@Rouny答案

check5, check8, check12, check15 : boolean;
  radiofivechanged(){

      this.check5 = true;

     this.check8 = false;
     this.check12 = false;
     this.check15 = false;
  }

答案 3 :(得分:0)

上面的答案并不能解决所有的极端情况,这是一种很好地绘制圆弧的方法:

func drawArcPolyline(startLocation: CLLocationCoordinate2D?, endLocation: CLLocationCoordinate2D?) {
    if let _ = startLocation, let _ = endLocation {
        //swap the startLocation & endLocation if you want to reverse the direction of polyline arc formed.

        var start = startLocation!
        var end = endLocation!
        var gradientColors = GMSStrokeStyle.gradient(
            from: UIColor(red: 11.0/255, green: 211.0/255, blue: 200.0/255, alpha: 1),
            to: UIColor(red: 0/255, green: 44.0/255, blue: 66.0/255, alpha: 1))

        if startLocation!.heading(to: endLocation!) < 0.0 {
            // need to reverse the start and end, and reverse the color
            start = endLocation!
            end = startLocation!

            gradientColors = GMSStrokeStyle.gradient(
                from: UIColor(red: 0/255, green: 44.0/255, blue: 66.0/255, alpha: 1),
                to:  UIColor(red: 11.0/255, green: 211.0/255, blue: 200.0/255, alpha: 1))
        }

        let path = GMSMutablePath()
        // Curve Line
        let k = abs(0.3 * sin((start.heading(to: end)).degreesToRadians)) // was 0.3


        let d = GMSGeometryDistance(start, end)
        let h = GMSGeometryHeading(start, end)
        //Midpoint position
        let p = GMSGeometryOffset(start, d * 0.5, h)
        //Apply some mathematics to calculate position of the circle center
        let x = (1-k*k)*d*0.5/(2*k);
        let r = (1+k*k)*d*0.5/(2*k);
        let c = GMSGeometryOffset(p, x, h + 90.0)

        //Polyline options
        //Calculate heading between circle center and two points
        var h1 =  GMSGeometryHeading(c, start)
        var h2 = GMSGeometryHeading(c, end)

        if(h1>180){
            h1 = h1 - 360
        }
        if(h2>180){
            h2 = h2 - 360
        }

        //Calculate positions of points on circle border and add them to polyline options
        let numpoints = 100.0
        let step = (h2 - h1) / numpoints
        for i in stride(from: 0.0, to: numpoints, by: 1) {
            let pi = GMSGeometryOffset(c, r, h1 + i * step)
            path.add(pi)
        }
        path.add(end)

        //Draw polyline
        let polyline = GMSPolyline(path: path)
        polyline.map = mapView // Assign GMSMapView as map
        polyline.strokeWidth = 5.0
        polyline.spans = [GMSStyleSpan(style: gradientColors)]
    }
}

答案 4 :(得分:0)

提到的答案中没有一个是完整的解决方案。对于一些位置,它绘制一个圆而不是一条折线。 为了解决这个问题,我们将计算方位角(从真北开始的顺时针方向度数),如果小于零,请交换起点和终点位置。

func createArc(
    startLocation: CLLocationCoordinate2D,
    endLocation: CLLocationCoordinate2D) -> GMSPolyline {

    var start = startLocation
    var end = endLocation

    if start.bearing(to: end) < 0.0 {
        start = endLocation
        end = startLocation
    }

    let angle = start.bearing(to: end) * Double.pi / 180.0
    let k = abs(0.3 * sin(angle))

    let path = GMSMutablePath()
    let d = GMSGeometryDistance(start, end)
    let h = GMSGeometryHeading(start, end)
    let p = GMSGeometryOffset(start, d * 0.5, h)
    let x = (1 - k * k) * d * 0.5 / (2 * k)
    let r = (1 + k * k) * d * 0.5 / (2 * k)
    let c = GMSGeometryOffset(p, x, h + 90.0)
    var h1 =  GMSGeometryHeading(c, start)
    var h2 = GMSGeometryHeading(c, end)

    if (h1 > 180) {
      h1 = h1 - 360
    }

    if (h2 > 180) {
      h2 = h2 - 360
    }

    let numpoints = 100.0
    let step = ((h2 - h1) / Double(numpoints))
    for i in stride(from: 0.0, to: numpoints, by: 1) {
      let pi = GMSGeometryOffset(c, r, h1 + i * step)
      path.add(pi)
    }
    path.add(end)

    let polyline = GMSPolyline(path: path)
    polyline.strokeWidth = 3.0
    polyline.spans = GMSStyleSpans(
      polyline.path!,
      [GMSStrokeStyle.solidColor(UIColor(hex: "#2962ff"))],
      [20, 20], .rhumb
    )
    return polyline
  }

方位角是地图上垂直线指向的方向,以北为准,以顺时针为单位。

func bearing(to point: CLLocationCoordinate2D) -> Double {
    func degreesToRadians(_ degrees: Double) -> Double { return degrees * Double.pi / 180.0 }
    func radiansToDegrees(_ radians: Double) -> Double { return radians * 180.0 / Double.pi }

    let lat1 = degreesToRadians(latitude)
    let lon1 = degreesToRadians(longitude)

    let lat2 = degreesToRadians(point.latitude);
    let lon2 = degreesToRadians(point.longitude);

    let dLon = lon2 - lon1;

    let y = sin(dLon) * cos(lat2);
    let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
    let radiansBearing = atan2(y, x);

    return radiansToDegrees(radiansBearing)
  }

答案 5 :(得分:0)

斯威夫特 5+

非常简单流畅的方式

//MARK: - Usage
let path = self.bezierPath(from: CLLocationCoordinate2D(latitude: kLatitude, longitude: kLongtitude), to: CLLocationCoordinate2D(latitude: self.restaurantLat, longitude: self.restaurantLong))
        
  let polyline = GMSPolyline(path: path)
  polyline.strokeWidth = 5.0
  polyline.strokeColor = appClr
  polyline.map = self.googleMapView // Google MapView

简单的功能

func drawArcPolyline(from startLocation: CLLocationCoordinate2D, to endLocation: CLLocationCoordinate2D) -> GMSMutablePath {

    let distance = GMSGeometryDistance(startLocation, endLocation)
    let midPoint = GMSGeometryInterpolate(startLocation, endLocation, 0.5)

    let midToStartLocHeading = GMSGeometryHeading(midPoint, startLocation)

    let controlPointAngle = 360.0 - (90.0 - midToStartLocHeading)
    let controlPoint = GMSGeometryOffset(midPoint, distance / 2.0 , controlPointAngle)
        
    let path = GMSMutablePath()
        
    let stepper = 0.05
    let range = stride(from: 0.0, through: 1.0, by: stepper)// t = [0,1]
        
        func calculatePoint(when t: Double) -> CLLocationCoordinate2D {
            let t1 = (1.0 - t)
            let latitude = t1 * t1 * startLocation.latitude + 2 * t1 * t * controlPoint.latitude + t * t * endLocation.latitude
            let longitude = t1 * t1 * startLocation.longitude + 2 * t1 * t * controlPoint.longitude + t * t * endLocation.longitude
            let point = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            return point
        }
        
        range.map { calculatePoint(when: $0) }.forEach { path.add($0) }
        return path
 }

enter image description here