如何在不最大化CPU使用率的情况下显示大量GMSPolylines?

时间:2019-04-07 01:23:37

标签: swift google-maps-sdk-ios

我正在制作一个使用NextBus API和Google Maps显示公交路线的应用程序。但是,我认为CPU使用有问题,我认为这是由地图上GMSPolylines的数量引起的。路径由多段线显示,该线由NextBus给定路径的点组成。在将折线添加到地图上并且GMSCamera概述了整个路线时,模拟器(iPhone X)上的CPU会以100%的速度最大化。但是,当放大路线的特定部分时,CPU使用率将下降到〜2%。

地图截图:https://i.imgur.com/jLmN26e.png 效果:https://i.imgur.com/nUbIv5w.png

NextBus API返回包括特定总线路径的路由信息​​。这是我正在使用的数据的一个小示例:

Route: {
    "path": [Path]
}

Path: {
    "points:" [Coordinate]
}

Coordinate: {
    "lat": Float,
    "lon": Float
}

这是我的方法,可以根据数据创建折线。总的来说,一条路线平均分布在〜28条折线(每个路径对象)上的〜700个坐标。请记住,我不在一页上显示多条路线,一次只显示一条。

func buildRoute(routePath: [Path?]) -> [GMSPolyline] {
    var polylines: [GMSPolyline] = []

    for path in routePath {
         let path = GMSMutablePath()
         guard let coords = path?.points else {continue}

         for coordinate in coords {
            // Safely unwrap latitude strings and convert them to doubles.
            guard let latStr = coordinate?.lat,
                  let lonStr = coordinate?.lon else {
                      continue
            }

            guard let latOne = Double(latStr),
                  let lonOne = Double(lonStr) else {
                      continue
            }

            // Create location coordinates.
            let pointCoordinatie = CLLocationCoordinate2D(latitude: latOne, longitude: lonOne)
            path.add(pointCoordinatie)
        }

        let line = GMSPolyline(path: path)
        line.strokeWidth = 6
        line.strokeColor = UIColor(red: 0/255, green: 104/255, blue: 139/255, alpha: 1.0)
        polylines.append(line)
    }

    return polylines
}

最后这是我的将折线添加到地图的方法:

fileprivate func buildRoute(routeConfig: RouteConfig?) {
    if let points = routeConfig?.route?.path {
        let polylines = RouteBuiler.shared.buildRoute(routePath: points)

        DispatchQueue.main.async {
            // Remove polylines from map if there are any.
            for line in self.currentRoute {
                line.map = nil
            }

            // Set new current route and add it to the map.
            self.currentRoute = polylines
            for line in self.currentRoute {
                line.map = self.mapView
            }
        }
    }
}

我如何构造折线是否存在问题?还是仅仅是坐标太多?

1 个答案:

答案 0 :(得分:1)

我遇到了这个确切的问题。这是一个非常奇怪的错误-当您超过折线的某个阈值时,CPU突然固定为100%。

我发现GMSPolygon不存在此问题。因此,我将所有GMSPolyline切换到了GMSPolygon。

要获得正确的笔划宽度,我正在使用以下代码创建一个多边形,该多边形以给定的笔划宽度跟踪折线的轮廓。我的计算需要LASwift线性代数库。

https://github.com/AlexanderTar/LASwift

import CoreLocation
import LASwift
import GoogleMaps

struct Segment {
    let from: CLLocationCoordinate2D
    let to: CLLocationCoordinate2D
}

enum RightLeft {
    case right, left
}

