├── .editorconfig ├── .eslintrc ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular-esri-map.png ├── bower.json ├── gulpfile.js ├── package-lock.json ├── package.json ├── scripts └── release.sh ├── site ├── app │ ├── app.js │ ├── appConfig.js │ ├── common │ │ ├── alertController.js │ │ ├── browserDetectionService.js │ │ ├── leftNavController.js │ │ ├── nav.js │ │ └── pathService.js │ ├── examples │ │ ├── .eslintrc │ │ ├── chaining-promises.html │ │ ├── chaining-promises.js │ │ ├── examples.html │ │ ├── examples.js │ │ ├── extrude-polygon.html │ │ ├── extrude-polygon.js │ │ ├── feature-layer.html │ │ ├── feature-layer.js │ │ ├── geodesic-buffers.html │ │ ├── geodesic-buffers.js │ │ ├── home-button.html │ │ ├── home-button.js │ │ ├── popups.html │ │ ├── popups.js │ │ ├── property-binding.html │ │ ├── property-binding.js │ │ ├── registry-pattern.html │ │ ├── registry-pattern.js │ │ ├── scene-toggle-elevation.html │ │ ├── scene-toggle-elevation.js │ │ ├── scene-view.html │ │ ├── scene-view.js │ │ ├── search.html │ │ ├── search.js │ │ ├── snippets.js │ │ ├── vector-tiles.html │ │ ├── vector-tiles.js │ │ ├── webscene-slides-as-directive.html │ │ ├── webscene-slides-as-directive.js │ │ ├── webscene-slides.html │ │ └── webscene-slides.js │ ├── home │ │ ├── home.html │ │ └── home.js │ └── patterns │ │ ├── create-your-own-directives.html │ │ ├── create-your-own-factories.html │ │ ├── lazy-load.html │ │ ├── other-esri-modules.html │ │ ├── patterns.html │ │ ├── patterns.js │ │ ├── property-binding.html │ │ ├── references-to-views.html │ │ └── using-creating-widgets.html ├── docs-resources │ ├── docs-main.css │ ├── docs-nav-template.html │ ├── esri.core.ngdoc │ ├── esri.map.ngdoc │ └── index.ngdoc ├── images │ └── jumbotron-background.jpg ├── index.html └── styles │ └── main.css ├── src ├── core │ ├── esri.core.module.js │ ├── esriLoader.js │ └── esriRegistry.js ├── esri.map.module.js └── map │ ├── EsriHomeButtonController.js │ ├── EsriMapViewController.js │ ├── EsriSceneViewController.js │ ├── EsriWebsceneSlidesController.js │ ├── esriHomeButton.js │ ├── esriMapView.js │ ├── esriSceneView.js │ └── esriWebsceneSlides.js └── test ├── .eslintrc ├── README.md ├── chaining-promises.html ├── custom-factory.html ├── deferred-map.html ├── e2e ├── .eslintrc ├── conf.js ├── helper.js └── specs │ ├── chaining-promises.js │ ├── custom-factory.js │ ├── deferred-map.js │ ├── extrude-polygon.js │ ├── feature-layer.js │ ├── geodesic-buffers.js │ ├── home-button.js │ ├── popups.js │ ├── property-binding.js │ ├── registry-pattern.js │ ├── scene-toggle-elevation.js │ ├── scene-view.js │ ├── search.js │ ├── vector-tiles.js │ ├── webscene-slides-as-directive.js │ └── webscene-slides.js ├── extrude-polygon.html ├── feature-layer.html ├── geodesic-buffers.html ├── home-button.html ├── popups.html ├── property-binding.html ├── registry-pattern.html ├── scene-toggle-elevation.html ├── scene-view.html ├── search.html ├── unit ├── .eslintrc ├── core │ ├── esriLoader.spec.js │ └── esriRegistry.spec.js └── karma.conf.js ├── vector-tiles.html ├── webscene-slides-as-directive.html └── webscene-slides.html /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [ 4 | 2, 5 | 4 6 | ], 7 | "quotes": [ 8 | 2, 9 | "single" 10 | ], 11 | "semi": [ 12 | 2, 13 | "always" 14 | ] 15 | }, 16 | "env": { 17 | "browser": true 18 | }, 19 | "globals": { 20 | "angular": false 21 | }, 22 | "extends": "eslint:recommended" 23 | } 24 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing). 2 | 3 | ### Before filing an issue 4 | 5 | Please look through both the open and closed [existing issues](https://github.com/Esri/angular-esri-map/issues) as well as the angular-esri-map discussons on [GeoNet](https://geonet.esri.com/tags/#/?tags=angular-esri-map) and [GIS Stackexchange](http://gis.stackexchange.com/questions/tagged/angular-esri-map) to see if anyone else has experienced (and maybe even solved) a similar issue. 6 | 7 | If you don't find anything there, and you're just looking for help, you'll probably attract the most eyes if you post in the [ArcGIS API for JavaScript place](https://geonet.esri.com/discussion/create.jspa?containerType=14&containerID=2128&question=true&tags=angular-esri-map,angularjs) on GeoNet, and/or on [GIS Stackexchange](http://gis.stackexchange.com/questions/ask?tags=angular-esri-map,arcgis-javascript-api,angularjs). Either way, *be sure to include the* ***angular-esri-map*** *tag* so that it gets seen by contributors to this repo. 8 | 9 | If you think you're encountering a new bug, please feel free to log an [issue](https://github.com/Esri/angular-esri-map/issues/new) and include the steps to reproduce the problem (and preferably a running sample). 10 | 11 | ### I want to contribute! 12 | 13 | If you have a feature request, or feel like tackling one of the [open issues](https://github.com/Esri/angular-esri-map/issues) we welcome your contributions! Make sure you checkout the [development instructions](https://github.com/Esri/angular-esri-map#development-instructions) in the readme to help you get started. 14 | 15 | #### Linting 16 | 17 | Please make sure your changes pass JS Hint. This will help make sure code is consistant throguh out. You can run JS Hint with `gulp lint`. 18 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Expected behavior 2 | 3 | - Describe what you expected or wanted to happen. 4 | - What you are trying to achieve? 5 | 6 | #### Actual behavior 7 | 8 | - Describe what occurs in your code. 9 | - Specifically, what seems to work differently than you intended? 10 | - Provide any error messages you see in the console. 11 | 12 | #### Steps to reproduce the behavior 13 | 14 | **We can only help you if we're able to reproduce the behavior you describe above.** Please provide: 15 | 16 | 1. Steps to reproduce the behavior 17 | 2. A link to an app where we can carry out those steps (either our example pages, your publicly facing app, or a Plunker, JSFiddle, JSBin, etc) 18 | 3. Relevant code snippet(s) (only if not easily obtained from the above link) 19 | 20 | We've created Plunkers that you can use as a starting point if the bug does not show in our example pages, or your app is not publicly accessible: 21 | - [Start here](https://plnkr.co/edit/tvyKCU?p=preview) if using v1 (Esri JSAPI 3.x) 22 | - [Start here](https://plnkr.co/edit/UGv7v5?p=preview) if using v2 (Esri JSAPI 4.x) 23 | 24 | Be sure to save your edits and save and include the link to your live sample in this issue description. 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - Choose the appropriate base branch that you want to merge into: 2 | - **v1.x** for v1 (Esri JSAPI 3.x) 3 | - **master** for v2 (Esri JSAPI 4.x) 4 | 5 | - Describe the proposed changes: 6 | - Is there an example or test page to demonstrate any new or changed features? 7 | - Does your PR include appropriate unit or functional tests for source code alterations? 8 | 9 | - Provide a reference to any related issue. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built files under site folder 2 | site/lib 3 | 4 | # ngdocs generated files 5 | ngdocs 6 | 7 | # Logs 8 | logs 9 | *.log 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # Commenting this out is preferred by some people, see 30 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 31 | node_modules 32 | 33 | # Users Environment Variables 34 | .lock-wscript 35 | 36 | # WebStorm IDE settings 37 | .idea 38 | 39 | # istanbull test coverage reports 40 | test/unit/coverage 41 | 42 | # distributable files - only included in releases 43 | # NOTE: make sure this is not included in .npmignore 44 | dist 45 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # built files under site folder 2 | site/lib 3 | 4 | # ngdocs generated files 5 | ngdocs 6 | 7 | # Logs 8 | logs 9 | *.log 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # Commenting this out is preferred by some people, see 30 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 31 | node_modules 32 | 33 | # Users Environment Variables 34 | .lock-wscript 35 | 36 | # WebStorm IDE settings 37 | .idea 38 | 39 | # istanbull test coverage reports 40 | test/unit/coverage 41 | 42 | # zip files 43 | *.zip 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "6" 5 | - "8" 6 | before_script: 7 | - "export DISPLAY=:99.0" 8 | - "sh -e /etc/init.d/xvfb start" 9 | - sleep 3 # give xvfb some time to start 10 | branches: 11 | except: 12 | - gh-release 13 | - gh-pages 14 | -------------------------------------------------------------------------------- /angular-esri-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/angular-esri-map/e084fce7fb3344dff180e2b9814a0477a7a485f3/angular-esri-map.png -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-esri-map", 3 | "homepage": "https://github.com/Esri/angular-esri-map", 4 | "authors": [ 5 | "Javier Abadía (https://github.com/jabadia)", 6 | "Dave Bouwman (https://github.com/dbouwman)", 7 | "Patrick Arlt (http://patrickarlt.com)", 8 | "Matt Priour (https://github.com/mpriour)", 9 | "Jacob Wasilkowski (https://github.com/jwasilgeo)", 10 | "Tom Wayson (http://tomwayson.com)" 11 | ], 12 | "description": "A collection of directives to help you use Esri maps and services in your Angular applications", 13 | "main": ["dist/angular-esri-map.js"], 14 | "keywords": [ 15 | "Angular", 16 | "Esri" 17 | ], 18 | "license": "Apache", 19 | "ignore": [ 20 | "**/.*", 21 | "app", 22 | "src", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "gulpfile.js" 27 | ], 28 | "dependencies": { 29 | "angular": "^1.3.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-esri-map", 3 | "version": "2.0.5", 4 | "description": "A collection of directives to help you use Esri maps and services in your Angular applications", 5 | "main": "dist/angular-esri-map.js", 6 | "dependencies": { 7 | "angular": "^1.3.0" 8 | }, 9 | "devDependencies": { 10 | "angular": "^1.4.7", 11 | "angular-mocks": "^1.4.7", 12 | "browser-sync": "^2.18.5", 13 | "chai": "^3.5.0", 14 | "eslint": "^3.12.0", 15 | "gh-release": "^2.0.1", 16 | "gulp": "^3.8.9", 17 | "gulp-angular-protractor": "~0.3.0", 18 | "gulp-clean": "~0.3.0", 19 | "gulp-concat": "~2.2.0", 20 | "gulp-eslint": "^3.0.0", 21 | "gulp-gh-pages": "^0.5.0", 22 | "gulp-ng-annotate": "^2.0.0", 23 | "gulp-ngdocs": "^0.3.0", 24 | "gulp-rename": "~1.2.0", 25 | "gulp-strip-debug": "^0.3.1", 26 | "gulp-uglify": "^2.0.0", 27 | "gulp-util": "^3.0.7", 28 | "jasmine-core": "^2.5.2", 29 | "karma": "^1.3.0", 30 | "karma-chrome-launcher": "^2.0.0", 31 | "karma-coverage": "^1.1.1", 32 | "karma-firefox-launcher": "^0.1.7", 33 | "karma-jasmine": "^1.1.0", 34 | "karma-phantomjs-launcher": "^1.0.0", 35 | "phantomjs": "^2.1.0", 36 | "protractor": "^4.0.0", 37 | "run-sequence": "^1.2.2" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/Esri/angular-esri-map.git" 42 | }, 43 | "keywords": [ 44 | "Angular", 45 | "Esri" 46 | ], 47 | "author": "Tom Wayson (http://tomwayson.com)", 48 | "contributors": [ 49 | "Javier Abadía (https://github.com/jabadia)", 50 | "Dave Bouwman (https://github.com/dbouwman)", 51 | "Patrick Arlt (http://patrickarlt.com)", 52 | "Matt Priour (https://github.com/mpriour)", 53 | "Jacob Wasilkowski (https://github.com/jwasilgeo)", 54 | "Tom Wayson (http://tomwayson.com)" 55 | ], 56 | "license": "Apache", 57 | "bugs": { 58 | "url": "https://github.com/Esri/angular-esri-map/issues" 59 | }, 60 | "homepage": "https://github.com/Esri/angular-esri-map", 61 | "scripts": { 62 | "build": "gulp build", 63 | "test": "karma start test/unit/karma.conf.js --browsers Firefox --single-run", 64 | "release": "./scripts/release.sh", 65 | "start": "gulp" 66 | }, 67 | "engines": { 68 | "node": "^4 || ^6 || ^8" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # config 4 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 5 | VERSION=$(node --eval "console.log(require('./package.json').version);") 6 | NAME=$(node --eval "console.log(require('./package.json').name);") 7 | 8 | # build and test 9 | gulp test || exit 1 10 | 11 | # checkout temp branch for release 12 | git checkout -b gh-release 13 | 14 | # run build 15 | npm run build 16 | 17 | # force add files 18 | git add dist -f 19 | 20 | # commit changes with a versioned commit message 21 | git commit -m "build $VERSION" 22 | 23 | # push commit so it exists on GitHub when we run gh-release 24 | git push origin gh-release 25 | 26 | # create a ZIP archive of the dist files 27 | zip -r $NAME-v$VERSION.zip dist 28 | 29 | # run gh-release to create the tag and push release to github 30 | gh-release --assets $NAME-v$VERSION.zip 31 | 32 | # checkout prev branch and delete release branch locally and on GitHub 33 | git checkout $BRANCH 34 | git branch -D gh-release 35 | git push origin :gh-release 36 | 37 | # re-run build in prevgit branch before publishing 38 | npm run build 39 | 40 | # publish release on NPM 41 | # TODO: only publish if gh-release was successful, currently 42 | # this fails often enough that we should do this manually 43 | # npm publish 44 | -------------------------------------------------------------------------------- /site/app/app.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | // NOTE: ngSelect and hljs are only included to support 4 | // tabs that dynamically load code and highlight syntax 5 | // see: https://github.com/pc035860/angular-highlightjs#demos 6 | angular 7 | .module('esri-map-docs', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngSelect', 'hljs', 'esri.map']) 8 | .config(function($locationProvider, $routeProvider, appConfig) { 9 | $locationProvider.hashPrefix(''); 10 | $routeProvider 11 | // TODO: add home page 12 | .when('/home', { 13 | templateUrl: 'app/home/home.html', 14 | controller: 'HomeCtrl' 15 | }) 16 | .when('/examples', { 17 | templateUrl: 'app/examples/examples.html', 18 | controller: 'ExamplesCtrl' 19 | }) 20 | .when('/patterns', { 21 | templateUrl: 'app/patterns/patterns.html', 22 | controller: 'PatternsCtrl' 23 | }) 24 | .when('/about', { 25 | redirectTo: '/patterns' 26 | }) 27 | .otherwise({ 28 | redirectTo: '/home' 29 | }); 30 | // set routes of examples pages from appConfig 31 | angular.forEach(appConfig.examplePageCategories, function(examplesArray) { 32 | angular.forEach(examplesArray, function(example) { 33 | $routeProvider 34 | .when(example.route.path, { 35 | templateUrl: example.route.templateUrl, 36 | controller: example.route.controller, 37 | controllerAs: example.route.controllerAs 38 | }); 39 | }); 40 | }); 41 | 42 | // set routes of patterns pages from appConfig 43 | angular.forEach(appConfig.patternsPages, function(page) { 44 | $routeProvider 45 | .when(page.path, { 46 | templateUrl: page.templateUrl, 47 | controller: 'PatternsCtrl' 48 | }); 49 | }); 50 | 51 | // redirects from previous version of docs 52 | $routeProvider.when('/patterns/references-to-map-and-layers', { 53 | redirectTo: '/patterns/references-to-views' 54 | }); 55 | }); 56 | })(angular); 57 | -------------------------------------------------------------------------------- /site/app/common/alertController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('AlertController', function($scope) { 5 | $scope.showAlert = true; 6 | $scope.hideAlert = function() { 7 | $scope.showAlert = false; 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /site/app/common/browserDetectionService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .service('browserDetectionService', function() { 5 | return { 6 | isMobile: function() { 7 | return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent); 8 | } 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /site/app/common/leftNavController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('LeftNavCtrl', function($scope, pathService, appConfig) { 5 | 6 | $scope.activeExample = null; 7 | $scope.leftNavStyle = 'none'; 8 | 9 | $scope.examplePageCategories = appConfig.examplePageCategories; 10 | $scope.patternsPages = appConfig.patternsPages; 11 | 12 | // toggle left nav visibility based on route path 13 | // toggle 'active' class of list items based on route controller name or path 14 | $scope.$on('$routeChangeStart', function(event, next) { 15 | var isExamplePath = (pathService.getPathParts(next.originalPath)[0] === 'examples'); 16 | $scope.examplesLeftNavShow = isExamplePath; 17 | $scope.examplesLeftNavStyle = isExamplePath ? 'block' : 'none'; 18 | 19 | var isPatternsPath = (pathService.getPathParts(next.originalPath)[0] === 'patterns'); 20 | $scope.patternsLeftNavShow = isPatternsPath; 21 | $scope.patternsLeftNavStyle = isPatternsPath ? 'block' : 'none'; 22 | 23 | $scope.activeExample = next.controller; 24 | $scope.activePath = next.originalPath; 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /site/app/common/nav.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('NavCtrl', function($scope, pathService) { 5 | 6 | // set page based on route 7 | $scope.$on('$routeChangeStart', function(event, next/*, current*/) { 8 | var pathParts = pathService.getPathParts(next.originalPath); 9 | $scope.page = pathParts.length > 0 ? pathParts[0] : ''; 10 | }); 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /site/app/common/pathService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .service('pathService', function() { 5 | return { 6 | getPathParts: function(path) { 7 | if (!path) { 8 | return []; 9 | } 10 | return path.slice(path.indexOf('/') + 1).split('/'); 11 | }, 12 | 13 | // parse snippet include file locations from route 14 | getSnippetPaths: function(path) { 15 | var pathParts = this.getPathParts(path), 16 | tabs, 17 | page; 18 | if (pathParts.length === 0) { 19 | return; 20 | } 21 | page = pathParts[0]; 22 | if (!page) { 23 | return; 24 | } 25 | if (page === 'examples' && pathParts.length > 1) { 26 | tabs = []; 27 | tabs.push('app/examples/' + pathParts[1] + '.html'); 28 | tabs.push('app/examples/' + pathParts[1] + '.js'); 29 | return tabs; 30 | } 31 | } 32 | 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /site/app/examples/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /site/app/examples/chaining-promises.html: -------------------------------------------------------------------------------- 1 | 13 |

