├── src ├── app │ ├── app.html │ ├── main.ts │ ├── app.component.ts │ ├── app.module.ts │ └── app.scss ├── assets │ └── icon │ │ └── favicon.ico ├── manifest.json ├── declarations.d.ts ├── service-worker.js ├── pages │ └── home │ │ ├── home.scss │ │ ├── home.html │ │ └── home.ts ├── index.html ├── theme │ └── variables.scss └── providers │ ├── geolocation-service.ts │ ├── accelerometer-service.ts │ ├── compass-service.ts │ ├── google-place-service.ts │ └── google-map-service.ts ├── resources ├── icon.png ├── splash.png ├── ios │ ├── icon │ │ ├── icon.png │ │ ├── icon-40.png │ │ ├── icon-50.png │ │ ├── icon-60.png │ │ ├── icon-72.png │ │ ├── icon-76.png │ │ ├── icon@2x.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-50@2x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72@2x.png │ │ ├── icon-76@2x.png │ │ ├── icon-small.png │ │ ├── icon-83.5@2x.png │ │ ├── icon-small@2x.png │ │ └── icon-small@3x.png │ └── splash │ │ ├── Default-667h.png │ │ ├── Default-736h.png │ │ ├── Default~iphone.png │ │ ├── Default@2x~iphone.png │ │ ├── Default-568h@2x~iphone.png │ │ ├── Default-Landscape-736h.png │ │ ├── Default-Landscape~ipad.png │ │ ├── Default-Portrait~ipad.png │ │ ├── Default-Portrait@2x~ipad.png │ │ └── Default-Landscape@2x~ipad.png └── android │ ├── icon │ ├── drawable-hdpi-icon.png │ ├── drawable-ldpi-icon.png │ ├── drawable-mdpi-icon.png │ ├── drawable-xhdpi-icon.png │ ├── drawable-xxhdpi-icon.png │ └── drawable-xxxhdpi-icon.png │ └── splash │ ├── drawable-land-hdpi-screen.png │ ├── drawable-land-ldpi-screen.png │ ├── drawable-land-mdpi-screen.png │ ├── drawable-port-hdpi-screen.png │ ├── drawable-port-ldpi-screen.png │ ├── drawable-port-mdpi-screen.png │ ├── drawable-land-xhdpi-screen.png │ ├── drawable-land-xxhdpi-screen.png │ ├── drawable-land-xxxhdpi-screen.png │ ├── drawable-port-xhdpi-screen.png │ ├── drawable-port-xxhdpi-screen.png │ └── drawable-port-xxxhdpi-screen.png ├── ionic.config.json ├── tslint.json ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── package.json ├── README.md └── config.xml /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/icon.png -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/splash.png -------------------------------------------------------------------------------- /resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon.png -------------------------------------------------------------------------------- /src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-ar-placement", 3 | "app_id": "", 4 | "v2": true, 5 | "typescript": true 6 | } 7 | -------------------------------------------------------------------------------- /resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-40@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-83.5@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-hdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-hdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-ldpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-ldpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-mdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-mdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-xhdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-xxhdpi-icon.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xxxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/icon/drawable-xxxhdpi-icon.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-land-xxxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FazioNico/ionic-ar-placement/HEAD/resources/android/splash/drawable-port-xxxhdpi-screen.png -------------------------------------------------------------------------------- /src/app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-duplicate-variable": true, 4 | "no-unused-variable": [ 5 | true 6 | ] 7 | }, 8 | "rulesDirectory": [ 9 | "node_modules/tslint-eslint-rules/dist/rules" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ionic", 3 | "short_name": "Ionic", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "icons": [{ 7 | "src": "assets/imgs/logo.png", 8 | "sizes": "512x512", 9 | "type": "image/png" 10 | }], 11 | "background_color": "#4e8ef7", 12 | "theme_color": "#4e8ef7" 13 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # We recommend you to keep these unchanged 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": [ 8 | "dom", 9 | "es2015" 10 | ], 11 | "module": "es2015", 12 | "moduleResolution": "node", 13 | "sourceMap": true, 14 | "target": "es5" 15 | }, 16 | "include": [ 17 | "src/**/*.ts" 18 | ], 19 | "exclude": [ 20 | "node_modules" 21 | ], 22 | "compileOnSave": false, 23 | "atom": { 24 | "rewriteTsconfig": false 25 | } 26 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | *.log 7 | *.tmp 8 | *.tmp.* 9 | log.txt 10 | *.sublime-project 11 | *.sublime-workspace 12 | .vscode/ 13 | npm-debug.log* 14 | 15 | .idea/ 16 | .sass-cache/ 17 | .tmp/ 18 | .versions/ 19 | coverage/ 20 | dist/ 21 | node_modules/ 22 | tmp/ 23 | temp/ 24 | hooks/ 25 | platforms/ 26 | plugins/ 27 | plugins/android.json 28 | plugins/ios.json 29 | www/ 30 | $RECYCLE.BIN/ 31 | 32 | .DS_Store 33 | Thumbs.db 34 | UserInterfaceState.xcuserstate 35 | 36 | *-config.ts 37 | -------------------------------------------------------------------------------- /src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Declaration files are how the Typescript compiler knows about the type information(or shape) of an object. 3 | They're what make intellisense work and make Typescript know all about your code. 4 | 5 | A wildcard module is declared below to allow third party libraries to be used in an app even if they don't 6 | provide their own type declarations. 7 | 8 | To learn more about using third party libraries in an Ionic app, check out the docs here: 9 | http://ionicframework.com/docs/v2/resources/third-party-libs/ 10 | 11 | For more info on type definition files, check out the Typescript docs here: 12 | https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html 13 | */ 14 | declare module '*'; -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for 3 | * more info on how to use sw-toolbox to custom configure your service worker. 4 | */ 5 | 6 | 7 | 'use strict'; 8 | importScripts('./build/sw-toolbox.js'); 9 | 10 | self.toolbox.options.cache = { 11 | name: 'ionic-cache' 12 | }; 13 | 14 | // pre-cache our key assets 15 | self.toolbox.precache( 16 | [ 17 | './build/main.js', 18 | './build/main.css', 19 | './build/polyfills.js', 20 | 'index.html', 21 | 'manifest.json' 22 | ] 23 | ); 24 | 25 | // dynamically cache any other local assets 26 | self.toolbox.router.any('/*', self.toolbox.cacheFirst); 27 | 28 | // for any other requests go to the network, cache, 29 | // and then only use that cached resource if your user goes offline 30 | self.toolbox.router.default = self.toolbox.networkFirst; -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 05-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 06-02-2017 7 | */ 8 | 9 | import { Component } from '@angular/core'; 10 | import { Platform } from 'ionic-angular'; 11 | import { StatusBar, Splashscreen } from 'ionic-native'; 12 | 13 | import { HomePage } from '../pages/home/home'; 14 | 15 | 16 | @Component({ 17 | templateUrl: 'app.html' 18 | }) 19 | export class MyApp { 20 | rootPage = HomePage; 21 | 22 | constructor(platform: Platform) { 23 | platform.ready().then(() => { 24 | // Okay, so the platform is ready and our plugins are available. 25 | // Here you can do any higher level native things you might need. 26 | StatusBar.styleDefault(); 27 | Splashscreen.hide(); 28 | 29 | }); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/home/home.scss: -------------------------------------------------------------------------------- 1 | // @Author: Nicolas Fazio 2 | // @Date: 05-02-2017 3 | // @Email: contact@nicolasfazio.ch 4 | // @Last modified by: webmaster-fazio 5 | // @Last modified time: 17-02-2017 6 | 7 | page-home { 8 | 9 | #topView, #arView { 10 | 11 | } 12 | #arView{ 13 | padding:30px 0; 14 | height:70px; 15 | text-align:center 16 | } 17 | ion-badge { 18 | padding: 8px 15px; 19 | font-size: 2rem; 20 | } 21 | .spotsList { 22 | ion-badge { 23 | padding: 8px 15px; 24 | font-size: 1.4rem; 25 | font-weight: 300; 26 | margin-top:5px; 27 | transition: 250ms ease; 28 | } 29 | } 30 | .gMap { 31 | position: absolute; 32 | top:0px; 33 | left:0px; 34 | min-height: 250px; 35 | width: 100%; 36 | 37 | .gm-style-mtc, .gm-svpc, .gm-style-cc{ 38 | display: none; 39 | } 40 | .gm-style .gm-style-iw { 41 | color: #333; 42 | } 43 | } 44 | } 45 | page-home, .ios .ion-page.show-page ~ .nav-decor { 46 | background-color: transparent !important; 47 | } 48 | #ezarpage { 49 | background-color: transparent !important; 50 | color: white !important; 51 | } 52 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 05-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 15-02-2017 7 | */ 8 | 9 | import { NgModule, ErrorHandler } from '@angular/core'; 10 | import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; 11 | import { MyApp } from './app.component'; 12 | 13 | import { HomePage } from '../pages/home/home'; 14 | 15 | import { AccelerometerService } from '../providers/accelerometer-service'; 16 | import { CompassService } from '../providers/compass-service'; 17 | import { GeolocationService } from '../providers/geolocation-service'; 18 | import { GoogleMapService } from '../providers/google-map-service'; 19 | import { GooglePlaceService } from '../providers/google-place-service'; 20 | 21 | @NgModule({ 22 | declarations: [ 23 | MyApp, 24 | HomePage 25 | ], 26 | imports: [ 27 | IonicModule.forRoot(MyApp) 28 | ], 29 | bootstrap: [IonicApp], 30 | entryComponents: [ 31 | MyApp, 32 | HomePage 33 | ], 34 | providers: [ 35 | {provide: ErrorHandler, useClass: IonicErrorHandler}, 36 | AccelerometerService, 37 | CompassService, 38 | GeolocationService, 39 | GoogleMapService, 40 | GooglePlaceService 41 | ] 42 | }) 43 | export class AppModule {} 44 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ionic App 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-ar-placement", 3 | "author": "Nicolas Fazio", 4 | "homepage": "http://nicolasfazion.ch/", 5 | "version": "0.0.1", 6 | "private": true, 7 | "scripts": { 8 | "clean": "ionic-app-scripts clean", 9 | "build": "ionic-app-scripts build", 10 | "ionic:build": "ionic-app-scripts build", 11 | "ionic:serve": "ionic-app-scripts serve" 12 | }, 13 | "dependencies": { 14 | "@angular/common": "2.2.1", 15 | "@angular/compiler": "2.2.1", 16 | "@angular/compiler-cli": "2.2.1", 17 | "@angular/core": "2.2.1", 18 | "@angular/forms": "2.2.1", 19 | "@angular/http": "2.2.1", 20 | "@angular/platform-browser": "2.2.1", 21 | "@angular/platform-browser-dynamic": "2.2.1", 22 | "@angular/platform-server": "2.2.1", 23 | "@ionic/storage": "1.1.7", 24 | "ionic-angular": "2.0.0", 25 | "ionic-native": "2.4.1", 26 | "ionicons": "3.0.0", 27 | "rxjs": "5.0.0-beta.12", 28 | "zone.js": "0.6.26", 29 | "sw-toolbox": "3.4.0" 30 | }, 31 | "devDependencies": { 32 | "@ionic/app-scripts": "1.0.0", 33 | "typescript": "2.0.9" 34 | }, 35 | "cordovaPlugins": [ 36 | "cordova-plugin-whitelist", 37 | "cordova-plugin-console", 38 | "cordova-plugin-statusbar", 39 | "cordova-plugin-device", 40 | "cordova-plugin-splashscreen", 41 | "ionic-plugin-keyboard" 42 | ], 43 | "cordovaPlatforms": [ 44 | "ios", 45 | { 46 | "platform": "ios", 47 | "version": "", 48 | "locator": "ios" 49 | } 50 | ], 51 | "description": "ionic-ar-placement: An Ionic project" 52 | } 53 | -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- 1 | // @Author: Nicolas Fazio 2 | // @Date: 05-02-2017 3 | // @Email: contact@nicolasfazio.ch 4 | // @Last modified by: webmaster-fazio 5 | // @Last modified time: 13-02-2017 6 | 7 | // http://ionicframework.com/docs/v2/theming/ 8 | 9 | 10 | // App Global Sass 11 | // -------------------------------------------------- 12 | // Put style rules here that you want to apply globally. These 13 | // styles are for the entire app and not just one component. 14 | // Additionally, this file can be also used as an entry point 15 | // to import other Sass files to be included in the output CSS. 16 | // 17 | // Shared Sass variables, which can be used to adjust Ionic's 18 | // default Sass variables, belong in "theme/variables.scss". 19 | // 20 | // To declare rules for a specific mode, create a child rule 21 | // for the .md, .ios, or .wp mode classes. The mode class is 22 | // automatically applied to the element in the app. 23 | 24 | //enable ezAR video to show through element 25 | body { 26 | background-color: transparent; 27 | } 28 | 29 | //enable ezAR video to show through ionic app's root component 30 | //hack: prevents theme specific overriding of background-color 31 | ion-app { 32 | background-color: transparent !important; 33 | } 34 | 35 | .log {text-shadow:0 1px #666} 36 | #debug {border:1px solid #999;display:none} 37 | 38 | // animation times 39 | .a250 { 40 | transition: 250ms ease; 41 | } 42 | .a500 { 43 | transition: 500ms ease; 44 | } 45 | .a1000 { 46 | transition: 1000ms ease; 47 | } 48 | // animation effects 49 | .fadeIn { 50 | opacity: 1; 51 | } 52 | .fadeOut { 53 | opacity: 0; 54 | } 55 | -------------------------------------------------------------------------------- /src/pages/home/home.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 |
11 |
12 | {{ userDirection }} 13 | ... 14 |
15 |
16 |
17 |
18 | 19 | {{ spot.pin.name }} - {{ spot.away.meter.toFixed(5) }} 20 | 21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 |
Geolocation
36 |
37 |
Compass
38 |
39 |
Accelerometer
40 |
41 | Acceleration X: {{acceleration.x}}
42 | Acceleration Y: {{acceleration.y}}
43 | 'Acceleration Z: {{acceleration.z}} 44 |
45 |
Log
46 |
47 |
48 | 49 |
50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | # Ionic 2 AR Geo-located 10 | Using augmented reality in a Ionic 2 Framework multi-platform application to display Google Places API pins around user. 11 | 12 | ## Overview 13 | This application using [ezAR](https://www.ezartech.com/) to display a Google Places API geo coordonnates point list around user in a augmented reality frame. 14 | 15 | Each geo coordonnates point will display on screen only if the mobile's user is oriented in the good direction. 16 | 17 | This app is a integration of this ezAR [cities project](https://github.com/ezartech/ezar-cities), into a Ionic 2 Framework 18 | 19 | ## Screenshot 20 | 21 | 22 | ## Prerequisites 23 | - NVM - [Download](https://github.com/creationix/nvm) & Install Node Version Manage 24 | - NodeJS v7 - Download & Install Node.js and the npm package manager with NVM `$ nvm install node 7`. 25 | - [Typescript](https://www.npmjs.com/package/typescript) Latest stable version install in Global `$ npm install -g typescript` 26 | - [Ionic 2](https://ionicframework.com/) & [Cordova](https://cordova.apache.org/) - Latest stable version install in Global `$ npm install -g ionic cordova` 27 | - And you should also have git installed to a better working flow. 28 | 29 | 30 | ## Quick start 31 | - open this project in your IDE and install all node_modules from IDE CLI `$ nvm use 7.2`, `$ npm install` 32 | - run project (server+client side) with `$ ionic build ios` 33 | - enable geolocation on your IOS device 34 | - then deploy on your IOS device with Xcode 35 | - run application 36 | 37 | ## About author 38 | Hi, i'm a Front-end developper living in Geneva Switzerland and i build hybrid mobile & web applications for almost 15 years. You can follow me on Twitter @FazioNico or checkout my own website http://nicolasfazio.ch 39 | -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 1 | // Ionic Variables and Theming. For more info, please see: 2 | // http://ionicframework.com/docs/v2/theming/ 3 | $font-path: "../assets/fonts"; 4 | 5 | @import "ionic.globals"; 6 | 7 | 8 | // Shared Variables 9 | // -------------------------------------------------- 10 | // To customize the look and feel of this app, you can override 11 | // the Sass variables found in Ionic's source scss files. 12 | // To view all the possible Ionic variables, see: 13 | // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/ 14 | 15 | 16 | 17 | 18 | // Named Color Variables 19 | // -------------------------------------------------- 20 | // Named colors makes it easy to reuse colors on various components. 21 | // It's highly recommended to change the default colors 22 | // to match your app's branding. Ionic uses a Sass map of 23 | // colors so you can add, rename and remove colors as needed. 24 | // The "primary" color is the only required color in the map. 25 | 26 | $colors: ( 27 | primary: #387ef5, 28 | secondary: #32db64, 29 | danger: #f53d3d, 30 | light: #f4f4f4, 31 | dark: #222 32 | ); 33 | 34 | 35 | // App iOS Variables 36 | // -------------------------------------------------- 37 | // iOS only Sass variables can go here 38 | 39 | 40 | 41 | 42 | // App Material Design Variables 43 | // -------------------------------------------------- 44 | // Material Design only Sass variables can go here 45 | 46 | 47 | 48 | 49 | // App Windows Variables 50 | // -------------------------------------------------- 51 | // Windows only Sass variables can go here 52 | 53 | 54 | 55 | 56 | // App Theme 57 | // -------------------------------------------------- 58 | // Ionic apps can have different themes applied, which can 59 | // then be future customized. This import comes last 60 | // so that the above variables are used and Ionic's 61 | // default are overridden. 62 | 63 | @import "ionic.theme.default"; 64 | 65 | 66 | // Ionicons 67 | // -------------------------------------------------- 68 | // The premium icon font for Ionic. For more info, please see: 69 | // http://ionicframework.com/docs/v2/ionicons/ 70 | 71 | @import "ionic.ionicons"; 72 | 73 | 74 | // Fonts 75 | // -------------------------------------------------- 76 | 77 | @import "roboto"; 78 | @import "noto-sans"; 79 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | ionic-ar-placement 12 | An awesome Ionic/Cordova app. 13 | Ionic Framework Team 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/providers/geolocation-service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 06-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 17-02-2017 7 | */ 8 | 9 | import { Injectable, EventEmitter } from '@angular/core'; 10 | import { Geolocation, Geoposition, PositionError } from 'ionic-native'; 11 | 12 | /* 13 | Generated class for the GeolocationService provider. 14 | 15 | See https://angular.io/docs/ts/latest/guide/dependency-injection.html 16 | for more info on providers and Angular 2 DI. 17 | */ 18 | @Injectable() 19 | export class GeolocationService extends EventEmitter { 20 | 21 | watchGeoID:any; 22 | myLat:number; 23 | myLng:number; 24 | 25 | constructor() { 26 | super() 27 | } 28 | 29 | startGeolocation():void { 30 | Geolocation.getCurrentPosition().then(pos => { 31 | this.watchGeoID = Geolocation.watchPosition(); 32 | this.watchGeoID.subscribe( 33 | (data:Geoposition) => { 34 | // data can be a set of coordinates, or an error (if an error occurred). 35 | // data.coords.latitude 36 | // data.coords.longitude 37 | this.onGeoSuccess(data) 38 | }, 39 | (err:PositionError) => this.onGeoError('startGeolocation Error: location not available') 40 | ); 41 | }).catch(err=> {this.onGeoError('startGeolocation Error: location not available')}); 42 | 43 | 44 | } 45 | 46 | // Stop watching the geolocation 47 | stopGeolocation():void { 48 | if (this.watchGeoID) { 49 | this.watchGeoID.unsubscribe() 50 | this.watchGeoID = null; 51 | } 52 | } 53 | 54 | // onSuccess: Get the current location 55 | onGeoSuccess(position:Geoposition):void { 56 | if(!position.coords) { 57 | this.onGeoError('No position find'); 58 | return 59 | } 60 | //console.log('geolocalisation success', position) 61 | this.myLat = position.coords.latitude; 62 | this.myLng = position.coords.longitude; 63 | // emit coordinates 64 | this.emit({ 65 | position: {lat:this.myLat, lng: this.myLng} 66 | }); 67 | } 68 | 69 | // onError: Failed to get the location 70 | onGeoError(err:any):void { 71 | console.log(err) 72 | this.emit({ 73 | error: err 74 | }); 75 | if(this.watchGeoID){ 76 | this.stopGeolocation() // TODO: check if trow error on enable 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/providers/accelerometer-service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 06-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 19-02-2017 7 | */ 8 | 9 | import { Injectable, EventEmitter } from '@angular/core'; 10 | import { DeviceMotion, DeviceMotionAccelerationData } from 'ionic-native'; 11 | 12 | /* 13 | Generated class for the AccelerometerService provider. 14 | 15 | See https://angular.io/docs/ts/latest/guide/dependency-injection.html 16 | for more info on providers and Angular 2 DI. 17 | */ 18 | @Injectable() 19 | export class AccelerometerService extends EventEmitter { 20 | 21 | watchAccelerometerID:any; 22 | 23 | constructor() { 24 | super() 25 | } 26 | 27 | // Start checking the accelerometer 28 | detectAccelerometer():void { 29 | // Get the device current acceleration 30 | let accelerationReady:boolean = false; 31 | DeviceMotion.getCurrentAcceleration() 32 | .then( 33 | (acceleration: DeviceMotionAccelerationData) => { 34 | accelerationReady = true 35 | //console.log('getCurrentAcceleration-> ',acceleration) 36 | }, 37 | (error: any) => { 38 | this.onAccelerometerError(error) 39 | } 40 | ) 41 | .then(_=>{ 42 | if(accelerationReady === true ){ 43 | this.startAccelerometer(); 44 | } 45 | else { 46 | this.onAccelerometerError('getCurrentAcceleration') 47 | } 48 | }); 49 | } 50 | 51 | startAccelerometer():void{ 52 | // Watch device acceleration 53 | this.watchAccelerometerID = DeviceMotion.watchAcceleration() 54 | .subscribe( 55 | (acceleration: DeviceMotionAccelerationData) => { 56 | this.onAccelerometerSuccess(acceleration) 57 | }, 58 | err => { 59 | this.onAccelerometerError(err) 60 | } 61 | ); 62 | } 63 | 64 | // Stop checking the accelerometer 65 | stopAccelerometer():void { 66 | if (this.watchAccelerometerID) { 67 | this.watchAccelerometerID.unsubscribe(); 68 | this.watchAccelerometerID = null; 69 | } 70 | } 71 | 72 | // onSuccess: Get current accelerometer values 73 | onAccelerometerSuccess(acceleration:DeviceMotionAccelerationData):void { 74 | //console.log('watchAcceleration-> ',acceleration); 75 | this.emit(acceleration); 76 | } 77 | 78 | // onError: Failed to get the acceleration 79 | onAccelerometerError(err):void { 80 | console.log('onAccelerometerError-> ',err); 81 | this.emit('Error: getCurrentAcceleration'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/providers/compass-service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 06-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 19-02-2017 7 | */ 8 | 9 | import { Injectable, EventEmitter } from '@angular/core'; 10 | import { DeviceOrientation, DeviceOrientationCompassHeading } from 'ionic-native'; 11 | 12 | import { Observable } from 'rxjs/Observable'; 13 | /* 14 | Generated class for the CompassService provider. 15 | 16 | See https://angular.io/docs/ts/latest/guide/dependency-injection.html 17 | for more info on providers and Angular 2 DI. 18 | */ 19 | @Injectable() 20 | export class CompassService extends EventEmitter { 21 | 22 | directions:string[] = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']; 23 | watchCompassID:Observable; 24 | 25 | constructor() { 26 | super() 27 | } 28 | // Start watching the compass 29 | startCompass():void { 30 | // Get the device current compass heading 31 | DeviceOrientation.getCurrentHeading() 32 | .then( 33 | (data: DeviceOrientationCompassHeading) => { 34 | console.log('getCurrentHeading',data) 35 | if(!data.magneticHeading){ 36 | this.onCompassError('cordova not available') 37 | } 38 | this.watchCompass(); 39 | }, 40 | (error: any) => this.onCompassError('cordova not available') 41 | ) 42 | .catch(err => this.onCompassError('CurrentHeading not available')); 43 | } 44 | 45 | // Watch the device compass heading change 46 | watchCompass():void{ 47 | this.watchCompassID = DeviceOrientation.watchHeading() 48 | this.watchCompassID.subscribe( 49 | (data: DeviceOrientationCompassHeading) => { 50 | this.onCompassSuccess(data) 51 | }, 52 | err => { 53 | this.onCompassError(err) 54 | } 55 | ); 56 | } 57 | // Stop watching the compass 58 | stopCompass():void { 59 | if (this.watchCompassID) { 60 | //this.watchCompassID.unsubscribe(); 61 | this.watchCompassID = null; 62 | } 63 | } 64 | 65 | // Compass onSuccess: Get the current heading 66 | onCompassSuccess(heading:DeviceOrientationCompassHeading):void { 67 | //console.log('compass success: heading-> ', heading) 68 | let magneticDataReady:any = heading.magneticHeading / 45; 69 | let direction:string = this.directions[Math.abs(parseInt(magneticDataReady) + 1)]; 70 | let degree:number = heading.magneticHeading; 71 | 72 | this.emit({ 73 | success: true, 74 | direction:direction, 75 | degree: degree 76 | }); 77 | 78 | } 79 | 80 | // Compass onError: Get the current error 81 | onCompassError(error):void{ 82 | console.log('Error startCompass-> ', error) 83 | this.emit({error:'Error: startCompass DeviceOrientationCompassHeading'}); 84 | this.stopCompass() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/providers/google-place-service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 14-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 19-02-2017 7 | */ 8 | 9 | import { Injectable } from '@angular/core'; 10 | import { GPLACE_API_KEY } from './apikey-config'; 11 | 12 | declare var google; 13 | 14 | /* 15 | Generated class for the GoogleMapService provider. 16 | 17 | See https://angular.io/docs/ts/latest/guide/dependency-injection.html 18 | for more info on providers and Angular 2 DI. 19 | */ 20 | @Injectable() 21 | export class GooglePlaceService { 22 | 23 | apiKey:string = GPLACE_API_KEY || ''; // add you own API KEY 24 | apiEnpoint:Object = { 25 | nearbysearch: 'nearbySearch', 26 | textsearch: 'textSearch', 27 | radarSearch: 'radarsearch' 28 | } 29 | service:any; 30 | 31 | constructor() { 32 | } 33 | 34 | /* 35 | Use params like this: 36 | let parmUrl = { 37 | location: { 38 | lat: -33.8670522, 39 | lng: 151.1957362 40 | }, 41 | radius: '500' 42 | type: 'food', 43 | name: 'cruise' 44 | } 45 | getData('nearbysearch', parmUrl) 46 | */ 47 | getData(url:string,parmUrl:any):Promise{ 48 | 49 | // let paramsReady:string = Object.keys(parmUrl).map((key)=>{ 50 | // return `${encodeURIComponent(key)}=${encodeURIComponent(parmUrl[key])}`; 51 | // }).join('&'); 52 | 53 | this.service = new google.maps.places.PlacesService(document.createElement('div')); 54 | let position:any = new google.maps.LatLng(parmUrl.location.lat,parmUrl.location.lng); 55 | let request:Object = { 56 | location: position, 57 | radius: parmUrl.radius 58 | }; 59 | console.log('request->', url, request, parmUrl) 60 | // return promise 61 | return new Promise((resolve,reject)=>{ 62 | this.service[this.apiEnpoint[url]](request, (results,status) =>{ 63 | if (status == google.maps.places.PlacesServiceStatus.OK) { 64 | // resolve promise with results on OK status 65 | // formate place element with geoposition 66 | let places:Object[] = []; 67 | results.map(place => { 68 | place.lat = place.geometry.location.lat(), 69 | place.lng = place.geometry.location.lng() 70 | return place; 71 | }) 72 | .filter( place => { 73 | // remove place city name 74 | return place.types.indexOf("locality") === -1; 75 | }) 76 | .filter( place => { 77 | // remove place sub locality name 78 | return place.types.indexOf('sublocality') === -1; 79 | }) 80 | .map(place => places.push(place)) 81 | resolve(places); 82 | }else { 83 | // reject promise otherwise 84 | reject(status); 85 | } 86 | }); 87 | }); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/providers/google-map-service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 06-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 19-02-2017 7 | */ 8 | 9 | import { Injectable, EventEmitter } from '@angular/core'; 10 | import { ElementRef } from '@angular/core'; 11 | import { GMAP_API_KEY } from './apikey-config'; 12 | 13 | declare var google; 14 | 15 | /* 16 | Generated class for the GoogleMapService provider. 17 | 18 | See https://angular.io/docs/ts/latest/guide/dependency-injection.html 19 | for more info on providers and Angular 2 DI. 20 | */ 21 | @Injectable() 22 | export class GoogleMapService extends EventEmitter { 23 | 24 | apiKey:string = GMAP_API_KEY || ''; // add you own API KEY 25 | mapInitialised:boolean = false; 26 | markersArray:any[] = []; 27 | bounds:any; 28 | map:any; 29 | infoWindow:any; 30 | gpsUserMarker:any; 31 | gmapEnable: boolean = false; 32 | 33 | constructor() { 34 | super() 35 | } 36 | 37 | /* Google Map loading & Initiallisation */ 38 | loadGoogleMap():void{ 39 | //this.addConnectivityListeners(); 40 | if(typeof google == "undefined" || typeof google.maps == "undefined"){ 41 | //console.log("Google maps JavaScript needs to be loaded."); 42 | if(navigator.onLine === true){ 43 | //console.log("online, loading map"); 44 | //Load the SDK with the callback 45 | window['mapInit'] = () => { 46 | this.initMap(); 47 | } 48 | let script:HTMLScriptElement = document.createElement("script"); 49 | script.id = "googleMaps"; 50 | script.async = true; 51 | if(this.apiKey){ 52 | script.src = 'http://maps.google.com/maps/api/js?key=' + this.apiKey + '&libraries=places&callback=mapInit'; 53 | } else { 54 | script.src = 'http://maps.google.com/maps/api/js?callback=mapInit'; 55 | } 56 | document.body.appendChild(script); 57 | } 58 | } 59 | else { 60 | if(navigator.onLine === true){ 61 | //console.log("map ready"); 62 | this.initMap(); 63 | } 64 | else { 65 | this.disableMap(); 66 | } 67 | } 68 | } 69 | 70 | /* Events Connectivity listener for Google Map */ 71 | addConnectivityListeners():void{ 72 | let onOnline = () => { 73 | setTimeout(()=> { 74 | if(typeof google == "undefined" || typeof google.maps == "undefined"){ 75 | this.loadGoogleMap(); 76 | } 77 | else { 78 | if(!this.mapInitialised){ 79 | this.initMap(); 80 | } 81 | } 82 | }, 1000); 83 | }; 84 | let onOffline = ()=> { 85 | this.disableMap(); 86 | }; 87 | document.addEventListener('online', _=> onOnline, false); 88 | document.addEventListener('offline', _=> onOffline, false); 89 | } 90 | 91 | /* Google Map Core Methodes */ 92 | disableMap():void{ 93 | setTimeout(()=>{ 94 | console.log('google API disable-> ', google) 95 | this.emit({ 96 | result: false, 97 | message: 'google Map API disable' 98 | }) 99 | },100) 100 | } 101 | 102 | initMap():void { 103 | this.mapInitialised = true; 104 | setTimeout(()=>{ 105 | console.log('google API init-> ', google) 106 | this.bounds = new google.maps.LatLngBounds(); 107 | this.infoWindow = new google.maps.InfoWindow(); 108 | this.emit({ 109 | result: true, 110 | message: 'google Map API init' 111 | }) 112 | },100) 113 | } 114 | 115 | // setup google maps element 116 | setupMap(coords,mapElement:ElementRef):void{ 117 | this.map = new google.maps.Map(mapElement.nativeElement, { 118 | center: {lat: coords.lat, lng: coords.lng}, 119 | zoom: 8 120 | }); 121 | mapElement.nativeElement.style.height = `${window.innerHeight}px`; 122 | this.gmapEnable = true; 123 | } 124 | 125 | // add blue gps marker for user position 126 | addUserMarker(position):void{ 127 | this.markersArray = []; 128 | this.bounds = new google.maps.LatLngBounds(); 129 | // add blue gps marker 130 | let icon:any = new google.maps.MarkerImage('http://www.google.com/intl/en_us/mapfiles/ms/micons/blue-dot.png',new google.maps.Size(30, 28),new google.maps.Point(0,0),new google.maps.Point(9, 28)); 131 | this.gpsUserMarker = new google.maps.Marker({position: new google.maps.LatLng(position.lat, position.lng), map: this.map, title: "My Position", icon:icon}); 132 | this.bounds.extend(new google.maps.LatLng(position.lat, position.lng)); 133 | this.markersArray.push(this.gpsUserMarker); 134 | } 135 | 136 | // add marker to map and in array 137 | addMarker(i:number, pin:Object):void{ 138 | let marker:any; 139 | if(pin[i].icon){ 140 | let image = { 141 | url: pin[i].icon, 142 | size: new google.maps.Size(71, 71), 143 | origin: new google.maps.Point(0, 0), 144 | anchor: new google.maps.Point(17, 34), 145 | scaledSize: new google.maps.Size(25, 25) 146 | }; 147 | marker = new google.maps.Marker({ 148 | position: new google.maps.LatLng(pin[i].lat, pin[i].lng), 149 | map: this.map, 150 | title: pin[i].name, 151 | icon: image, 152 | animation: google.maps.Animation.DROP 153 | }); 154 | } 155 | else { 156 | marker = new google.maps.Marker({ 157 | position: new google.maps.LatLng(pin[i].lat, pin[i].lng), 158 | map: this.map, 159 | title: pin[i].name, 160 | animation: google.maps.Animation.DROP 161 | }); 162 | } 163 | // add window box on click 164 | this.addOpenWindowEnvent(i,pin,marker) 165 | 166 | this.bounds.extend(new google.maps.LatLng(pin[i].lat, pin[i].lng)); 167 | this.markersArray.push(marker); 168 | 169 | // automatiquement du zoom de la carte afin que celle-ci affiche 170 | // l’ensemble des markers de la map (methode .fitBounds() de google Map API v3) 171 | this.map.fitBounds(this.bounds); 172 | } 173 | 174 | addOpenWindowEnvent(i:number, pin:Object, marker:any){ 175 | let contentString:string = ` 176 |
177 |

${pin[i].name}

178 |
179 |

${pin[i].vicinity}

180 |
`; 181 | google.maps.event.addListener(marker, 'click', ()=> { 182 | this.infoWindow.setContent(contentString); 183 | this.infoWindow.open(this.map, marker); 184 | }); 185 | } 186 | 187 | updateUserMarkerPos(position:any):void{ 188 | let newLatLng = new google.maps.LatLng(position.lat, position.lng); 189 | this.gpsUserMarker.setPosition(newLatLng); 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/pages/home/home.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Nicolas Fazio 3 | * @Date: 05-02-2017 4 | * @Email: contact@nicolasfazio.ch 5 | * @Last modified by: webmaster-fazio 6 | * @Last modified time: 19-02-2017 7 | */ 8 | 9 | import { Component, ViewChild, ElementRef } from '@angular/core'; 10 | import { NavController, Platform } from 'ionic-angular'; 11 | 12 | import { AccelerometerService } from '../../providers/accelerometer-service'; 13 | import { CompassService } from '../../providers/compass-service'; 14 | import { GeolocationService } from '../../providers/geolocation-service'; 15 | import { GoogleMapService } from '../../providers/google-map-service'; 16 | import { GooglePlaceService } from '../../providers/google-place-service'; 17 | 18 | declare var google; 19 | 20 | @Component({ 21 | selector: 'page-home', 22 | templateUrl: 'home.html' 23 | }) 24 | export class HomePage { 25 | pin:any[] = []; 26 | dataStatus:number = 0; 27 | log:any[] = []; 28 | userAcceleration:any; 29 | userLocation:any; 30 | userDirection:any; 31 | spots:any[] = []; 32 | 33 | @ViewChild('azView') azElement: ElementRef; 34 | @ViewChild('topView') topElement: ElementRef; 35 | @ViewChild('map') mapElement: ElementRef; 36 | 37 | constructor( 38 | public platform: Platform, 39 | public navCtrl: NavController, 40 | private _accelerometerService: AccelerometerService, 41 | private _compassService: CompassService, 42 | private _geolocationService: GeolocationService, 43 | private _googleMapService: GoogleMapService, 44 | private _googlePlaceService: GooglePlaceService 45 | ) { 46 | this.loadGoogleSDK(); 47 | platform.ready().then(() => { 48 | // Okay, so the platform is ready and our plugins are available. 49 | // Here you can do any higher level native things you might need. 50 | //initialize ezAR videoOverlay plugin and start back camera 51 | this.initezAR(); 52 | this.startGeolocation(); 53 | this.detectAccelerometer(); 54 | this.startCompass(); // need geoposition data ready 55 | }); 56 | } 57 | 58 | initezAR():void{ 59 | let win:any = window; 60 | if (win.ezar) { 61 | let ezar: any = win.ezar; 62 | ezar.initializeVideoOverlay( 63 | ()=> { 64 | ezar.getBackCamera().start(); 65 | }, 66 | (err)=> { 67 | //alert('unable to init ezar: ' + err); 68 | console.log('unable to init ezar: ' + err); 69 | }); 70 | } else { 71 | //alert('Unable to detect the ezAR plugin'); 72 | console.log('Unable to detect the ezAR plugin'); 73 | } 74 | } 75 | 76 | /* Bof Accelerometer Methode */ 77 | // Start checking the accelerometer 78 | detectAccelerometer():void { 79 | this._accelerometerService.detectAccelerometer() 80 | this._accelerometerService.subscribe( 81 | data => this.AccelerometerResult(data) 82 | ) 83 | } 84 | 85 | AccelerometerResult(data:any):void{ 86 | // throw error 87 | if(!data.y && !data.x && !data.y){ 88 | this.log.push("onAccelerometerError Error.") 89 | this.topElement.nativeElement.classList.add('fadeIn') 90 | this.topElement.nativeElement.classList.remove('fadeOut') 91 | 92 | this.azElement.nativeElement.classList.add('fadeOut') 93 | this.azElement.nativeElement.classList.remove('fadeIn') 94 | return; 95 | } 96 | // display result 97 | // assign data to display 98 | this.userAcceleration = data; 99 | console.log('userAcceleration-> ', data) 100 | if(this.userAcceleration.y > 5){ 101 | // stay on AR page 102 | if(this.azElement.nativeElement.className.indexOf('fadeOut') > -1){ 103 | this.azElement.nativeElement.classList.remove('fadeOut') 104 | this.azElement.nativeElement.classList.add('fadeIn') 105 | 106 | this.topElement.nativeElement.classList.remove('fadeIn') 107 | this.topElement.nativeElement.classList.add('fadeOut') 108 | } 109 | } else { 110 | // pop Google Map Page 111 | if(this.topElement.nativeElement.className.indexOf('fadeOut') > -1){ 112 | this.topElement.nativeElement.classList.remove('fadeOut') 113 | this.topElement.nativeElement.classList.add('fadeIn') 114 | 115 | this.azElement.nativeElement.classList.remove('fadeIn') 116 | this.azElement.nativeElement.classList.add('fadeOut') 117 | } 118 | } 119 | } 120 | /* Eof Accelerometer Methode */ 121 | 122 | /* Bof Compass Methode */ 123 | // Start watching the compass 124 | startCompass() { 125 | console.log('start compass') 126 | this._compassService.startCompass() 127 | this._compassService.subscribe( 128 | data => { 129 | this.compassResult(data) 130 | } 131 | ) 132 | } 133 | 134 | compassResult(data):void{ 135 | //console.log('compassResult-> ', data) 136 | if(data.error){ 137 | this.log.push("Error onCompas"); 138 | return; 139 | } 140 | // print data result 141 | this.userDirection = data.direction; 142 | // calculateDirection of each pinPoints with data.degree 143 | this.calculateDirection(data.degree); 144 | } 145 | /* Eof Compass Methode */ 146 | 147 | /* Bof - Geolocation Methode */ 148 | // startGeolocation with GeolocationService Observable 149 | startGeolocation():void{ 150 | this._geolocationService.startGeolocation() 151 | this._geolocationService.subscribe( 152 | data => { 153 | this.geolocationResult(data) 154 | }, 155 | err => { 156 | this.geolocationResult(err) 157 | } 158 | ); 159 | } 160 | 161 | // Geolocation onSuccess: Get the current location 162 | geolocationResult(data):void { 163 | //console.log('geolocationResult',data); 164 | if(!data.position) { 165 | this.log.push("Error onGeolocation") 166 | return 167 | } 168 | // test if user have move more than 5 meters 169 | if(this.userLocation){ 170 | let distance:number = this.calculateDistance(this.userLocation.position.lat, this.userLocation.position.lng, data.position.lat, data.position.lng); 171 | console.log('check distance-> ',distance ) 172 | if (distance <= 15) { 173 | // Stop load data 174 | console.log('stop load data') 175 | return; 176 | } 177 | } 178 | // asign user location to display 179 | this.userLocation = data; 180 | this.loadData(data.position) 181 | if(google){ 182 | this.loadGoogleMapData(data.position) 183 | } 184 | } 185 | /* Eof - Geolocation Methode */ 186 | 187 | /* Bof - googleMap Methode */ 188 | loadGoogleSDK():void{ 189 | this._googleMapService.loadGoogleMap() 190 | this._googleMapService.subscribe( 191 | data => { 192 | if(!data.result){ 193 | console.log('error with google map sdk -> ', data); 194 | this.log.push("Error with google map sdk"); 195 | return; 196 | } 197 | console.log('google map sdk ready-> ', data, google); 198 | let myLat = this._geolocationService.myLat 199 | let myLng = this._geolocationService.myLng 200 | if(myLat && myLng){ 201 | this.loadGoogleMapData({lat: myLat,lng: myLng}); 202 | } 203 | }, 204 | err => { 205 | console.log('error with google map sdk -> ', err); 206 | this.log.push("Error with google map sdk"); 207 | } 208 | ) 209 | } 210 | 211 | loadGoogleMapData(userPosition):void{ 212 | console.log('loadGoogleMapData') 213 | // check if map is alerady loaded and in case update user position 214 | // with updateUserMarkerPos(this.userLocation.position) 215 | // else load & set all Gmap data 216 | if(this._googleMapService.gmapEnable === false){ 217 | console.log('create user position on map-> ', this._googleMapService.gmapEnable) 218 | this._googleMapService.setupMap(userPosition,this.mapElement); // TODO need native geoposition ready 219 | this._googleMapService.addUserMarker(userPosition) // add blue gps marker for user position 220 | } 221 | else { 222 | console.log('update user position-> ', this._googleMapService.gmapEnable) 223 | this._googleMapService.updateUserMarkerPos(userPosition) 224 | } 225 | console.log('add all places position-> ', this._googleMapService.gmapEnable) 226 | for(var i=0; i< this.pin.length; i++){ 227 | this.addToDOMList(i); // add to google map page liste item 228 | this._googleMapService.addMarker(i,this.pin); // google map markers placement 229 | } 230 | } 231 | 232 | addToDOMList(i:number):void{ 233 | // TODO 234 | } 235 | /* Eof - googleMap Methode */ 236 | 237 | 238 | /* ########################### */ 239 | /* Bof - HomePage Core Methode */ 240 | calculateDirection(degree:number):void{ 241 | //console.log('calculateDirection degree-> ', degree) 242 | let detected:number = 0; 243 | this.spots = [] 244 | //document.getElementById('spot').innerHTML = ''; 245 | for(var i=0;i1500 || this.pin[i].distance.meter > 2414010){ 250 | away = this.pin[i].distance; 251 | fontSize = "16"; 252 | fontColor = "#ccc"; 253 | } else if(this.pin[i].distance.miles >500 || this.pin[i].distance.meter > 804670){ 254 | away = this.pin[i].distance; 255 | fontSize = "24"; 256 | fontColor = "#ddd"; 257 | } else { 258 | away = this.pin[i].distance; 259 | fontSize = "30"; 260 | fontColor = "#eee"; 261 | } 262 | let move:number = ((this.pin[i].bearing - degree) * 5)+50; 263 | let spot:Object = { 264 | id: i, 265 | pin: this.pin[i], 266 | fontSize: fontSize, 267 | fontColor: fontColor, 268 | away: away, 269 | move: move.toFixed(0) 270 | } 271 | //console.log('test-> ', spot.away, spot.move) 272 | // asign pin to array to display visible pins 273 | this.spots.push(spot) 274 | detected = 1; 275 | } else { 276 | if(!detected){ 277 | // clean array to display -> no pins 278 | this.spots = [] 279 | } 280 | } 281 | } 282 | } 283 | 284 | // get data from API and store in array, add to list view and create markers on map, calculate 285 | loadData(position):void{ 286 | console.log('load data position-> ', position); 287 | // set params query 288 | let parmUrl:Object = { 289 | location: { 290 | lat: position.lat, 291 | lng: position.lng 292 | }, 293 | radius: '500' 294 | } 295 | // run request query with params 296 | this._googlePlaceService.getData('nearbysearch', parmUrl) 297 | .then((response:any)=>{ 298 | // clean previous data array 299 | this.pin = [] 300 | return response 301 | }) 302 | .then((response:any) => { 303 | response.map(place => { 304 | console.log(place) 305 | // add new place in data array 306 | this.pin.push(place) 307 | }) 308 | }) 309 | .then(_=>{ 310 | // calacule distance relative between user position and each pin element 311 | this.pin.map(place => { 312 | this.relativePosition(place,position); 313 | }) 314 | }) 315 | .then(_=> { 316 | /// create marker 317 | if(this._geolocationService.myLat && this._geolocationService.myLng){ 318 | this.loadGoogleMapData({lat: this._geolocationService.myLat,lng: this._geolocationService.myLng}); 319 | } 320 | }) 321 | .then(_=>{ 322 | this.dataStatus = 1; 323 | }) 324 | } 325 | 326 | // calulate distance and bearing value for each of the points wrt gps lat/lng 327 | relativePosition(pin,position):void{ 328 | let pinLat:number = pin.lat; 329 | let pinLng:number = pin.lng; 330 | let dLat:number = (position.lat-pinLat)* Math.PI / 180; 331 | let dLon:number = (position.lng-pinLng)* Math.PI / 180; 332 | let lat1:number = pinLat * Math.PI / 180; 333 | let lat2:number = position.lat * Math.PI / 180; 334 | let y:number = Math.sin(dLon) * Math.cos(lat2); 335 | let x:number = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); 336 | let bearing:number = Math.atan2(y, x) * 180 / Math.PI; 337 | bearing = bearing + 180; 338 | pin['bearing'] = bearing; 339 | 340 | let a:number = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 341 | let c:number = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 342 | 343 | let distance:Object = { 344 | miles: 3958.76 * c, 345 | meter: (3958.76 * c) * 1609.34 346 | }; 347 | pin['distance'] = distance; 348 | } 349 | 350 | // Calculates the distance between two GPS points 351 | calculateDistance(lat1:number, long1:number, lat2:number, long2:number):number { 352 | return 11*10000*Math.sqrt(Math.pow(lat1-lat2,2)+Math.pow(long1-long2,2)) 353 | } 354 | 355 | /* Eof - HomePage Core Methode */ 356 | 357 | } 358 | --------------------------------------------------------------------------------