ObjC/Footprint/AAPLMKMapRectRotated.m
/*  | 
Copyright (C) 2016 Apple Inc. All Rights Reserved.  | 
See LICENSE.txt for this sample’s licensing information  | 
Abstract:  | 
In order to properly clamp the MKMapView (see AAPLVisibleMapRegionDelegate) to inside a floorplan (that may not be "North up", and therefore may not be aligned with the standard MKMapRect coordinate frames), we'll need a way to store and quickly compute whether a specific MKMapPoint is inside your floorplan or not, and the displacement to the nearest edge of the floorplan.  | 
Since all PDF bounding boxes are still PDFs, after all, in the case of this sample code we need only represent a "rotated" MKMapRect. If you have transparency in your PDF or need something fancier, consider an MKPolygon and some combination of CGPathContainsPoint(), etc.  | 
*/  | 
#include "AAPLMKMapRectRotated.h"  | 
/**  | 
Displacement from two MKMapPoints -- a direction and distance.  | 
@param direction The direction of displacement, a unit vector.  | 
@param distance The magnitude of the displacement.  | 
*/  | 
typedef struct { | 
AAPLMKMapDirection direction;  | 
double distance;  | 
} AAPLMKMapPointDisplacement;  | 
/**  | 
@param val value to clamp.  | 
@param min least possible value.  | 
@param max greatest possible value.  | 
@return clamped version of \c val such that it falls between \c min and \c max  | 
*/  | 
static double clamp(double val, double min, double max) { | 
return (val < min) ? min : ((val > max) ? max : val);  | 
}  | 
/**  | 
@param a Point A.  | 
@param b Point B.  | 
@return An \c MKMapPoint object representing the midpoints of \c a and \c b  | 
*/  | 
static MKMapPoint AAPLMKMapPointMidpoint(MKMapPoint a, MKMapPoint b) { | 
    return (MKMapPoint) { | 
.x = (a.x + b.x) * 0.5,  | 
.y = (a.y + b.y) * 0.5  | 
};  | 
}  | 
/**  | 
@param to ending point.  | 
@param from starting point.  | 
@return The displacement between two MKMapPoint objects.  | 
*/  | 
static AAPLMKMapPointDisplacement AAPLMKMapPointSubtract(MKMapPoint to, MKMapPoint from) { | 
double dx = to.x - from.x;  | 
double dy = to.y - from.y;  | 
double distance = hypot(dx, dy);  | 
    return (AAPLMKMapPointDisplacement) { | 
        .direction = (AAPLMKMapDirection) { | 
.eX = dx / distance,  | 
.eY = dy / distance  | 
},  | 
.distance = distance  | 
};  | 
}  | 
AAPLMKMapRectRotated AAPLMKMapRectRotatedMake(MKMapPoint corner1, MKMapPoint corner2, MKMapPoint corner3, MKMapPoint corner4) { | 
// Avg the points to get the center of the rectangle in MKMapPoint space.  | 
    MKMapPoint center = { | 
.x = (corner1.x + corner2.x + corner3.x + corner4.x) / 4.0,  | 
.y = (corner1.y + corner2.y + corner3.y + corner4.y) / 4.0  | 
};  | 
// Figure out the "width direction" and "height direction"...  | 
MKMapPoint heightMax = AAPLMKMapPointMidpoint(corner1, corner2);  | 
MKMapPoint heightMin = AAPLMKMapPointMidpoint(corner4, corner3);  | 
MKMapPoint widthMax = AAPLMKMapPointMidpoint(corner1, corner4);  | 
MKMapPoint widthMin = AAPLMKMapPointMidpoint(corner2, corner3);  | 
// ...as well as the actual width and height.  | 
AAPLMKMapPointDisplacement width = AAPLMKMapPointSubtract(widthMax, widthMin);  | 
AAPLMKMapPointDisplacement height = AAPLMKMapPointSubtract(heightMax, heightMin);  | 
    return (AAPLMKMapRectRotated) { | 
.rectCenter = center,  | 
        .rectSize = (MKMapSize) { | 
.width = width.distance,  | 
.height = height.distance  | 
},  | 
.widthDirection = width.direction,  | 
.heightDirection = height.direction  | 
};  | 
}  | 
MKMapPoint AAPLMKMapRectRotatedNearestPoint(AAPLMKMapRectRotated mapRectRotated, MKMapPoint point) { | 
double dxCenter = (point.x - mapRectRotated.rectCenter.x);  | 
double dyCenter = (point.y - mapRectRotated.rectCenter.y);  | 
/*  | 
We use a dot product against a unit vector (a.k.a. projection) to find  | 
distance "along a particular direction."  | 
*/  | 
double widthDistance = dxCenter * mapRectRotated.widthDirection.eX +  | 
dyCenter * mapRectRotated.widthDirection.eY;  | 
/*  | 
We use a dot product against a unit vector (a.k.a. projection) to find  | 
distance "along a particular direction."  | 
*/  | 
double heightDistance = dxCenter * mapRectRotated.heightDirection.eX +  | 
dyCenter * mapRectRotated.heightDirection.eY;  | 
// "If this rectangle _were_ upright, this would be the result."  | 
double widthNearestPoint = clamp(widthDistance, -0.5 * mapRectRotated.rectSize.width, 0.5 * mapRectRotated.rectSize.width);  | 
double heightNearestPoint = clamp(heightDistance, -0.5 * mapRectRotated.rectSize.height, 0.5 * mapRectRotated.rectSize.height);  | 
/*  | 
Since it's not upright, just combine the width and height in their corresponding  | 
directions!  | 
*/  | 
    return (MKMapPoint) { | 
.x = mapRectRotated.rectCenter.x + widthNearestPoint * mapRectRotated.widthDirection.eX + heightNearestPoint * mapRectRotated.heightDirection.eX,  | 
.y = mapRectRotated.rectCenter.y + widthNearestPoint * mapRectRotated.widthDirection.eY + heightNearestPoint * mapRectRotated.heightDirection.eY  | 
};  | 
}  | 
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-28