// Offset the given path to the left or right by the given distance
func offsetPath(rightLeft: RightLeft, path: [CLLocationCoordinate2D], offset: Double) -> [CLLocationCoordinate2D] {
    var offsetPoints = [CLLocationCoordinate2D]()
    var prevSegment: Segment!

    for i in 0..<path.count {
        // Test if this is the last point
        if i == path.count-1 {
            if let to = prevSegment?.to {
                offsetPoints.append(to)
            }
            continue
        }

        let from = path[i]
        let to = path[i+1]

        // Skip duplicate points
        if from.latitude == to.latitude && from.longitude == to.longitude {
            continue
        }

        // Calculate the miter corner for the offset point
        let segmentAngle = -atan2(to.latitude - from.latitude, to.longitude - from.longitude)
        let sinA = sin(segmentAngle)
        let cosA = cos(segmentAngle)
        let rotate =
            Matrix([[cosA, -sinA, 0.0],
                    [sinA, cosA, 0.0],
                    [0.0, 0.0, 1.0]])
        let translate =
            Matrix([[1.0, 0.0, 0.0 ],
                    [0.0, 1.0, rightLeft == .left ? offset : -offset ],
                    [0.0, 0.0, 1.0]])
        let mat = inv(rotate) * translate * rotate

        let fromOff = mat * Matrix([[from.x], [from.y], [1.0]])
        let toOff = mat * Matrix([[to.x], [to.y], [1.0]])

        let offsetSegment = Segment(
            from: CLLocationCoordinate2D(latitude: fromOff[1,0], longitude: fromOff[0,0]),
            to: CLLocationCoordinate2D(latitude: toOff[1,0], longitude: toOff[0,0]))

        if prevSegment == nil {
            prevSegment = offsetSegment
            offsetPoints.append(offsetSegment.from)
            continue
        }

        // Calculate line intersection
        guard let intersection = getLineIntersection(line0: prevSegment, line1: offsetSegment, segment: false) else {
            prevSegment = offsetSegment
            continue
        }

        prevSegment = offsetSegment
        offsetPoints.append(intersection)
    }

    return offsetPoints
}

// Returns the intersection point if the line segments intersect, otherwise nil
func getLineIntersection(line0: Segment, line1: Segment, segment: Bool) -> CLLocationCoordinate2D? {
    return getLineIntersection(p0: line0.from, p1: line0.to, p2: line1.from, p3: line1.to, segment: segment)
}

// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
// Returns the intersection point if the line segments intersect, otherwise nil
func getLineIntersection(p0: CLLocationCoordinate2D, p1: CLLocationCoordinate2D, p2: CLLocationCoordinate2D, p3: CLLocationCoordinate2D, segment: Bool) -> CLLocationCoordinate2D? {
    let s1x = p1.longitude - p0.longitude
    let s1y = p1.latitude - p0.latitude
    let s2x = p3.longitude - p2.longitude
    let s2y = p3.latitude - p2.latitude

    let numerator = (s2x * (p0.latitude - p2.latitude) - s2y * (p0.longitude - p2.longitude))
    let denominator = (s1x * s2y - s2x * s1y)
    if denominator == 0.0 {
        return nil
    }
    let t =  numerator / denominator

    if segment {
        let s = (s1y * (p0.longitude - p2.longitude) + s1x * (p0.latitude - p2.latitude)) / (s1x * s2y - s2x * s1y)
        guard (s >= 0 && s <= 1 && t >= 0 && t <= 1) else {
            return nil
        }
    }

    return CLLocationCoordinate2D(latitude: p0.latitude + (t  * s1y), longitude: p0.longitude + (t * s1x))
}


// The path from NextBus
let path: CLLocationCoordinate2D = pathFromNextBus()

// The desired width of the polyline
let strokeWidth: Double = desiredPolylineWidth()

let polygon: GMSPolygon
do {
    let polygonPath = GMSMutablePath()
    let w = strokeWidth / 2.0
    for point in offsetPath(rightLeft: .left, path: route.offsetPath, offset: w) {
        polygonPath.add(CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude))
    }
    for point in offsetPath(rightLeft: .right, path: route.offsetPath, offset: w).reversed() {
        polygonPath.add(CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude))
    }
    polygon = GMSPolygon(path: polygonPath)
    polygon.strokeWidth = 0.0
}