将MKMapView置于引脚下方的点N像素上

问题描述:

想要将MKMapView置于给定引脚下方的N个像素当前MapRect 中可能显示或未显示)的位置。

Want to center MKMapView on a point N-pixels below a given pin (which may or may not be visible in the current MapRect).

我一直试图用 - (CLLocationCoordinate2D)convertPoint:(CGPoint)指向CoordinateFromView:(UIView *)视图$ c $来解决这个问题c>没有成功。

I've been trying to solve this using various plays with -(CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view to no success.

任何人都走这条路(没有双关语)?

Anyone been down this road (no pun intended)?

最简单的方法是将地图向下移动,比如坐标所用的40%,利用 span MKMapView 区域的code>。如果你不需要实际的像素,只需要让它向下移动,以便有问题的 CLLocationCoordinate2D 靠近地图的顶部(比如说距离顶部10%) ):

The easiest technique is to just shift the map down, say 40% from where the coordinate would be, taking advantage of the span of the region of the MKMapView. If you don't need actual pixels, but just need it to move down so that the CLLocationCoordinate2D in question is near the top of the map (say 10% away from the top):

CLLocationCoordinate2D center = coordinate;
center.latitude -= self.mapView.region.span.latitudeDelta * 0.40;
[self.mapView setCenterCoordinate:center animated:YES];






如果你想考虑轮换和投球相机,上述技术可能不够。在这种情况下,您可以:


If you want to account for rotation and pitch of the camera, the above technique may not be adequate. In that case, you could:


  • 确定要转移用户位置的视图中的位置;

  • Identify the position in the view to which you want to shift the user location;

将其转换为 CLLocation ;

计算当前用户位置与新的所需位置的距离;

Calculate the distance of the current user location from that new desired location;

将相机沿180°方向移动该距离;来自地图相机的当前标题。

Move the camera by that distance in the direction 180° from the current heading of the map's camera.

例如。在Swift 3中,类似于:

E.g. in Swift 3, something like:

var point = mapView.convert(mapView.centerCoordinate, toPointTo: view)
point.y -= offset
let coordinate = mapView.convert(point, toCoordinateFrom: view)
let offsetLocation = coordinate.location

let distance = mapView.centerCoordinate.location.distance(from: offsetLocation) / 1000.0

let camera = mapView.camera
let adjustedCenter = mapView.centerCoordinate.adjust(by: distance, at: camera.heading - 180.0)
camera.centerCoordinate = adjustedCenter

其中 CLLocationCoordinate2D 具有以下扩展名

extension CLLocationCoordinate2D {
    var location: CLLocation {
        return CLLocation(latitude: latitude, longitude: longitude)
    }

    private func radians(from degrees: CLLocationDegrees) -> Double {
        return degrees * .pi / 180.0
    }

    private func degrees(from radians: Double) -> CLLocationDegrees {
        return radians * 180.0 / .pi
    }

    func adjust(by distance: CLLocationDistance, at bearing: CLLocationDegrees) -> CLLocationCoordinate2D {
        let distanceRadians = distance / 6_371.0   // 6,371 = Earth's radius in km
        let bearingRadians = radians(from: bearing)
        let fromLatRadians = radians(from: latitude)
        let fromLonRadians = radians(from: longitude)

        let toLatRadians = asin( sin(fromLatRadians) * cos(distanceRadians)
            + cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians) )

        var toLonRadians = fromLonRadians + atan2(sin(bearingRadians)
            * sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians)
                - sin(fromLatRadians) * sin(toLatRadians))

        // adjust toLonRadians to be in the range -180 to +180...
        toLonRadians = fmod((toLonRadians + 3.0 * .pi), (2.0 * .pi)) - .pi

        let result = CLLocationCoordinate2D(latitude: degrees(from: toLatRadians), longitude: degrees(from: toLonRadians))

        return result
    }
}

所以,即使相机投放了除正北以外的标题,这会移动用户的位置(位于下方十字准线的中心位置)高达150像素(上面的十字准线),产生类似于:

So, even with the camera pitched and at a heading other than due north, this moves the user's location (which is centered, where the lower crosshair is) up 150 pixels (where the upper crosshair is), yielding something like:

显然,您应该注意堕落情况(例如距离南极1公里,你试图将地图向上移动2公里;你使用的摄像机角度到目前为止所需的屏幕位置已超过地平线;等等,但对于实际的,现实世界的场景,上述内容可能就足够了。显然,如果你不让用户改变相机的音高,答案就更容易了。

Obviously, you should be conscious about degenerate situations (e.g. you're 1 km from the south pole and you try to shift the map up 2 km meters; you're using a camera angle pitched so far that the desired screen location is past the horizon; etc.), but for practical, real-world scenarios, something like the above might be sufficient. Obviously, if you don't let the user change the pitch of the camera, the answer is even easier.

原始答案:用于移动注释 n 像素

Original answer: for moving the annotation n pixels

如果您有 CLLocationCoordinate2D ,您可以将其转换为 CGPoint ,将其移动x像素,然后将其转换回 CLLocationCoordinate2D

If you have a CLLocationCoordinate2D, you can convert it to a CGPoint, move it x pixels, and then convert it back to a CLLocationCoordinate2D:

- (void)moveCenterByOffset:(CGPoint)offset from:(CLLocationCoordinate2D)coordinate
{
    CGPoint point = [self.mapView convertCoordinate:coordinate toPointToView:self.mapView];
    point.x += offset.x;
    point.y += offset.y;
    CLLocationCoordinate2D center = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
    [self.mapView setCenterCoordinate:center animated:YES];
}

您可以通过以下方式致电:

You can call this by:

[self moveCenterByOffset:CGPointMake(0, 100) from:coordinate];

不幸的是,这仅适用于坐标在您开始之前可见,因此您可能必须先转到原始坐标,然后调整中心。

Unfortunately, this only works if the coordinate is visible before you start, so you might have to go to the original coordinate first, and then adjust the center.