2

我在 MkMapView 上画线,我想以米为单位指定它们的宽度。

我试过这样的事情:

-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>) overlay
{
    if([overlay isKindOfClass:[MKPolyline class]])
    {
        MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
        double ppm = MKMapPointsPerMeterAtLatitude(myLatitude);
        renderer.lineWidth = ppm * myMetersValue;
       ...

这会产生一条比 myMetersValue 宽得多的线,并且在我放大和缩小时它不会缩放。

ppm 保持恒定在大约。9.49 即使我放大和缩小地图。我已经验证 myLatitude 是一个有效的纬度(大约 45 度)。我已经验证 myMetersValue 是一个合理的 #(大约 18m)。

(编辑)

我发现 MKMapPointsPerMeterAtLatitude 返回的 MapPoints 不是屏幕点,所以我不能使用那个函数。

所以我的基本问题是如何将米转换为 mkMapView 中的屏幕点。更简洁地说,如何在我的代码中获得 ppm 的值?我试过这个(在SO的其他地方找到):

    MKCoordinateRegion myRegion = MKCoordinateRegionMakeWithDistance(mapView.centerCoordinate, 400, 0);
    CGRect myRect = [mapView convertRegion: myRegion toRectToView: nil];
    double ppm = myRect.size.width / 400.0;

那没有用。

然后我尝试了这个:

    CLLocationCoordinate2D l1 = [mapView convertPoint:CGPointMake(0,0) toCoordinateFromView:mapView];
    CLLocation *ll1 = [[CLLocation alloc] initWithLatitude:l1.latitude longitude:l1.longitude];
    CLLocationCoordinate2D l2 = [mapView convertPoint:CGPointMake(0,500) toCoordinateFromView:mapView];
    CLLocation *ll2 = [[CLLocation alloc] initWithLatitude:l2.latitude longitude:l2.longitude];
    double ppm = 500.0 / [ll1 distanceFromLocation:ll2];

这里的想法是获取相隔 500 个屏幕点的两个点的纬度/经度。然后得到它们之间的距离(以米为单位),这样我就可以计算 ppm。这种工作,但看起来结果 ppm 不太正确,所以我不相信这是正确的。此外,当我缩放地图时,地图上的现有线条不会重新渲染,因此缩放后保持相同的宽度(但这是一个不同的问题)。

(编辑)

现在看起来我对 ppm 的计算是正确的。问题是在用户完成缩放之前调用了渲染器,因此线条不会以最终缩放比例渲染。一旦确定了最终缩放比例,当我强制重新渲染时,ppm 是正确的并且我的代码可以工作。

4

2 回答 2

2

结合 Bruce 和 Anna 提供的信息,我编写了以下 Polyline Renderer 子类。

public extension MKMapView {

    public func metersToPoints(meters: Double) -> Double {

        let deltaPoints = 500.0

        let point1 = CGPoint(x: 0, y: 0)
        let coordinate1 = convert(point1, toCoordinateFrom: self)
        let location1 = CLLocation(latitude: coordinate1.latitude, longitude: coordinate1.longitude)

        let point2 = CGPoint(x: 0, y: deltaPoints)
        let coordinate2 = convert(point2, toCoordinateFrom: self)
        let location2 = CLLocation(latitude: coordinate2.latitude, longitude: coordinate2.longitude)

        let deltaMeters = location1.distance(from: location2)

        let pointsPerMeter = deltaPoints / deltaMeters

        return meters * pointsPerMeter
    }
}

public class ZoomingPolylineRenderer : MKPolylineRenderer {

    private var mapView: MKMapView!
    private var polylineWidth: Double! // Meters

    convenience public init(polyline: MKPolyline, mapView: MKMapView, polylineWidth: Double) {
        self.init(polyline: polyline)

        self.mapView = mapView
        self.polylineWidth = polylineWidth
    }

    override public func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        self.lineWidth = CGFloat(mapView.metersToPoints(meters: polylineWidth))
        super.draw(mapRect, zoomScale: zoomScale, in: context)
    }
}
于 2017-10-06T13:46:27.417 回答
0

因此,以下是绘制特定宽度(以米为单位)的 MkPolyline 的一种方法。请注意,一旦在地图上绘制线,如果用户缩放,它可能不会重新渲染。或者,如果是这样,它可能会在用户完成缩放之前重新渲染。在这种情况下,缩放后的宽度将不再反映您想要的宽度(以米为单位)。处理此问题的一种方法是覆盖 regionDidChangeAnimated 并删除覆盖并将其添加回来。这种方法在我的具体情况下工作正常,但我认为你应该看看 Anna 建议的解决方案,因为它可能是一种更好的通用方法。

-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>) overlay
{

if([overlay isKindOfClass:[MKPolyline class]])
{
    // the following are two methods of calculating PPM (screen points per meter).

    // method 1: requires that you have access to a latitude that is near to the line you are drawing
    CLLocationCoordinate2D l1 = CLLocationCoordinate2DMake(myLatitude, 0);
    CLLocationCoordinate2D l2 = CLLocationCoordinate2DMake(myLatitude + 1.0, 0);
    CGPoint p1 = [mapView convertCoordinate:l1 toPointToView:mapView];
    CGPoint p2 = [mapView convertCoordinate:l2 toPointToView:mapView];
    double ppm = (p1.y - p2.y) / (60.0 * METERS_PER_NAUTICALMILE);

    // method 2:
    CLLocationCoordinate2D l1 = [mapView convertPoint:CGPointMake(0,0) toCoordinateFromView:mapView];
    CLLocation *ll1 = [[CLLocation alloc] initWithLatitude:l1.latitude longitude:l1.longitude];
    CLLocationCoordinate2D l2 = [mapView convertPoint:CGPointMake(0,500) toCoordinateFromView:mapView];
    CLLocation *ll2 = [[CLLocation alloc] initWithLatitude:l2.latitude longitude:l2.longitude];
    ppm = 500.0 / [ll1 distanceFromLocation:ll2];

    MKPolyline *l = (MKPolyline *) overlay;
    MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
    renderer.lineWidth = ppm * myMetersValue;
    renderer.strokeColor = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:.3];
    renderer.lineCap = CGLineCap::kCGLineCapButt;
    return renderer;
}

return nil;

}
于 2014-07-18T18:14:26.617 回答