swift - 如何在不最大化 CPU 使用率的情况下显示大量 GMSPolylines?
问题描述
我正在制作一个使用 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
}
这是我从数据中创建折线的方法。总而言之,一条路线平均有约 700 个坐标分布在约 28 条折线(每个路径对象)上。请记住,我不会在一页上显示多条路线,我一次只显示一条。
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
}
}
}
}
我如何构建折线有问题吗?还是坐标太多了?
解决方案
我遇到了这个确切的问题。这是一个非常奇怪的错误——当你超过某个折线阈值时,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
}
推荐阅读
- date - Power Query 筛选器日期从今天减去 ? 到今天的几天
- python - 没有名为“惊喜”的模块
- r - 总结R中数据框中的重复项
- react-native - 世博会不显示日志
- javascript - 检查字段有效性和提交前填写的字段?
- python - for 循环获取 pandas 中的数据框
- excel - 如何创建动态范围和改进 vba 代码
- javascript - 如何发送纯 JSON 对象(位于模型对象内)以在 Spring Boot 中查看?
- java - 如何在 Gradle 中排除 JUnit 作为传递依赖
- c - C: 客户端程序报告 SSL Certificate verify failed 错误 with localCA