├── src ├── ios │ ├── ARKit │ │ ├── Images │ │ │ ├── bgCallout.png │ │ │ ├── bgCallout@2x.png │ │ │ ├── bgCalloutDisclosure.png │ │ │ └── bgCalloutDisclosure@2x.png │ │ ├── ARKit.h │ │ ├── RadarViewPortView.h │ │ ├── Radar.h │ │ ├── GEOLocations.h │ │ ├── ARLocationDelegate.h │ │ ├── GEOLocations.m │ │ ├── MarkerView.h │ │ ├── ARViewProtocol.h │ │ ├── ARCoordinate.h │ │ ├── ARGeoCoordinate.h │ │ ├── ARKit.m │ │ ├── RadarViewPortView.m │ │ ├── ARViewController.h │ │ ├── ARCoordinate.m │ │ ├── ARGeoCoordinate.m │ │ ├── AugmentedRealityController.h │ │ ├── Radar.m │ │ ├── MarkerView.m │ │ ├── ARViewController.m │ │ └── AugmentedRealityController.m │ ├── CsAR.h │ └── CsAR.m └── www │ └── csAr.js ├── package.json ├── README.md ├── plugin.xml └── LICENSE /src/ios/ARKit/Images/bgCallout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjwoon/csAR/HEAD/src/ios/ARKit/Images/bgCallout.png -------------------------------------------------------------------------------- /src/ios/ARKit/Images/bgCallout@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjwoon/csAR/HEAD/src/ios/ARKit/Images/bgCallout@2x.png -------------------------------------------------------------------------------- /src/ios/ARKit/Images/bgCalloutDisclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjwoon/csAR/HEAD/src/ios/ARKit/Images/bgCalloutDisclosure.png -------------------------------------------------------------------------------- /src/ios/ARKit/Images/bgCalloutDisclosure@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjwoon/csAR/HEAD/src/ios/ARKit/Images/bgCalloutDisclosure@2x.png -------------------------------------------------------------------------------- /src/ios/CsAR.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "ARKit.h" 4 | 5 | @interface CsAR : CDVPlugin 6 | 7 | - (void)canDoAR: (CDVInvokedUrlCommand*)command; 8 | - (void)showGeolocationsForSelection: (CDVInvokedUrlCommand*)command; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARKit.h 3 | // AR Kit 4 | // 5 | // Modified by Niels Hansen on 11/20/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "ARViewController.h" 13 | #import "ARLocationDelegate.h" 14 | 15 | @interface ARKit : NSObject 16 | 17 | + (BOOL)deviceSupportsAR; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/ios/ARKit/RadarViewPortView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RadarViewPortView.h 3 | // ARKitDemo 4 | // 5 | // Created by Ed Rackham (a1phanumeric) 2013 6 | // Based on mixare's implementation. 7 | // 8 | 9 | #import 10 | #import "Radar.h" 11 | 12 | @interface RadarViewPortView : UIView 13 | 14 | @property (assign, nonatomic) float referenceAngle; 15 | @property (strong, nonatomic) UIColor *viewportColour; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/ios/ARKit/Radar.h: -------------------------------------------------------------------------------- 1 | // 2 | // Radar.h 3 | // ARKitDemo 4 | // 5 | // Created by Ed Rackham (a1phanumeric) 2013 6 | // Based on mixare's implementation. 7 | // 8 | 9 | #import 10 | #import "ARGeoCoordinate.h" 11 | 12 | #define RADIUS 60.0 13 | 14 | @interface Radar : UIView 15 | 16 | @property (nonatomic, strong) NSArray *pois; 17 | @property (nonatomic, assign) float radius; 18 | 19 | @property (strong, nonatomic) UIColor *radarBackgroundColour; 20 | @property (strong, nonatomic) UIColor *pointColour; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /src/ios/ARKit/GEOLocations.h: -------------------------------------------------------------------------------- 1 | // 2 | // GEOLocations.h 3 | // AR Kit 4 | // 5 | // Created by Niels W Hansen on 12/19/09. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import "ARLocationDelegate.h" 11 | 12 | @class ARCoordinate; 13 | 14 | @interface GEOLocations : NSObject 15 | 16 | @property(nonatomic,assign) id delegate; 17 | 18 | - (id)initWithDelegate:(id) aDelegate; 19 | -(NSMutableArray*) returnLocations; 20 | 21 | 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARLocationDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARLocationDelegate.h 3 | // AR Kit 4 | // 5 | // Created by Jared Crawford on 2/13/10. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import "ARGeoCoordinate.h" 11 | 12 | 13 | @protocol ARLocationDelegate 14 | 15 | //returns an array of ARGeoCoordinates 16 | - (NSMutableArray *)geoLocations; 17 | - (void)locationClicked:(ARGeoCoordinate *)coordinate; 18 | - (void)didFinishSnapshotGeneration:(UIImage*)image error:(NSError*)error; 19 | - (void)didClickCancel; 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /src/ios/ARKit/GEOLocations.m: -------------------------------------------------------------------------------- 1 | // 2 | // GEOLocations.m 3 | // AR Kit 4 | // 5 | // Modified by Niels W Hansen on 12/19/09. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "GEOLocations.h" 10 | #import "ARGeoCoordinate.h" 11 | #import 12 | #import 13 | 14 | @implementation GEOLocations 15 | 16 | 17 | @synthesize delegate; 18 | 19 | - (id)initWithDelegate:(id) aDelegate{ 20 | [self setDelegate:aDelegate]; 21 | return self; 22 | } 23 | 24 | - (NSMutableArray *)returnLocations{ 25 | return [delegate geoLocations]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /src/ios/ARKit/MarkerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CoordinateView.h 3 | // AR Kit 4 | // 5 | // Created by Niels W Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import "ARViewProtocol.h" 11 | 12 | @class ARGeoCoordinate; 13 | 14 | @interface MarkerView : UIView 15 | 16 | @property (nonatomic) BOOL usesMetric; 17 | 18 | - (id)initForCoordinate:(ARGeoCoordinate *)coordinate withDelgate:(id) aDelegate; 19 | - (id)initForCoordinate:(ARGeoCoordinate *)coordinate withDelgate:(id)aDelegate allowsCallout:(BOOL)allowsCallout; 20 | @end 21 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARViewProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARViewProtocol.h 3 | // AR Kit 4 | // 5 | // Modified by Niels Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ARGeoCoordinate; 13 | 14 | @protocol ARMarkerDelegate 15 | - (void)didTapMarker:(ARGeoCoordinate *)coordinate; 16 | @end 17 | 18 | @protocol ARDelegate 19 | - (void)didUpdateHeading:(CLHeading *)newHeading; 20 | - (void)didUpdateLocation:(CLLocation *)newLocation; 21 | - (void)didUpdateOrientation:(UIDeviceOrientation)orientation; 22 | @end 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-ar", 3 | "version": "0.1.0-alpha1", 4 | "description": "A Cordova plugin for implementing Augmented Reality apps.", 5 | "cordova": { 6 | "id": "cordova-plugin-ar", 7 | "platforms": [ 8 | "ios" 9 | ] 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/tjwoon/csAR" 14 | }, 15 | "keywords": [ 16 | "augmented", 17 | "reality", 18 | "augmented reality", 19 | "arkit", 20 | "ar", 21 | "ecosystem:cordova", 22 | "cordova-ios" 23 | ], 24 | "author": "TJ Woon (tj@cloudsky.org)", 25 | "license": "Apache 2.0" 26 | } 27 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARCoordinate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARCoordinate.h 3 | // AR Kit 4 | // 5 | // Created by Zac White on 8/1/09. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #define degreesToRadians(x) (M_PI * x / 180.0) 13 | #define radiansToDegrees(x) (x * (180.0/M_PI)) 14 | 15 | @interface ARCoordinate : NSObject 16 | 17 | - (NSUInteger)hash; 18 | - (BOOL)isEqual:(id)other; 19 | - (BOOL)isEqualToCoordinate:(ARCoordinate *)otherCoordinate; 20 | 21 | + (ARCoordinate *)coordinateWithRadialDistance:(double)newRadialDistance inclination:(double)newInclination azimuth:(double)newAzimuth; 22 | 23 | @property (nonatomic, retain) NSString *title; 24 | @property (nonatomic, copy) NSString *subtitle; 25 | @property (nonatomic) double radialDistance; 26 | @property (nonatomic) double inclination; 27 | @property (nonatomic) double azimuth; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARGeoCoordinate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARGeoCoordinate.h 3 | // AR Kit 4 | // 5 | // Created by Haseman on 8/1/09. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import 11 | #import "ARCoordinate.h" 12 | 13 | @interface ARGeoCoordinate : ARCoordinate 14 | 15 | @property (nonatomic, retain) CLLocation *geoLocation; 16 | @property (nonatomic, retain) UIView *displayView; 17 | @property (nonatomic) double distanceFromOrigin; 18 | 19 | 20 | - (float)angleFromCoordinate:(CLLocationCoordinate2D)first toCoordinate:(CLLocationCoordinate2D)second; 21 | 22 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location locationTitle:(NSString *)titleOfLocation; 23 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location fromOrigin:(CLLocation *)origin; 24 | 25 | - (void)calibrateUsingOrigin:(CLLocation *)origin; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARKit.m 3 | // AR Kit 4 | // 5 | // Modifed by Niels Hansen 11/20/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "ARKit.h" 10 | 11 | 12 | @implementation ARKit 13 | 14 | + (BOOL)deviceSupportsAR{ 15 | 16 | // Go thru and see if the device supports Video Capture. 17 | NSArray *devices = [AVCaptureDevice devices]; 18 | 19 | BOOL suportsVideo = NO; 20 | 21 | if (devices != nil && [devices count] > 0) { 22 | for (AVCaptureDevice *device in devices) { 23 | if ([device hasMediaType:AVMediaTypeVideo]) { 24 | suportsVideo = YES; 25 | break; 26 | } 27 | } 28 | } 29 | 30 | if (!suportsVideo) 31 | return NO; 32 | 33 | //TODO: Check to see if Device supports the Gyroscope (iPhone4 and higher) 34 | 35 | 36 | if(![CLLocationManager headingAvailable]){ 37 | return NO; 38 | } 39 | 40 | return YES; 41 | } 42 | @end 43 | -------------------------------------------------------------------------------- /src/ios/ARKit/RadarViewPortView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RadarViewPortView.m 3 | // ARKitDemo 4 | // 5 | // Created by Ed Rackham (a1phanumeric) 2013 6 | // Based on mixare's implementation. 7 | // 8 | 9 | #import "RadarViewPortView.h" 10 | #define radians(x) (M_PI * (x) / 180.0) 11 | 12 | @implementation RadarViewPortView{ 13 | float _newAngle; 14 | } 15 | 16 | - (id)initWithFrame:(CGRect)frame { 17 | if ((self = [super initWithFrame:frame])) { 18 | _newAngle = 45.0; 19 | _referenceAngle = 247.5; 20 | self.backgroundColor = [UIColor clearColor]; 21 | _viewportColour = [UIColor colorWithRed:14.0/255.0 green:140.0/255.0 blue:14.0/255.0 alpha:0.5]; 22 | } 23 | 24 | return self; 25 | } 26 | 27 | - (void)drawRect:(CGRect)rect{ 28 | CGContextRef contextRef = UIGraphicsGetCurrentContext(); 29 | CGContextSetFillColorWithColor(contextRef, _viewportColour.CGColor); 30 | CGContextMoveToPoint(contextRef, RADIUS, RADIUS); 31 | CGContextAddArc(contextRef, RADIUS, RADIUS, RADIUS, radians(_referenceAngle), radians(_referenceAngle+_newAngle),0); 32 | CGContextClosePath(contextRef); 33 | CGContextFillPath(contextRef); 34 | } 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /src/www/csAr.js: -------------------------------------------------------------------------------- 1 | var argscheck = require('cordova/argscheck'), 2 | exec = require('cordova/exec'); 3 | 4 | function AR () {}; 5 | 6 | AR.prototype = { 7 | 8 | canDoAr: function (success, failure) 9 | { 10 | argscheck.checkArgs('fF', 'CsAR.canDoAR', arguments); 11 | exec(success, failure, 'CsAR', 'canDoAR', []); 12 | }, 13 | 14 | showGeolocationsForSelection: function (params, success, failure) 15 | { 16 | argscheck.checkArgs('ofF', 'CsAR.showGeolocationsForSelection', arguments); 17 | 18 | // Perform all input validation here (native does not check) - - - - - - 19 | 20 | var maxDistance; 21 | var locs = []; 22 | 23 | if(params.maxDistance === undefined) maxDistance = 1000; 24 | else maxDistance = params.maxDistance; 25 | 26 | if(params.geoLocations === undefined) locs = []; 27 | params.geoLocations.forEach(function (loc) { 28 | if(typeof loc.latitude == "number" && 29 | typeof loc.longitude == "number" && 30 | typeof loc.name == "string" 31 | ) { 32 | locs.push(loc); 33 | } 34 | }); 35 | 36 | exec(success, failure, 'CsAR', 'showGeolocationsForSelection', [{ 37 | maxDistance: maxDistance, 38 | geoLocations: locs, 39 | }]); 40 | }, 41 | 42 | }; 43 | 44 | module.exports = new AR; 45 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARViewController.h 3 | // ARKitDemo 4 | // 5 | // Modified by Niels W Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import "ARLocationDelegate.h" 11 | #import "ARViewProtocol.h" 12 | #import 13 | 14 | @class AugmentedRealityController; 15 | 16 | @interface ARViewController : UIViewController 17 | { 18 | AVCaptureStillImageOutput *stillImageOutput; 19 | } 20 | 21 | @property (nonatomic, assign) id delegate; 22 | @property (assign, nonatomic) BOOL showsCloseButton; 23 | 24 | @property (assign, nonatomic, setter = setDebugMode:) BOOL debugMode; 25 | @property (assign, nonatomic, setter = setShowsRadar:) BOOL showsRadar; 26 | @property (assign, nonatomic, setter = setScaleViewsBasedOnDistance:) BOOL scaleViewsBasedOnDistance; 27 | @property (assign, nonatomic, setter = setMinimumScaleFactor:) float minimumScaleFactor; 28 | @property (assign, nonatomic, setter = setRotateViewsBasedOnPerspective:) BOOL rotateViewsBasedOnPerspective; 29 | @property (strong, nonatomic, setter = setRadarPointColour:) UIColor *radarPointColour; 30 | @property (strong, nonatomic, setter = setRadarBackgroundColour:) UIColor *radarBackgroundColour; 31 | @property (strong, nonatomic, setter = setRadarViewportColour:) UIColor *radarViewportColour; 32 | @property (assign, nonatomic, setter = setRadarRange:) float radarRange; 33 | @property (assign, nonatomic, setter = setOnlyShowItemsWithinRadarRange:) BOOL onlyShowItemsWithinRadarRange; 34 | //@property (readonly, nonatomic) AVCaptureSession* captureSession; 35 | 36 | - (id)initWithDelegate:(id)aDelegate; 37 | - (void)takeSnapshotAsynch; 38 | @end 39 | 40 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARCoordinate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARCoordinate.m 3 | // AR Kit 4 | // 5 | // Modified by Niels Hansen on 10/02/11 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "ARCoordinate.h" 10 | 11 | @implementation ARCoordinate 12 | 13 | @synthesize radialDistance; 14 | @synthesize inclination; 15 | @synthesize azimuth; 16 | @synthesize title; 17 | @synthesize subtitle; 18 | 19 | + (ARCoordinate *)coordinateWithRadialDistance:(double)newRadialDistance inclination:(double)newInclination azimuth:(double)newAzimuth { 20 | 21 | ARCoordinate *newCoordinate = [[ARCoordinate alloc] init]; 22 | [newCoordinate setRadialDistance: newRadialDistance]; 23 | [newCoordinate setInclination: newInclination]; 24 | [newCoordinate setAzimuth: newAzimuth]; 25 | [newCoordinate setTitle: @""]; 26 | 27 | return newCoordinate; 28 | } 29 | 30 | - (NSUInteger)hash { 31 | return ([[self title] hash] ^ [[self subtitle] hash]) + (int)([self radialDistance] + [self inclination] + [self azimuth]); 32 | } 33 | 34 | - (BOOL)isEqual:(id)other { 35 | 36 | if (other == self) 37 | return YES; 38 | 39 | if (!other || ![other isKindOfClass:[self class]]) 40 | return NO; 41 | 42 | return [self isEqualToCoordinate:other]; 43 | } 44 | 45 | - (BOOL)isEqualToCoordinate:(ARCoordinate *)otherCoordinate { 46 | 47 | if (self == otherCoordinate) 48 | return YES; 49 | 50 | BOOL equal = [self radialDistance] == [otherCoordinate radialDistance]; 51 | equal = equal && [self inclination] == [otherCoordinate inclination]; 52 | equal = equal && [self azimuth] == [otherCoordinate azimuth]; 53 | 54 | if (([self title] && [otherCoordinate title]) || ([self title] && ![otherCoordinate title]) || (![self title] && [otherCoordinate title])) 55 | equal = equal && [[self title] isEqualToString:[otherCoordinate title]]; 56 | 57 | return equal; 58 | } 59 | 60 | - (NSString *)description { 61 | return [NSString stringWithFormat:@"%@ r: %.3fm φ: %.3f° θ: %.3f°", [self title], [self radialDistance], radiansToDegrees([self azimuth]), radiansToDegrees([self inclination])]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CloudSky AR Plugin for Cordova 2 | ============================== 3 | 4 | This plugin makes it easy to display AR overlays onto the device's live camera 5 | feed. Currently supported features: 6 | 7 | - Location based points (with names) 8 | 9 | 10 | Installation 11 | ------------ 12 | 13 | ### Using the Cordova CLI 14 | 15 | ``` 16 | cordova plugins add cordova-plugin-ar \ 17 | --variable IOS_PERMISSION_DESCRIPTION="Description when requestion Location Permission from user (on iOS)" 18 | ``` 19 | 20 | *This plugin is published on NPM. You should be using Cordova CLI version 5 or 21 | above.* 22 | 23 | 24 | Usage 25 | ----- 26 | 27 | ### canDoAr 28 | 29 | ``` 30 | cloudSky.ar.canDoAr( 31 | function (canDoAr) { 32 | // canDoAr == true if we can do AR on this device, false otherwise. 33 | }, 34 | function () { 35 | // Error retrieving AR capability. 36 | // Optional. 37 | } 38 | ) 39 | ``` 40 | 41 | ### showGeolocationsForSelection 42 | 43 | ``` 44 | cloudSky.ar.showGeolocationsForSelection( 45 | { 46 | maxDistance: 1000, // OPTIONAL, defaults to 1000 47 | geoLocations: [ 48 | { 49 | latitude: 1.2345, 50 | longitude: 101.2345678, 51 | name: "Location name" 52 | }, 53 | ... 54 | ] 55 | }, 56 | function (location) { 57 | // location is a new object with the same latitude, longitude, and name 58 | // as the location which the user tapped on. 59 | // If the AR view was dismissed using the Cancel button, 60 | // location === undefined. 61 | }, 62 | function () { 63 | // Error launching the AR view. 64 | // Optional. 65 | } 66 | ) 67 | ``` 68 | 69 | Where: 70 | 71 | - `maxDistance` is the maximum distance from the device's current coordinates to 72 | a geolocation before it is no longer displayed. 73 | - `geoLocations` is an array of locations to display. Any location not matching 74 | the given format will be ignored / not displayed. 75 | 76 | 77 | Credits 78 | ------- 79 | 80 | iOS native AR implementation is based on [my fork](https://github.com/tjwoon/iPhone-AR-Toolkit) of @chrjs's 81 | (fork of the) iPhone AR Kit. 82 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARGeoCoordinate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARGeoCoordinate.m 3 | // AR Kit 4 | // 5 | // Modified by Niels Hansen on 11/23/11 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "ARGeoCoordinate.h" 10 | 11 | @implementation ARGeoCoordinate 12 | @synthesize distanceFromOrigin; 13 | @synthesize geoLocation; 14 | @synthesize displayView; 15 | 16 | - (float)angleFromCoordinate:(CLLocationCoordinate2D)first toCoordinate:(CLLocationCoordinate2D)second{ 17 | 18 | float longitudinalDifference = second.longitude - first.longitude; 19 | float latitudinalDifference = second.latitude - first.latitude; 20 | float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference); 21 | 22 | if (longitudinalDifference > 0) 23 | return possibleAzimuth; 24 | else if (longitudinalDifference < 0) 25 | return possibleAzimuth + M_PI; 26 | else if (latitudinalDifference < 0) 27 | return M_PI; 28 | 29 | return 0.0f; 30 | } 31 | 32 | - (void)calibrateUsingOrigin:(CLLocation *)origin { 33 | 34 | if (![self geoLocation]) 35 | return; 36 | 37 | [self setDistanceFromOrigin:[origin distanceFromLocation:[self geoLocation]]]; 38 | [self setRadialDistance: sqrt(pow([origin altitude] - [[self geoLocation] altitude], 2) + pow([self distanceFromOrigin], 2))]; 39 | 40 | float angle = sin(ABS([origin altitude] - [[self geoLocation] altitude]) / [self radialDistance]); 41 | 42 | if ([origin altitude] > [[self geoLocation] altitude]) 43 | angle = -angle; 44 | 45 | [self setInclination: angle]; 46 | [self setAzimuth: [self angleFromCoordinate:[origin coordinate] toCoordinate:[[self geoLocation] coordinate]]]; 47 | 48 | //NSLog(@"distance from %@ is %f, angle is %f, azimuth is %f",[self title], [self distanceFromOrigin],angle,[self azimuth]); 49 | } 50 | 51 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location locationTitle:(NSString *)titleOfLocation { 52 | 53 | ARGeoCoordinate *newCoordinate = [[ARGeoCoordinate alloc] init]; 54 | [newCoordinate setGeoLocation: location]; 55 | [newCoordinate setTitle: titleOfLocation]; 56 | 57 | return newCoordinate; 58 | } 59 | 60 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location fromOrigin:(CLLocation *)origin { 61 | 62 | ARGeoCoordinate *newCoordinate = [ARGeoCoordinate coordinateWithLocation:location locationTitle:@""]; 63 | [newCoordinate calibrateUsingOrigin:origin]; 64 | return newCoordinate; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /src/ios/ARKit/AugmentedRealityController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AugmentedRealityController.h 3 | // AR Kit 4 | // 5 | // Modified by Niels W Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import 10 | #import 11 | #import "ARViewController.h" 12 | #import 13 | #import 14 | #import "Radar.h" 15 | #import "RadarViewPortView.h" 16 | #import 17 | 18 | @class ARCoordinate; 19 | 20 | @interface AugmentedRealityController : NSObject { 21 | 22 | @private 23 | double latestHeading; 24 | double degreeRange; 25 | 26 | BOOL debugMode; 27 | 28 | float viewAngle; 29 | float prevHeading; 30 | int cameraOrientation; 31 | 32 | NSMutableArray *coordinates; 33 | 34 | AVCaptureSession *captureSession; 35 | AVCaptureVideoPreviewLayer *previewLayer; 36 | 37 | CLLocation *centerLocation; 38 | UIView *displayView; 39 | UILabel *radarNorthLabel; 40 | } 41 | 42 | @property BOOL scaleViewsBasedOnDistance; 43 | @property BOOL rotateViewsBasedOnPerspective; 44 | @property BOOL debugMode; 45 | 46 | @property double maximumScaleDistance; 47 | @property double minimumScaleFactor; 48 | @property double maximumRotationAngle; 49 | 50 | @property (nonatomic, assign, setter = setShowsRadar:) BOOL showsRadar; 51 | 52 | @property (nonatomic, retain) CMMotionManager* motionManager; 53 | @property (nonatomic, retain) CLLocationManager* locationManager; 54 | @property (nonatomic, retain) ARCoordinate *centerCoordinate; 55 | @property (nonatomic, retain) CLLocation *centerLocation; 56 | @property (nonatomic, retain) UIView *displayView; 57 | @property (nonatomic, retain) UIView *cameraView; 58 | @property (nonatomic, retain) UIViewController *rootViewController; 59 | @property (nonatomic, retain) AVCaptureSession *captureSession; 60 | @property (nonatomic, retain) AVCaptureVideoPreviewLayer *previewLayer; 61 | 62 | @property (assign, nonatomic) BOOL onlyShowItemsWithinRadarRange; 63 | @property (strong, nonatomic) Radar *radarView; 64 | @property (strong, nonatomic) RadarViewPortView *radarViewPort; 65 | @property (assign, nonatomic) float radarRange; 66 | 67 | @property (nonatomic, assign) id delegate; 68 | 69 | @property (nonatomic,retain) NSMutableArray *coordinates; 70 | 71 | - (id)initWithViewController:(UIViewController *)theView withDelgate:(id) aDelegate; 72 | 73 | - (void) updateLocations; 74 | - (void) stopListening; 75 | 76 | // Adding coordinates to the underlying data model. 77 | - (void)addCoordinate:(ARGeoCoordinate *)coordinate; 78 | 79 | // Removing coordinates 80 | - (void)removeCoordinate:(ARGeoCoordinate *)coordinate; 81 | - (void)removeCoordinates:(NSArray *)coordinateArray; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | AR Plugin 10 | TJ Woon (tj@cloudsky.org) 11 | A Cordova plugin for implementing Augmented Reality apps. 12 | Apache 2.0 13 | augmented,reality,augmented reality,arkit,ar 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | $IOS_PERMISSION_DESCRIPTION 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/ios/ARKit/Radar.m: -------------------------------------------------------------------------------- 1 | // 2 | // Radar.m 3 | // ARKitDemo 4 | // 5 | // Created by Ed Rackham (a1phanumeric) 2013 6 | // Based on mixare's implementation. 7 | // 8 | 9 | #import "Radar.h" 10 | 11 | @implementation Radar{ 12 | float _range; 13 | } 14 | 15 | @synthesize pois = _pois; 16 | @synthesize radius = _radius; 17 | 18 | - (id)initWithFrame:(CGRect)frame{ 19 | if ((self = [super initWithFrame:frame])) { 20 | self.backgroundColor = [UIColor clearColor]; 21 | _radarBackgroundColour = [UIColor colorWithRed:14.0/255.0 green:140.0/255.0 blue:14.0/255.0 alpha:0.2]; 22 | _pointColour = [UIColor whiteColor]; 23 | } 24 | return self; 25 | } 26 | 27 | 28 | - (void)drawRect:(CGRect)rect{ 29 | CGContextRef contextRef = UIGraphicsGetCurrentContext(); 30 | CGContextSetFillColorWithColor(contextRef, _radarBackgroundColour.CGColor); 31 | 32 | // Draw a radar and the view port 33 | CGContextFillEllipseInRect(contextRef, CGRectMake(0.5, 0.5, RADIUS*2, RADIUS*2)); 34 | CGContextSetRGBStrokeColor(contextRef, 0, 255, 0, 0.5); 35 | 36 | _range = _radius *1000; 37 | float scale = _range / RADIUS; 38 | if (_pois != nil) { 39 | for (ARGeoCoordinate *poi in _pois) { 40 | float x, y; 41 | //case1: azimiut is in the 1 quadrant of the radar 42 | if (poi.azimuth >= 0 && poi.azimuth < M_PI / 2) { 43 | x = RADIUS + cosf((M_PI / 2) - poi.azimuth) * (poi.radialDistance / scale); 44 | y = RADIUS - sinf((M_PI / 2) - poi.azimuth) * (poi.radialDistance / scale); 45 | } else if (poi.azimuth > M_PI / 2 && poi.azimuth < M_PI) { 46 | //case2: azimiut is in the 2 quadrant of the radar 47 | x = RADIUS + cosf(poi.azimuth - (M_PI / 2)) * (poi.radialDistance / scale); 48 | y = RADIUS + sinf(poi.azimuth - (M_PI / 2)) * (poi.radialDistance / scale); 49 | } else if (poi.azimuth > M_PI && poi.azimuth < (3 * M_PI / 2)) { 50 | //case3: azimiut is in the 3 quadrant of the radar 51 | x = RADIUS - cosf((3 * M_PI / 2) - poi.azimuth) * (poi.radialDistance / scale); 52 | y = RADIUS + sinf((3 * M_PI / 2) - poi.azimuth) * (poi.radialDistance / scale); 53 | } else if(poi.azimuth > (3 * M_PI / 2) && poi.azimuth < (2 * M_PI)) { 54 | //case4: azimiut is in the 4 quadrant of the radar 55 | x = RADIUS - cosf(poi.azimuth - (3 * M_PI / 2)) * (poi.radialDistance / scale); 56 | y = RADIUS - sinf(poi.azimuth - (3 * M_PI / 2)) * (poi.radialDistance / scale); 57 | } else if (poi.azimuth == 0) { 58 | x = RADIUS; 59 | y = RADIUS - poi.radialDistance / scale; 60 | } else if(poi.azimuth == M_PI/2) { 61 | x = RADIUS + poi.radialDistance / scale; 62 | y = RADIUS; 63 | } else if(poi.azimuth == (3 * M_PI / 2)) { 64 | x = RADIUS; 65 | y = RADIUS + poi.radialDistance / scale; 66 | } else if (poi.azimuth == (3 * M_PI / 2)) { 67 | x = RADIUS - poi.radialDistance / scale; 68 | y = RADIUS; 69 | } else { 70 | //If none of the above match we use the scenario where azimuth is 0 71 | x = RADIUS; 72 | y = RADIUS - poi.radialDistance / scale; 73 | } 74 | //drawing the radar point 75 | CGContextSetFillColorWithColor(contextRef, _pointColour.CGColor); 76 | if (x <= RADIUS * 2 && x >= 0 && y >= 0 && y <= RADIUS * 2) { 77 | CGContextFillEllipseInRect(contextRef, CGRectMake(x, y, 2, 2)); 78 | } 79 | } 80 | } 81 | } 82 | @end 83 | -------------------------------------------------------------------------------- /src/ios/ARKit/MarkerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CoordinateView.m 3 | // ARKitDemo 4 | // 5 | // Modified by Niels W Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "ARViewProtocol.h" 10 | #import "ARGeoCoordinate.h" 11 | #import "MarkerView.h" 12 | 13 | #define LABEL_HEIGHT 20 14 | #define LABEL_MARGIN 5 15 | #define DISCLOSURE_MARGIN 10 16 | 17 | @implementation MarkerView{ 18 | BOOL _allowsCallout; 19 | UIImage *_bgImage; 20 | UILabel *_lblDistance; 21 | id _delegate; 22 | ARGeoCoordinate *_coordinateInfo; 23 | } 24 | 25 | - (id)initForCoordinate:(ARGeoCoordinate *)coordinate withDelgate:(id)aDelegate{ 26 | return [self initForCoordinate:coordinate withDelgate:aDelegate allowsCallout:YES]; 27 | } 28 | 29 | - (id)initForCoordinate:(ARGeoCoordinate *)coordinate withDelgate:(id)aDelegate allowsCallout:(BOOL)allowsCallout{ 30 | 31 | _coordinateInfo = coordinate; 32 | _delegate = aDelegate; 33 | _allowsCallout = allowsCallout; 34 | _bgImage = [UIImage imageNamed:@"bgCallout.png"]; 35 | 36 | UIImage *disclosureImage = [UIImage imageNamed:@"bgCalloutDisclosure.png"]; 37 | CGSize calloutSize = _bgImage.size; 38 | CGRect theFrame = CGRectMake(0, 0, calloutSize.width, calloutSize.height); 39 | 40 | 41 | if(self = [super initWithFrame:theFrame]){ 42 | 43 | [self setContentMode:UIViewContentModeScaleAspectFit]; 44 | [self setAutoresizesSubviews:YES]; 45 | 46 | if(_allowsCallout){ 47 | [self setUserInteractionEnabled:YES]; 48 | } 49 | 50 | UIImageView *bgImageView = [[UIImageView alloc] initWithImage:_bgImage]; 51 | [self addSubview:bgImageView]; 52 | 53 | CGSize labelSize = CGSizeMake(calloutSize.width - (LABEL_MARGIN * 2), LABEL_HEIGHT); 54 | if(_allowsCallout){ 55 | labelSize.width -= disclosureImage.size.width + (DISCLOSURE_MARGIN * 2); 56 | } 57 | 58 | UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(LABEL_MARGIN, LABEL_MARGIN, labelSize.width, labelSize.height)]; 59 | [titleLabel setBackgroundColor: [UIColor clearColor]]; 60 | [titleLabel setTextColor: [UIColor whiteColor]]; 61 | [titleLabel setTextAlignment: NSTextAlignmentCenter]; 62 | [titleLabel setFont: [UIFont fontWithName:@"Helvetica-Bold" size:17.0]]; 63 | [titleLabel setText: [coordinate title]]; 64 | [self addSubview:titleLabel]; 65 | 66 | NSLocale *locale = [NSLocale currentLocale]; 67 | _usesMetric = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue]; 68 | 69 | 70 | _lblDistance = [[UILabel alloc] initWithFrame:CGRectMake(0, LABEL_HEIGHT + LABEL_MARGIN, labelSize.width, labelSize.height)]; 71 | [_lblDistance setBackgroundColor: [UIColor clearColor]]; 72 | [_lblDistance setTextColor: [UIColor whiteColor]]; 73 | [_lblDistance setTextAlignment: NSTextAlignmentCenter]; 74 | [_lblDistance setFont: [UIFont fontWithName:@"Helvetica" size:13.0]]; 75 | if(_usesMetric == YES){ 76 | [_lblDistance setText:[NSString stringWithFormat:@"%.2f km", [_coordinateInfo distanceFromOrigin]/1000.0f]]; 77 | } else { 78 | [_lblDistance setText:[NSString stringWithFormat:@"%.2f mi", ([_coordinateInfo distanceFromOrigin]/1000.0f) * 0.621371]]; 79 | } 80 | [self addSubview:_lblDistance]; 81 | 82 | 83 | if(_allowsCallout){ 84 | UIImageView *disclosureImageView = [[UIImageView alloc] initWithFrame:CGRectMake(calloutSize.width - disclosureImage.size.width - DISCLOSURE_MARGIN, DISCLOSURE_MARGIN, disclosureImage.size.width, disclosureImage.size.height)]; 85 | [disclosureImageView setImage:[UIImage imageNamed:@"bgCalloutDisclosure.png"]]; 86 | [self addSubview:disclosureImageView]; 87 | } 88 | 89 | 90 | [self setBackgroundColor:[UIColor clearColor]]; 91 | } 92 | 93 | return self; 94 | 95 | } 96 | 97 | - (void)drawRect:(CGRect)rect{ 98 | [super drawRect:rect]; 99 | if(_usesMetric == YES){ 100 | [_lblDistance setText:[NSString stringWithFormat:@"%.2f km", [_coordinateInfo distanceFromOrigin]/1000.0f]]; 101 | } else { 102 | [_lblDistance setText:[NSString stringWithFormat:@"%.2f mi", ([_coordinateInfo distanceFromOrigin]/1000.0f) * 0.621371]]; 103 | } 104 | } 105 | 106 | 107 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 108 | [_delegate didTapMarker:_coordinateInfo]; 109 | } 110 | 111 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 112 | } 113 | 114 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ 115 | CGRect theFrame = CGRectMake(0, 0, _bgImage.size.width, _bgImage.size.height); 116 | if(CGRectContainsPoint(theFrame, point)) 117 | return YES; // touched the view; 118 | 119 | return NO; 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /src/ios/CsAR.m: -------------------------------------------------------------------------------- 1 | #import "CsAR.h" 2 | 3 | @interface CsAR () 4 | 5 | @property CDVInvokedUrlCommand *callback; 6 | @property CLLocationManager *locationManager; 7 | 8 | // Config/state per run... 9 | @property ARViewController *arViewController; 10 | @property NSMutableArray *geoLocations; 11 | @property double radarRange; 12 | 13 | @end 14 | 15 | 16 | @implementation CsAR 17 | 18 | @synthesize callback; 19 | @synthesize locationManager; 20 | @synthesize arViewController; 21 | @synthesize geoLocations; 22 | @synthesize radarRange; 23 | 24 | #pragma mark - 25 | #pragma mark Helpers 26 | 27 | - (void)sendErrorString: (NSString*)msg withCommand:(CDVInvokedUrlCommand*)command 28 | { 29 | CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR 30 | messageAsString:msg]; 31 | [self.commandDelegate sendPluginResult:result callbackId:[command callbackId]]; 32 | } 33 | 34 | - (void)ingestGeoLocations: (NSArray*)locs 35 | { 36 | NSMutableArray *ingested = [[NSMutableArray alloc] initWithCapacity:[locs count]]; 37 | 38 | for(NSDictionary *loc in locs) { 39 | double lat = [[loc objectForKey:@"latitude"] doubleValue]; 40 | double lng = [[loc objectForKey:@"longitude"] doubleValue]; 41 | NSString *name = [loc objectForKey:@"name"]; 42 | CLLocation *clLoc = [[CLLocation alloc] initWithLatitude:lat longitude:lng]; 43 | ARGeoCoordinate *arCoord = [ARGeoCoordinate coordinateWithLocation:clLoc 44 | locationTitle:name]; 45 | [ingested addObject:arCoord]; 46 | } 47 | 48 | self.geoLocations = ingested; 49 | } 50 | 51 | - (void)requestLocationPermissions 52 | { 53 | if(self.locationManager == nil) { 54 | self.locationManager = [[CLLocationManager alloc] init]; 55 | [self.locationManager setDelegate:self]; 56 | } 57 | 58 | [self.locationManager requestWhenInUseAuthorization]; 59 | } 60 | 61 | - (void)startARWithGeoLocations 62 | { 63 | ARViewController *ctrl; 64 | self.arViewController = ctrl = [[ARViewController alloc] initWithDelegate:self]; 65 | // TMP FIXME [ctrl setShowsRadar:YES]; 66 | // TMP FIXME [ctrl setRadarBackgroundColour:[UIColor blackColor]]; 67 | // TMP FIXME [ctrl setRadarViewportColour:[UIColor darkGrayColor]]; 68 | // TMP FIXME [ctrl setRadarPointColour:[UIColor whiteColor]]; 69 | [ctrl setRadarRange:self.radarRange]; 70 | [ctrl setOnlyShowItemsWithinRadarRange:YES]; 71 | [ctrl setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal]; // ? TODO CHECK FIXME 72 | [self.viewController presentViewController:ctrl animated:YES completion:nil]; 73 | } 74 | 75 | 76 | #pragma mark - 77 | #pragma mark CLLocationManagerDelegate 78 | 79 | - (void)locationManager:(CLLocationManager*)manager 80 | didChangeAuthorizationStatus:(CLAuthorizationStatus)status 81 | { 82 | switch(status) { 83 | case kCLAuthorizationStatusRestricted: 84 | case kCLAuthorizationStatusDenied: 85 | [self sendErrorString:@"Location permission not granted" withCommand:self.callback]; 86 | case kCLAuthorizationStatusNotDetermined: 87 | return; 88 | default: 89 | [self startARWithGeoLocations]; 90 | } 91 | } 92 | 93 | 94 | #pragma mark - 95 | #pragma mark Plugin API 96 | 97 | - (void)canDoAR: (CDVInvokedUrlCommand*)command 98 | { 99 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK 100 | messageAsBool:[ARKit deviceSupportsAR]] 101 | callbackId:[command callbackId]]; 102 | } 103 | 104 | /*** 105 | * - showGeolocationsForSelection 106 | * args: [ 107 | * { 108 | * maxDistance: 1234 109 | * geoLocations: [ 110 | * { 111 | * longitude: 123.456 112 | * latitude: 1.23 113 | * name: "Location name" 114 | * } 115 | * ... 116 | * ] 117 | * } 118 | * ] 119 | ***/ 120 | - (void)showGeolocationsForSelection: (CDVInvokedUrlCommand*)command 121 | { 122 | // Ensure we don't display more than one AR view at a time - - - - - - - - - 123 | 124 | if(callback != nil) { 125 | [self sendErrorString:@"AR is already in progress." withCommand:command]; 126 | return; 127 | } 128 | 129 | // Get arguments and save for use - - - - - - - - - - - - - - - - - - - - - 130 | // Validity is checked in JS. 131 | 132 | NSDictionary *args = (NSDictionary*) [command argumentAtIndex:0 133 | withDefault:nil 134 | andClass:[NSDictionary class]]; 135 | 136 | NSNumber *radarRange = [args objectForKey:@"maxDistance"]; 137 | self.radarRange = [radarRange doubleValue]; 138 | 139 | NSArray *locs = [args objectForKey:@"geoLocations"]; 140 | [self ingestGeoLocations:locs]; 141 | 142 | callback = command; 143 | 144 | // Check for / request Location Services permissions - - - - - - - - - - - - 145 | 146 | switch([CLLocationManager authorizationStatus]) { 147 | case kCLAuthorizationStatusRestricted: 148 | case kCLAuthorizationStatusDenied: 149 | [self sendErrorString:@"Location permission not granted" withCommand:command]; 150 | return; 151 | case kCLAuthorizationStatusNotDetermined: 152 | [self requestLocationPermissions]; 153 | break; 154 | default: 155 | [self startARWithGeoLocations]; 156 | } 157 | } 158 | 159 | 160 | #pragma mark - 161 | #pragma mark ARLocationDelegate 162 | 163 | - (void)locationClicked:(ARGeoCoordinate *)coordinate 164 | { 165 | CLLocationCoordinate2D clLoc = coordinate.geoLocation.coordinate; 166 | NSNumber *lat = [NSNumber numberWithDouble:clLoc.latitude]; 167 | NSNumber *lon = [NSNumber numberWithDouble:clLoc.longitude]; 168 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:coordinate.title, @"name" 169 | ,lat, @"latitude" 170 | ,lon, @"longitude" 171 | ,NULL]; 172 | 173 | NSString *cbId = [self.callback callbackId]; 174 | 175 | [self.arViewController dismissViewControllerAnimated:YES completion:nil]; 176 | self.callback = nil; 177 | self.radarRange = 0; 178 | 179 | CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 180 | messageAsDictionary:dict]; 181 | 182 | [self.commandDelegate sendPluginResult:result callbackId:cbId]; 183 | } 184 | 185 | - (void)didFinishSnapshotGeneration:(UIImage*)image error:(NSError*)error 186 | { 187 | // Ignore this - our plugin does not need this / not sure exactly how this 188 | // is triggered. 189 | } 190 | 191 | - (void)didClickCancel 192 | { 193 | NSString *cbId = [self.callback callbackId]; 194 | self.callback = nil; 195 | self.radarRange = 0; 196 | 197 | CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT]; 198 | [self.commandDelegate sendPluginResult:result callbackId:cbId]; 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /src/ios/ARKit/ARViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARViewController.m 3 | // ARKitDemo 4 | // 5 | // Modified by Niels W Hansen on 12/31/11. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "ARViewController.h" 10 | #import "AugmentedRealityController.h" 11 | #import "GEOLocations.h" 12 | #import "MarkerView.h" 13 | 14 | @implementation ARViewController{ 15 | AugmentedRealityController *_agController; 16 | } 17 | 18 | @synthesize delegate; 19 | 20 | - (id)initWithDelegate:(id)aDelegate{ 21 | 22 | [self setDelegate:aDelegate]; 23 | 24 | if (!(self = [super init])){ 25 | return nil; 26 | } 27 | 28 | [self setWantsFullScreenLayout:NO]; 29 | 30 | // Defaults 31 | _debugMode = NO; 32 | _scaleViewsBasedOnDistance = YES; 33 | _minimumScaleFactor = 0.5; 34 | _rotateViewsBasedOnPerspective = YES; 35 | _showsRadar = YES; 36 | _showsCloseButton = YES; 37 | _radarRange = 20.0; 38 | _onlyShowItemsWithinRadarRange = NO; 39 | 40 | // Create ARC 41 | _agController = [[AugmentedRealityController alloc] initWithViewController:self withDelgate:self]; 42 | 43 | [_agController setShowsRadar:_showsRadar]; 44 | [_agController setRadarRange:_radarRange]; 45 | [_agController setScaleViewsBasedOnDistance:_scaleViewsBasedOnDistance]; 46 | [_agController setMinimumScaleFactor:_minimumScaleFactor]; 47 | [_agController setRotateViewsBasedOnPerspective:_rotateViewsBasedOnPerspective]; 48 | [_agController setOnlyShowItemsWithinRadarRange:_onlyShowItemsWithinRadarRange]; 49 | 50 | GEOLocations *locations = [[GEOLocations alloc] initWithDelegate:delegate]; 51 | 52 | if([[locations returnLocations] count] > 0){ 53 | for (ARGeoCoordinate *coordinate in [locations returnLocations]){ 54 | if (coordinate.displayView == nil){ 55 | MarkerView *cv = [[MarkerView alloc] initForCoordinate:coordinate withDelgate:self allowsCallout:YES]; 56 | [coordinate setDisplayView:cv]; 57 | } 58 | [_agController addCoordinate:coordinate]; 59 | } 60 | } 61 | 62 | [self.view setAutoresizesSubviews:YES]; 63 | 64 | stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; 65 | NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil]; 66 | [stillImageOutput setOutputSettings:outputSettings]; 67 | [_agController.captureSession addOutput:stillImageOutput]; 68 | 69 | return self; 70 | } 71 | 72 | - (void)closeButtonClicked:(id)sender { 73 | _agController = nil; 74 | [self dismissViewControllerAnimated:YES completion:nil]; 75 | } 76 | 77 | - (void)viewDidAppear:(BOOL)animated { 78 | [super viewDidAppear:animated]; 79 | } 80 | 81 | - (void)viewWillAppear:(BOOL)animated { 82 | [super viewWillAppear:animated]; 83 | 84 | if(_showsCloseButton == YES) { 85 | UIButton *closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 30, 60, 30)]; 86 | 87 | [closeBtn setTitle:NSLocalizedString(@"Close", @"Close") forState:UIControlStateNormal]; 88 | [closeBtn.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]]; 89 | [closeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 90 | [closeBtn.titleLabel setShadowColor:[UIColor colorWithWhite:0.1 alpha:1.0]]; 91 | [closeBtn.titleLabel setShadowOffset:CGSizeMake(0, -1)]; 92 | [closeBtn setBackgroundColor:[UIColor colorWithWhite:0.3 alpha:1.0]]; 93 | [closeBtn addTarget:self action:@selector(closeButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 94 | [[self view] addSubview:closeBtn]; 95 | } 96 | } 97 | 98 | - (void)viewDidDisappear:(BOOL)animated { 99 | [super viewDidDisappear:animated]; 100 | [delegate didClickCancel]; 101 | } 102 | 103 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { 104 | return YES; 105 | } 106 | 107 | - (BOOL)shouldAutorotate{ 108 | return YES; 109 | } 110 | 111 | - (void)didTapMarker:(ARGeoCoordinate *)coordinate { 112 | NSLog(@"delegate worked click on %@", [coordinate title]); 113 | [delegate locationClicked:coordinate]; 114 | 115 | } 116 | 117 | - (void)didUpdateHeading:(CLHeading *)newHeading { 118 | //NSLog(@"Heading Updated"); 119 | } 120 | - (void)didUpdateLocation:(CLLocation *)newLocation { 121 | //NSLog(@"Location Updated"); 122 | } 123 | - (void)didUpdateOrientation:(UIDeviceOrientation)orientation { 124 | /*NSLog(@"Orientation Updated"); 125 | 126 | if(orientation == UIDeviceOrientationPortrait) 127 | NSLog(@"Portrait"); 128 | */ 129 | } 130 | 131 | - (void)takeSnapshotAsynch 132 | { 133 | [stillImageOutput captureStillImageAsynchronouslyFromConnection:[stillImageOutput.connections objectAtIndex:0] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) 134 | { 135 | if (error == nil) 136 | { 137 | NSData* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; 138 | UIImage* cameraImg = [UIImage imageWithData:imageData]; 139 | 140 | UIGraphicsBeginImageContext(self.view.bounds.size); 141 | //CGContextRef c = UIGraphicsGetCurrentContext(); 142 | 143 | // Rendering camera snapshot 144 | [cameraImg drawInRect:self.view.bounds]; 145 | 146 | UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext(); 147 | UIGraphicsEndImageContext(); 148 | 149 | [delegate didFinishSnapshotGeneration:snapshotImage error:nil]; 150 | } 151 | else 152 | { 153 | [delegate didFinishSnapshotGeneration:nil error:error]; 154 | } 155 | }]; 156 | } 157 | 158 | #pragma mark - Custom Setters 159 | - (void)setDebugMode:(BOOL)debugMode{ 160 | _debugMode = debugMode; 161 | [_agController setDebugMode:_debugMode]; 162 | } 163 | 164 | - (void)setShowsRadar:(BOOL)showsRadar{ 165 | _showsRadar = showsRadar; 166 | [_agController setShowsRadar:_showsRadar]; 167 | } 168 | 169 | - (void)setScaleViewsBasedOnDistance:(BOOL)scaleViewsBasedOnDistance{ 170 | _scaleViewsBasedOnDistance = scaleViewsBasedOnDistance; 171 | [_agController setScaleViewsBasedOnDistance:_scaleViewsBasedOnDistance]; 172 | } 173 | 174 | - (void)setMinimumScaleFactor:(float)minimumScaleFactor{ 175 | _minimumScaleFactor = minimumScaleFactor; 176 | [_agController setMinimumScaleFactor:_minimumScaleFactor]; 177 | } 178 | 179 | - (void)setRotateViewsBasedOnPerspective:(BOOL)rotateViewsBasedOnPerspective{ 180 | _rotateViewsBasedOnPerspective = rotateViewsBasedOnPerspective; 181 | [_agController setRotateViewsBasedOnPerspective:_rotateViewsBasedOnPerspective]; 182 | } 183 | 184 | - (void)setRadarPointColour:(UIColor *)radarPointColour{ 185 | _radarPointColour = radarPointColour; 186 | [_agController.radarView setPointColour:_radarPointColour]; 187 | } 188 | 189 | - (void)setRadarBackgroundColour:(UIColor *)radarBackgroundColour{ 190 | _radarBackgroundColour = radarBackgroundColour; 191 | [_agController.radarView setRadarBackgroundColour:_radarBackgroundColour]; 192 | } 193 | 194 | - (void)setRadarViewportColour:(UIColor *)radarViewportColour{ 195 | _radarViewportColour = radarViewportColour; 196 | [_agController.radarViewPort setViewportColour:_radarViewportColour]; 197 | } 198 | 199 | - (void)setRadarRange:(float)radarRange{ 200 | _radarRange = radarRange; 201 | [_agController setRadarRange:_radarRange]; 202 | } 203 | 204 | - (void)setOnlyShowItemsWithinRadarRange:(BOOL)onlyShowItemsWithinRadarRange{ 205 | _onlyShowItemsWithinRadarRange = onlyShowItemsWithinRadarRange; 206 | [_agController setOnlyShowItemsWithinRadarRange:_onlyShowItemsWithinRadarRange]; 207 | } 208 | 209 | 210 | 211 | #pragma mark - View Cleanup 212 | 213 | - (void)didReceiveMemoryWarning { 214 | // Releases the view if it doesn't have a superview. 215 | [super didReceiveMemoryWarning]; 216 | } 217 | 218 | - (void)viewDidUnload { 219 | _agController = nil; 220 | } 221 | 222 | @end 223 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/ios/ARKit/AugmentedRealityController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AugmentedRealityController.m 3 | // AR Kit 4 | // 5 | // Modified by Niels W Hansen on 5/25/12. 6 | // Modified by Ed Rackham (a1phanumeric) 2013 7 | // 8 | 9 | #import "AugmentedRealityController.h" 10 | #import "ARCoordinate.h" 11 | #import "ARGeoCoordinate.h" 12 | #import 13 | #import 14 | #import 15 | 16 | #define kFilteringFactor 0.05 17 | #define degreesToRadian(x) (M_PI * (x) / 180.0) 18 | #define radianToDegrees(x) ((x) * 180.0/M_PI) 19 | #define M_2PI 2.0 * M_PI 20 | #define BOX_WIDTH 150 21 | #define BOX_HEIGHT 100 22 | #define BOX_GAP 10 23 | #define ADJUST_BY 30 24 | #define DISTANCE_FILTER 2.0 25 | #define HEADING_FILTER 1.0 26 | #define INTERVAL_UPDATE 0.75 27 | #define SCALE_FACTOR 1.0 28 | #define HEADING_NOT_SET -1.0 29 | #define DEGREE_TO_UPDATE 1 30 | 31 | #define RADAR_TOP_MARGIN 30 32 | 33 | 34 | @interface AugmentedRealityController (Private) 35 | - (void) updateCenterCoordinate; 36 | - (void) startListening; 37 | - (void) currentDeviceOrientation; 38 | 39 | - (double) findDeltaOfRadianCenter:(double*)centerAzimuth coordinateAzimuth:(double)pointAzimuth betweenNorth:(BOOL*) isBetweenNorth; 40 | - (CGPoint) pointForCoordinate:(ARCoordinate *)coordinate; 41 | - (BOOL) shouldDisplayCoordinate:(ARCoordinate *)coordinate; 42 | 43 | @end 44 | 45 | @implementation AugmentedRealityController 46 | 47 | @synthesize locationManager; 48 | @synthesize motionManager; 49 | @synthesize displayView; 50 | @synthesize cameraView; 51 | @synthesize rootViewController; 52 | @synthesize centerCoordinate; 53 | @synthesize scaleViewsBasedOnDistance; 54 | @synthesize rotateViewsBasedOnPerspective; 55 | @synthesize maximumScaleDistance; 56 | @synthesize minimumScaleFactor; 57 | @synthesize maximumRotationAngle; 58 | @synthesize centerLocation; 59 | @synthesize coordinates; 60 | @synthesize debugMode; 61 | @synthesize captureSession; 62 | @synthesize previewLayer; 63 | @synthesize delegate; 64 | 65 | 66 | - (id)initWithViewController:(UIViewController *)vc withDelgate:(id) aDelegate { 67 | 68 | if (!(self = [super init])) 69 | return nil; 70 | 71 | [self setDelegate:aDelegate]; 72 | 73 | latestHeading = HEADING_NOT_SET; 74 | prevHeading = HEADING_NOT_SET; 75 | 76 | [self setRootViewController: vc]; 77 | [self setMaximumScaleDistance: 0.0]; 78 | [self setMinimumScaleFactor: SCALE_FACTOR]; 79 | [self setScaleViewsBasedOnDistance: NO]; 80 | [self setRotateViewsBasedOnPerspective: NO]; 81 | [self setOnlyShowItemsWithinRadarRange:NO]; 82 | [self setMaximumRotationAngle: M_PI / 6.0]; 83 | [self setCoordinates:[NSMutableArray array]]; 84 | [self currentDeviceOrientation]; 85 | 86 | CGRect screenRect = [[UIScreen mainScreen] bounds]; 87 | 88 | UIView *camView = [[UIView alloc] initWithFrame:screenRect]; 89 | UIView *displayV= [[UIView alloc] initWithFrame:screenRect]; 90 | 91 | [displayV setAutoresizesSubviews:YES]; 92 | [camView setAutoresizesSubviews:YES]; 93 | 94 | camView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 95 | displayV.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 96 | 97 | degreeRange = [camView bounds].size.width / ADJUST_BY; 98 | 99 | 100 | [vc setView:displayV]; 101 | [[vc view] insertSubview:camView atIndex:0]; 102 | 103 | 104 | #if !TARGET_IPHONE_SIMULATOR 105 | 106 | AVCaptureSession *avCaptureSession = [[AVCaptureSession alloc] init]; 107 | AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 108 | 109 | NSError *error = nil; 110 | 111 | AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error]; 112 | 113 | if (videoInput) { 114 | [avCaptureSession addInput:videoInput]; 115 | } 116 | else { 117 | // Handle the failure. 118 | } 119 | 120 | AVCaptureVideoPreviewLayer *newCaptureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:avCaptureSession]; 121 | 122 | [[camView layer] setMasksToBounds:NO]; 123 | 124 | [newCaptureVideoPreviewLayer setFrame:[camView bounds]]; 125 | 126 | if ([newCaptureVideoPreviewLayer.connection isVideoOrientationSupported]) { 127 | [newCaptureVideoPreviewLayer.connection setVideoOrientation:cameraOrientation]; 128 | } 129 | 130 | [newCaptureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; 131 | 132 | [[camView layer] insertSublayer:newCaptureVideoPreviewLayer below:[[[camView layer] sublayers] objectAtIndex:0]]; 133 | 134 | [self setPreviewLayer:newCaptureVideoPreviewLayer]; 135 | 136 | [avCaptureSession setSessionPreset:AVCaptureSessionPresetHigh]; 137 | [avCaptureSession startRunning]; 138 | 139 | [self setCaptureSession:avCaptureSession]; 140 | #endif 141 | 142 | CLLocation *newCenter = [[CLLocation alloc] initWithLatitude:37.41711 longitude:-122.02528]; //TODO: We should get the latest heading here. 143 | 144 | [self setCenterLocation: newCenter]; 145 | 146 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) 147 | name: UIDeviceOrientationDidChangeNotification object:nil]; 148 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 149 | 150 | 151 | [self startListening]; 152 | [self setCameraView:camView]; 153 | [self setDisplayView:displayV]; 154 | 155 | self.motionManager = [[CMMotionManager alloc] init]; 156 | // NSLog(@"%d, %d", self.motionManager.accelerometerActive, self.motionManager.accelerometerAvailable); 157 | self.motionManager.accelerometerUpdateInterval = 1/60.0; 158 | [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { 159 | // CMAcceleration accelData = accelerometerData.acceleration; 160 | 161 | // CLLocationCoordinate2D coord = self.locationManager.location.coordinate; 162 | 163 | // CLLocationDirection heading = self.locationManager.heading.magneticHeading; 164 | // CLLocationDistance altitude = self.locationManager.location.altitude; 165 | 166 | switch (cameraOrientation) { 167 | case UIDeviceOrientationLandscapeLeft: 168 | viewAngle = atan2(accelerometerData.acceleration.x, accelerometerData.acceleration.z); 169 | break; 170 | case UIDeviceOrientationLandscapeRight: 171 | viewAngle = atan2(-accelerometerData.acceleration.x, accelerometerData.acceleration.z); 172 | break; 173 | case UIDeviceOrientationPortrait: 174 | viewAngle = atan2(accelerometerData.acceleration.y, accelerometerData.acceleration.z); 175 | break; 176 | case UIDeviceOrientationPortraitUpsideDown: 177 | viewAngle = atan2(-accelerometerData.acceleration.y, accelerometerData.acceleration.z); 178 | break; 179 | default: 180 | break; 181 | } 182 | 183 | }]; 184 | 185 | self.locationManager = [[CLLocationManager alloc] init]; 186 | self.locationManager.delegate = self; 187 | [self.locationManager startUpdatingHeading]; 188 | [self.locationManager startUpdatingLocation]; 189 | // Do any additional setup after loading the view, typically from a nib. 190 | 191 | return self; 192 | } 193 | 194 | - (BOOL)shouldAutorotate{ 195 | return YES; 196 | } 197 | 198 | - (void)setShowsRadar:(BOOL)showsRadar{ 199 | _showsRadar = showsRadar; 200 | 201 | [_radarView removeFromSuperview]; 202 | [_radarViewPort removeFromSuperview]; 203 | [radarNorthLabel removeFromSuperview]; 204 | 205 | _radarView = nil; 206 | _radarViewPort = nil; 207 | radarNorthLabel = nil; 208 | 209 | if(_showsRadar){ 210 | 211 | CGRect displayFrame = displayView.bounds; 212 | 213 | int radarSize = 2 * RADIUS + 1; 214 | int margin = 4; 215 | _radarView = [[Radar alloc] initWithFrame:CGRectMake(displayFrame.size.width - radarSize - margin, margin + RADAR_TOP_MARGIN, radarSize, radarSize)]; 216 | _radarViewPort = [[RadarViewPortView alloc] initWithFrame:CGRectMake(displayFrame.size.width - radarSize - margin, margin + RADAR_TOP_MARGIN, radarSize, radarSize)]; 217 | 218 | radarNorthLabel = [[UILabel alloc] initWithFrame:CGRectMake(displayFrame.size.width - RADIUS - 11, margin + 3, 10, 10)]; 219 | radarNorthLabel.backgroundColor = [UIColor clearColor]; 220 | radarNorthLabel.textColor = [UIColor whiteColor]; 221 | radarNorthLabel.font = [UIFont boldSystemFontOfSize:10.0]; 222 | radarNorthLabel.textAlignment = NSTextAlignmentCenter; 223 | radarNorthLabel.text = @"N"; 224 | radarNorthLabel.alpha = 0.8; 225 | 226 | 227 | _radarView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; 228 | _radarViewPort.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; 229 | radarNorthLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; 230 | 231 | [self.displayView addSubview:_radarView]; 232 | [self.displayView addSubview:_radarViewPort]; 233 | [self.displayView addSubview:radarNorthLabel]; 234 | } 235 | } 236 | 237 | -(void)unloadAV { 238 | [captureSession stopRunning]; 239 | AVCaptureInput* input = [captureSession.inputs objectAtIndex:0]; 240 | [captureSession removeInput:input]; 241 | [[self previewLayer] removeFromSuperlayer]; 242 | [self setCaptureSession:nil]; 243 | [self setPreviewLayer:nil]; 244 | } 245 | 246 | - (void)dealloc { 247 | [self stopListening]; 248 | [self unloadAV]; 249 | [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; 250 | locationManager.delegate = nil; 251 | 252 | } 253 | 254 | #pragma mark - 255 | #pragma mark Location Manager methods 256 | - (void)startListening { 257 | 258 | // start our heading readings and our accelerometer readings. 259 | if (![self locationManager]) { 260 | CLLocationManager *newLocationManager = [[CLLocationManager alloc] init]; 261 | 262 | [newLocationManager setHeadingFilter: HEADING_FILTER]; 263 | [newLocationManager setDistanceFilter:DISTANCE_FILTER]; 264 | [newLocationManager setDesiredAccuracy: kCLLocationAccuracyNearestTenMeters]; 265 | [newLocationManager startUpdatingHeading]; 266 | [newLocationManager startUpdatingLocation]; 267 | [newLocationManager setDelegate: self]; 268 | 269 | [self setLocationManager: newLocationManager]; 270 | } 271 | 272 | if (![self motionManager]) 273 | { 274 | // [self.motionManager ] 275 | } 276 | 277 | 278 | if (![self centerCoordinate]) 279 | [self setCenterCoordinate:[ARCoordinate coordinateWithRadialDistance:1.0 inclination:0 azimuth:0]]; 280 | } 281 | 282 | - (void)stopListening { 283 | [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; 284 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 285 | 286 | if ([self locationManager]) { 287 | [[self locationManager] setDelegate: nil]; 288 | } 289 | 290 | // if ([self accelerometerManager]) { 291 | // [[self accelerometerManager] setDelegate: nil]; 292 | // } 293 | } 294 | 295 | - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { 296 | 297 | latestHeading = degreesToRadian(newHeading.magneticHeading); 298 | 299 | //Let's only update the Center Coordinate when we have adjusted by more than X degrees 300 | if (fabs(latestHeading-prevHeading) >= degreesToRadian(DEGREE_TO_UPDATE) || prevHeading == HEADING_NOT_SET) { 301 | prevHeading = latestHeading; 302 | [self updateCenterCoordinate]; 303 | [[self delegate] didUpdateHeading:newHeading]; 304 | } 305 | 306 | 307 | if(_showsRadar){ 308 | int gradToRotate = newHeading.magneticHeading - 90 - 22.5; 309 | if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) { 310 | gradToRotate += 90; 311 | } else if([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight){ 312 | gradToRotate -= 90; 313 | } else if([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown){ 314 | gradToRotate -= 180; 315 | } 316 | 317 | if (gradToRotate < 0) { 318 | gradToRotate = 360 + gradToRotate; 319 | } 320 | 321 | _radarViewPort.referenceAngle = gradToRotate; 322 | [_radarViewPort setNeedsDisplay]; 323 | } 324 | } 325 | 326 | - (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager { 327 | return YES; 328 | } 329 | 330 | - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { 331 | [self setCenterLocation:newLocation]; 332 | [[self delegate] didUpdateLocation:newLocation]; 333 | 334 | } 335 | 336 | - (void)updateCenterCoordinate { 337 | 338 | double adjustment = 0; 339 | 340 | switch (cameraOrientation) { 341 | case UIDeviceOrientationLandscapeLeft: 342 | adjustment = degreesToRadian(270); 343 | break; 344 | case UIDeviceOrientationLandscapeRight: 345 | adjustment = degreesToRadian(90); 346 | break; 347 | case UIDeviceOrientationPortraitUpsideDown: 348 | adjustment = degreesToRadian(180); 349 | break; 350 | default: 351 | adjustment = 0; 352 | break; 353 | } 354 | 355 | [[self centerCoordinate] setAzimuth: latestHeading - adjustment]; 356 | [self updateLocations]; 357 | } 358 | 359 | - (void)setCenterLocation:(CLLocation *)newLocation { 360 | centerLocation = newLocation; 361 | 362 | for (ARGeoCoordinate *geoLocation in [self coordinates]) { 363 | 364 | if ([geoLocation isKindOfClass:[ARGeoCoordinate class]]) { 365 | [geoLocation calibrateUsingOrigin:centerLocation]; 366 | 367 | if(_onlyShowItemsWithinRadarRange){ 368 | if(([geoLocation radialDistance] / 1000) > _radarRange){ 369 | continue; 370 | } 371 | } 372 | 373 | if ([geoLocation radialDistance] > [self maximumScaleDistance]) 374 | [self setMaximumScaleDistance:[geoLocation radialDistance]]; 375 | } 376 | } 377 | } 378 | 379 | #pragma mark - 380 | #pragma mark Coordinate methods 381 | 382 | - (void)addCoordinate:(ARGeoCoordinate *)coordinate { 383 | 384 | [[self coordinates] addObject:coordinate]; 385 | 386 | if ([coordinate radialDistance] > [self maximumScaleDistance]) 387 | [self setMaximumScaleDistance: [coordinate radialDistance]]; 388 | } 389 | 390 | - (void)removeCoordinate:(ARGeoCoordinate *)coordinate { 391 | [[self coordinates] removeObject:coordinate]; 392 | } 393 | 394 | - (void)removeCoordinates:(NSArray *)coordinateArray { 395 | 396 | for (ARGeoCoordinate *coordinateToRemove in coordinateArray) { 397 | NSUInteger indexToRemove = [[self coordinates] indexOfObject:coordinateToRemove]; 398 | 399 | //TODO: Error checking in here. 400 | [[self coordinates] removeObjectAtIndex:indexToRemove]; 401 | } 402 | } 403 | 404 | #pragma mark - 405 | #pragma mark Location methods 406 | 407 | -(double) findDeltaOfRadianCenter:(double*)centerAzimuth coordinateAzimuth:(double)pointAzimuth betweenNorth:(BOOL*) isBetweenNorth { 408 | 409 | if (*centerAzimuth < 0.0) 410 | *centerAzimuth = M_2PI + *centerAzimuth; 411 | 412 | if (*centerAzimuth > M_2PI) 413 | *centerAzimuth = *centerAzimuth - M_2PI; 414 | 415 | double deltaAzimuth = ABS(pointAzimuth - *centerAzimuth); 416 | *isBetweenNorth = NO; 417 | 418 | // If values are on either side of the Azimuth of North we need to adjust it. Only check the degree range 419 | if (*centerAzimuth < degreesToRadian(degreeRange) && pointAzimuth > degreesToRadian(360-degreeRange)) { 420 | deltaAzimuth = (*centerAzimuth + (M_2PI - pointAzimuth)); 421 | *isBetweenNorth = YES; 422 | } 423 | else if (pointAzimuth < degreesToRadian(degreeRange) && *centerAzimuth > degreesToRadian(360-degreeRange)) { 424 | deltaAzimuth = (pointAzimuth + (M_2PI - *centerAzimuth)); 425 | *isBetweenNorth = YES; 426 | } 427 | 428 | return deltaAzimuth; 429 | } 430 | 431 | - (BOOL)shouldDisplayCoordinate:(ARCoordinate *)coordinate { 432 | 433 | double currentAzimuth = [[self centerCoordinate] azimuth]; 434 | double pointAzimuth = [coordinate azimuth]; 435 | BOOL isBetweenNorth = NO; 436 | double deltaAzimuth = [self findDeltaOfRadianCenter: ¤tAzimuth coordinateAzimuth:pointAzimuth betweenNorth:&isBetweenNorth]; 437 | BOOL result = NO; 438 | 439 | // NSLog(@"Current %f, Item %f, delta %f, range %f",currentAzimuth,pointAzimuth,deltaAzimith,degreesToRadian([self degreeRange])); 440 | 441 | if (deltaAzimuth <= degreesToRadian(degreeRange)){ 442 | result = YES; 443 | } 444 | 445 | // Limit results to only those within radar range (if set) 446 | if(_onlyShowItemsWithinRadarRange){ 447 | if(([coordinate radialDistance] / 1000) > _radarRange){ 448 | result = NO; 449 | } 450 | } 451 | 452 | return result; 453 | } 454 | 455 | - (CGPoint)pointForCoordinate:(ARCoordinate *)coordinate { 456 | 457 | CGPoint point; 458 | CGRect realityBounds = [[self displayView] bounds]; 459 | double currentAzimuth = [[self centerCoordinate] azimuth]; 460 | double pointAzimuth = [coordinate azimuth]; 461 | BOOL isBetweenNorth = NO; 462 | double deltaAzimith = [self findDeltaOfRadianCenter: ¤tAzimuth coordinateAzimuth:pointAzimuth betweenNorth:&isBetweenNorth]; 463 | 464 | if ((pointAzimuth > currentAzimuth && !isBetweenNorth) || 465 | (currentAzimuth > degreesToRadian(360- degreeRange) && pointAzimuth < degreesToRadian(degreeRange))) { 466 | point.x = (realityBounds.size.width / 2) + ((deltaAzimith / degreesToRadian(1)) * ADJUST_BY); // Right side of Azimuth 467 | } 468 | else 469 | point.x = (realityBounds.size.width / 2) - ((deltaAzimith / degreesToRadian(1)) * ADJUST_BY); // Left side of Azimuth 470 | 471 | float radialDistanceKm = coordinate.radialDistance / 1000; 472 | float yFactor = radialDistanceKm / self.radarRange; 473 | float ySpan = realityBounds.size.height / 4; 474 | //point.y = (realityBounds.size.height / 2) + (radianToDegrees(M_PI_2 + viewAngle) * 2.0); 475 | point.y = (realityBounds.size.height / 2) - yFactor * ySpan; 476 | 477 | return point; 478 | } 479 | 480 | - (void)updateLocations { 481 | 482 | NSMutableArray *radarPointValues = [[NSMutableArray alloc] initWithCapacity:[self.coordinates count]]; 483 | 484 | for (ARGeoCoordinate *item in [self coordinates]) { 485 | 486 | UIView *markerView = [item displayView]; 487 | 488 | if ([self shouldDisplayCoordinate:item]) { 489 | 490 | CGPoint loc = [self pointForCoordinate:item]; 491 | CGFloat scaleFactor = SCALE_FACTOR; 492 | 493 | if ([self scaleViewsBasedOnDistance]) { 494 | scaleFactor = scaleFactor - [self minimumScaleFactor]*([item radialDistance] / [self maximumScaleDistance]); 495 | } 496 | 497 | float width = [markerView bounds].size.width * scaleFactor; 498 | float height = [markerView bounds].size.height * scaleFactor; 499 | 500 | [markerView setFrame:CGRectMake(loc.x - width / 2.0, loc.y, width, height)]; 501 | [markerView setNeedsDisplay]; 502 | 503 | CATransform3D transform = CATransform3DIdentity; 504 | 505 | // Set the scale if it needs it. Scale the perspective transform if we have one. 506 | if ([self scaleViewsBasedOnDistance]) 507 | transform = CATransform3DScale(transform, scaleFactor, scaleFactor, scaleFactor); 508 | 509 | if ([self rotateViewsBasedOnPerspective]) { 510 | transform.m34 = 1.0 / 300.0; 511 | /* 512 | double itemAzimuth = [item azimuth]; 513 | double centerAzimuth = [[self centerCoordinate] azimuth]; 514 | 515 | if (itemAzimuth - centerAzimuth > M_PI) 516 | centerAzimuth += M_2PI; 517 | 518 | if (itemAzimuth - centerAzimuth < -M_PI) 519 | itemAzimuth += M_2PI; 520 | */ 521 | // double angleDifference = itemAzimuth - centerAzimuth; 522 | // transform = CATransform3DRotate(transform, [self maximumRotationAngle] * angleDifference / 0.3696f , 0, 1, 0); 523 | } 524 | [[markerView layer] setTransform:transform]; 525 | 526 | //if marker is not already set then insert it 527 | if (!([markerView superview])) { 528 | [[self displayView] insertSubview:markerView atIndex:1]; 529 | } 530 | }else { 531 | if([markerView superview]){ 532 | [markerView removeFromSuperview]; 533 | } 534 | } 535 | 536 | [radarPointValues addObject:item]; 537 | 538 | } 539 | 540 | if(_showsRadar){ 541 | _radarView.pois = radarPointValues; 542 | _radarView.radius = _radarRange; 543 | [_radarView setNeedsDisplay]; 544 | } 545 | } 546 | 547 | - (NSComparisonResult)LocationSortClosestFirst:(ARCoordinate *)s1 secondCoord:(ARCoordinate*)s2{ 548 | 549 | if ([s1 radialDistance] < [s2 radialDistance]) 550 | return NSOrderedAscending; 551 | else if ([s1 radialDistance] > [s2 radialDistance]) 552 | return NSOrderedDescending; 553 | else 554 | return NSOrderedSame; 555 | } 556 | 557 | #pragma mark - 558 | #pragma mark Device Orientation 559 | 560 | - (void)currentDeviceOrientation { 561 | UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; 562 | 563 | if (orientation != UIDeviceOrientationUnknown && orientation != UIDeviceOrientationFaceUp && orientation != UIDeviceOrientationFaceDown) { 564 | switch (orientation) { 565 | case UIDeviceOrientationLandscapeLeft: 566 | cameraOrientation = AVCaptureVideoOrientationLandscapeRight; 567 | break; 568 | case UIDeviceOrientationLandscapeRight: 569 | cameraOrientation = AVCaptureVideoOrientationLandscapeLeft; 570 | break; 571 | case UIDeviceOrientationPortraitUpsideDown: 572 | cameraOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 573 | break; 574 | case UIDeviceOrientationPortrait: 575 | cameraOrientation = AVCaptureVideoOrientationPortrait; 576 | break; 577 | default: 578 | break; 579 | } 580 | } 581 | } 582 | 583 | - (void)deviceOrientationDidChange:(NSNotification *)notification { 584 | 585 | prevHeading = HEADING_NOT_SET; 586 | 587 | [self currentDeviceOrientation]; 588 | 589 | [[self previewLayer].connection setVideoOrientation:cameraOrientation]; 590 | 591 | UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; 592 | 593 | CGRect newFrame = [[UIScreen mainScreen] bounds]; 594 | 595 | [previewLayer setFrame:[self.cameraView bounds]]; 596 | 597 | if ([previewLayer.connection isVideoOrientationSupported]) { 598 | [previewLayer.connection setVideoOrientation:cameraOrientation]; 599 | } 600 | 601 | [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; 602 | 603 | //Last but not least we need to move the radar if we are displaying one 604 | if(_radarViewPort && _radarView) 605 | { 606 | int radarSize = 2 * RADIUS + 1; 607 | int margin = 4; 608 | [radarNorthLabel setFrame:CGRectMake(newFrame.size.width - RADIUS - 11, margin + RADAR_TOP_MARGIN + 3, 10, 10)]; 609 | [_radarView setFrame:CGRectMake(newFrame.size.width - radarSize - margin, margin + RADAR_TOP_MARGIN, radarSize, radarSize)]; 610 | [_radarViewPort setFrame:CGRectMake(newFrame.size.width - radarSize - margin, margin + RADAR_TOP_MARGIN, radarSize, radarSize)]; 611 | } 612 | } 613 | 614 | @end 615 | --------------------------------------------------------------------------------