Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/113.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 设置MkPolyLineRender的lineWidth属性,以便以x米的宽度绘制线?_Ios_Mkmapview_Mkoverlay_Mkpolyline - Fatal编程技术网

Ios 设置MkPolyLineRender的lineWidth属性,以便以x米的宽度绘制线?

Ios 设置MkPolyLineRender的lineWidth属性,以便以x米的宽度绘制线?,ios,mkmapview,mkoverlay,mkpolyline,Ios,Mkmapview,Mkoverlay,Mkpolyline,我正在MkMapView上绘制线条,并希望以米为单位指定其宽度 我试过这样的方法: -(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>) overlay { if([overlay isKindOfClass:[MKPolyline class]]) { MKPolylineRenderer *renderer = [[MKPolyl

我正在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;
       ...
那是行不通的

然后我试了一下:

    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个屏幕点的lat/lon。然后得到它们之间的距离,以米为单位,这样我可以计算ppm。这种方法是有效的,但它看起来产生的ppm不太正确,所以我不相信这是正确的。此外,当我缩放地图时,地图上的现有线条不会重新渲染,因此缩放后的线条保持相同的宽度(但这是一个不同的问题)

(编辑)


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

因此,以下是一种绘制特定宽度(以米为单位)的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;

}
-(mkoverlayrender*)地图视图:(MKMapView*)地图视图渲染器foroverlay:(id)overlay
{
if([overlay iskindof类:[MKPolyline类]])
{
//以下是计算PPM(每米屏幕点)的两种方法。
//方法1:要求您可以访问靠近正在绘制的线的纬度
CLLocationCoordinate2D l1=CLLocationCoordinate2DMake(Mylatude,0);
CLLocationCoordinate2D l2=CLLocationCoordinate2DMake(myLatitude+1.0,0);
CGPoint p1=[mapView convertCoordinate:l1 toPointToView:mapView];
CGPoint p2=[mapView转换坐标:l2 toPointToView:mapView];
双ppm=(p1.y-p2.y)/(60.0*米/每平方英里);
//方法2:
CLLocationCoordinate2D l1=[mapView转换点:CGPointMake(0,0)到CoordinateFromView:mapView];
CLLocation*ll1=[[CLLocation alloc]initWithLatitude:l1.纬度经度:l1.经度];
CLLocationCoordinate2D l2=[mapView转换点:CGPointMake(0500)到CoordinateFromView:mapView];
CLLocation*ll2=[[CLLocation alloc]initWithLatitude:l2.纬度经度:l2.经度];
ppm=500.0/[ll1距离位置:ll2];
MKPolyline*l=(MKPolyline*)叠加;
MKPolylineRenderer*渲染器=[[MKPolylineRenderer alloc]initWithOverlay:overlay];
renderer.lineWidth=ppm*myMetersValue;
renderer.strokeColor=[uicolorWithred:0.0绿色:1.0蓝色:0.0 alpha:.3];
renderer.lineCap=CGLineCap::kCGLineCapButt;
返回渲染器;
}
返回零;
}

结合Bruce和Anna提供的信息,我编写了以下多段线渲染器子类

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)
    }
}

不幸的是(或幸运的是),线宽位于屏幕上的CG点中,因此无论缩放如何,线宽都将保持不变(因此,当放大到远处时,线条仍然可见)。要获得以米为单位的固定宽度,需要实现自定义覆盖渲染器。看见答案适用于MkoveryView,但对MkoveryRenderer也同样适用。将此处的道路宽度计算替换为此处的线宽设置方式。Anna,我认为由mkmappointspermeteralatitude返回的MkMapPoint与屏幕点不等效。见我编辑的帖子。但是,也许您建议的自定义渲染器可以使用MkMapPoints来获得我想要的结果?我会看看你的链接。我想我没有说MkmapPointsMeteralatitude是屏幕点。线宽值以屏幕点为单位。在自定义覆盖渲染器的drawMapRect中,您可以绘制MKMapPoints,当地图视图将地图绘制上下文转换为屏幕时,它会自动将MKMapPoints转换为屏幕点。好的,谢谢。我不是想暗示你说的MkmapPointsPermeteralatitude是屏幕点。我说的是我自己的代码做出了这个假设,我错了。不管怎样,看起来我对ppm的计算是正确的。但在用户完成缩放之前会调用“我的渲染器”,因此线不会以最终缩放比例渲染。当我在确定最终缩放比例后强制重新渲染时,ppm是正确的,我的代码可以工作。很好,没问题。我想你可以现在或者在你有了更多的代表后发布你自己问题的答案。
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)
    }
}