ObjC/Footprint/AAPLFloorplanOverlayRenderer.m
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
This class draws your AAPLFloorplanOverlay into an MKMapView. It is also capable of drawing diagnostic visuals to help with debugging, if needed. |
*/ |
#import "AAPLFloorplanOverlayRenderer.h" |
#import "AAPLFloorplanOverlay.h" |
/* |
For additional debugging set AAPLFloorplanOverlayDebugEnabled to 1. This will |
highlight the floorplan's boundingMapRect in cyan. |
*/ |
#define AAPLFloorplanOverlayDebugEnabled 0 |
@interface AAPLFloorplanOverlay () |
/** |
This draws directly in the PDF coordinate system. |
If drawing onto MapKit, the context object provided must already have the |
appropriate transforms applied. |
If you have the transform correct, you should see the following: |
[A] 1.0 m radius red square (50% alpha) centered on the 1st anchor point. |
[B] 1.0 m radius green square (50% alpha) centered on the 2nd anchor point. |
[C] a 1x1 point magenta square centered at the (0.0, 0.0) point of your PDF. |
This square is created by the precise overlap of the following two |
rectangles. |
[C.1] a 10x1 point red rectangle (50% alpha) that covers the 1x1 point |
square centered at PDF coordinate (0.0, 0.0) through the 1x1 point |
square centered at PDF coordinate (10.0, 0.0). |
[C.2] a 1x10 point blue rectangle (50% alpha) that covers the 1x1 point |
square centered at PDF coordinate (0.0, 0.0) and the 1x1 point |
square centered at PDF coordinate (10.0, 1.0). |
Use [A] & [B] to verify that your anchor points have been set to the correct |
points on your PDF. If this does not match: |
+ check your PDF reader and make sure it is giving you values in "points" |
and not "pixels" or some other unit of measure. |
+ look for typos in the CGPoint values of your AAPLGeoAnchor structs. |
Use [C] to verify the location of (0.0, 0.0) on your PDF. If this does not |
match: |
+ check your PDF reader and make sure it is showing you values of the |
underlying PDF coordinate system, and not its own internal display |
coordinate system. A proper PDF coordinate system should have +x be |
rightward and +y be upward. |
Use [C.1] & [C.2] to verify the sizes of "1.0 point" and "10.0 points" on |
your PDF. If this does not match: |
+ check your PDF reader and make sure it is giving you values in "points" |
and not "pixels" or some other unit of measure. |
*/ |
+ (void)drawDiagnosticVisuals:(CGContextRef)context floorplan:(AAPLFloorplanOverlay *)floorplanOverlay; |
@end |
@implementation AAPLFloorplanOverlayRenderer |
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { |
NSAssert([self.overlay isKindOfClass:[AAPLFloorplanOverlay class]], @"Wrong overlay type."); |
AAPLFloorplanOverlay *floorplanOverlay = self.overlay; |
MKMapRect boundingMapRect = self.overlay.boundingMapRect; |
/* |
Mapkit converts to its own dynamic CGPoint frame, which we can read |
through rectForMapRect. |
*/ |
CGRect mapkitToGraphicsConversion = [self rectForMapRect:boundingMapRect]; |
CGPoint graphicsFloorplanCenter = CGPointMake( |
CGRectGetMidX(mapkitToGraphicsConversion), |
CGRectGetMidY(mapkitToGraphicsConversion) |
); |
CGFloat graphicsFloorplanWidth = CGRectGetWidth(mapkitToGraphicsConversion); |
CGFloat graphicsFloorplanHeight = CGRectGetHeight(mapkitToGraphicsConversion); |
// Now, how does this compare to MapKit coordinates? |
MKMapPoint mapkitFloorplanCenter = (MKMapPoint) { |
.x = MKMapRectGetMidX(self.overlay.boundingMapRect), |
.y = MKMapRectGetMidY(self.overlay.boundingMapRect) |
}; |
double mapkitFloorplanWidth = MKMapRectGetWidth(self.overlay.boundingMapRect); |
double mapkitFloorplanHeight = MKMapRectGetHeight(self.overlay.boundingMapRect); |
/* |
Create the transformation that converts to Graphics coordinates from |
MapKit coordinates. |
graphics.x = (mapkit.x - mapkitFloorplanCenter.x) * graphicsFloorplanWidth / mapkitFloorplanWidth + graphicsFloorplanCenter.x |
*/ |
CGAffineTransform fromMapKitToGraphics = CGAffineTransformIdentity; |
fromMapKitToGraphics = CGAffineTransformTranslate(fromMapKitToGraphics, -mapkitFloorplanCenter.x, -mapkitFloorplanCenter.y); |
fromMapKitToGraphics = CGAffineTransformScale( |
fromMapKitToGraphics, |
graphicsFloorplanWidth / mapkitFloorplanWidth, |
graphicsFloorplanHeight / mapkitFloorplanHeight |
); |
fromMapKitToGraphics = CGAffineTransformTranslate(fromMapKitToGraphics, graphicsFloorplanCenter.x, graphicsFloorplanCenter.y); |
/* |
Using this, we can send draw commands in MapKit coordinates and cause |
the equivalent drawing in (the correct) graphics coordinates. |
*/ |
/* |
Highlight the floorplan's boundingMapRect in cyan if AAPLFloorplanOverlayDebugEnabled |
is enabled. |
*/ |
#if AAPLFloorplanOverlayDebugEnabled |
CGContextSetRGBFillColor(context, 0.0, 1.0, 1.0, 0.5); |
CGContextFillRect(context, mapkitToGraphicsConversion); |
#endif |
/* |
However, we want to be able to send draw commands in the original PDF |
coordinates though, so we'll also need the transformations that convert |
to MapKit coordinates from PDF coordinates. |
*/ |
CGAffineTransform fromPdfToMapKit = floorplanOverlay.transformerFromPDFToMk; |
CGContextConcatCTM(context, CGAffineTransformConcat(fromPdfToMapKit, fromMapKitToGraphics)); |
CGContextDrawPDFPage(context, floorplanOverlay.PDFPage); |
/* |
The following diagnostic visuals are provided for debugging only. In production, |
you'll want to remove them. |
*/ |
#if AAPLFloorplanOverlayDebugEnabled |
[AAPLFloorplanOverlayRenderer drawDiagnosticVisuals:context floorplan:floorplanOverlay]; |
#endif |
} |
+ (void)drawDiagnosticVisuals:(CGContextRef)context floorplan:(AAPLFloorplanOverlay *)floorplanOverlay { |
// Draw a 1.0 meter radius square around each anchor point. |
CGFloat radiusPDFPoints = 1.0 / floorplanOverlay.PDFPointSizeInMeters; |
CGSize anchorMarkerSize = CGSizeMake(radiusPDFPoints * 2.0, radiusPDFPoints * 2.0); |
CGRect fromAnchorMarker = (CGRect) { |
.origin = (CGPoint) { |
.x = floorplanOverlay.anchors.fromAnchor.PDFPoint.x - radiusPDFPoints, |
.y = floorplanOverlay.anchors.fromAnchor.PDFPoint.y - radiusPDFPoints, |
}, |
.size = anchorMarkerSize |
}; |
CGRect toAnchorMarker = (CGRect) { |
.origin = (CGPoint) { |
.x = floorplanOverlay.anchors.toAnchor.PDFPoint.x - radiusPDFPoints, |
.y = floorplanOverlay.anchors.toAnchor.PDFPoint.y - radiusPDFPoints, |
}, |
.size = anchorMarkerSize |
}; |
// Anchor 1: Red. |
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 0.75); |
CGContextFillRect(context, fromAnchorMarker); |
// Anchor 2: Green. |
CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 0.75); |
CGContextFillRect(context, toAnchorMarker); |
/* |
Draw a 10pt x 1pt red rectangle that covers the square centered at |
(0.0, 0.0) through the square centered at (10.0, 0.0). |
*/ |
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 0.5); |
CGContextFillRect(context, CGRectMake(-0.5, -0.5, 10.0, 1.0)); |
/* |
Draw a 1pt x 10pt blue rectangle that covers the square centered at |
(0.0, 0.0) through the square centered at (0.0, 10.0). |
*/ |
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 0.5); |
CGContextFillRect(context, CGRectMake(-0.5, -0.5, 1.0, 10.0)); |
} |
@end |
#undef AAPLFloorplanOverlayDebugEnabled |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-28