Chaining Promises

14 | 15 |
WebGL is not supported on your platform/browser.
16 | 17 | 20 |
21 | 22 |
23 | Area = {{vm.area | number: 0}} acres 24 |
25 |
26 |
27 | 28 |

Based on this sample.

29 | -------------------------------------------------------------------------------- /site/app/examples/chaining-promises.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('ChainingPromisesCtrl', function(esriLoader, $scope, browserDetectionService) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/geometry/geometryEngineAsync', 7 | 'esri/geometry/Point', 8 | 'esri/Graphic', 9 | 'esri/layers/GraphicsLayer', 10 | 'esri/Map', 11 | 12 | 'esri/symbols/SimpleFillSymbol', 13 | 'esri/symbols/SimpleLineSymbol', 14 | 'esri/symbols/SimpleMarkerSymbol' 15 | ], function( 16 | geometryEngineAsync, Point, Graphic, GraphicsLayer, Map, 17 | SimpleFillSymbol, SimpleLineSymbol, SimpleMarkerSymbol 18 | ) { 19 | // check that the device/browser can support WebGL 20 | // by inspecting the userAgent and 21 | // by handling the scene view directive's on-error 22 | self.showViewError = browserDetectionService.isMobile(); 23 | self.onViewError = function() { 24 | self.showViewError = true; 25 | }; 26 | 27 | // define semi-transparent red point symbol 28 | var pointSym = new SimpleMarkerSymbol({ 29 | style: 'circle', 30 | color: [255, 0, 0, 0.5], 31 | size: 6, 32 | outline: new SimpleLineSymbol({ 33 | style: 'solid', 34 | color: [255, 255, 255, 0.5] 35 | }) 36 | }); 37 | 38 | // define semi-transparent white symbol for buffers 39 | var polySym = new SimpleFillSymbol({ 40 | style: 'solid', 41 | color: [255, 255, 255, 0.5], 42 | outline: new SimpleLineSymbol({ 43 | style: 'solid', 44 | color: [0, 0, 0], 45 | width: 2 46 | }) 47 | }); 48 | 49 | // create the map for the esri-scene-view 50 | self.map = new Map({ 51 | basemap: 'hybrid', 52 | ground: 'world-elevation' 53 | }); 54 | 55 | // create layer to store graphics and add to map 56 | var layer = new GraphicsLayer(); 57 | self.map.add(layer); 58 | 59 | // location of meteor crater centroid in Arizona desert 60 | var meteorPoint = new Point({ 61 | longitude: -111.022887, 62 | latitude: 35.027410 63 | }); 64 | 65 | self.onViewCreated = function(view) { 66 | self.view = view; 67 | // add the analysis button's parent container to the view's UI, 68 | // instead of relying on CSS positioning 69 | // https://developers.arcgis.com/javascript/latest/api-reference/esri-views-ui-DefaultUI.html 70 | self.view.ui.add('resultsDiv', 'top-right'); 71 | self.viewLoaded = true; 72 | }; 73 | 74 | self.onStartButtonClick = function() { 75 | // buffer crater point and chain promise to additional functions 76 | geometryEngineAsync.geodesicBuffer(meteorPoint, 700, 'yards') 77 | .then(addGraphics) // when promise resolves, send buffer to addGraphics() 78 | .then(zoomTo) // when promise resolves, send buffer to zoomTo() 79 | .then(calculateArea) // when promise resolves, send buffer to calculateArea() 80 | .then(printArea); // when promise resolves, send buffer to printArea() 81 | }; 82 | 83 | // adds the point and buffer graphics to the layer 84 | function addGraphics(buffer) { 85 | layer.add(new Graphic({ 86 | geometry: meteorPoint, 87 | symbol: pointSym 88 | })); 89 | layer.add(new Graphic({ 90 | geometry: buffer, 91 | symbol: polySym 92 | })); 93 | 94 | return buffer; 95 | } 96 | 97 | // zooms to the buffer location 98 | function zoomTo(geom) { 99 | // when the view is ready 100 | return self.view.when(function() { 101 | // zoom to the buffer geometry 102 | return self.view.goTo({ 103 | target: geom, 104 | scale: 24000, 105 | tilt: 0, 106 | heading: 0 107 | }).then(function() { 108 | // resolve the promises with the input geometry 109 | return geom; 110 | }); 111 | }); 112 | } 113 | 114 | // calculates the area of the buffer in acres 115 | function calculateArea(polyGeom) { 116 | return geometryEngineAsync.geodesicArea(polyGeom, 'acres'); 117 | } 118 | 119 | // prints the area to the DOM 120 | function printArea(area) { 121 | self.area = area; 122 | $scope.$apply('ChainingPromisesCtrl.area'); 123 | } 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /site/app/examples/examples.html: -------------------------------------------------------------------------------- 1 |

Examples 2 | showing how to use the angular-esri-map directives 3 |

4 |
5 |
6 |

7 | {{example.toc.title}} 8 |

9 |

{{example.toc.description}}

10 |
11 | 12 |
13 |

14 | {{example.toc.title}} 15 |

16 |

{{example.toc.description}}

17 |
18 |
19 | -------------------------------------------------------------------------------- /site/app/examples/examples.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('ExamplesCtrl', function($scope, appConfig) { 5 | var examplePages = []; 6 | angular.forEach(appConfig.examplePageCategories, function(examplesArray/*, categoryKeyName*/) { 7 | examplePages = examplePages.concat(examplesArray); 8 | }); 9 | var splitIndex = Math.floor(examplePages.length / 2); 10 | $scope.examplePageColumns = { 11 | left: examplePages.slice(0, splitIndex), 12 | right: examplePages.slice(splitIndex) 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /site/app/examples/extrude-polygon.html: -------------------------------------------------------------------------------- 1 | 10 | 11 |

Data-Driven Extrusion

12 | 13 |
WebGL is not supported on your platform/browser.
14 | 15 | 30 | 31 | 32 |
33 | 34 |

Based on this sample.

35 | -------------------------------------------------------------------------------- /site/app/examples/extrude-polygon.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('ExtrudePolygonCtrl', function(esriLoader, browserDetectionService, $scope) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/layers/FeatureLayer', 8 | 'esri/renderers/SimpleRenderer', 9 | 'esri/symbols/PolygonSymbol3D', 10 | 'esri/symbols/ExtrudeSymbol3DLayer', 11 | 'esri/widgets/Legend' 12 | ], function(Map, FeatureLayer, SimpleRenderer, PolygonSymbol3D, ExtrudeSymbol3DLayer, Legend) { 13 | // check that the device/browser can support WebGL 14 | // by inspecting the userAgent and 15 | // by handling the scene view directive's on-error 16 | self.showViewError = browserDetectionService.isMobile(); 17 | self.onViewError = function() { 18 | self.showViewError = true; 19 | }; 20 | 21 | // limit visualization to southeast U.S. counties 22 | var defExp = ['STATE = \'LA\'', 'STATE = \'AL\'', 'STATE = \'AR\'', 23 | 'STATE = \'MS\'', 'STATE = \'TN\'', 'STATE = \'GA\'', 24 | 'STATE = \'FL\'', 'STATE = \'SC\'', 'STATE = \'NC\'', 25 | 'STATE = \'VA\'', 'STATE = \'KY\'', 'STATE = \'WV\'' 26 | ]; 27 | 28 | var renderer = new SimpleRenderer({ 29 | symbol: new PolygonSymbol3D({ 30 | symbolLayers: [new ExtrudeSymbol3DLayer()] 31 | }), 32 | label: '% population in poverty by county', 33 | visualVariables: [{ 34 | type: 'size', 35 | field: 'POP_POVERTY', 36 | normalizationField: 'TOTPOP_CY', 37 | stops: [{ 38 | value: 0.10, 39 | size: 10000, 40 | label: '<10%' 41 | }, { 42 | value: 0.50, 43 | size: 500000, 44 | label: '>50%' 45 | }] 46 | }, { 47 | type: 'color', 48 | field: 'POP_POVERTY', 49 | normalizationField: 'TOTPOP_CY', 50 | stops: [{ 51 | value: 0.10, 52 | color: '#FFFCD4', 53 | label: '<15%' 54 | }, { 55 | value: 0.35, 56 | color: [153, 83, 41], 57 | label: '>35%' 58 | }] 59 | }] 60 | }); 61 | 62 | var povLyr = new FeatureLayer({ 63 | url: '//services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/counties_politics_poverty/FeatureServer/0', 64 | renderer: renderer, 65 | outFields: ['*'], 66 | popupTemplate: { 67 | title: '{COUNTY}, {STATE}', 68 | content: '{POP_POVERTY} of {TOTPOP_CY} people live below the poverty line.', 69 | fieldInfos: [{ 70 | fieldName: 'POP_POVERTY', 71 | format: { 72 | digitSeparator: true, 73 | places: 0 74 | } 75 | }, { 76 | fieldName: 'TOTPOP_CY', 77 | format: { 78 | digitSeparator: true, 79 | places: 0 80 | } 81 | }] 82 | }, 83 | definitionExpression: defExp.join(' OR ') // only display counties from states in defExp 84 | }); 85 | 86 | // create the map 87 | self.map = new Map({ 88 | basemap: 'gray', 89 | layers: [povLyr] 90 | }); 91 | 92 | // create the legend widget after the view has been successfully created 93 | self.onViewCreated = function(view) { 94 | var legendWidget = new Legend({ 95 | view: view 96 | }, 'legendDiv'); 97 | 98 | // destroy the legend widget when angular scope is also being destroyed 99 | $scope.$on('$destroy', function() { 100 | legendWidget.destroy(); 101 | }); 102 | }; 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /site/app/examples/feature-layer.html: -------------------------------------------------------------------------------- 1 |

Feature Layer

2 | 3 | 13 | 14 | 15 |

Based on this sample.

16 | -------------------------------------------------------------------------------- /site/app/examples/feature-layer.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('FeatureLayerCtrl', function(esriLoader) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/layers/FeatureLayer' 8 | ], function(Map, FeatureLayer) { 9 | // create the map 10 | self.map = new Map({ 11 | basemap: 'hybrid' 12 | }); 13 | 14 | // and add a feature layer 15 | var featureLayer = new FeatureLayer({ 16 | url: '//services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Landscape_Trees/FeatureServer/0' 17 | }); 18 | 19 | self.map.add(featureLayer); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /site/app/examples/geodesic-buffers.html: -------------------------------------------------------------------------------- 1 | 28 |

Geodesic Buffers

29 | 30 |
WebGL is not supported on your platform/browser.
31 | 32 | 38 |
MapView
39 |
40 | 41 | 49 |
SceneView
50 |
51 | 52 |
53 |

Based on this sample.

54 |
55 | -------------------------------------------------------------------------------- /site/app/examples/geodesic-buffers.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('GeodesicBuffersCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/layers/GraphicsLayer', 8 | 'esri/Graphic', 9 | 'esri/geometry/geometryEngine', 10 | 'esri/geometry/Point', 11 | 'esri/symbols/SimpleMarkerSymbol', 12 | 'esri/symbols/SimpleFillSymbol' 13 | ], function( 14 | Map, GraphicsLayer, Graphic, 15 | geometryEngine, Point, 16 | SimpleMarkerSymbol, SimpleFillSymbol 17 | ) { 18 | // check that the device/browser can support WebGL 19 | // by inspecting the userAgent and 20 | // by handling the scene view directive's on-error 21 | self.showViewError = browserDetectionService.isMobile(); 22 | self.onViewError = function() { 23 | self.showViewError = true; 24 | }; 25 | 26 | self.map = new Map({ 27 | basemap: 'satellite' 28 | }); 29 | 30 | // add two graphics layers to map: one for points, another for buffers 31 | var polygonSymbol = new SimpleFillSymbol({ 32 | color: [255, 255, 255, 0.5], 33 | outline: { 34 | color: [0, 0, 0, 0.5], 35 | width: 2 36 | } 37 | }); 38 | 39 | var pointSymbol = new SimpleMarkerSymbol({ 40 | color: [255, 0, 0], 41 | outline: { 42 | color: [255, 255, 255], 43 | width: 1 44 | }, 45 | size: 7 46 | }); 47 | 48 | var bufferLayer = new GraphicsLayer(); 49 | 50 | var pointLayer = new GraphicsLayer({ 51 | elevationInfo: { 52 | mode: 'on-the-ground' 53 | } 54 | }); 55 | 56 | self.map.addMany([bufferLayer, pointLayer]); 57 | 58 | // Generate points every 10 degrees along Prime Meridian. Add to layer. 59 | // Buffer each point by 560km using GeometryEngine. Add buffers to map. 60 | for (var lat = -80; lat <= 80; lat += 10) { 61 | var point = new Point({ 62 | longitude: 0, 63 | latitude: lat 64 | }); 65 | pointLayer.add(new Graphic({ 66 | geometry: point, 67 | symbol: pointSymbol 68 | })); 69 | 70 | var buffer = geometryEngine.geodesicBuffer(point, 560, 'kilometers'); 71 | bufferLayer.add(new Graphic({ 72 | geometry: buffer, 73 | symbol: polygonSymbol 74 | })); 75 | } 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /site/app/examples/home-button.html: -------------------------------------------------------------------------------- 1 |

Home Button

2 | 3 |
WebGL is not supported on your platform/browser.
4 | 5 | 12 | 13 | 19 | 20 | 21 | 22 | 23 |

Based on this sample.

24 | -------------------------------------------------------------------------------- /site/app/examples/home-button.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('HomeButtonCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | self.viewLoaded = false; 5 | // load esri modules 6 | esriLoader.require('esri/Map', function(Map) { 7 | // check that the device/browser can support WebGL 8 | // by inspecting the userAgent and 9 | // by handling the scene view directive's on-error 10 | self.showViewError = browserDetectionService.isMobile(); 11 | self.onViewError = function() { 12 | self.showViewError = true; 13 | }; 14 | 15 | self.map = new Map({ 16 | basemap: 'streets', 17 | ground: 'world-elevation' 18 | }); 19 | 20 | self.onViewCreated = function(view) { 21 | // provide the view instance to the bound scope 22 | // of the custom esri-home-button directive 23 | self.sceneView = view; 24 | // update the bound property for ng-show 25 | self.viewLoaded = true; 26 | }; 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /site/app/examples/popups.html: -------------------------------------------------------------------------------- 1 |

Get Started with PopupTemplate

2 | 3 | 8 | 9 | 10 |

Based on this sample.

11 | -------------------------------------------------------------------------------- /site/app/examples/popups.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('PopupsCtrl', function(esriLoader) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/PopupTemplate', 8 | 'esri/layers/FeatureLayer' 9 | ], function(Map, PopupTemplate, FeatureLayer) { 10 | // create the map 11 | self.map = new Map({ 12 | basemap: 'gray' 13 | }); 14 | 15 | var template = new PopupTemplate({ 16 | title: 'Marriage in NY, Zip Code: {ZIP}', 17 | content: '

As of 2015, {MARRIEDRATE}% of the population in this zip code is married.

' + 18 | '
  • {MARRIED_CY} people are married
  • ' + 19 | '
  • {NEVMARR_CY} have never married
  • ' + 20 | '
  • {DIVORCD_CY} are divorced
', 21 | fieldInfos: [{ 22 | fieldName: 'MARRIED_CY', 23 | format: { 24 | digitSeparator: true, 25 | places: 0 26 | } 27 | }, { 28 | fieldName: 'NEVMARR_CY', 29 | format: { 30 | digitSeparator: true, 31 | places: 0 32 | } 33 | }, { 34 | fieldName: 'DIVORCD_CY', 35 | format: { 36 | digitSeparator: true, 37 | places: 0 38 | } 39 | }] 40 | }); 41 | 42 | var featureLayer = new FeatureLayer({ 43 | url: '//services.arcgis.com/V6ZHFr6zdgNZuVG0/ArcGIS/rest/services/NYCDemographics1/FeatureServer/0', 44 | outFields: ['*'], 45 | popupTemplate: template 46 | }); 47 | 48 | self.map.add(featureLayer); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /site/app/examples/property-binding.html: -------------------------------------------------------------------------------- 1 |

Property Binding

2 | 3 |

These properties are updated directly by the MapView:

4 |
    5 |
  • Lat: {{ vm.mapView.center.latitude | number:3 }}, Lng: {{ vm.mapView.center.longitude | number:3 }}
  • 6 |
  • Scale: {{ vm.mapView.scale | number:2 }}
  • 7 |
  • Zoom: {{ vm.mapView.zoom | number:2 }}
  • 8 |
9 | 10 |

This property can be changed by interacting with the input form and the MapView:

11 |
    12 |
  • Rotation:
  • 13 |
14 | 15 | 20 | 21 | 22 |

Learn more about property binding patterns between Angular and Esri JSAPI.

23 | -------------------------------------------------------------------------------- /site/app/examples/property-binding.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('PropertyBindingCtrl', function(esriLoader, $scope) { 3 | var self = this; 4 | 5 | esriLoader.require('esri/Map', function(Map) { 6 | // Create the map 7 | self.map = new Map({ 8 | basemap: 'satellite' 9 | }); 10 | }); 11 | 12 | this.onViewCreated = function(view) { 13 | self.mapView = view; 14 | // Setup a JSAPI 4.x property watch outside of Angular 15 | // and update bound Angular controller properties. 16 | self.mapView.watch('center,scale,zoom,rotation', function() { 17 | $scope.$applyAsync('vm.mapView'); 18 | }); 19 | }; 20 | }); 21 | -------------------------------------------------------------------------------- /site/app/examples/registry-pattern.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |

Registry Pattern

8 | 9 | 14 | 15 | 16 |
17 | 18 |
WebGL is not supported on your platform/browser.
19 | 20 | 27 | 28 | 29 |
30 |

31 | Click the MapView to see point X / Y. 32 |

33 |

34 | Clicked MapView point X: {{ anotherCtrl.mapViewPoint.x }}, Y: {{ anotherCtrl.mapViewPoint.y }} 35 |

36 | 37 |

38 | Click the SceneView to see point X / Y. 39 |

40 |

41 | Clicked SceneView point X: {{ anotherCtrl.sceneViewPoint.x }}, Y: {{ anotherCtrl.sceneViewPoint.y }} 42 |

43 |
44 | 45 |

Learn more about the registry pattern.

46 |

Based on this sample.

47 | -------------------------------------------------------------------------------- /site/app/examples/registry-pattern.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('RegistryPatternCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | 5 | self.mapViewOptions = { 6 | zoom: 4, 7 | center: [15, 65] 8 | }; 9 | 10 | self.sceneViewOptions = { 11 | zoom: 4, 12 | center: [15, 65] 13 | }; 14 | 15 | // load esri modules 16 | esriLoader.require([ 17 | 'esri/Map' 18 | ], function(Map) { 19 | // check that the device/browser can support WebGL 20 | // by inspecting the userAgent and 21 | // by handling the scene view directive's on-error 22 | self.showSceneViewError = browserDetectionService.isMobile(); 23 | self.onSceneViewError = function() { 24 | self.showSceneViewError = true; 25 | }; 26 | 27 | // create the map 28 | self.map = new Map({ 29 | basemap: 'streets' 30 | }); 31 | 32 | // NOTE: This is one way to get a reference to the map or scene view within 33 | // the SAME parent controller, by binding to the on-create callback. 34 | self.onMapViewCreated = function(view) { 35 | self.mapView = view; 36 | // do something with the map view 37 | }; 38 | self.onSceneViewCreated = function(view) { 39 | self.sceneView = view; 40 | // do something with the scene view 41 | }; 42 | }); 43 | }) 44 | .controller('AnotherController', function(esriRegistry, $scope) { 45 | // NOTE: This is a way to get a reference to a view in 46 | // a DIFFERENT controller, by setting the register-as attribute 47 | // on the view directive and then using esriRegistry to get the view by name. 48 | var self = this; 49 | 50 | esriRegistry.get('myMapView').then(function(res) { 51 | // establish a click listener on the view in the response 52 | res.view.on('click', function(e) { 53 | // set or update the point property that is used in the html template 54 | self.mapViewPoint = e.mapPoint; 55 | 56 | // NOTE: $scope.$apply() is needed b/c the view's click event 57 | // happens outside of Angular's digest cycle 58 | $scope.$apply(); 59 | }); 60 | }); 61 | 62 | esriRegistry.get('mySceneView').then(function(res) { 63 | res.view.on('click', function(e) { 64 | self.sceneViewPoint = e.mapPoint; 65 | $scope.$apply(); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /site/app/examples/scene-toggle-elevation.html: -------------------------------------------------------------------------------- 1 | 13 | 14 |

Toggle Ground Elevation

15 | 16 |
WebGL is not supported on your platform/browser.
17 | 18 | 28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 |

Based on this sample.

36 | -------------------------------------------------------------------------------- /site/app/examples/scene-toggle-elevation.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('SceneToggleElevationCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | self.viewLoaded = false; 5 | // load esri modules 6 | esriLoader.require('esri/Map', function(Map) { 7 | // check that the device/browser can support WebGL 8 | // by inspecting the userAgent and 9 | // by handling the scene view directive's on-error 10 | self.showViewError = browserDetectionService.isMobile(); 11 | self.onViewError = function() { 12 | self.showViewError = true; 13 | }; 14 | 15 | self.map = new Map({ 16 | basemap: 'hybrid', 17 | ground: 'world-elevation' 18 | }); 19 | 20 | self.onViewLoaded = function(view) { 21 | // add the analysis button's parent container to the view's UI, 22 | // instead of relying on CSS positioning 23 | // https://developers.arcgis.com/javascript/latest/api-reference/esri-views-ui-DefaultUI.html 24 | view.ui.add('elevationDiv', 'top-right'); 25 | self.viewLoaded = true; 26 | }; 27 | 28 | self.updateElevation = function(e) { 29 | self.map.ground.layers.forEach(function(layer) { 30 | layer.visible = e.currentTarget.checked; 31 | }); 32 | }; 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /site/app/examples/scene-view.html: -------------------------------------------------------------------------------- 1 | 14 | 15 |

SceneView

16 | 17 |
WebGL is not supported on your platform/browser.
18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

Based on this sample.

32 | -------------------------------------------------------------------------------- /site/app/examples/scene-view.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('SceneViewCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | self.viewLoaded = false; 5 | // load esri modules 6 | esriLoader.require([ 7 | 'esri/Map', 8 | 'esri/layers/TileLayer' 9 | ], function( 10 | Map, TileLayer 11 | ) { 12 | // check that the device/browser can support WebGL 13 | // by inspecting the userAgent and 14 | // by handling the scene view directive's on-error 15 | self.showViewError = browserDetectionService.isMobile(); 16 | self.onViewError = function() { 17 | self.showViewError = true; 18 | }; 19 | 20 | // put this layer on the controller scope so that the checkbox can be used directly with ng-model 21 | self.transportationLyr = new TileLayer({ 22 | url: '//server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer', 23 | id: 'streets', 24 | visible: false 25 | }); 26 | 27 | var housingLyr = new TileLayer({ 28 | url: '//tiles.arcgis.com/tiles/nGt4QxSblgDfeJn9/arcgis/rest/services/New_York_Housing_Density/MapServer', 29 | id: 'ny-housing', 30 | opacity: 0.9 31 | }); 32 | 33 | // layers may be added to the map in the map's constructor 34 | self.map = new Map({ 35 | basemap: 'oceans', 36 | layers: [housingLyr] 37 | }); 38 | 39 | // or they may be added to the map using map.add() 40 | self.map.add(self.transportationLyr); 41 | 42 | self.onViewLoaded = function(view) { 43 | // add the layer toggle control to the view's UI top right corner 44 | view.ui.add('layerToggle', 'top-right'); 45 | self.viewLoaded = true; 46 | 47 | // The map handles the layers' data, while the view 48 | // and layer views take care of renderering the layers. 49 | view.on('layerview-create', function(evt) { 50 | if (evt.layer.id === 'ny-housing') { 51 | // Explore the properties of the population layer's layer view here. 52 | console.log('LayerView for male population created!', evt.layerView); 53 | } 54 | if (evt.layer.id === 'streets') { 55 | // Explore the properties of the transportation layer's layer view here. 56 | console.log('LayerView for streets created!', evt.layerView); 57 | } 58 | }); 59 | 60 | // Once the housing layer has loaded, 61 | // the view will animate to it's initial extent. 62 | housingLyr.when(function() { 63 | view.goTo(housingLyr.fullExtent); 64 | }); 65 | 66 | }; 67 | 68 | self.changeMap = function() { 69 | // test to show that changing the map property works for the same view 70 | self.map = new Map({ 71 | basemap: 'gray' 72 | }); 73 | }; 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /site/app/examples/search.html: -------------------------------------------------------------------------------- 1 |

Search Widget

2 | 3 | 8 | 9 | 10 |

Based on this sample.

11 | -------------------------------------------------------------------------------- /site/app/examples/search.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('SearchCtrl', function(esriLoader, $scope) { 3 | var self = this; 4 | 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/widgets/Search' 8 | ], function(Map, Search) { 9 | self.map = new Map({ 10 | basemap: 'streets-relief-vector' 11 | }); 12 | 13 | self.onViewCreated = function(view) { 14 | var searchWidget = new Search({ 15 | view: view 16 | }); 17 | 18 | // add the search widget to the top left corner of the view 19 | view.ui.add(searchWidget, { 20 | position: 'top-left', 21 | index: 0 22 | }); 23 | 24 | // destroy the search widget when angular scope is also being destroyed 25 | $scope.$on('$destroy', function() { 26 | searchWidget.destroy(); 27 | }); 28 | }; 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /site/app/examples/snippets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('SnippetsCtrl', function($scope, pathService) { 5 | 6 | // update snippet parameters based on route 7 | $scope.$on('$routeChangeStart', function(event, next/*, current*/) { 8 | $scope.tabs = pathService.getSnippetPaths(next.originalPath); 9 | $scope.currentTab = $scope.tabs && $scope.tabs.length > 0 ? $scope.tabs[0] : null; 10 | }); 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /site/app/examples/vector-tiles.html: -------------------------------------------------------------------------------- 1 |

Vector Tiles

2 | 3 | 8 | 9 | 10 |

Based on this sample.

11 | -------------------------------------------------------------------------------- /site/app/examples/vector-tiles.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('VectorTileLayerCtrl', function(esriLoader) { 3 | var self = this; 4 | // load esri modules 5 | esriLoader.require([ 6 | 'esri/Map', 7 | 'esri/layers/VectorTileLayer' 8 | ], function( 9 | Map, VectorTileLayer 10 | ) { 11 | self.map = new Map(); 12 | 13 | // add a tile layer to the map 14 | var tileLyr = new VectorTileLayer({ 15 | url: '//www.arcgis.com/sharing/rest/content/items/bf79e422e9454565ae0cbe9553cf6471/resources/styles/root.json' 16 | }); 17 | self.map.add(tileLyr); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /site/app/examples/webscene-slides-as-directive.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |

Southern California Ski Resorts

21 |

Work with Slides in a WebScene using the <esri-webscene-slides> directive

22 | 23 |
WebGL is not supported on your platform/browser.
24 | 25 |
26 | 27 | 28 |
29 | 30 |

Based on this sample.

31 | -------------------------------------------------------------------------------- /site/app/examples/webscene-slides-as-directive.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('WebSceneSlidesAsDirectiveCtrl', function(esriLoader, browserDetectionService) { 3 | var self = this; 4 | self.slides = []; 5 | self.sceneView = null; 6 | 7 | // load esri modules 8 | esriLoader.require([ 9 | 'esri/portal/PortalItem', 10 | 'esri/WebScene' 11 | ], function(PortalItem, WebScene) { 12 | // check that the device/browser can support WebGL 13 | // by inspecting the userAgent and 14 | // by handling the scene view directive's on-error 15 | self.showViewError = browserDetectionService.isMobile(); 16 | self.onViewError = function() { 17 | self.showViewError = true; 18 | }; 19 | 20 | // create a new WebScene 21 | var webScene = new WebScene({ 22 | portalItem: new PortalItem({ 23 | id: '08974d4b5fcd446b8c8b1663783c5549' 24 | }) 25 | }); 26 | 27 | // establish the WebScene as the bound "map" property for the 28 | self.map = webScene; 29 | 30 | // to be sure that the view is both created and loaded with all slides, 31 | // perform additional logic in the view directive's load callback 32 | self.onViewLoaded = function(view) { 33 | self.sceneView = view; 34 | 35 | // assign slides array to controller property, which will update and set 36 | // the required binding in the directive 37 | self.slides = view.map.presentation.slides.toArray(); 38 | }; 39 | 40 | self.onSlideChange = function(slide) { 41 | // handle the on-slide-change callback 42 | // by setting the scene view location to the slide's viewpoint 43 | self.sceneView.goTo(slide.viewpoint); 44 | }; 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /site/app/examples/webscene-slides.html: -------------------------------------------------------------------------------- 1 | 36 | 37 |

Work with Slides in a WebScene

38 | 39 |
WebGL is not supported on your platform/browser.
40 | 41 |
42 | 43 | 44 | 45 |
46 | New slide: 47 | 48 |
49 | 50 |
51 |
52 |
{{slide.title.text}}
53 | 54 |
55 |
56 | 57 |
58 | 59 |

Based on this sample.

60 | -------------------------------------------------------------------------------- /site/app/examples/webscene-slides.js: -------------------------------------------------------------------------------- 1 | angular.module('esri-map-docs') 2 | .controller('WebSceneSlidesCtrl', function(esriLoader, browserDetectionService, $scope) { 3 | var self = this; 4 | self.slides = []; 5 | self.sceneView = null; 6 | 7 | // load esri modules 8 | esriLoader.require([ 9 | 'esri/portal/PortalItem', 10 | 'esri/WebScene', 11 | 'esri/webscene/Slide' 12 | ]).then(function(esriModules) { 13 | // check that the device/browser can support WebGL 14 | // by inspecting the userAgent and 15 | // by handling the scene view directive's on-error 16 | self.showViewError = browserDetectionService.isMobile(); 17 | self.onViewError = function() { 18 | self.showViewError = true; 19 | }; 20 | 21 | var PortalItem = esriModules[0]; 22 | var WebScene = esriModules[1]; 23 | var Slide = esriModules[2]; 24 | 25 | // create a new WebScene 26 | var webScene = new WebScene({ 27 | portalItem: new PortalItem({ 28 | id: '1c7a06421a314ac9b7d0fae22aa367fb' 29 | }) 30 | }); 31 | 32 | // establish the WebScene as the bound "map" property for the 33 | self.map = webScene; 34 | 35 | // to be sure that the view is both created and loaded with all slides, 36 | // perform additional logic in the view directive's load callback 37 | self.onViewLoaded = function(view) { 38 | self.sceneView = view; 39 | 40 | var createSlideDiv = document.getElementsByClassName('createSlideDiv')[0]; 41 | var slidesDiv = document.getElementsByClassName('slidesDiv')[0]; 42 | view.ui.add([createSlideDiv, slidesDiv], 'top-right'); 43 | 44 | // presentation slides are in fact an "esri/core/Collection" 45 | // make a shallow copy as a new array object for angular scope 46 | self.slides = view.map.presentation.slides.toArray(); 47 | }; 48 | 49 | self.onSlideClick = function(slide) { 50 | // add and manage an extra property for ng-class css styling 51 | self.slides.forEach(function(slide) { 52 | slide.isActiveSlide = false; 53 | }); 54 | slide.isActiveSlide = true; 55 | 56 | // animate to the given slide's viewpoint and 57 | // turn on its visible layers and basemap layers in the view 58 | slide.applyTo(self.sceneView); 59 | }; 60 | 61 | // create a new slide using Slide.createFrom after clicking on the button 62 | self.onCreateClick = function() { 63 | Slide.createFrom(self.sceneView).then(function(slide) { 64 | // set the slide title 65 | slide.title.text = self.createSlideTitleInput; 66 | // add the slide to the slides collection of the scene presentation 67 | self.sceneView.map.presentation.slides.add(slide, 0); 68 | 69 | // make a shallow copy as a new array object for angular scope 70 | self.slides = self.sceneView.map.presentation.slides.toArray(); 71 | // apply angular scope since the Slide.createFrom callback is outside the digest cycle 72 | $scope.$apply('WebSceneSlidesCtrl.slides'); 73 | }); 74 | }; 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /site/app/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | <esri-map-view> 5 | <esri-scene-view> 6 |

7 |

Directives and services to help you use Esri maps in your Angular apps

8 |
9 |
10 |
11 |
12 |

13 | Examples 14 |

15 |

See the directives in action in these live examples. Each includes code snippets to help you get started quickly.

16 |
17 |
18 |

19 | Patterns 20 |

21 |

Understand the patterns that will help you successfully integrate Esri's maps and services into your applications.

22 |
23 |
24 |

25 | Documentation 26 |

27 |

Learn the API for the directives and services in depth.

28 |
29 |
30 | -------------------------------------------------------------------------------- /site/app/home/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('HomeCtrl', function($scope, $timeout) { 5 | $scope.esriMapViewTitle = true; 6 | 7 | var toggleJumbotronTitles = function() { 8 | $timeout(function() { 9 | $scope.esriMapViewTitle = $scope.esriMapViewTitle ? false : true; 10 | toggleJumbotronTitles(); 11 | }, 5000); 12 | }; 13 | 14 | toggleJumbotronTitles(); 15 | }); 16 | -------------------------------------------------------------------------------- /site/app/patterns/create-your-own-directives.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Create Your Own Directives

4 |

We feel that the value of directives (or web components for that matter) is to package up complex components, exposing a limited, simple declarative interface as HTML elements & attributes. We believe that you will have the best success integrating maps with your angular applications if you build directives that focus on your problem domain. For example, you can imagine the following map directives: 5 |

6 |
<parcel-map pin="ABC123"></parcel-map> 7 | <lot-layout-map lot="93920-A-23"></lot-layout-map> 8 | <workorder-map order="10-31-14-002"></workorder-map>
9 |

These directives could be easily integrated into any views in the over-arching application, or even shared across a suite of applications. 10 |

Using the esri.core Module

11 |

The directives in the esri.map module use services from a module called esri.core. This dependency is bundled into angular-esri-map.js, but the release also builds this module into a standalone file, angular-esri-core.js. You can use the services in this module to help you create your own map and layer directives.

12 |

See the custom directive page for a demonstration of this pattern. The source code is below:

13 |
14 |

Additional Examples

15 |

These other applications demonstrate the custom directive pattern:

16 |
    17 |
  • Parcel Map - a demo application featuring a custom directive for mapping parcels
  • 18 |
  • Angular Esri Playground - features a custom directive for mapping points as clusters or as a heat map.
  • 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /site/app/patterns/create-your-own-factories.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Create Your Own Factories

4 |

Knowing how to get references to MapView and SceneView instances from parent controllers 5 | or unrelated controllers can provide a lot of flexibility in how Angular v1 apps can be 6 | structured and organized. The 7 | Registry Pattern 8 | provided by angular-esri-map already allows for MapView and SceneView 9 | states to be shared and accessed in a common service. 10 |

11 |

As your application continues to grow, it may benefit from having its own custom factory that 12 | can be injected into any directive or controller. Factories using this kind of approach 13 | are fully in charge of maintaining and manipulating data models. 14 | For more information, see Todd Motto's 15 | Rethinking AngularJS Controllers. 16 |

17 |

Below is an example of a "MapFactory" that is injected into two separate controllers. 18 | It is responsible for loading Esri modules and changing a property of a shared Map instance, 19 | while the controllers that use this factory rely on it for getting access to the Map instance 20 | for their own <esri-map-view> directives. 21 |

22 |

The overall workflow is:

23 |
    24 |
  1. Establish a factory with its own Angular $q deferred.
  2. 25 |
  3. Return the deferred's promise in a method that other directives and controllers can call.
  4. 26 |
  5. Load one or more Esri modules with esriLoader, such as 'esri/Map'.
  6. 27 |
  7. Resolve the deferred with the Map instance once it has been established and is ready.
  8. 28 |
  9. Then, other directives and controllers will have access to a fully loaded Map instance.
  10. 29 |
  11. Rinse and repeat for other Esri modules and your own data models that the factory will control.
  12. 30 |
31 |

See the Custom Factory page for a demonstration of this pattern. The source code is below:

32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /site/app/patterns/lazy-load.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Lazy Load the ArcGIS API for JavaScript

4 |

If your application doesn't show a map in every view, it may not make sense to incur the cost of loading the ArcGIS API for JavaScript up front. The esriLoader.bootstrap() method can be used to load the ArcGIS API for JavaScript at any point in your application's life cycle.

5 |

See the Deferred Map page for a demonstration of this pattern. The source code is below:

6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /site/app/patterns/other-esri-modules.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Loading and Using Other Esri Classes

4 |

Much of the work involved in creating a map and loading, styling, and managing its layers happens outside of the declarative directive APIs that we have provided here. So we've provided a way for you to load the modules (such as renderers, symbols, toolbars, task, etc.) that you'll need to accomplish those tasks. The esriLoader service helps you load and use those modules.

5 |

Just about every example we've provided demonstrates this pattern.

6 |
7 |
8 | -------------------------------------------------------------------------------- /site/app/patterns/patterns.html: -------------------------------------------------------------------------------- 1 |

Patterns

2 |

You have probably noticed that there are very few directives and services included here. In fact, there are even fewer that we included in version 1. The reason for this is that the latest version of this library targets version 4 of the ArcGIS API for JavaScript, which does a better job of seperating concerns between views and models. This makes it easier to use many of the Esri modules directly within an Angular application. 3 |

4 |

For this reason, we have focused on providing only the minimal set of re-usable directives and services required to make it easy to use the ArcGIS API for JavaScript in your Angular 1 apps. If your application has modest mapping needs, these directives and services may be all you need.

5 |

If your application makes use of some of the more advanced capabilities of the ArcGIS API for JavaScript, then the directives and examples in this repository will also demonstrate the following patterns that will help you extend what we've provided to meet the needs of your application.

6 | 9 |

The above patterns demonstrate how you can extend what we've provided in this library to meet the needs of any application.

10 | -------------------------------------------------------------------------------- /site/app/patterns/patterns.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('esri-map-docs') 4 | .controller('PatternsCtrl', function() { 5 | }); 6 | -------------------------------------------------------------------------------- /site/app/patterns/property-binding.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Binding Properties Between AngularJS and Esri

4 |

5 | Developers can bind properties between Esri JSAPI 4.x and Angular 1.x by making use of 6 | watching JSAPI properties 7 | as well as Angular's $scope.$applyAsync() or $scope.$apply(). 8 |

9 |

10 | But, as an example, if a user input element only needs to make changes to Esri JSAPI properties, then you can use ng-model to achieve simple "1-direction" binding from Angular to JSAPI. 11 |

12 |

13 | The table below can be used as a guide to help you decide which methods to use when binding properties between the two frameworks. 14 |

15 | 16 |

17 | See the Property Binding example for a demonstration of these concepts. 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 | 52 |
DirectionJSAPI
watch()
Angular
$scope.$applyAsync()
Angular
ng-model
JSAPI to Angularyesyesno
Angular to JSAPInonoyes
Bothyesyesyes
53 |
54 |
55 | 56 |
57 |
58 | -------------------------------------------------------------------------------- /site/app/patterns/references-to-views.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Getting a Reference to MapViews and SceneViews

4 |

The <esri-map-view> and <esri-scene-view> 5 | directives include function binding (events) as a way for you to get direct 6 | references to the underlying MapView and SceneView objects from a parent controller. 7 | They also can be registered with a service for access among different controllers. 8 | This will allow you to manipulate those objects in ways beyond what is allowed by the 9 | declarative directive API.

10 | 11 |

Events

12 |

The <esri-map-view> and <esri-scene-view> 13 | directives each include both on-create 14 | and on-load events that pass a reference to the underlying MapView or 15 | SceneView object to a callback function. The on-create event is 16 | fired immediately after the view has been created (i.e. new MapView()) 17 | and the on-load event fires after the view's 18 | promise has been resolved. 19 |

20 |

The <esri-map-view> and <esri-scene-view> 21 | directives also include an on-error event 22 | that passes the error information object if a view's promise has been rejected. 23 | This can be useful, for example, if you wish to use a <esri-scene-view> directive but 24 | need to gracefully handle 25 | browsers that do not support WebGL. Most examples using the <esri-scene-view> directive 26 | also demonstrate how to use this binding. 27 |

28 |

See these examples:

29 |
On-create
30 | 35 |
On-load
36 | 41 | 42 |

Registry

43 |

We've also included a registry service 44 | that you can use to get a direct reference to a MapView or SceneView object in any controller by setting the 45 | register-as attribute on the <esri-map-view> or 46 | <esri-scene-view> directive. 47 | See the Registry Pattern example.

48 |
49 |
50 | -------------------------------------------------------------------------------- /site/app/patterns/using-creating-widgets.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Using and Creating Esri Widgets

4 |

Widgets—or as they are known in Angular 1.x, directives—are used and 5 | developed in several ways. Out of the box Esri widgets can be 6 | loaded and used as-is, Esri view models provide the ability to 7 | combine a custom user interface with existing Esri widget logic, and 8 | completely custom Angular directives without reference to any widgets or 9 | view models can also be developed to work alongside the Esri JSAPI. 10 |

11 | 12 |

Esri Widgets

13 |

Widgets are usually comprised of properties, methods, and a user interface. 14 | The Esri JSAPI offers a collection of 15 | built-in widgets 16 | that can be loaded with 17 | esriLoader.require() 18 | and constructed once there is 19 | reference to 20 | a MapView or SceneView. This pattern also recommends that you properly tear down and 21 | destroy() 22 | widgets when Angular scope is being destroyed. 23 |

24 | 25 |

Example:

26 | 29 | 30 |

Esri View Models

31 |

View models allow for the separation of a widget's methods and properties from 32 | its UI presentation. By taking advantage of view models, you can use 33 | Esri widget functionality, but apply custom directive templates and styles. 34 | For example, the included 35 | <esri-home-button> 36 | Angular directive makes use of the Esri 37 | HomeViewModel 38 | in its controller logic but defines its own look and feel. 39 |

40 |

Example:

41 | 44 | 45 |

Custom Angular Directives

46 |

Directives do not necessarily need to to build directly on top of 47 | Esri widget or view model functionality. For instance, the 48 | WebScene Slides example page 49 | could have been written to separate the slide bookmarks into their own 50 | directive; however, the purpose of those example pages is to provide parity 51 | with corresponding, authoritative Esri JSAPI examples. 52 |

53 |

The WebScene Slides with Custom Directive 54 | example demonstrates how to encapsulate the slide bookmarks into a custom Angular directive 55 | (<esri-webscene-slides>) 56 | which has been added to this library. 57 |

58 |

Example:

59 | 62 |
63 |
64 | -------------------------------------------------------------------------------- /site/docs-resources/docs-main.css: -------------------------------------------------------------------------------- 1 | /*gulp-ngdocs overrides*/ 2 | li.api-list-item { 3 | overflow-wrap: break-word; 4 | } 5 | -------------------------------------------------------------------------------- /site/docs-resources/docs-nav-template.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /site/docs-resources/esri.core.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @id esri.core 3 | @name esri.core 4 | 5 | @description 6 | 7 | ## `esri.core` Module 8 | 9 | The `esri.core` module includes services used by the directives in the {@link esri.map esri.map} module. These services can also be used directly by your own custom directives. 10 | 11 | ### Loading Esri Modules 12 | 13 | Use {@link esri.core.factory:esriLoader esriLoader} to lazy load the ArcGIS API for JavaScript or to require individual API modules. 14 | 15 | ### Getting a Reference to a MapView or SceneView 16 | 17 | Use {@link esri.core.factory:esriRegistry esriRegistry} to store and retrieve Esri MapView or SceneView instances for use in different controllers. 18 | -------------------------------------------------------------------------------- /site/docs-resources/esri.map.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @id esri.map 3 | @name esri.map 4 | 5 | @description 6 | 7 | ## `esri.map` Module 8 | 9 | The `esri.map` module includes reusable directives for an Esri MapView, a SceneView, and related UI elements such as a home button. 10 | 11 | ### Working with 2D Maps (MapView) 12 | 13 | Using the {@link esri.map.directive:esriMapView esriMapView} directive allows you to place a 2D map on the page. 14 | 15 | ### Working with 3D Maps (SceneView) 16 | 17 | Using the {@link esri.map.directive:esriSceneView esriSceneView} directive allows you to place a 3D scene on the page. 18 | -------------------------------------------------------------------------------- /site/docs-resources/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @id index 3 | @name index 4 | 5 | @description 6 | 7 | ## API Documentation 8 | 9 | ### `esri.map` Module 10 | 11 | The {@link esri.map esri.map} module includes reusable directives for an Esri MapView, a SceneView, and related UI elements such as a home button. 12 | 13 | ### `esri.core` Module 14 | 15 | The {@link esri.core esri.core} module includes services used by the directives in the {@link esri.map esri.map} module. These services can also be used directly by your own custom directives. 16 | -------------------------------------------------------------------------------- /site/images/jumbotron-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/angular-esri-map/e084fce7fb3344dff180e2b9814a0477a7a485f3/site/images/jumbotron-background.jpg -------------------------------------------------------------------------------- /site/styles/main.css: -------------------------------------------------------------------------------- 1 | .jumbotron { 2 | background: #091926 url('../images/jumbotron-background.jpg') no-repeat right; 3 | color: #fff; 4 | } 5 | 6 | .primary-jumbotron-title { 7 | position: absolute; 8 | } 9 | 10 | .show-hide-jumbotron-title { 11 | transition: opacity linear 0.75s; 12 | } 13 | 14 | .show-hide-jumbotron-title.ng-hide { 15 | opacity: 0; 16 | display: block !important; 17 | } 18 | 19 | /* custom page footer */ 20 | .footer { 21 | position: relative; 22 | margin-top: 19px; 23 | padding-top: 19px; 24 | color: #777; 25 | border-top: 1px solid #e5e5e5; 26 | background-color: inherit; 27 | } 28 | 29 | .break-word { 30 | overflow-wrap: break-word; 31 | } 32 | 33 | /* examples title and summary spacing */ 34 | .marketing h4 + p { 35 | margin-bottom: 28px; 36 | } 37 | 38 | /* Esri JSAPI */ 39 | .esri-view { 40 | height: 400px; 41 | } 42 | 43 | /* highlightjs */ 44 | .hljs.javascript, 45 | .hljs.xml { 46 | display: inline-block; 47 | overflow-x: auto; 48 | white-space: pre; 49 | font-size: 13px; 50 | background-color: inherit; /* only use if syntax highlighting background is light gray or near white */ 51 | } 52 | .tab-container pre { 53 | margin-bottom: 0; 54 | border-top: 0; 55 | } 56 | .snippet-file-name { 57 | margin-bottom: 12px; 58 | } 59 | 60 | /* tables */ 61 | .table { 62 | font-size: 0.95em; 63 | } 64 | .table td { 65 | white-space: nowrap; 66 | } 67 | 68 | /* warning for non-webgl browsers */ 69 | .web-gl-warning { 70 | color: #b52e31; 71 | font-weight: bold; 72 | } 73 | 74 | /* dismissable alert to warn about docs version */ 75 | .version-alert { 76 | position: fixed; 77 | bottom: -24px; 78 | width: 80%; 79 | left: calc(50% - 40%); 80 | text-align: center; 81 | font-size: small; 82 | z-index: 100; 83 | } 84 | 85 | /* navbar size and position overrides of calcite-bootstrap */ 86 | .navbar.navbar-inverse.navbar-static-top { 87 | margin-bottom: 20px; 88 | } 89 | .navbar-header { 90 | float: left; 91 | } 92 | .navbar-brand { 93 | font-size: 25px; 94 | font-weight: 200; 95 | } 96 | .navbar-right { 97 | float: right; 98 | } 99 | .navbar-nav>li { 100 | float: left; 101 | } 102 | @media (max-width: 768px) { 103 | /* bootstrap's extra small devices (width less than 768px) */ 104 | /* reduced default padding to fit nav links along 1 row */ 105 | .navbar-nav>li>a { 106 | padding-left: 8px; 107 | padding-right: 8px; 108 | } 109 | } 110 | 111 | /* angular color overrides of calcite-bootstrap (red highlight, white background, black font/header) */ 112 | body { 113 | background-color: #ffffff; 114 | color: #000000; 115 | } 116 | .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { 117 | color: #000000; 118 | } 119 | a { 120 | color: #b52e31; 121 | } 122 | a:focus, 123 | a:hover { 124 | color: #b52e31; 125 | } 126 | .navbar-nav>li>a:focus, 127 | .navbar-nav>li>a:hover { 128 | background-image: linear-gradient(to top,transparent 92%,#b52e31 93%,#b52e31 100%); 129 | } 130 | .nav>li>a:focus, 131 | .nav>li>a:hover { 132 | text-decoration: none; 133 | background-color: #f8f8f8; 134 | } 135 | .nav-tabs>li>a:hover { 136 | border-top: 2px solid #b52e31; 137 | } 138 | .badge { 139 | background-color: #b52e31; 140 | } 141 | .list-group-item.active, 142 | .list-group-item.active:focus, 143 | .list-group-item.active:hover { 144 | background-color: #b52e31; 145 | border-color: #b52e31; 146 | } 147 | a.list-group-item:focus, 148 | a.list-group-item:hover, 149 | button.list-group-item:focus, 150 | button.list-group-item:hover { 151 | /*color: #000000;*/ 152 | background-color: #f8f8f8; 153 | } 154 | 155 | button.btn-default, button.btn-default[disabled] { 156 | color: #b52e31; 157 | background-color: #fff; 158 | border-color: #b52e31; 159 | } 160 | button.btn-default:focus, button.btn-default[disabled]:focus, 161 | button.btn-default:hover, button.btn-default[disabled]:hover, 162 | button.btn-default:active, button.btn-default[disabled]:active { 163 | color: #fff!important; 164 | background-color: #b52e31!important; 165 | border-color: #b52e31 !important; 166 | } 167 | -------------------------------------------------------------------------------- /src/core/esri.core.module.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | angular.module('esri.core', []); 5 | 6 | })(angular); 7 | -------------------------------------------------------------------------------- /src/core/esriLoader.js: -------------------------------------------------------------------------------- 1 | /*global require: false*/ 2 | (function(angular) { 3 | 'use strict'; 4 | 5 | /** 6 | * @ngdoc service 7 | * @name esri.core.factory:esriLoader 8 | * 9 | * @requires $q 10 | * 11 | * @description 12 | * Use `esriLoader` to lazy load the ArcGIS API for JavaScript or to require individual API modules. 13 | */ 14 | angular.module('esri.core').factory('esriLoader', function ($q) { 15 | 16 | /** 17 | * @ngdoc function 18 | * @name bootstrap 19 | * @methodOf esri.core.factory:esriLoader 20 | * 21 | * @description 22 | * Loads the ArcGIS API for JavaScript. 23 | * 24 | * @param {Object=} options Send a list of options of how to load the ArcGIS API for JavaScript. 25 | * This defaults to `{url: 'https://js.arcgis.com/4.8'}`. 26 | * 27 | * @return {Promise} Returns a $q style promise which is resolved once the ArcGIS API for JavaScript has been loaded. 28 | */ 29 | function bootstrap(options) { 30 | var deferred = $q.defer(); 31 | 32 | // Default options object to empty hash 33 | var opts = options || {}; 34 | 35 | // Don't reload API if it is already loaded 36 | if (isLoaded()) { 37 | deferred.reject('ArcGIS API for JavaScript is already loaded.'); 38 | return deferred.promise; 39 | } 40 | 41 | // Create Script Object to be loaded 42 | var script = document.createElement('script'); 43 | script.type = 'text/javascript'; 44 | script.src = opts.url || window.location.protocol + '//js.arcgis.com/4.8'; 45 | 46 | // Set onload callback to resolve promise 47 | script.onload = function() { deferred.resolve( window.require ); }; 48 | 49 | document.body.appendChild(script); 50 | 51 | return deferred.promise; 52 | } 53 | 54 | /** 55 | * @ngdoc function 56 | * @name isLoaded 57 | * @methodOf esri.core.factory:esriLoader 58 | * 59 | * @return {Boolean} Returns a boolean if the ArcGIS API for JavaScript is already loaded. 60 | */ 61 | function isLoaded() { 62 | return typeof window.require !== 'undefined'; 63 | } 64 | 65 | /** 66 | * @ngdoc function 67 | * @name require 68 | * @methodOf esri.core.factory:esriLoader 69 | * 70 | * @description 71 | * Load an Esri module(s) using the Dojo AMD loader. 72 | * 73 | * @param {Array|String} modules An array of module strings (or a string of a single module) to be loaded. 74 | * @param {Function=} callback An optional function used to support AMD style loading. 75 | * @return {Promise} Returns a $q style promise which is resolved once modules are loaded. 76 | */ 77 | function requireModule(moduleName, callback){ 78 | var deferred = $q.defer(); 79 | 80 | // Throw Error if Esri is not loaded yet 81 | if (!isLoaded()) { 82 | deferred.reject('Trying to call esriLoader.require(), but the ArcGIS API for JavaScript has not been loaded yet. Run esriLoader.bootstrap() if you are lazy loading the ArcGIS API for JavaScript.'); 83 | return deferred.promise; 84 | } 85 | 86 | if (typeof moduleName === 'string') { 87 | require([moduleName], function (module) { 88 | // grab the single module passed back from require callback and send to promise 89 | deferred.resolve(module); 90 | }); 91 | 92 | // return a chained promise that calls the callback function 93 | // to ensure it occurs within the digest cycle 94 | return deferred.promise.then(function(module) { 95 | if (callback && typeof callback === 'function') { 96 | callback(module); 97 | } 98 | return module; 99 | }); 100 | } else if (moduleName instanceof Array) { 101 | require(moduleName, function () { 102 | var modules = Array.prototype.slice.call(arguments); 103 | // grab all of the modules passed back from require callback and send as array to promise 104 | deferred.resolve(modules); 105 | }); 106 | 107 | // return a chained promise that calls the callback function 108 | // to ensure it occurs within the digest cycle 109 | return deferred.promise.then(function(modules) { 110 | if (callback && typeof callback === 'function') { 111 | callback.apply(this, modules); 112 | } 113 | return modules; 114 | }); 115 | } else { 116 | deferred.reject('An Array or String is required to load modules.'); 117 | return deferred.promise; 118 | } 119 | } 120 | 121 | // Return list of aformentioned functions 122 | return { 123 | bootstrap: bootstrap, 124 | isLoaded: isLoaded, 125 | require: requireModule 126 | }; 127 | }); 128 | 129 | })(angular); 130 | -------------------------------------------------------------------------------- /src/core/esriRegistry.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc service 6 | * @name esri.core.factory:esriRegistry 7 | * 8 | * @description 9 | * Use `esriRegistry` to store and retrieve MapView or SceneView instances for use in different controllers. 10 | * 11 | * ## Examples 12 | * - {@link ../#/examples/registry-pattern Registry Pattern} 13 | */ 14 | angular.module('esri.core').service('esriRegistry', function($q) { 15 | var registry = {}; 16 | 17 | return { 18 | _register: function(name, promise) { 19 | // if there isn't a promise in the registry yet make one... 20 | // this is the case where a directive is nested higher then the controller 21 | // needing the instance 22 | if (!registry[name]) { 23 | registry[name] = $q.defer(); 24 | } 25 | 26 | var instance = registry[name]; 27 | 28 | // when the promise from the directive is rejected/resolved 29 | // reject/resolve the promise in the registry with the appropriate value 30 | promise.then(function(arg) { 31 | instance.resolve(arg); 32 | return arg; 33 | }, function(arg) { 34 | instance.reject(arg); 35 | return arg; 36 | }); 37 | 38 | // return a function to "deregister" the promise 39 | // by deleting it from the registry 40 | return function() { 41 | delete registry[name]; 42 | }; 43 | }, 44 | 45 | /** 46 | * @ngdoc function 47 | * @name get 48 | * @methodOf esri.core.factory:esriRegistry 49 | * 50 | * @description 51 | * Get the MapView or SceneView instance registered with the given name. 52 | * See {@link esri.map.directive:esriMapView esriMapView} or 53 | * {@link esri.map.directive:esriSceneView esriSceneView} 54 | * for info on how to register a map using the `register-as` attribute. 55 | * 56 | * @param {String} name The name that the view was registered with. 57 | * 58 | * @return {Promise} Returns a $q style promise which is resolved with the view once it has been loaded. 59 | */ 60 | get: function(name) { 61 | // If something is already in the registry return its promise ASAP. 62 | // This is the case where you might want to get a registry item in an event handler. 63 | if (registry[name]) { 64 | return registry[name].promise; 65 | } 66 | 67 | // If we dont already have a registry item create one. This covers the 68 | // case where the directive is nested inside the controller. The parent 69 | // controller will be executed and gets a promise that will be resolved 70 | // later when the item is registered 71 | var deferred = $q.defer(); 72 | 73 | registry[name] = deferred; 74 | 75 | return deferred.promise; 76 | } 77 | }; 78 | }); 79 | 80 | })(angular); 81 | -------------------------------------------------------------------------------- /src/esri.map.module.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | angular.module('esri.map', ['esri.core']); 5 | 6 | })(angular); 7 | -------------------------------------------------------------------------------- /src/map/EsriHomeButtonController.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc controller 6 | * @name esri.map.controller:EsriHomeButtonController 7 | * 8 | * @description 9 | * Functions to help create and manage 10 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Home-HomeViewModel.html HomeViewModel} 11 | * instances. 12 | * 13 | * @requires esri.core.factory:esriLoader 14 | * @requires $element 15 | */ 16 | angular.module('esri.map') 17 | .controller('EsriHomeButtonController', function EsriHomeButtonController($element, esriLoader) { 18 | var self = this; 19 | var element; 20 | 21 | // Put initialization logic inside `$onInit()` 22 | // to make sure bindings have been initialized. 23 | this.$onInit = function() { 24 | element = $element.children()[0]; 25 | self.uiPosition = self.viewUiPosition(); 26 | }; 27 | 28 | // Prior to v1.5, we need to call `$onInit()` manually. 29 | // (Bindings will always be pre-assigned in these versions.) 30 | if (angular.version.major === 1 && angular.version.minor < 5) { 31 | this.$onInit(); 32 | } 33 | 34 | /** 35 | * @ngdoc function 36 | * @name getViewModel 37 | * @methodOf esri.map.controller:EsriHomeButtonController 38 | * 39 | * @description 40 | * Load and get a reference to a HomeViewModel module. 41 | * 42 | * @return {Promise} Returns a $q style promise which is 43 | * resolved with an object with a `viewModel` property that refers to the HomeViewModel module 44 | */ 45 | this.getViewModel = function() { 46 | return esriLoader.require('esri/widgets/Home/HomeViewModel').then(function(HomeVM) { 47 | return { 48 | viewModel: HomeVM 49 | }; 50 | }); 51 | }; 52 | 53 | /** 54 | * @ngdoc function 55 | * @name setView 56 | * @methodOf esri.map.controller:EsriHomeButtonController 57 | * 58 | * @description 59 | * Set a view on the HomeViewModel. 60 | * A new HomeViewModel will be constructed. 61 | * To be fully functional, the HomeViewModel requires a valid view property. 62 | * This will also add the directive to a view's UI position if using the 63 | * optional {@link esri.map.directive:esriHomeButton `view-ui-position`} isolate scope property. 64 | * 65 | * @param {Object} view view instance 66 | */ 67 | this.setView = function(view) { 68 | if (!view) { 69 | return; 70 | } 71 | return this.getViewModel().then(function(result) { 72 | self.viewModel = new result.viewModel({ 73 | view: view 74 | }); 75 | 76 | if (self.uiPosition) { 77 | view.ui.add(element, self.uiPosition); 78 | } 79 | }); 80 | }; 81 | 82 | /** 83 | * @ngdoc function 84 | * @name go 85 | * @methodOf esri.map.controller:EsriHomeButtonController 86 | * 87 | * @description 88 | * A wrapper around the ArcGIS API for JavaScript `HomeViewModel.go()` method, 89 | * which is executed when the esriHomeButton is clicked. 90 | */ 91 | this.go = function() { 92 | if (!this.viewModel) { 93 | return; 94 | } 95 | this.viewModel.go(); 96 | }; 97 | }); 98 | })(angular); 99 | -------------------------------------------------------------------------------- /src/map/EsriMapViewController.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc controller 6 | * @name esri.map.controller:EsriMapViewController 7 | * 8 | * @description 9 | * Functions to help create 10 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html MapView} 11 | * instances. This contoller is used by the {@link esri.map.directive:esriMapView esriMapView} directive. 12 | * 13 | * @requires esri.core.factory:esriLoader 14 | * @requires esri.core.factory:esriRegistry 15 | * @requires $element 16 | * @requires $scope 17 | * @requires $q 18 | */ 19 | angular.module('esri.map') 20 | .controller('EsriMapViewController', function EsriMapViewController($element, $scope, $q, esriLoader, esriRegistry) { 21 | var self = this; 22 | 23 | // Put initialization logic inside `$onInit()` 24 | // to make sure bindings have been initialized. 25 | this.$onInit = function() { 26 | // read options passed in as either a JSON string expression 27 | // or as a function bound object 28 | self.options = this.viewOptions() || {}; 29 | // assign required and available properties 30 | self.options.container = $element.children()[0]; 31 | }; 32 | 33 | // Prior to v1.5, we need to call `$onInit()` manually. 34 | // (Bindings will always be pre-assigned in these versions.) 35 | if (angular.version.major === 1 && angular.version.minor < 5) { 36 | this.$onInit(); 37 | } 38 | 39 | /** 40 | * @ngdoc function 41 | * @name getMapView 42 | * @methodOf esri.map.controller:EsriMapViewController 43 | * 44 | * @description 45 | * Load and get a reference to a MapView module. 46 | * 47 | * @return {Promise} Returns a $q style promise which is 48 | * resolved with an object with a `view` property that refers to the MapView module. 49 | */ 50 | this.getMapView = function() { 51 | return esriLoader.require('esri/views/MapView').then(function(MapView) { 52 | return { 53 | view: MapView 54 | }; 55 | }); 56 | }; 57 | 58 | /** 59 | * @ngdoc function 60 | * @name setMap 61 | * @methodOf esri.map.controller:EsriMapViewController 62 | * 63 | * @description 64 | * Set a Map on the MapView. 65 | * A new MapView will be constructed if it does not already exist, 66 | * and also execute the optional `on-load` and `on-create` events. 67 | * If a new MapView is rejected, the optional `on-error` event will be executed. 68 | * 69 | * @param {Object} map Map instance 70 | */ 71 | this.setMap = function(map) { 72 | if (!map) { 73 | return; 74 | } 75 | 76 | if (!self.view) { 77 | // construct a new MapView with the supplied map and options 78 | self.options.map = map; 79 | return this.getMapView().then(function(result) { 80 | self.view = new result.view(self.options); 81 | 82 | // set up a deferred for dealing with the (optional) esriRegistry 83 | var viewRegistryDeferred = $q.defer(); 84 | if (typeof self.registerAs === 'string') { 85 | self.deregister = esriRegistry._register(self.registerAs, viewRegistryDeferred.promise); 86 | $scope.$on('$destroy', function() { 87 | if (self.deregister) { 88 | self.deregister(); 89 | } 90 | }); 91 | } 92 | 93 | if (typeof self.onCreate() === 'function') { 94 | self.onCreate()(self.view); 95 | } 96 | 97 | self.view.when(function() { 98 | if (typeof self.onLoad() === 'function') { 99 | $scope.$apply(function() { 100 | self.onLoad()(self.view); 101 | }); 102 | } 103 | // handle the deferred that is intended for use with the esriRegistry 104 | viewRegistryDeferred.resolve({ 105 | view: self.view 106 | }); 107 | }, function(err) { 108 | if (typeof self.onError() === 'function') { 109 | self.onError()(err); 110 | } 111 | // handle the deferred that is intended for use with the esriRegistry 112 | viewRegistryDeferred.reject(err); 113 | }); 114 | }); 115 | } else { 116 | // MapView already constructed; only set the map property 117 | self.view.map = map; 118 | } 119 | }; 120 | }); 121 | })(angular); 122 | -------------------------------------------------------------------------------- /src/map/EsriSceneViewController.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc controller 6 | * @name esri.map.controller:EsriSceneViewController 7 | * 8 | * @description 9 | * Functions to help create 10 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html SceneView} 11 | * instances. This contoller is used by the {@link esri.map.directive:esriSceneView esriSceneView} directive. 12 | * 13 | * @requires esri.core.factory:esriLoader 14 | * @requires esri.core.factory:esriRegistry 15 | * @requires $element 16 | * @requires $scope 17 | * @requires $q 18 | */ 19 | angular.module('esri.map') 20 | .controller('EsriSceneViewController', function EsriSceneViewController($element, $scope, $q, esriLoader, esriRegistry) { 21 | var self = this; 22 | 23 | // Put initialization logic inside `$onInit()` 24 | // to make sure bindings have been initialized. 25 | this.$onInit = function() { 26 | // read options passed in as either a JSON string expression 27 | // or as a function bound object 28 | self.options = this.viewOptions() || {}; 29 | // assign required and available properties 30 | self.options.container = $element.children()[0]; 31 | }; 32 | 33 | // Prior to v1.5, we need to call `$onInit()` manually. 34 | // (Bindings will always be pre-assigned in these versions.) 35 | if (angular.version.major === 1 && angular.version.minor < 5) { 36 | this.$onInit(); 37 | } 38 | 39 | /** 40 | * @ngdoc function 41 | * @name getSceneView 42 | * @methodOf esri.map.controller:EsriSceneViewController 43 | * 44 | * @description 45 | * Load and get a reference to a SceneView module. 46 | * 47 | * @return {Promise} Returns a $q style promise which is 48 | * resolved with an object with a `view` property that refers to the SceneView module. 49 | */ 50 | this.getSceneView = function() { 51 | return esriLoader.require('esri/views/SceneView').then(function(SceneView) { 52 | return { 53 | view: SceneView 54 | }; 55 | }); 56 | }; 57 | 58 | /** 59 | * @ngdoc function 60 | * @name setMap 61 | * @methodOf esri.map.controller:EsriSceneViewController 62 | * 63 | * @description 64 | * Set a Map or WebScene on the SceneView. 65 | * A new SceneView will be constructed if it does not already exist, 66 | * and also execute the optional `on-load` and `on-create` events. 67 | * If a new SceneView is rejected, the optional `on-error` event will be executed. 68 | * 69 | * @param {Object} map Map instance or WebScene instance 70 | * 71 | * @return {Promise} Returns a $q style promise and then 72 | * sets the map property and other options on the SceneView. 73 | */ 74 | this.setMap = function(map) { 75 | if (!map) { 76 | return; 77 | } 78 | 79 | if (!self.view) { 80 | // construct a new SceneView with the supplied map and options 81 | self.options.map = map; 82 | return this.getSceneView().then(function(result) { 83 | self.view = new result.view(self.options); 84 | 85 | // set up a deferred for dealing with the (optional) esriRegistry 86 | var viewRegistryDeferred = $q.defer(); 87 | if (typeof self.registerAs === 'string') { 88 | self.deregister = esriRegistry._register(self.registerAs, viewRegistryDeferred.promise); 89 | $scope.$on('$destroy', function() { 90 | if (self.deregister) { 91 | self.deregister(); 92 | } 93 | }); 94 | } 95 | 96 | if (typeof self.onCreate() === 'function') { 97 | self.onCreate()(self.view); 98 | } 99 | 100 | self.view.when(function() { 101 | if (typeof self.onLoad() === 'function') { 102 | $scope.$apply(function() { 103 | self.onLoad()(self.view); 104 | }); 105 | } 106 | // handle the deferred that is intended for use with the esriRegistry 107 | viewRegistryDeferred.resolve({ 108 | view: self.view 109 | }); 110 | }, function(err) { 111 | if (typeof self.onError() === 'function') { 112 | self.onError()(err); 113 | } 114 | // handle the deferred that is intended for use with the esriRegistry 115 | viewRegistryDeferred.reject(err); 116 | }); 117 | }); 118 | } else { 119 | // SceneView already constructed; only set the map property 120 | self.view.map = map; 121 | } 122 | }; 123 | }); 124 | })(angular); 125 | -------------------------------------------------------------------------------- /src/map/EsriWebsceneSlidesController.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc controller 6 | * @name esri.map.controller:EsriWebsceneSlidesController 7 | * 8 | * @description 9 | * Functions to help create and manage individual slide properties and behaviors. 10 | */ 11 | angular.module('esri.map') 12 | .controller('EsriWebsceneSlidesController', function EsriWebsceneSlidesController() { 13 | var self = this; 14 | 15 | /** 16 | * @ngdoc function 17 | * @name setSlides 18 | * @methodOf esri.map.controller:EsriWebsceneSlidesController 19 | * 20 | * @description 21 | * Set a an array of slides for the directive. Each slide will have its own entry in the template. 22 | * 23 | * @param {Array} slides Array of {@link https://developers.arcgis.com/javascript/beta/api-reference/esri-webscene-Slide.html Slide} instances. 24 | * 25 | */ 26 | this.setSlides = function(slides) { 27 | // tack on an extra property for ng-class css styling 28 | slides.forEach(function(slide) { 29 | slide.isActiveSlide = false; 30 | }); 31 | }; 32 | 33 | /** 34 | * @ngdoc function 35 | * @name onSlideClick 36 | * @methodOf esri.map.controller:EsriWebsceneSlidesController 37 | * 38 | * @description 39 | * This method is executed when an individual slide is clicked. 40 | * It passes the slide object to the **`on-slide-change`** bound callback function, 41 | * which can be useful for manipulating an associated Esri SceneView. 42 | * It also sets the clicked slide's active status to true, to assist with CSS styling. 43 | * 44 | * @param {Object} slide slide instance 45 | */ 46 | this.onSlideClick = function(slide) { 47 | // set isActiveSlide state on each slide to assist with CSS styling 48 | self.slides.forEach(function(slide) { 49 | slide.isActiveSlide = false; 50 | }); 51 | slide.isActiveSlide = true; 52 | 53 | // handle click action without direct reference to the ArcGIS API for JavaScript 54 | // by passing the viewpoint of the slide to the onSlideChange function binding 55 | if (typeof self.onSlideChange === 'function') { 56 | self.onSlideChange()(slide); 57 | } 58 | }; 59 | }); 60 | })(angular); 61 | -------------------------------------------------------------------------------- /src/map/esriHomeButton.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc directive 6 | * @name esri.map.directive:esriHomeButton 7 | * @restrict E 8 | * @element 9 | * @scope 10 | * 11 | * @description 12 | * This is the directive which will create a home button using the ArcGIS API for JavaScript. 13 | * 14 | * ## Examples 15 | * - {@link ../#/examples/home-button Home Button} 16 | * 17 | * @param {Object} view Instance of a MapView or SceneView. 18 | * @param {Object=} view-ui-position The MapView or SceneView UI position object which this directive 19 | * can be added to, as an alternative to element positioning with other DOM elements and CSS rules. 20 | * For details on valid object properties, see the 21 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-ui-DefaultUI.html#add `DefaultUI.add()`} 22 | * **`position`** object argument. 23 | */ 24 | angular.module('esri.map') 25 | .directive('esriHomeButton', function esriHomeButton() { 26 | return { 27 | // element only 28 | restrict: 'E', 29 | 30 | // isolate scope 31 | scope: { 32 | view: '=', 33 | viewUiPosition: '&' 34 | }, 35 | 36 | template: [ 37 | '
', 38 | ' ', 39 | ' Home', 40 | '
' 41 | ].join(''), 42 | 43 | controllerAs: 'homeButtonCtrl', 44 | 45 | bindToController: true, 46 | 47 | // directive api 48 | controller: 'EsriHomeButtonController', 49 | 50 | link: function esriHomeButtonLink(scope, element, attrs, controller) { 51 | scope.$watch('homeButtonCtrl.view', function(newVal) { 52 | controller.setView(newVal); 53 | }); 54 | } 55 | }; 56 | }); 57 | })(angular); 58 | -------------------------------------------------------------------------------- /src/map/esriMapView.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc directive 6 | * @name esri.map.directive:esriMapView 7 | * @restrict E 8 | * @element 9 | * @scope 10 | * 11 | * @description 12 | * This is the directive which will create a 13 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html MapView} 14 | * instance using the ArcGIS API for JavaScript. 15 | * There are plenty of examples showing how to use this directive and its bound parameters. 16 | * 17 | * ## Examples 18 | * - {@link ../#/examples/feature-layer Feature Layer} 19 | * - {@link ../#/examples/vector-tiles Vector Tiles} 20 | * - {@link ../#/examples/search Search} 21 | * - {@link ../#/examples and more...} 22 | * 23 | * @param {Object} map Instance of a {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-Map.html Map} 24 | * or {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-WebMap.html WebMap}. 25 | * @param {Function=} on-create Callback for successful creation of the MapView. 26 | * @param {Function=} on-load Callback for successful loading of the MapView. 27 | * @param {Function=} on-error Callback for rejected/failed loading of the MapView. 28 | * @param {Object | String=} view-options An object or inline object hash string defining additional 29 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#properties MapView properties}. 30 | * @param {String=} register-as A name to use when registering the view so that it can be used by other controllers. 31 | * See {@link esri.core.factory:esriRegistry esriRegistry}. 32 | */ 33 | angular.module('esri.map') 34 | .directive('esriMapView', function esriMapView() { 35 | return { 36 | // element only 37 | restrict: 'E', 38 | 39 | transclude: true, 40 | 41 | // isolate scope 42 | scope: { 43 | // one-way binding 44 | registerAs: '@?', 45 | // two-way binding 46 | map: '=?', 47 | // function binding for event handlers 48 | onCreate: '&', 49 | onLoad: '&', 50 | onError: '&', 51 | // function binding for reading object hash from attribute string 52 | // or from scope object property 53 | viewOptions: '&' 54 | }, 55 | 56 | template: '
', 57 | 58 | controllerAs: 'mapViewCtrl', 59 | 60 | bindToController: true, 61 | 62 | // directive api 63 | controller: 'EsriMapViewController', 64 | link: function esriMapViewLink(scope, element, attrs, controller) { 65 | scope.$watch('mapViewCtrl.map', function(newVal) { 66 | controller.setMap(newVal); 67 | }); 68 | } 69 | }; 70 | }); 71 | })(angular); 72 | -------------------------------------------------------------------------------- /src/map/esriSceneView.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc directive 6 | * @name esri.map.directive:esriSceneView 7 | * @restrict E 8 | * @element 9 | * @scope 10 | * 11 | * @description 12 | * This is the directive which will create a 13 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html SceneView} 14 | * instance using the ArcGIS API for JavaScript. 15 | * There are plenty of examples showing how to use this directive and its bound parameters. 16 | * 17 | * ## Examples 18 | * - {@link ../#/examples/scene-view SceneView} 19 | * - {@link ../#/examples/extrude-polygon Extrude Polygon} 20 | * - {@link ../#/examples/scene-toggle-elevation Toggle Basemap Elevation} 21 | * - {@link ../#/examples and more...} 22 | * 23 | * @param {Object} map Instance of a {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-Map.html Map} 24 | * or {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-WebScene.html WebScene}. 25 | * @param {Function=} on-create Callback for successful creation of the SceneView. 26 | * @param {Function=} on-load Callback for successful loading of the SceneView. 27 | * @param {Function=} on-error Callback for rejected/failed loading of the SceneView, for example when WebGL is not supported. 28 | * @param {Object | String=} view-options An object or inline object hash string defining additional 29 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html#properties SceneView properties}. 30 | * @param {String=} register-as A name to use when registering the view so that it can be used by other controllers. 31 | * See {@link esri.core.factory:esriRegistry esriRegistry}. 32 | */ 33 | angular.module('esri.map') 34 | .directive('esriSceneView', function esriSceneView() { 35 | return { 36 | // element only 37 | restrict: 'E', 38 | 39 | transclude: true, 40 | 41 | // isolate scope 42 | scope: { 43 | // one-way binding 44 | registerAs: '@?', 45 | // two-way binding 46 | map: '=?', 47 | // function binding for event handlers 48 | onCreate: '&', 49 | onLoad: '&', 50 | onError: '&', 51 | // function binding for reading object hash from attribute string 52 | // or from scope object property 53 | viewOptions: '&' 54 | }, 55 | 56 | template: '
', 57 | 58 | controllerAs: 'sceneViewCtrl', 59 | 60 | bindToController: true, 61 | 62 | // directive api 63 | controller: 'EsriSceneViewController', 64 | link: function esriSceneViewLink(scope, element, attrs, controller) { 65 | scope.$watch('sceneViewCtrl.map', function(newVal) { 66 | controller.setMap(newVal); 67 | }); 68 | } 69 | }; 70 | }); 71 | })(angular); 72 | -------------------------------------------------------------------------------- /src/map/esriWebsceneSlides.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | 'use strict'; 3 | 4 | /** 5 | * @ngdoc directive 6 | * @name esri.map.directive:esriWebsceneSlides 7 | * @restrict E 8 | * @element 9 | * @scope 10 | * 11 | * @description 12 | * This is the directive which will create slide bookmarks for 13 | * {@link https://developers.arcgis.com/javascript/latest/api-reference/esri-webscene-Slide.html WebScene Slides} 14 | * for the ArcGIS API for JavaScript. 15 | * 16 | * Each bookmark will include the title and screenshot of the slide, and clicking on a bookmark will 17 | * provide the slide object to a callback function (`on-slide-change`). For example, the slide provided 18 | * in the callback will have a viewpoint property that could be used to change the location of an associated Esri SceneView. 19 | * 20 | * **Note:** this directive does not rely on any out of the box Esri widgets or view models. 21 | * It demonstrates how a custom directive can be created and made to interact with other parts of the ArcGIS API for JavaScript. 22 | * 23 | * ## Styling and CSS 24 | * Use the following class names to supply styling to this directive: 25 | * - **`slides-container`**: outer-most container (`div`) 26 | * - **`slide`**: individual slide bookmark (`span`) 27 | * - **`active-slide`**: conditional class which is set by `ng-class` when an individual slide is clicked 28 | * 29 | * For example, to arrange slides horizontally and give a highlighted effect on the selected slide, the following styles could be used: 30 | * ```css 31 | * .slides-container { 32 | * background-color: black; 33 | * color: white; 34 | * padding: 10px; 35 | * } 36 | * .slide { 37 | * cursor: pointer; 38 | * display: inline-block; 39 | * margin: 0 10px; 40 | * } 41 | * .active-slide { 42 | * box-shadow: 0 0 12px white; 43 | * border-style: solid; 44 | * border-width: thin; 45 | * border-color: white; 46 | * } 47 | * ``` 48 | * 49 | * ## Examples 50 | * - {@link ../#/examples/webscene-slides-as-directive WebScene Slides with Custom Directive} 51 | * 52 | * @param {Array} slides Array of {@link https://developers.arcgis.com/javascript/beta/api-reference/esri-webscene-Slide.html Slide} instances. 53 | * @param {Function=} on-slide-change Callback for handling a change in the active slide. 54 | * It may be used, for example, to update the viewpoint location of an associated Esri SceneView. 55 | */ 56 | angular.module('esri.map') 57 | .directive('esriWebsceneSlides', function esriWebsceneSlides() { 58 | return { 59 | // element only 60 | restrict: 'E', 61 | 62 | // isolate scope 63 | scope: { 64 | slides: '=', // array of slides 65 | onSlideChange: '&' // returns a slide property 66 | }, 67 | 68 | template: [ 69 | '
', 70 | ' ', 71 | ' {{slide.title.text}}', 72 | '
', 73 | ' ', 74 | '
', 75 | '
', 76 | '
' 77 | ].join(''), 78 | 79 | controllerAs: 'websceneSlidesCtrl', 80 | 81 | bindToController: true, 82 | 83 | // directive api 84 | controller: 'EsriWebsceneSlidesController', 85 | 86 | link: function websceneSlidesLink(scope, element, attrs, controller) { 87 | scope.$watch('websceneSlidesCtrl.slides', function(newVal) { 88 | // bound slides property can be updated at any time 89 | // and will most likely occur asynchronously after an Esri view is loaded 90 | controller.setSlides(newVal); 91 | }); 92 | } 93 | }; 94 | }); 95 | })(angular); 96 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Test Pages 2 | 3 | The test pages in this folder serve two purposes: 4 | 5 | 1. Demonstrate a specific feature of the directives and services with the bare minimum code (i.e. no Bootstrap, no extra module dependencies like ngRouter, etc) 6 | 2. To be run by the e2e test specs when you execute `gulp test` 7 | 8 | These pages are served along with the docs site when you run the `gulp` task and are accessible from the root (i.e. `http://localhost:9002/feature-layer.html`). 9 | 10 | You may notice that most of these test pages have corresponding the examples in the docs site. You can expect test pages that have corresponding e2e test specs to be up to date. 11 | -------------------------------------------------------------------------------- /test/custom-factory.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Custom Factory 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Custom Factory

18 |

With Two Separate Controllers

19 | 20 |
21 |

ExampleControllerA

22 | 23 | 28 | 29 |
30 | 31 |
32 | 33 |
34 |

ExampleControllerB

35 | 36 | 37 | (from ExampleControllerB, using the factory) 38 | 39 | 44 | 45 |
46 | 47 |

Based on this sample.

48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /test/deferred-map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deferred Map 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Deferred Map

18 |
19 |

Click the button to lazy load the Esri JSAPI. This can be used when you want to defer its inclusion until necessary, which can be particularly helpful when developing Single Page Applications (SPAs). You can see the resources being pulled from dev tools, once you click the button.

20 | 21 | 22 |
23 | 28 | 29 |
30 |

Based on this sample.

31 |
32 | 33 | 34 | 35 | 36 | 37 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /test/e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "protractor": true, 5 | "jasmine": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/e2e/conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.config = { 4 | seleniumAddress: 'http://localhost:4444/wd/hub', 5 | framework: 'jasmine2', 6 | specs: ['specs/**.js'], 7 | capabilities: { 8 | browserName: 'chrome' 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/e2e/helper.js: -------------------------------------------------------------------------------- 1 | var waitUntilElementIsReady = function(element) { 2 | browser.wait(function() { 3 | return element.isPresent(); 4 | }, 5000); 5 | browser.wait(function() { 6 | return element.isDisplayed(); 7 | }, 5000); 8 | }; 9 | 10 | // helper for waiting on async map attributes to change 11 | var getAsyncAttributeValue = function(element, attribute) { 12 | return browser.wait(function() { 13 | var deferred = protractor.promise.defer(); 14 | // setting an artificial timeout to wait and hope 15 | // that an async map attribute such as "data-zoom" is different 16 | setTimeout(function() { 17 | element.getAttribute(attribute).then(function(value) { 18 | // resolve the deferred for both the browser.wait() 19 | // and to get outside access to the attribute value 20 | deferred.fulfill(value); 21 | }); 22 | }, 2000); 23 | return deferred.promise; 24 | }, 5000); 25 | }; 26 | 27 | var _getEsriViewElement = function(elementArrayFinder, deferred) { 28 | browser.wait(function() { 29 | // try to wait until there are 1 or more elements available (either a "esri-display-object" OR esri-view-surface") 30 | return elementArrayFinder.count().then(function(countValue) { 31 | return countValue > 0; 32 | }); 33 | }, 5000).then(function() { 34 | // return the first DOM node (either a "esri-display-object" OR esri-view-surface") 35 | var firstElement = elementArrayFinder.first(); 36 | deferred.fulfill(firstElement); 37 | }); 38 | }; 39 | 40 | // wait for map's "esri-display-object" in DOM to be ready (for a JSAPI 4.x MapView) 41 | var getMapViewElement = function() { 42 | var deferred = protractor.promise.defer(); 43 | var mapViewParent = element(by.tagName('esri-map-view')).all(by.css('.esri-display-object')); 44 | 45 | _getEsriViewElement(mapViewParent, deferred); 46 | 47 | return deferred.promise; 48 | }; 49 | 50 | // wait for canvas in DOM to be ready (for a JSAPI 4.x SceneView) 51 | var getSceneViewElement = function() { 52 | var deferred = protractor.promise.defer(); 53 | var sceneViewParent = element(by.tagName('esri-scene-view')).all(by.tagName('canvas')); 54 | 55 | _getEsriViewElement(sceneViewParent, deferred); 56 | 57 | return deferred.promise; 58 | }; 59 | 60 | module.exports = { 61 | waitUntilElementIsReady: waitUntilElementIsReady, 62 | getAsyncAttributeValue: getAsyncAttributeValue, 63 | getMapViewElement: getMapViewElement, 64 | getSceneViewElement: getSceneViewElement 65 | }; 66 | -------------------------------------------------------------------------------- /test/e2e/specs/chaining-promises.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Chaining Promises', function() { 6 | beforeAll(function() { 7 | browser.get('/chaining-promises.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | 16 | it('should report the area at the end of the promise chain', function() { 17 | helper.getSceneViewElement().then(function() { 18 | var startPromiseChainButton = element(by.buttonText('Start Promise Chain')); 19 | var areaDiv = element(by.id('areaDiv')); 20 | areaDiv.getText().then(function(value) { 21 | var areaTextBefore = value; 22 | 23 | startPromiseChainButton.click(); 24 | 25 | browser.wait(function() { 26 | return areaDiv.getAttribute('class').then(function(value) { 27 | return value.indexOf('ng-hide') === -1; 28 | }); 29 | }, 8000).then(function() { 30 | areaDiv.getText().then(function(value) { 31 | var areaTextAfter = value; 32 | expect(areaTextBefore).not.toEqual(areaTextAfter); 33 | }); 34 | }); 35 | }); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/e2e/specs/custom-factory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Search Widget', function() { 6 | beforeAll(function() { 7 | browser.get('/custom-factory.html'); 8 | }); 9 | 10 | it('should have a standard map view that is not rotated', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/e2e/specs/deferred-map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | xdescribe('Deferred Map', function() { 6 | // shared element locator(s) 7 | var loadJSAPIButton = element(by.buttonText('Load Esri JSAPI and MapView')); 8 | 9 | beforeAll(function() { 10 | // refer to conf.js to get the baseUrl that is prepended 11 | browser.get('/deferred-map.html'); 12 | }); 13 | 14 | it('should click on the load JSAPI button, and create a standard map view that is not rotated', function() { 15 | loadJSAPIButton.click(); 16 | 17 | // var mapView = helper.getMapViewElement(); 18 | helper.getMapViewElement().then(function(mapView) { 19 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/e2e/specs/extrude-polygon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Extrude Polygon', function() { 6 | beforeAll(function() { 7 | browser.get('/extrude-polygon.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/e2e/specs/feature-layer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Feature Layer', function() { 6 | beforeAll(function() { 7 | browser.get('/feature-layer.html'); 8 | }); 9 | 10 | it('should have a map view that is rotated by 90 degrees', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(90deg\))/); 13 | }); 14 | }); 15 | 16 | it('should have a feature layer with many circle symbols, identified by svg "circle" elements', function() { 17 | helper.getMapViewElement().then(function(mapView) { 18 | var allCircles = mapView.all(by.tagName('circle')); 19 | 20 | browser.wait(function() { 21 | return allCircles.count().then(function(countValue) { 22 | return countValue > 0; 23 | }); 24 | }, 8000).then(function() { 25 | expect(allCircles.count()).toBeGreaterThan(1); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/e2e/specs/geodesic-buffers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Feature Layer', function() { 6 | beforeAll(function() { 7 | browser.get('/geodesic-buffers.html'); 8 | }); 9 | 10 | it('should have a standard map view that is not rotated', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 13 | }); 14 | }); 15 | 16 | it('should have a point layer containing 17 graphics, identified by "circle" elements', function() { 17 | helper.getMapViewElement().then(function(mapView) { 18 | var circleElements = mapView.all(by.tagName('circle')); 19 | 20 | browser.wait(function() { 21 | return circleElements.count().then(function(countValue) { 22 | return countValue > 0; 23 | }); 24 | }, 8000).then(function() { 25 | expect(circleElements.count()).toEqual(17); 26 | }); 27 | }); 28 | }); 29 | 30 | it('should have a buffer polygon layer containing 17 graphics, identified by "path" elements', function() { 31 | helper.getMapViewElement().then(function(mapView) { 32 | var pathElements = mapView.all(by.tagName('path')); 33 | 34 | browser.wait(function() { 35 | return pathElements.count().then(function(countValue) { 36 | return countValue > 0; 37 | }); 38 | }, 8000).then(function() { 39 | expect(pathElements.count()).toEqual(17); 40 | }); 41 | }); 42 | }); 43 | 44 | it('should have a scene view', function() { 45 | helper.getSceneViewElement().then(function(sceneView) { 46 | expect(sceneView).toBeDefined(); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/e2e/specs/home-button.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Home Button', function() { 6 | beforeAll(function() { 7 | browser.get('/home-button.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | 16 | it('should have a home button with a bound "view" attribute', function() { 17 | var esriHomeButton = element(by.tagName('esri-home-button')); 18 | helper.getAsyncAttributeValue(esriHomeButton, 'view').then(function(value) { 19 | expect(value).toBe('exampleCtrl.sceneView'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/e2e/specs/popups.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | xdescribe('Popups', function() { 6 | beforeAll(function() { 7 | browser.get('/popups.html'); 8 | }); 9 | 10 | it('should how a popup when the feature layer is clicked on', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | var allGs = mapView.all(by.tagName('g')); 13 | 14 | browser.wait(function() { 15 | return allGs.count().then(function(countValue) { 16 | return countValue > 0; 17 | }); 18 | }, 8000).then(function() { 19 | // briefly slow down the test runner and also 20 | // ensure that there are features to be clicked on in the center of the map view element 21 | var zoomInDiv = element(by.css('.esri-button.esri-widget-button.esri-interactive')); 22 | helper.waitUntilElementIsReady(zoomInDiv); 23 | zoomInDiv.click(); 24 | zoomInDiv.click(); 25 | zoomInDiv.click(); 26 | 27 | var popupDiv = element(by.css('.esri-popup')); 28 | 29 | var clickableMapElements = element.all(by.css('.esri-view-surface')); 30 | clickableMapElements.click(); 31 | 32 | helper.waitUntilElementIsReady(zoomInDiv); 33 | zoomInDiv.click(); 34 | 35 | // SHOULD NOT INCLUDE CLASS "esri-invisible" 36 | expect(popupDiv.getAttribute('class')).toEqual('esri-popup esri-widget'); 37 | }); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/e2e/specs/property-binding.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Property Binding', function() { 6 | beforeAll(function() { 7 | browser.get('/property-binding.html'); 8 | }); 9 | 10 | it('should have a standard map view that is not rotated', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 13 | }); 14 | }); 15 | 16 | it('should have a bound rotation property model to an input element', function() { 17 | helper.getMapViewElement().then(function() { 18 | var rotationInput = element(by.model('exampleCtrl.mapView.rotation')); 19 | expect(rotationInput.getAttribute('value')).toBe('0'); 20 | }); 21 | }); 22 | 23 | it('should rotate the map by changing the bound rotation property model of an input element', function() { 24 | helper.getMapViewElement().then(function(mapView) { 25 | var rotationInput = element(by.model('exampleCtrl.mapView.rotation')); 26 | rotationInput.sendKeys('180'); 27 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(180deg\))/); 28 | }); 29 | }); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /test/e2e/specs/registry-pattern.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Registry Pattern', function() { 6 | beforeAll(function() { 7 | browser.get('/registry-pattern.html'); 8 | }); 9 | 10 | it('should click on the map and scene views and begin showing click information in a text element', function() { 11 | var mapViewClickInfo = element(by.id('mapViewClickInfo')); 12 | 13 | helper.getMapViewElement().then(function() { 14 | var clickableMapElements = element.all(by.css('.esri-view-surface')); 15 | clickableMapElements.click(); 16 | 17 | // briefly slow down the test runner 18 | var zoomInDiv = element(by.css('.esri-button.esri-widget-button.esri-interactive')); 19 | helper.waitUntilElementIsReady(zoomInDiv); 20 | zoomInDiv.click(); 21 | 22 | expect(mapViewClickInfo.getCssValue('display')).toEqual('block'); 23 | 24 | var sceneViewClickInfo = element(by.id('sceneViewClickInfo')); 25 | 26 | // nesting this to give the scene view some more time to load and settle 27 | helper.getSceneViewElement().then(function() { 28 | var clickableMapElements = element.all(by.css('.esri-view-surface')); 29 | clickableMapElements.last().click(); 30 | 31 | // briefly slow down the test runner 32 | var zoomInDiv = element(by.css('.esri-button.esri-widget-button.esri-interactive')); 33 | helper.waitUntilElementIsReady(zoomInDiv); 34 | zoomInDiv.click(); 35 | 36 | expect(sceneViewClickInfo.getCssValue('display')).toEqual('block'); 37 | }); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/e2e/specs/scene-toggle-elevation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Scene View', function() { 6 | beforeAll(function() { 7 | browser.get('/scene-toggle-elevation.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | 16 | it('should change the data attribution text when toggling elevation layers', function() { 17 | helper.getSceneViewElement().then(function() { 18 | var elevationToggle = element(by.id('elevationDiv')); 19 | var attributionTextElement = element(by.css('.esri-attribution__sources')); 20 | browser.wait(function() { 21 | return attributionTextElement.getText().then(function(textValue) { 22 | return textValue.indexOf('Source: USGS, NGA, NASA, CGIAR') > -1; 23 | }); 24 | }, 5000).then(function() { 25 | attributionTextElement.getText().then(function(textValue) { 26 | var elevationOnText = textValue; 27 | 28 | elevationToggle.click(); 29 | 30 | attributionTextElement.getText().then(function(textValue) { 31 | var elevationOffText = textValue; 32 | 33 | expect(elevationOffText).not.toEqual(elevationOnText); 34 | }); 35 | }); 36 | }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/e2e/specs/scene-view.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Scene View', function() { 6 | beforeAll(function() { 7 | browser.get('/scene-view.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/e2e/specs/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Search Widget', function() { 6 | beforeAll(function() { 7 | browser.get('/search.html'); 8 | }); 9 | 10 | it('should have a standard map view that is not rotated', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 13 | }); 14 | }); 15 | 16 | it('should have a search widget', function() { 17 | helper.getMapViewElement().then(function() { 18 | var search = element(by.css('.esri-search')); 19 | expect(search).toBeDefined(); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/e2e/specs/vector-tiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Feature Layer', function() { 6 | beforeAll(function() { 7 | browser.get('/vector-tiles.html'); 8 | }); 9 | 10 | it('should have a standard map view that is not rotated', function() { 11 | helper.getMapViewElement().then(function(mapView) { 12 | expect(mapView.getAttribute('style')).toMatch(/(transform: rotateZ\(0deg\))/); 13 | }); 14 | }); 15 | 16 | it('should have 1 vector tiles layer, identified by a "canvas" element', function() { 17 | helper.getMapViewElement().then(function(mapView) { 18 | var canvasElements = mapView.all(by.tagName('canvas')); 19 | 20 | browser.wait(function() { 21 | return canvasElements.count().then(function(countValue) { 22 | return countValue > 0; 23 | }); 24 | }, 8000).then(function() { 25 | expect(canvasElements.count()).toEqual(1); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/e2e/specs/webscene-slides-as-directive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Webscene Slides as Directive', function() { 6 | beforeAll(function() { 7 | browser.get('/webscene-slides-as-directive.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | 16 | it('should have five slides', function() { 17 | helper.getSceneViewElement().then(function() { 18 | var slides = element.all(by.repeater('slide in websceneSlidesCtrl.slides')); 19 | 20 | browser.wait(function() { 21 | return slides.count().then(function(countValue) { 22 | return countValue > 0; 23 | }); 24 | }, 8000).then(function() { 25 | expect(slides.count()).toEqual(5); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/e2e/specs/webscene-slides.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var helper = require('../helper'); 4 | 5 | describe('Webscene Slides', function() { 6 | beforeAll(function() { 7 | browser.get('/webscene-slides.html'); 8 | }); 9 | 10 | it('should have a scene view', function() { 11 | helper.getSceneViewElement().then(function(sceneView) { 12 | expect(sceneView).toBeDefined(); 13 | }); 14 | }); 15 | 16 | it('should have six slides', function() { 17 | helper.getSceneViewElement().then(function() { 18 | var slides = element.all(by.repeater('slide in exampleCtrl.slides')); 19 | 20 | browser.wait(function() { 21 | return slides.count().then(function(countValue) { 22 | return countValue > 0; 23 | }); 24 | }, 8000).then(function() { 25 | expect(slides.count()).toEqual(6); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/feature-layer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Feature Layer 7 | 8 | 9 | 10 | 20 | 21 | 22 |

Feature Layer

23 |
24 | 35 | 36 | 37 | 38 |

Based on this sample.

39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /test/geodesic-buffers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Geodesic Buffers 7 | 8 | 9 | 10 | 37 | 38 | 39 |

Geodesic Buffers

40 |
41 | 47 |
MapView
48 |
49 | 50 | 56 |
SceneView
57 |
58 | 59 |
60 |

Based on this sample.

61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /test/home-button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home Button 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Home Button

18 |
19 | 24 | 30 | 31 | 32 |

Based on this sample.

33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /test/popups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Get Started with PopupTemplate 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Get Started with PopupTemplate

18 |
19 | 24 | 25 |

Based on this sample.

26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/property-binding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Property Binding 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Property Binding

18 |
19 |

These properties are updated directly by the MapView:

20 |
    21 |
  • Lat: {{ exampleCtrl.mapView.center.latitude | number:3 }}, Lng: {{ exampleCtrl.mapView.center.longitude | number:3 }}
  • 22 |
  • Scale: {{ exampleCtrl.mapView.scale | number:2 }}
  • 23 |
  • Zoom: {{ exampleCtrl.mapView.zoom | number:2 }}
  • 24 |
25 | 26 |

This property can be changed by interacting with the input form and the MapView:

27 |
    28 |
  • Rotation:
  • 29 |
30 | 31 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /test/registry-pattern.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Registry Pattern 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Registry Pattern

18 | 19 |
20 | 25 | 26 | 27 |
28 | 29 | 34 | 35 |
36 | 37 |
38 |

39 | Click the MapView to see point X / Y. 40 |

41 |

42 | Clicked MapView point X: {{ anotherCtrl.mapViewPoint.x }}, Y: {{ anotherCtrl.mapViewPoint.y }} 43 |

44 | 45 |

46 | Click the SceneView to see point X / Y. 47 |

48 |

49 | Clicked SceneView point X: {{ anotherCtrl.sceneViewPoint.x }}, Y: {{ anotherCtrl.sceneViewPoint.y }} 50 |

51 |
52 | 53 |

Based on this sample.

54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /test/scene-toggle-elevation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Toggle Ground Elevation 7 | 8 | 9 | 10 | 24 | 25 | 26 |

Toggle Ground Elevation

27 |
28 | 35 |
36 | 37 |
38 |
39 |

Based on this sample.

40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /test/scene-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SceneView 7 | 8 | 9 | 10 | 25 | 26 | 27 |

SceneView

28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |

Based on this sample.

39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /test/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search Widget 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Search Widget

18 |
19 | 24 | 25 |

Based on this sample.

26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "module": false, 7 | "inject": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/core/esriRegistry.spec.js: -------------------------------------------------------------------------------- 1 | describe('esriRegistry', function() { 2 | 3 | // service we're testing 4 | var esriRegistry; 5 | 6 | // angular mocks 7 | var $rootScope; 8 | var deferred; 9 | 10 | // load the core module 11 | beforeEach(module('esri.core')); 12 | 13 | // inject modules and mocks 14 | beforeEach(inject(function(_$rootScope_, _$q_, _esriRegistry_) { 15 | esriRegistry = _esriRegistry_; 16 | $rootScope = _$rootScope_; 17 | deferred = _$q_.defer(); 18 | })); 19 | 20 | describe('_register', function() { 21 | 22 | var removeHandle; 23 | var fakeMap; 24 | beforeEach(function() { 25 | fakeMap = {}; 26 | removeHandle = esriRegistry._register('test', deferred.promise); 27 | deferred.resolve(fakeMap); 28 | }); 29 | 30 | afterEach(function() { 31 | removeHandle(); 32 | }); 33 | 34 | it('should get the registerd promise', function() { 35 | esriRegistry.get('test').then(function(map) { 36 | expect(map).toEqual(fakeMap); 37 | }); 38 | $rootScope.$digest(); 39 | }); 40 | 41 | it('should return a remove function', function() { 42 | expect(typeof removeHandle).toEqual('function'); 43 | }); 44 | 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2015-11-11 using 4 | // generator-karma 1.0.0 5 | 6 | module.exports = function(config) { 7 | 'use strict'; 8 | 9 | config.set({ 10 | // enable / disable watching file and executing tests whenever any file changes 11 | autoWatch: true, 12 | 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '', 15 | 16 | // testing framework to use (jasmine/mocha/qunit/...) 17 | // as well as any additional frameworks (requirejs/chai/sinon/...) 18 | frameworks: [ 19 | 'jasmine' 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | '../../node_modules/angular/angular.js', 25 | '../../node_modules/angular-mocks/angular-mocks.js', 26 | '../../src/**/*.js', 27 | '../../test/unit/**/*.spec.js' 28 | ], 29 | 30 | // list of files / patterns to exclude 31 | exclude: [], 32 | 33 | // web server port 34 | port: 8080, 35 | 36 | // Start these browsers, currently available: 37 | // - Chrome 38 | // - ChromeCanary 39 | // - Firefox 40 | // - Opera 41 | // - Safari (only Mac) 42 | // - PhantomJS 43 | // - IE (only Windows) 44 | browsers: [ 45 | 'Chrome' 46 | ], 47 | 48 | // Which plugins to enable 49 | plugins: [ 50 | 'karma-chrome-launcher', 51 | 'karma-phantomjs-launcher', 52 | 'karma-firefox-launcher', 53 | 'karma-jasmine', 54 | 'karma-coverage' 55 | ], 56 | 57 | // Continuous Integration mode 58 | // if true, it capture browsers, run tests and exit 59 | singleRun: false, 60 | 61 | colors: true, 62 | 63 | // level of logging 64 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 65 | logLevel: config.LOG_INFO 66 | 67 | // Uncomment the following lines if you are using grunt's server to run the tests 68 | // proxies: { 69 | // '/': 'http://localhost:9000/' 70 | // }, 71 | // URL root prevent conflicts with the site root 72 | // urlRoot: '_karma_' 73 | }); 74 | }; 75 | -------------------------------------------------------------------------------- /test/vector-tiles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vector Tiles 7 | 8 | 9 | 10 | 15 | 16 | 17 |

Vector Tiles

18 |
19 | 24 | 25 |

Based on this sample.

26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /test/webscene-slides-as-directive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Work with Slides in a WebScene 7 | 8 | 9 | 10 | 32 | 33 | 34 |

Southern California Ski Resorts

35 |

Work with Slides in a WebScene using the <esri-webscene-slides> directive

36 |
37 | 38 | 39 |

Based on this sample.

40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 93 | 94 | 95 | --------------------------------------------------------------------------------