├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── AUTHORS.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── README.md ├── arcgis └── server │ ├── WeightedOverlay_7_0_9_histogram.rft.xml │ └── WeightedOverlay_7_1_9_colormap.rft.xml ├── docs └── img │ └── function_manage_raster_function_templates.png ├── karma.conf.js ├── karma.dist.conf.js ├── license.txt ├── package.json ├── src ├── app │ ├── Controller.js │ ├── FeatureLayerPane.js │ ├── MapControls.js │ ├── ModelerPane.js │ ├── config.js │ ├── css │ │ ├── images │ │ │ ├── 16x16_X.png │ │ │ ├── 16x16_minimize_store.png │ │ │ ├── loading_circle_sm.gif │ │ │ └── spriteArrows.png │ │ └── modeler.css │ ├── geometryUtils.js │ ├── nls │ │ └── resources.js │ ├── portal │ │ ├── ModelItemEditor.js │ │ ├── ModelItemList.js │ │ ├── OAuthHelper.js │ │ ├── PortalControls.js │ │ ├── portalUtils.js │ │ └── templates │ │ │ ├── ModelItemEditor.html │ │ │ ├── ModelItemList.html │ │ │ └── PortalControls.html │ ├── reports │ │ ├── AreaBreakdown.js │ │ ├── Chart.js │ │ ├── chartUtils.js │ │ ├── css │ │ │ ├── images │ │ │ │ ├── add.png │ │ │ │ ├── hollow-arrow.png │ │ │ │ └── pie-background-small.png │ │ │ └── reports.css │ │ └── templates │ │ │ ├── AreaBreakdown.html │ │ │ └── Chart.html │ └── templates │ │ ├── FeatureLayerPane.html │ │ ├── MapControls.html │ │ └── ModelerPane.html ├── index.html ├── lib │ ├── bootstrap_v2 │ │ ├── css │ │ │ └── bootstrap.min.css │ │ └── img │ │ │ ├── glyphicons-halflings-white.png │ │ │ └── glyphicons-halflings.png │ └── weighted-overlay-modeler │ │ ├── WeightedOverlayService.js │ │ └── widget │ │ ├── Colormap.js │ │ ├── WeightedOverlayLayerEditor.js │ │ ├── WeightedOverlayLayersSelector.js │ │ ├── WeightedOverlayModelDesigner.js │ │ ├── containerUtils.js │ │ ├── nls │ │ └── resources.js │ │ ├── resources │ │ └── weighted-overlay-modeler.css │ │ └── templates │ │ ├── Colormap.html │ │ ├── RemapRangeEditor.html │ │ ├── WeightedOverlayLayerEditor.html │ │ ├── WeightedOverlayLayerSelector.html │ │ └── WeightedOverlayModelDesigner.html ├── proxy.ashx └── proxy.config └── test ├── data ├── arcgis │ └── rest │ │ └── services │ │ └── Landscape_Modeler │ │ └── USA_Weighted_Overlay │ │ └── ImageServer ├── query.json └── webmap.json └── spec ├── .jshintrc ├── config.js ├── dist.main.js ├── main.js └── weightedOverlayServiceSpec.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Standard to msysgit 5 | *.doc diff=astextplain 6 | *.DOC diff=astextplain 7 | *.docx diff=astextplain 8 | *.DOCX diff=astextplain 9 | *.dot diff=astextplain 10 | *.DOT diff=astextplain 11 | *.pdf diff=astextplain 12 | *.PDF diff=astextplain 13 | *.rtf diff=astextplain 14 | *.RTF diff=astextplain 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # distribution files 2 | dist/* 3 | 4 | # test data 5 | spec/*.json 6 | 7 | # SublimeText 8 | /*.sublime-project 9 | *.sublime-workspace 10 | *.sublime-project 11 | .idea/* 12 | 13 | # Node 14 | node_modules 15 | 16 | ############# 17 | ## Windows detritus 18 | ############# 19 | 20 | # Windows image file caches 21 | Thumbs.db 22 | ehthumbs.db 23 | 24 | # Folder config file 25 | Desktop.ini 26 | 27 | # Recycle Bin used on file shares 28 | $RECYCLE.BIN/ 29 | 30 | # Mac crap 31 | .DS_Store 32 | 33 | # code coverage 34 | coverage -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Configuration, esri jsapi 3 | // Modified from [jshint web defaults][1]. 4 | // Differences between the default and our file are noted 5 | // Options that are commented out completely are uneccesary or problematic for our codebase 6 | // [1] : https://github.com/jshint/jshint/blob/2.x/examples/.jshintrc 7 | // See http://jshint.com/docs/ for more details 8 | 9 | // TODO: turn the following back on and refactor code: 10 | // forin, unused (set to "var"), quotmark 11 | 12 | "maxerr" : 5000, // {int} Maximum error before stopping ** Get ALL the errors ** 13 | 14 | // Enforcing - true = enforce this rule, false = don't enforce this rule 15 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 16 | "camelcase" : false, // true: Identifiers must be in camelCase 17 | "curly" : true, // true: Require {} for every new block or scope 18 | "eqeqeq" : false, // true: Require triple equals (===) for comparison ** Just use triples with undefined, null, false, 0 and 1 ** 19 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() ** Still needed until IE8 support is dropped ** 20 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` ** Avoids confusion and minification errors ** 21 | "latedef" : false, // true: Require variables/functions to be defined before being used 22 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` ** Coding style enforcement ** 23 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 24 | "noempty" : true, // true: Prohibit use of empty blocks 25 | "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) ** Coding style enforcement ** 26 | "plusplus" : false, // true: Prohibit use of `++` & `--` 27 | "quotmark" : false, // Quotation mark consistency: ** Use the same style. Doubles should be used in most cases ** 28 | // false : do nothing (default) 29 | // true : ensure whatever is used is consistent 30 | // "single" : require single quotes 31 | // "double" : require double quotes 32 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 33 | "unused" : false, // true: Require all defined variables be used 34 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode ** Dojo style and existing codebase conflicts ** 35 | "trailing" : false, // true: Prohibit trailing whitespaces 36 | //"indent" : 4, // {int} Number of spaces to use for indentation 37 | //"maxparams" : false, // {int} Max number of formal params allowed per function 38 | //"maxdepth" : false, // {int} Max depth of nested blocks (within functions) 39 | //"maxstatements" : false, // {int} Max number statements per function 40 | //"maxcomplexity" : false, // {int} Max cyclomatic complexity per function 41 | //"maxlen" : false, // {int} Max number of characters per line 42 | 43 | // Relaxing - false = continue to enforce this rule, true = don't enforce this rule (relax it) 44 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 45 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 46 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 47 | "eqnull" : true, // true: Tolerate use of `== null` 48 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 49 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 50 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 51 | // (ex: `for each`, multiple try/catch, function expression…) 52 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 53 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 54 | "funcscope" : true, // true: Tolerate defining variables inside control statements ** Other variable checks keep use from abusing this ** 55 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 56 | "iterator" : false, // true: Tolerate using the `__iterator__` property 57 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 58 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 59 | "laxcomma" : false, // true: Tolerate comma-first style coding 60 | "loopfunc" : true, // true: Tolerate functions being defined in loops ** Almost required in some callback & promise style code ** 61 | "multistr" : false, // true: Tolerate multi-line strings 62 | "proto" : false, // true: Tolerate using the `__proto__` property 63 | "scripturl" : true, // true: Tolerate script-targeted URLs ** If this is being used, there is probably a good reason ** 64 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment 65 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 66 | "sub" : true, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 67 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 68 | "validthis" : true, // true: Tolerate using this in a non-constructor function ** We don't run in `strict mode` & coding style conflicts ** 69 | 70 | // Environments 71 | "browser" : true, // Web Browser (window, document, etc) 72 | "devel" : true, // Development/debugging (alert, confirm, etc) 73 | "couch" : false, // CouchDB 74 | "dojo" : false, // Dojo Toolkit ** Don't use global dojo objects. Use AMD style coding ** 75 | "jquery" : false, // jQuery 76 | "mootools" : false, // MooTools 77 | "node" : false, // Node.js 78 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 79 | "prototypejs" : false, // Prototype and Scriptaculous 80 | "rhino" : false, // Rhino 81 | "worker" : false, // Web Workers ** Make a jshint comment when this is `true` ** 82 | "wsh" : false, // Windows Scripting Host 83 | "yui" : false, // Yahoo User Interface 84 | 85 | // Legacy ** According to jshint docs, these options are NOT to be used or relied on. Removing them. 86 | //"nomen" : false, // true: Prohibit dangling `_` in variables 87 | //"onevar" : false, // true: Allow only one `var` statement per function 88 | //"passfail" : false, // true: Stop on first error 89 | //"white" : false, // true: Check against strict whitespace and indentation rules 90 | 91 | // Custom Globals - additional predefined global variables 92 | // Using both `predef` and `globals` to support tools with older jshint parsers 93 | "predef" : [ 94 | "define", 95 | "require" 96 | ], 97 | "globals" : { // ** `false` = don't allow variable to be redefined locally 98 | "define": false, 99 | "require": false 100 | } 101 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_install: 5 | - currentfolder=${PWD##*/} 6 | - if [ "$currentfolder" != 'generator-esri-appbuilder-js' ]; then cd .. && eval "mv $currentfolder generator-esri-appbuilder-js" && cd generator-esri-appbuilder-js; fi 7 | 8 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | Tom Wayson (https://github.com/tomwayson) 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing). 2 | 3 | The [issues](https://github.com/Esri/landscape-modeler-js/issues) is a place for you to report bugs or request feature enhancements. Please post support questions to the [GeoPlanner for ArcGIS forum](https://geonet.esri.com/community/gis/applications/geoplanner-for-arcgis). -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Gruntfile.js configuration 3 | */ 4 | module.exports = function ( grunt ) { 5 | 6 | var browsers = grunt.option('browsers') ? grunt.option('browsers').split(',') : ['PhantomJS']; 7 | 8 | /* 9 | * Dynamically load the npm tasks 10 | */ 11 | // require( 'matchdep' ).filterDev('grunt-*').forEach( grunt.loadNpmTasks ); 12 | 13 | /* 14 | * Grunt init 15 | */ 16 | grunt.initConfig({ 17 | 18 | /* 19 | * Grunt JSON for project 20 | */ 21 | pkg: grunt.file.readJSON( 'package.json' ), 22 | 23 | /* 24 | * Credit banner 25 | */ 26 | tag: { 27 | banner: "/*!\n" + 28 | " * <%= pkg.name %>\n" + 29 | " * @version <%= pkg.version %>\n" + 30 | " * @author <%= pkg.author %>\n" + 31 | // " * Project: <%= pkg.homepage %>\n" + 32 | " *\n" + 33 | " * <%= pkg.description %>\n" + 34 | // " * Copyright <%= pkg.year %>." + 35 | // " <%= pkg.licenses[0].type %> licensed.\n" + 36 | " */\n" 37 | }, 38 | 39 | /* 40 | * jsHint 41 | */ 42 | jshint: { 43 | files: [ 44 | "src/app/**/*.js", 45 | "!src/app/reports/*.js", 46 | "src/lib/weighted-overlay-modeler/**/*.js" 47 | ], 48 | options: { 49 | jshintrc: ".jshintrc" 50 | } 51 | }, 52 | 53 | /* test */ 54 | karma: { 55 | options: { 56 | configFile: 'karma.conf.js' 57 | }, 58 | run: { 59 | reporters: ['progress'], 60 | browsers: browsers 61 | }, 62 | coverage: { 63 | reporters: ['progress', 'coverage'], 64 | browsers: browsers, 65 | preprocessors: { 66 | 'src/**/*.js': 'coverage' 67 | } 68 | }, 69 | watch: { 70 | singleRun: false, 71 | autoWatch: true, 72 | browsers: browsers 73 | } 74 | }, 75 | 76 | /* 77 | * Concat 78 | */ 79 | // concat: { 80 | // dist: { 81 | // src: ["src/psswrd.js"], 82 | // dest: "dist/psswrd.js" 83 | // }, 84 | // options: { 85 | // banner: "<%= tag.banner %>" 86 | // } 87 | // }, 88 | 89 | // * clean 90 | clean: ["dist"], 91 | 92 | // * UglifyJS 93 | 94 | uglify: { 95 | dynamic_mappings: { 96 | // Grunt will search for "**/*.js" under "lib/" when the "minify" task 97 | // runs and build the appropriate src-dest file mappings then, so you 98 | // don't need to update the Gruntfile when files are added or removed. 99 | files: [ 100 | { 101 | expand: true, // Enable dynamic expansion. 102 | cwd: 'src', // Src matches are relative to this path. 103 | src: ['**/*.js','!tests/**','!app/config.js'], // Actual pattern(s) to match. 104 | dest: 'dist/', // Destination path prefix. 105 | ext: '.js' // Dest filepaths will have this extension. 106 | } 107 | ] 108 | }, 109 | options: { 110 | banner: "<%= tag.banner %>" 111 | } 112 | }, 113 | 114 | // copy html/css files 115 | 116 | copy: { 117 | main: { 118 | files: [ 119 | // copy src html/css/image files to dist, 120 | // but not tests 121 | { 122 | expand: true, // Enable dynamic expansion. 123 | cwd: 'src', // Src matches are relative to this path. 124 | src: ['**/*.html','**/*.css','**/images/*','**/img/*','!tests/**'], // Actual pattern(s) to match. 125 | dest: 'dist/' // Destination path prefix. 126 | } 127 | ] 128 | }, 129 | local: { 130 | files: [ 131 | // copy the local config file unminified 132 | { 133 | expand: true, // Enable dynamic expansion. 134 | cwd: 'src', // Src matches are relative to this path. 135 | src: ['app/config.js','proxy*'], // Actual pattern(s) to match. 136 | dest: 'dist/' // Destination path prefix. 137 | } 138 | 139 | ] 140 | }, 141 | dev: { 142 | files: [ 143 | // copy dev config file unminified 144 | { 145 | expand: true, // Enable dynamic expansion. 146 | cwd: 'src', // Src matches are relative to this path. 147 | src: ['app/config.dev'], // Actual pattern(s) to match. 148 | dest: 'dist/', // Destination path prefix. 149 | ext: '.js' 150 | } 151 | ] 152 | }, 153 | africa: { 154 | files: [ 155 | // copy dev config file unminified 156 | { 157 | expand: true, // Enable dynamic expansion. 158 | cwd: 'src', // Src matches are relative to this path. 159 | src: ['app/config.africa','proxy*'], // Actual pattern(s) to match. 160 | dest: 'dist/', // Destination path prefix. 161 | ext: '.js' 162 | } 163 | ] 164 | } 165 | } 166 | 167 | }); 168 | 169 | // Load the plugin that provides the "jshint" task. 170 | grunt.loadNpmTasks('grunt-contrib-jshint'); 171 | grunt.loadNpmTasks('grunt-contrib-clean'); 172 | grunt.loadNpmTasks('grunt-contrib-uglify'); 173 | grunt.loadNpmTasks('grunt-contrib-copy'); 174 | grunt.loadNpmTasks('grunt-karma'); 175 | /* 176 | * Register tasks 177 | */ 178 | grunt.registerTask("default", [ 179 | "jshint", 180 | // "concat", 181 | 'clean', 182 | "uglify", 183 | "copy:main", 184 | "copy:local" 185 | ]); 186 | 187 | grunt.registerTask("dev", [ 188 | "jshint", 189 | // "concat", 190 | 'clean', 191 | "uglify", 192 | "copy:main", 193 | "copy:dev" 194 | ]); 195 | 196 | grunt.registerTask("africa", [ 197 | "jshint", 198 | // "concat", 199 | 'clean', 200 | "uglify", 201 | "copy:main", 202 | "copy:africa" 203 | ]); 204 | 205 | grunt.registerTask("hint", ["jshint"]); 206 | 207 | grunt.registerTask("test", ['jshint', 'karma:run']); 208 | 209 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Esri Landscape Modeler 2 | 3 | [![Build Status](https://travis-ci.org/Esri/landscape-modeler-js.svg)](https://travis-ci.org/Esri/landscape-modeler-js) 4 | 5 | 6 | Landscape Modeler is a sample JavaScript web application that demonstrates how to perform site suitablility analysis by leveraging the landscape data hosted on ArcGIS Online and the speed of image service raster functions. This is ideal when a users want to test and share their ideas about suitability or risk analysis models at multiple scales or over a large area. 7 | 8 | ![Landscape Modeler Screenshot](http://resources.arcgis.com/en/help/landscape-modeler/guide/0321/GUID-D9C6C1DC-4597-4997-B72A-C303D49D3423-web.png) 9 | 10 | [View it live](http://landscapemodeler.arcgis.com/) 11 | 12 | Landscape Modeler is built on top of the [ArcGIS API for JavaScript](https://developers.arcgis.com/en/javascript/) and ArcGIS Server image services. By default it is configured to use the 13 | [ready-to-use landscape layers on ArcGIS Online](http://esri.maps.arcgis.com/home/group.html?owner=esri&title=Landscape%20Layers). However, the [application can be configured](https://github.com/Esri/landscape-modeler-js/wiki/Configuring-the-Application-to-Work-with-Other-Data) to use other data that is exposed as an image service published with a weighted overlay raster function. 14 | 15 | ## Features 16 | 17 | Landscape Modeler allows any user with a valid [ArcGIS Online](http://www.arcgis.com/) organizational account to: 18 | * Design weighted overlay models using the [ready-to-use landscape layers on ArcGIS Online](http://esri.maps.arcgis.com/home/group.html?owner=esri&title=Landscape%20Layers). 19 | * Run those models in real time at a variety of scales and extents. 20 | * Save models as ArcGIS Online web maps that can be loaded into other applications or ArcGIS for Desktop. 21 | * Load and view models that have been saved and shared by others. 22 | * Overlay the model with a feature service and see graphs of model results by feature type. 23 | 24 | ## Instructions 25 | 26 | ### Using the Application 27 | 28 | See the [Landscape Modeler Help](http://resources.arcgis.com/en/help/landscape-modeler/guide/) for instructions on how to use the application. 29 | 30 | ### Development Instructions 31 | 32 | 1. Fork and clone the repository 33 | 2. `cd landscape-modeler-js` 34 | 3. `npm install` for Grunt/Karma 35 | 36 | [See the wiki](https://github.com/Esri/landscape-modeler-js/wiki) for instructions on configuring and deploying the application. 37 | 38 | ### Configuring the Application 39 | 40 | If you wish to configure the application to use a different weighted overlay image service (i.e. your own data), [see the Configuring the Application to Work with Other Data page of the wiki](https://github.com/Esri/landscape-modeler-js/wiki/Configuring-the-Application-to-Work-with-Other-Data). 41 | 42 | ## Requirements 43 | 44 | * Must be configured to point to an image service that exposes weighted overlay raster functions 45 | * If you plan to host the weighed overlay image service, it must be running on ArcGIS Server v10.2 or greater 46 | 47 | ### Dependencies 48 | 49 | #### Runtime 50 | 51 | * [ArcGIS for JavaScript API](https://developers.arcgis.com/en/javascript/) v3.5 or later 52 | 53 | #### Development (optional) 54 | 55 | * [Karma](http://karma-runner.github.io) (for running tests) 56 | * [Grunt](http://gruntjs.com) (for builds) 57 | 58 | ## Resources 59 | 60 | * [Landscape Modeler Help](http://resources.arcgis.com/en/help/landscape-modeler/guide/) 61 | * [Landscape Modeler Wiki](https://github.com/Esri/landscape-modeler-js/wiki) 62 | * [GeoNet Forum](https://geonet.esri.com/community/gis/applications/geoplanner-for-arcgis) 63 | * [ArcGIS for JavaScript API](https://developers.arcgis.com/en/javascript/) 64 | * [ArcGIS REST Services](http://resources.arcgis.com/en/help/arcgis-rest-api/) 65 | * [@esri](http://twitter.com/esri) 66 | 67 | ## Support 68 | 69 | If you experience problems configuring the application, carefully review all documentation including the [Landscape Modeler help](http://resources.arcgis.com/en/help/landscape-modeler/guide/) and the [Wiki](https://github.com/Esri/landscape-modeler-js/wiki). 70 | 71 | If you still need help, please post questions or comments to the [GeoPlanner for ArcGIS forum](https://geonet.esri.com/community/gis/applications/geoplanner-for-arcgis). 72 | 73 | ## Issues 74 | 75 | Find a bug or want to request a new feature? Please let us know by [submitting an issue](https://github.com/Esri/landscape-modeler-js/issues). 76 | 77 | ## Contributing 78 | 79 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing). 80 | 81 | ## Credit 82 | 83 | Landscape Modeler was developed along with [GeoPlanner for ArcGIS](http://www.esri.com/software/geoplanner-for-arcgis) by a team comprised of members from both the [ArcGIS Content](http://www.esri.com/data/find-data) and [Professional Services](http://www.esri.com/services/professional-services) divisions at Esri. 84 | 85 | Landscape Modeler is based on work done by the [Esri Application Prototypes Lab](https://maps.esri.com/demo/) and the [Esri Imagery Team](http://resources.arcgis.com/en/communities/imagery/) to create applications that perform fast weighted overlay analysis by leveraging ArcGIS Server image service raster functions. 86 | 87 | ## Licensing 88 | Copyright 2013 Esri 89 | 90 | Licensed under the Apache License, Version 2.0 (the "License"); 91 | you may not use this file except in compliance with the License. 92 | You may obtain a copy of the License at 93 | 94 | http://www.apache.org/licenses/LICENSE-2.0 95 | 96 | Unless required by applicable law or agreed to in writing, software 97 | distributed under the License is distributed on an "AS IS" BASIS, 98 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 99 | See the License for the specific language governing permissions and 100 | limitations under the License. 101 | 102 | A copy of the license is available in the repository's [license.txt]( https://raw.github.com/Esri/esri-leaflet/master/license.txt) file. 103 | 104 | [](Esri Tags: ArcGIS Web landscape Weighted-overlay raster analysis) 105 | [](Esri Language: JavaScript) 106 | -------------------------------------------------------------------------------- /docs/img/function_manage_raster_function_templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/docs/img/function_manage_raster_function_templates.png -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | // base path, that will be used to resolve files and exclude 4 | basePath: '', 5 | 6 | frameworks: ['mocha', 'chai', 'dojo'], 7 | 8 | // list of files / patterns to load in the browser 9 | files: [ 10 | 'test/spec/main.js', 11 | 12 | // all the sources, tests, data 13 | {pattern: 'src/lib/weighted-overlay-modeler/*.js', included: false}, 14 | {pattern: 'test/spec/*.js', included: false}, 15 | {pattern: 'test/data/*.json', included: false}, 16 | {pattern: 'test/data/arcgis/**/*', included: false} 17 | ], 18 | 19 | // list of files to exclude 20 | // exclude: [ 21 | // ], 22 | 23 | // test results reporter to use 24 | // possible values: dots || progress 25 | reporters: ['progress'], 26 | 27 | 28 | // web server port 29 | port: 9876, 30 | 31 | 32 | // proxy for cross domain requests 33 | // proxies: { 34 | // '/arcgis/': 'https://landscape3.arcgis.com/arcgis/' 35 | // }, 36 | 37 | 38 | // cli runner port 39 | runnerPort: 9100, 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | 46 | // level of logging 47 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 48 | logLevel: config.LOG_INFO, 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: true, 53 | 54 | 55 | // Start these browsers, currently available: 56 | // - Chrome 57 | // - ChromeCanary 58 | // - Firefox 59 | // - Opera 60 | // - Safari 61 | // - 62 | browsers: ['PhantomJS'], 63 | 64 | // Continuous Integration mode 65 | // if true, it capture browsers, run tests and exit 66 | singleRun: true, 67 | 68 | // Configure the coverage reporters 69 | coverageReporter: { 70 | reporters:[ 71 | {type: 'html', dir:'coverage/'}, 72 | {type: 'text'} 73 | ] 74 | }, 75 | 76 | plugins: [ 77 | 'karma-dojo', 78 | 'karma-mocha', 79 | 'karma-chai', 80 | 'karma-coverage', 81 | 'karma-phantomjs-launcher', 82 | 'karma-chrome-launcher', 83 | 'karma-firefox-launcher', 84 | 'karma-ie-launcher' 85 | ] 86 | }); 87 | }; -------------------------------------------------------------------------------- /karma.dist.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | // base path, that will be used to resolve files and exclude 4 | basePath: '', 5 | 6 | frameworks: ['jasmine', 'dojo'], 7 | 8 | // list of files / patterns to load in the browser 9 | files: [ 10 | 'spec/dist.main.js', 11 | 12 | // all the sources, tests 13 | {pattern: 'dist/*.js', included: false}, 14 | {pattern: 'spec/*.js', included: false} 15 | ], 16 | 17 | 18 | // test results reporter to use 19 | // possible values: dots || progress 20 | reporters: ['dots'], 21 | 22 | 23 | // web server port 24 | port: 9876, 25 | 26 | 27 | // cli runner port 28 | runnerPort: 9100, 29 | 30 | 31 | // enable / disable colors in the output (reporters and logs) 32 | colors: true, 33 | 34 | 35 | // level of logging 36 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 37 | logLevel: config.LOG_INFO, 38 | 39 | 40 | // enable / disable watching file and executing tests whenever any file changes 41 | autoWatch: true, 42 | 43 | 44 | // Start these browsers, currently available: 45 | // - Chrome 46 | // - ChromeCanary 47 | // - Firefox 48 | // - Opera 49 | // - Safari 50 | // - PhantomJS 51 | browsers: ['Chrome'], 52 | 53 | // Continuous Integration mode 54 | // if true, it capture browsers, run tests and exit 55 | singleRun: false, 56 | 57 | plugins: [ 58 | 'karma-dojo', 59 | 'karma-jasmine', 60 | 'karma-chrome-launcher', 61 | 'karma-firefox-launcher' 62 | ] 63 | }); 64 | }; -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License – 2.0 2 | 3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 4 | 5 | 1. Definitions. 6 | 7 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 8 | 9 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 10 | 11 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 12 | 13 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 14 | 15 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 16 | 17 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 18 | 19 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 20 | 21 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 22 | 23 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 24 | 25 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 26 | 27 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 28 | 29 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 30 | 31 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 32 | 33 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 34 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 35 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 36 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 37 | 38 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 39 | 40 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 41 | 42 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 43 | 44 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 45 | 46 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 47 | 48 | END OF TERMS AND CONDITIONS 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "landscape-modeler-js", 3 | "version": "0.2.1", 4 | "description": "A web application for designing, running, and saving weighted overlay models using the Esri ArcGIS API for JavaScript and ArcGIS Server image services.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/ArcGIS/landscape-modeler-js.git" 8 | }, 9 | "main": "", 10 | "keywords": [ 11 | "landscape", 12 | "weighted overlay", 13 | "ArcGIS", 14 | "image service", 15 | "raster analysis" 16 | ], 17 | "scripts": { 18 | "test": "node -e \"require('grunt').tasks(['test']);\"" 19 | }, 20 | "author": "Tom Wayson (http://tomwayson.com)", 21 | "license": "Apache", 22 | "devDependencies": { 23 | "grunt": "^0.4.2", 24 | "grunt-contrib-clean": "^0.5.0", 25 | "grunt-contrib-copy": "^0.5.0", 26 | "grunt-contrib-jshint": "^0.6.4", 27 | "grunt-contrib-uglify": "^0.2.0", 28 | "grunt-karma": "^0.8.3", 29 | "karma": "^0.12.16", 30 | "karma-chai": "^0.1.0", 31 | "karma-chrome-launcher": "^0.1.3", 32 | "karma-coverage": "^0.1", 33 | "karma-dojo": "^0.0.1", 34 | "karma-firefox-launcher": "^0.1.3", 35 | "karma-ie-launcher": "^0.1.1", 36 | "karma-mocha": "^0.1.0", 37 | "karma-mocha-reporter": "^0.2.5", 38 | "karma-phantomjs-launcher": "^0.1.4", 39 | "karma-safari-launcher": "^0.1.1" 40 | }, 41 | "readmeFilename": "README.md" 42 | } 43 | -------------------------------------------------------------------------------- /src/app/Controller.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/lang", 16 | "dojo/dom", 17 | "dojo/dom-construct", 18 | "dojo/topic", 19 | 20 | "esri/map", 21 | "esri/domUtils", 22 | "esri/config", 23 | 24 | "./MapControls", 25 | "./ModelerPane", 26 | "./FeatureLayerPane" 27 | ], 28 | function ( 29 | declare, lang, dom, domConstruct, topic, 30 | Map, domUtils, esriConfig, 31 | MapControls, ModelerPane, FeatureLayerPane, AreaBreakdown 32 | ) { 33 | 34 | return declare(null, { 35 | constructor: function(args) { 36 | declare.safeMixin(this, args); 37 | }, 38 | // instanciate top level widgets 39 | // and wire up communication between them 40 | init: function(options) { 41 | var _this = this; 42 | 43 | // init loading spinner 44 | this.loadingNode = dom.byId(options.loadingNode); 45 | 46 | // init map 47 | esriConfig.defaults.io.proxyUrl = this.config.proxyUrl; 48 | this.map = new Map(options.mapNode, lang.mixin(this.config.mapOptions, { 49 | sliderPosition: "top-right" 50 | })); 51 | this.map.on("update-start", function(){ 52 | _this.showLoading(); 53 | }); 54 | this.map.on("update-end", function(){ 55 | _this.hideLoading(); 56 | }); 57 | this.map.on("layer-add-result", function(results){ 58 | var err = results.error; 59 | if (err) { 60 | err.name = "LayerAddError"; 61 | _this.showError(results.error); 62 | } 63 | _this.hideLoading(); 64 | }); 65 | 66 | // init map controls 67 | this.mapControls = new MapControls({ 68 | map: this.map 69 | }, options.mapControlsNode); 70 | this.mapControls.startup(); 71 | 72 | // init app/modeler controls 73 | this.modelerPane = new ModelerPane({ 74 | map: this.map, 75 | portalUser: this.portalUser, 76 | config: this.config // TODO: just the config options relevant to the app 77 | }, options.modelerNode); 78 | this.modelerPane.startup(); 79 | 80 | // init feature layer controls 81 | this.featureLayerPane = new FeatureLayerPane({ 82 | map: this.map, 83 | config: this.config // TODO: just the config options relevant to the feature layer 84 | }, options.featurePaneNode); 85 | this.featureLayerPane.startup(); 86 | this.featureLayerPane.on("layer-load-start", function(){ 87 | _this.showLoading(); 88 | }); 89 | 90 | // wire up topics for communication between modules 91 | topic.subscribe(this.config.topics.MODELER_SIGNOUT, function() { 92 | _this.oAuthHelper.signOut(); 93 | }); 94 | }, 95 | showLoading: function() { 96 | domUtils.show(this.loadingNode); 97 | }, 98 | hideLoading: function() { 99 | domUtils.hide(this.loadingNode); 100 | }, 101 | showError: function(error) { 102 | // TODO: replace alert w/ dialog 103 | window.alert(error.toString()); 104 | console.error(error); 105 | } 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /src/app/FeatureLayerPane.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/lang", 16 | "dojo/_base/array", 17 | "dojo/on", 18 | "dojo/Evented", 19 | "dojo/dom-construct", 20 | "dojo/topic", 21 | 22 | "dijit/_WidgetBase", 23 | "dijit/_TemplatedMixin", 24 | "dijit/_WidgetsInTemplateMixin", 25 | 26 | "esri/layers/FeatureLayer", 27 | "esri/dijit/Legend", 28 | "esri/dijit/editing/Editor", 29 | "esri/dijit/editing/TemplatePicker", 30 | 31 | "./reports/AreaBreakdown", 32 | 33 | "dojo/text!./templates/FeatureLayerPane.html", 34 | "dojo/i18n!./nls/resources", 35 | 36 | "dijit/TitlePane", 37 | "dijit/Dialog", 38 | "dijit/form/Button", 39 | "dijit/form/ValidationTextBox", 40 | "dijit/form/HorizontalSlider", 41 | "dojo/parser", 42 | "dojox/validate/regexp" 43 | 44 | ], function ( 45 | declare, lang, array, on, Evented, domConstruct, topic, 46 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, 47 | FeatureLayer, Legend, Editor, TemplatePicker, 48 | AreaBreakdown, 49 | template, i18n 50 | ) { 51 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 52 | templateString: template, 53 | i18n: i18n, 54 | baseClass: "mdlrFeatureLayerPane", 55 | featureLayerUrl: "", 56 | typeField: "", 57 | // many layer properties are not available 58 | // until the layer is added to the map, 59 | // so we have to handle when this layer is loaded 60 | // and then build the UI based on the layers capabilities 61 | _setMapAttr: function(newMap) { 62 | var _this = this; 63 | // set the reference to the map 64 | // and listen for layers added 65 | this.map = newMap; 66 | this.own(this.map.on("layer-add-result", function(results){ 67 | if (_this.featureLayer && results.layer && results.layer.id === _this.featureLayer.id) { 68 | if (!results.error) { 69 | _this._intit(); 70 | } 71 | } 72 | })); 73 | }, 74 | postCreate: function() { 75 | var _this = this; 76 | this.inherited(arguments); 77 | // wire up events 78 | // only allow valid urls 79 | this.own(on(this.featureLayerUrlNode, "Change", function(/*e*/) { 80 | var loadButton = _this.loadButtonNode; 81 | loadButton.set("disabled", !_this.featureLayerUrlNode.isValid()); 82 | loadButton.focus(); 83 | })); 84 | // controls to manage layer visibility 85 | this.own(on(this.visibilityNode, "change", function() { 86 | _this.setVisibility(this.checked); 87 | })); 88 | this.own(on(this.transparencyNode, "Change", function() { 89 | _this.setTransparency(this.value); 90 | })); 91 | // when model is cleared, remove charts 92 | this.own(topic.subscribe(this.config.topics.MODELER_MODEL_UPDATED, function (sender, args) { 93 | if (!(args && args.model && args.model.overlayLayers && args.model.overlayLayers.length > 0)) { 94 | _this._destroyChartControls(); 95 | } 96 | })); 97 | }, 98 | setVisibility: function(visible) { 99 | if (this.visibilityNode.checked !== visible) { 100 | this.visibilityNode.checked = visible; 101 | } 102 | if (this.featureLayer) { 103 | if (this.visibilityNode.checked) { 104 | this.featureLayer.show(); 105 | } else { 106 | this.featureLayer.hide(); 107 | } 108 | } 109 | }, 110 | setTransparency: function(transparency) { 111 | if (transparency >= 0 && transparency <= 1) { 112 | // keep value in sync 113 | if (this.transparencyNode.value !== transparency) { 114 | this.transparencyNode.set("value", transparency); 115 | } 116 | if (this.featureLayer) { 117 | // set feature layer opacity to invers of transparency value 118 | this.featureLayer.setOpacity(1 - transparency); 119 | if (this.legend) { 120 | // refresh legend 121 | this.legend.refresh(); 122 | } 123 | } 124 | } 125 | }, 126 | _intit: function() { 127 | var layer = this.featureLayer; 128 | if (layer) { 129 | // show layer 130 | this.setVisibility(true); 131 | // init layer controls 132 | this.nameNode.innerHTML = layer.name; 133 | document.querySelector(".mdlrFeatureLayerVisibility").style["display"] = ""; 134 | // enable remove button 135 | this.removeFeaturesButton.set("disabled", false); 136 | // LATER: better check than declaredClass OR add support for class break renderers 137 | // layer.renderer.infos && layer.renderer.infos.length > 0 && layer.renderer.infos[0].value OR 138 | if (layer.geometryType === "esriGeometryPolygon" && layer.renderer.attributeField && layer.renderer.declaredClass === "esri.renderer.UniqueValueRenderer") { 139 | // enable charts 140 | this.typeField = layer.renderer.attributeField; 141 | this.showChartsButtonNode.set("disabled", false); 142 | this.showChartsButtonNode.set("title", ""); 143 | // test for editing 144 | if (layer.isEditable()) { 145 | // configure template editor 146 | if (layer.types && layer.typeIdField) { 147 | // sort layer types 148 | if (layer.types.sort) { 149 | layer.types.sort(function(a, b) { 150 | return a.name > b.name; 151 | }); 152 | } 153 | } 154 | // show template picker and editor 155 | this._showTemplatePicker(); 156 | this._showEditor(); 157 | } else { 158 | // show legend 159 | this._initLegend(); 160 | } 161 | } else { 162 | this.typeField = null; 163 | this.showChartsButtonNode.set("disabled", true); 164 | this.showChartsButtonNode.set("title", "Charts are only available for polygon feature layers with unique value renderers"); 165 | // show legend 166 | this._initLegend(); 167 | } 168 | } 169 | }, 170 | _destroyLayerControls: function() { 171 | // hide visibility elements 172 | document.querySelector(".mdlrFeatureLayerVisibility").style["display"] = "none"; 173 | this.nameNode.innerHTML = ""; 174 | // destroy widgets 175 | if (this.legend) { 176 | this.legend.destroy(); 177 | this.legend = null; 178 | } 179 | if (this.editor) { 180 | this.editor.destroy(); 181 | this.editor = null; 182 | } 183 | if (this.templatePicker) { 184 | this.templatePicker.destroy(); 185 | this.templatePicker = null; 186 | } 187 | this._destroyChartControls(); 188 | }, 189 | _destroyChartControls: function() { 190 | if (this.chartPane) { 191 | this.chartPane.destroy(); 192 | this.chartPane = null; 193 | } 194 | }, 195 | _initLegend: function() { 196 | var layer = this.featureLayer; 197 | if (layer) { 198 | this.legend = new Legend({ 199 | map: this.map, 200 | layerInfos: [{layer:layer, title:""}] 201 | }, domConstruct.create('div', null, this.legendNode, 'only')); 202 | this.legend.startup(); 203 | } 204 | }, 205 | _onLoadFeaturesClick: function(/*e*/) { 206 | // show load layer dialog 207 | this.loadDialog.show(); 208 | }, 209 | _showCharts: function(/*e*/) { 210 | if (this.typeField) { 211 | // remove if already exists 212 | if (this.chartPane) { 213 | this.chartPane.destroy(); 214 | this.chartPane = null; 215 | } 216 | // create and show new dashboard report 217 | this.chartPane = new AreaBreakdown({ 218 | config: this.config, 219 | fPolygonLayer: this.featureLayer, 220 | selectedFieldName: this.featureLayer.renderer.attributeField // this.typeField, 221 | }, domConstruct.create("div", null, this.chartPaneNode)); 222 | this.chartPane.startup(); 223 | this.chartPane.show(); 224 | } 225 | }, 226 | _onCancelClick: function(/*e*/) { 227 | this.loadDialog.hide(); 228 | }, 229 | _onLoadClick: function(/*e*/) { 230 | // validate: 231 | if (this.featureLayerUrlNode.isValid()) { 232 | // set the feature layer 233 | this.setFeatureLayerUrl(this.featureLayerUrlNode.value); 234 | this.loadDialog.hide(); 235 | } 236 | }, 237 | setFeatureLayerUrl: function(url) { 238 | var map = this.map; 239 | // LATER: merge w/ options from config 240 | // or load dialog 241 | var options = { 242 | id: "featureLayer", 243 | mode: FeatureLayer.MODE_ONDEMAND, 244 | outFields: ['*'], 245 | visible: this.visibilityNode.checked, 246 | opacity: 1 - this.transparencyNode.value 247 | }; 248 | // TODO: validate URL here too? 249 | if (url) { 250 | // remove current feature layer if any 251 | this.removeFeatureLayer(); 252 | // add new feature service to the map 253 | this.emit("layer-load-start", { 254 | url: url, 255 | options: options 256 | }); 257 | this.featureLayer = new FeatureLayer(url, options); 258 | map.addLayer(this.featureLayer); 259 | } 260 | }, 261 | _showTemplatePicker: function() { 262 | var container = domConstruct.create('div', null, this.templatePickerNode, 'only'); 263 | this.templatePicker = new TemplatePicker({ 264 | featureLayers: [this.featureLayer], 265 | grouping: true, 266 | rows: 'auto', 267 | columns: 3 268 | }, container); 269 | this.templatePicker.startup(); 270 | }, 271 | _showEditor: function () { 272 | var container = domConstruct.create('div', null, this.editorNode, 'only'); 273 | var settings = { 274 | map: this.map, 275 | templatePicker: this.templatePicker, 276 | layerInfos:[{featureLayer:this.featureLayer}], 277 | geometryService: this.config.geometryServiceUrl, 278 | toolbarVisible: true, 279 | // TODO: set freehand as default 280 | createOptions: { 281 | polylineDrawTools:[ Editor.CREATE_TOOL_FREEHAND_POLYLINE], 282 | polygonDrawTools: [ Editor.CREATE_TOOL_FREEHAND_POLYGON 283 | ] 284 | }, 285 | toolbarOptions: {} 286 | }; 287 | this.editor = new Editor({settings: settings}, container); 288 | this.editor.startup(); 289 | }, 290 | _onRemoveFeaturesClick: function() { 291 | this.removeFeatureLayer(); 292 | }, 293 | // remove layer from map 294 | // and destroy controls 295 | removeFeatureLayer: function() { 296 | var layer; 297 | if (this.featureLayer) { 298 | if (this.map) { 299 | layer = this.map.getLayer(this.featureLayer.id); 300 | if (layer) { 301 | this.map.removeLayer(layer); 302 | } 303 | } 304 | // destroy controls 305 | this._destroyLayerControls(); 306 | // disable charts and remove button 307 | this.showChartsButtonNode.set("disabled", true); 308 | this.removeFeaturesButton.set("disabled", true); 309 | // remove the type field 310 | this.typeField = ""; 311 | // remove the ref to the layer 312 | this.featureLayer = null; 313 | } 314 | } 315 | }); 316 | }); 317 | -------------------------------------------------------------------------------- /src/app/MapControls.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/array", 16 | "dojo/on", 17 | 18 | "dijit/_WidgetBase", 19 | "dijit/_TemplatedMixin", 20 | "dijit/_WidgetsInTemplateMixin", 21 | 22 | "esri/dijit/BasemapGallery", 23 | "esri/dijit/Geocoder", 24 | 25 | "dojo/text!./templates/MapControls.html", 26 | "dojo/i18n!./nls/resources", 27 | 28 | "dijit/TitlePane", 29 | "dijit/layout/ContentPane" 30 | ], function ( 31 | declare, array, on, 32 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, 33 | BasemapGallery, Geocoder, 34 | template, i18n 35 | ) { 36 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { 37 | templateString: template, 38 | i18n: i18n, 39 | baseClass: "mdlrMapControls", 40 | // set local reference to map and 41 | // (re)create the basemap gallery and geocoder widgets 42 | _setMapAttr: function(newMap) { 43 | var _this = this; 44 | this.map = newMap; 45 | if (this.basemapGallery) { 46 | this.basemapGallery.destroy(); 47 | } 48 | this.basemapGallery = new BasemapGallery({ 49 | showArcGISBasemaps: true, 50 | map: this.map 51 | }, this.basemapGalleryNode); 52 | this.basemapGallery.startup(); 53 | // close the basemap gallery after selected 54 | this.own(on(this.basemapGallery, "SelectionChange", function() { 55 | _this.updateMapBasemapInfo(); 56 | _this.basemapGalleryTitlePane.toggle(); 57 | })); 58 | if (this.geocoder) { 59 | this.geocoder.destroy(); 60 | } 61 | this.geocoder = new Geocoder({ 62 | map: this.map 63 | }, this.geocoderNode); 64 | this.geocoder.startup(); 65 | }, 66 | updateMapBasemapInfo: function() { 67 | var selectedBasemap = this.basemapGallery.getSelected(); 68 | var basemapLayerIds = []; 69 | if (selectedBasemap) { 70 | array.forEach(this.map.layerIds, function(layerId) { 71 | var layer = this.map.getLayer(layerId); 72 | if (array.some(selectedBasemap.layers, function(basemapLayer) {return basemapLayer.url === layer.url;})) { 73 | basemapLayerIds.push(layer.id); 74 | } 75 | }, this); 76 | if (basemapLayerIds.length > 0) { 77 | this.map.basemapLayerIds = basemapLayerIds; 78 | } 79 | } 80 | } 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /src/app/ModelerPane.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/lang", 16 | "dojo/_base/array", 17 | "dojo/on", 18 | "dojo/Evented", 19 | "dojo/topic", 20 | "dojo/dom", 21 | "dojo/dom-construct", 22 | "dojo/dom-attr", 23 | "dojo/Deferred", 24 | 25 | "dijit/_WidgetBase", 26 | "dijit/_TemplatedMixin", 27 | "dijit/_WidgetsInTemplateMixin", 28 | "dijit/form/Button", 29 | 30 | "esri/layers/ArcGISImageServiceLayer", 31 | "esri/geometry/Extent", 32 | "esri/tasks/GeometryService", 33 | "esri/tasks/ProjectParameters", 34 | "esri/domUtils", 35 | 36 | "modeler/WeightedOverlayService", 37 | "modeler/widget/WeightedOverlayLayersSelector", 38 | "modeler/widget/WeightedOverlayModelDesigner", 39 | 40 | "./geometryUtils", 41 | "./portal/PortalControls", 42 | "./reports/chartUtils", 43 | 44 | "dojo/text!./templates/ModelerPane.html", 45 | "dojo/i18n!./nls/resources", 46 | 47 | "dijit/form/Button" 48 | ], 49 | function ( 50 | declare, lang, array, on, Evented, topic, dom, domConstruct, domAttr, Deferred, 51 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Button, 52 | ArcGISImageServiceLayer, Extent, GeometryService, ProjectParameters, domUtils, 53 | WeightedOverlayService, WeightedOverlayLayersSelector, WeightedOverlayModelDesigner, 54 | geometryUtils, 55 | PortalControls, chartUtils, 56 | template, i18n 57 | ) { 58 | 59 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 60 | 61 | templateString: template, 62 | i18n: i18n, 63 | baseClass: "mdlrAppPane", 64 | 65 | _setPortalUserAttr: function(newPortalUser) { 66 | this.portalUser = newPortalUser; 67 | // show logged in user name 68 | this.userNameNode.innerHTML = this.portalUser.fullName; 69 | // reset portal controls? 70 | if (this.portalControls && this.portal.set) { 71 | this.portalControls.set("portal", this.portalUser.portal); 72 | } 73 | }, 74 | 75 | // read config to set the app/page title, help link 76 | // and initialize the geometry service 77 | postCreate: function() { 78 | this.inherited(arguments); 79 | this.appTitleNode.innerHTML = this.config.appTitle; 80 | document.title = this.config.appTitle; 81 | domAttr.set(this.helpLink, "href", this.config.helpUrl); 82 | this._geometryService = new GeometryService(this.config.geometryServiceUrl); 83 | }, 84 | 85 | // cretate weighted overlay image service 86 | // get raster info from service 87 | // then initialize remaining widgets 88 | startup: function() { 89 | this.inherited(arguments); 90 | var _this = this; 91 | this.weightedOverlayService = new WeightedOverlayService( 92 | new ArcGISImageServiceLayer(this.config.weightedOverlayService.url), 93 | this.config.weightedOverlayService.options); 94 | this.own(this.weightedOverlayService.imageServiceLayer.on("load", function(loadResults) { 95 | _this.weightedOverlayServiceSR = loadResults.layer.spatialReference; 96 | })); 97 | this.weightedOverlayModel = this.weightedOverlayService.createNewModel(); 98 | this.weightedOverlayService.initRasterLayers().then(function(/*rasterLayers*/){ 99 | _this._init(); 100 | }); 101 | }, 102 | 103 | // init widgets and wire up events/topics 104 | _init: function() { 105 | var _this = this; 106 | 107 | // init portal controls 108 | this.portalControls = new PortalControls(lang.mixin(this.config.portalOptions, { 109 | map: this.map, 110 | portal: this.portalUser.portal, 111 | weightedOverlayService: this.weightedOverlayService 112 | }), this.portalControlsNode); 113 | this.portalControls.startup(); 114 | this.portalControls.on("item-loaded", function(loadedItem) { 115 | _this.modelItemLoaded(loadedItem); 116 | }); 117 | this.portalControls.startup(); 118 | 119 | // init select layers view 120 | this.selectLayersView = new WeightedOverlayLayersSelector({ 121 | model: this.weightedOverlayModel, 122 | weightedOverlayService: this.weightedOverlayService, 123 | map: this.map, 124 | previewLayerOptions: { 125 | id: "preview", 126 | visible: false 127 | } 128 | }, this.selectLayersNode); 129 | this.selectLayersView.startup(); 130 | 131 | // init design model view 132 | this.designModelView = new WeightedOverlayModelDesigner({ 133 | model: this.weightedOverlayModel, 134 | weightedOverlayService: this.weightedOverlayService 135 | }, this.designModelNode); 136 | this.designModelView.startup(); 137 | 138 | // when a model is run 139 | // enable saving 140 | // hide the preview layer 141 | // notify the rest of the app 142 | this.own(on(this.designModelView, "model-run", function(model) { 143 | _this.weightedOverlayModel = model; 144 | _this.portalControls.set("model", model); 145 | _this.selectLayersView.hidePreviewLayer(); 146 | topic.publish(_this.config.topics.MODELER_MODEL_UPDATED, _this, { 147 | model: model 148 | }); 149 | })); 150 | 151 | // when a model is cleared 152 | // disable saving of model 153 | // clear model title/description 154 | // update selected layers 155 | // notify the rest of the app 156 | this.own(on(this.designModelView, "model-clear", function(model) { 157 | var modelItem; 158 | _this.weightedOverlayModel = model; 159 | if (_this.portalControls) { 160 | _this.portalControls.set("model", null); 161 | modelItem = _this.portalControls.modelItem; 162 | if (modelItem) { 163 | modelItem.title = ""; 164 | modelItem.description = ""; 165 | _this.portalControls.set("modelItem", modelItem); 166 | } 167 | } 168 | _this.selectLayersView.set("model", model); 169 | topic.publish(_this.config.topics.MODELER_MODEL_UPDATED, _this, { 170 | model: model 171 | }); 172 | })); 173 | 174 | // adding model layer to the map 175 | this.map.addLayer(this.weightedOverlayService.imageServiceLayer); 176 | 177 | // respond to topics 178 | topic.subscribe(this.config.topics.CHART_FEATURETYPE_SELECTED, function (sender, args) { 179 | var data = { 180 | type: args.type, 181 | currentModelName: _this.portalControls ? _this.portalControls.getTitle() : "" 182 | }; 183 | if (args.geometries.length > 0) { 184 | _this.getChartData(args.geometries).then(function(chartData) { 185 | data.dataset = chartData.dataset; 186 | data.colors = chartData.colors; 187 | sender.showModelCharts(data); 188 | }); 189 | } 190 | }); 191 | 192 | // finally, load default model if any 193 | if (this.config.modelItemId) { 194 | this.portalControls.loadModelItem(this.config.modelItemId); 195 | } 196 | }, 197 | 198 | // hide model design view and 199 | // show select layers view 200 | showSelectOverlayLayersView: function() { 201 | domUtils.hide(this.designModelViewNode); 202 | domUtils.show(this.selectLayersViewNode); 203 | }, 204 | 205 | // hide model design view and 206 | // show select layers view 207 | showDesignModelView: function() { 208 | // update design view's model 209 | this.designModelView.set("model", this.weightedOverlayModel); 210 | domUtils.hide(this.selectLayersViewNode); 211 | domUtils.show(this.designModelViewNode); 212 | }, 213 | // whenever an item is loaded from portal: 214 | // get the model from the item data 215 | // update the select model layers view 216 | // update the design model view 217 | // show the design model tab 218 | // run the model 219 | modelItemLoaded: function(loadedItem) { 220 | var itemData = loadedItem.itemData; 221 | if (itemData) { 222 | if (itemData.operationalLayers) { 223 | // loading a web map 224 | // parse model properties out of rendering rule 225 | // TODO: this.set("weightedOverlayModel") 226 | this.weightedOverlayModel = this.weightedOverlayService.operationalLayersToModel(itemData.operationalLayers); 227 | if (itemData.mapOptions && itemData.mapOptions.extent) { 228 | // set map extent 229 | this.map.setExtent(new Extent(itemData.mapOptions.extent)); 230 | } 231 | // LATER: model layer basemap? 232 | } else { 233 | // loading an image service layer 234 | this.weightedOverlayModel = this.weightedOverlayService.imageServiceLayerToModel(itemData); 235 | } 236 | // LATER: model layer opacity? 237 | // update model designer controls w/ new web map 238 | this.selectLayersView.set("model", this.weightedOverlayModel); 239 | this.showDesignModelView(); 240 | // run model 241 | this.designModelView.runModel(); 242 | } 243 | }, 244 | 245 | // get breakdown of model data (histogram) 246 | // when the user selects a specific type of feature 247 | getChartData: function(geometries) { 248 | var _this = this; 249 | return this.getModelPixelSize({forceSquare: true}).then(function(modelPixelSize) { 250 | var polygon = geometryUtils.createMergedPolygon(geometries, geometries[0].spatialReference); 251 | return _this.weightedOverlayService.getHistogram(_this.weightedOverlayModel, polygon, { 252 | pixelSize: modelPixelSize 253 | }).then(function(histogram) { 254 | // convert histogram pixels to area units using pixel size 255 | var conversionFactor; 256 | if (_this.config && _this.config.areaUnit && _this.config.areaUnit.conversionFactor > 0) { 257 | conversionFactor = _this.config.areaUnit.conversionFactor; 258 | } else { 259 | conversionFactor = 1; 260 | } 261 | histogram.counts = array.map(histogram.counts, function(count) { 262 | return count * modelPixelSize.x * modelPixelSize.y * conversionFactor; 263 | }); 264 | return chartUtils.getChartData(_this.weightedOverlayModel.colormapDefinition, histogram); 265 | }); 266 | }); 267 | }, 268 | 269 | getModelPixelSize: function(options) { 270 | var _this = this; 271 | var params; 272 | var def; 273 | var opts; 274 | if (this.weightedOverlayService && this.weightedOverlayService.imageServiceLayer && this.weightedOverlayService.imageServiceLayer.spatialReference && this.map && this.map.extent) { 275 | opts = lang.mixin(options, { 276 | width: _this.map.width, 277 | height: _this.map.height 278 | }); 279 | if (this.map.extent.spatialReference.wkid !== this.weightedOverlayService.imageServiceLayer.spatialReference.wkid) { 280 | // reproject extent to image service SRID 281 | params = new ProjectParameters(); 282 | params.geometries = [this.map.extent]; 283 | params.outSR = this.weightedOverlayService.imageServiceLayer.spatialReference; 284 | return this._geometryService.project(params).then(function(projectedGeometries) { 285 | return _this.weightedOverlayService.getModelPixelSize(projectedGeometries[0], opts); 286 | }); 287 | } else { 288 | // same ref as weighted overlay service 289 | // no need to reproject 290 | def = new Deferred(); 291 | def.resolve(this.weightedOverlayService.getModelPixelSize(this.map.extent, opts)); 292 | return def; 293 | } 294 | 295 | } else { 296 | def = new Deferred(); 297 | def.reject('Map or image service not initialized'); 298 | return def; 299 | } 300 | }, 301 | 302 | _onDesignModelButtonClick: function(/*e*/) { 303 | this.showDesignModelView(); 304 | }, 305 | 306 | _onSelectLayersButtonClick: function(/*e*/) { 307 | this.showSelectOverlayLayersView(); 308 | }, 309 | 310 | _onLogOutClick: function(e) { 311 | e.preventDefault(); 312 | topic.publish(this.config.topics.MODELER_SIGNOUT); 313 | } 314 | }); 315 | }); 316 | -------------------------------------------------------------------------------- /src/app/css/images/16x16_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/css/images/16x16_X.png -------------------------------------------------------------------------------- /src/app/css/images/16x16_minimize_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/css/images/16x16_minimize_store.png -------------------------------------------------------------------------------- /src/app/css/images/loading_circle_sm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/css/images/loading_circle_sm.gif -------------------------------------------------------------------------------- /src/app/css/images/spriteArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/css/images/spriteArrows.png -------------------------------------------------------------------------------- /src/app/css/modeler.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | html, body, .mdlrMap, .mdlrAppContainer { 14 | height:100%; 15 | width:100%; 16 | margin:0; 17 | padding:0; 18 | } 19 | body { 20 | background-color:#FFF; 21 | overflow:hidden; 22 | font-family:"Trebuchet MS"; 23 | } 24 | 25 | /*custom classes*/ 26 | .group:after { 27 | content: ""; 28 | display: table; 29 | clear: both; 30 | } 31 | .clearfix:after { 32 | content: ""; 33 | display: table; 34 | clear: both; 35 | } 36 | .group .left { 37 | float: left; 38 | } 39 | .group .right { 40 | float: right; 41 | } 42 | .mdlrFloatingPane { 43 | position: absolute; 44 | /* appear over map, but under map pop up */ 45 | z-index: 35; 46 | color:#000; 47 | background-color:#fff; 48 | border: 2px solid #666666; 49 | -moz-border-radius:10px; 50 | -webkit-border-radius: 10px; 51 | border-radius: 10px; 52 | } 53 | .mdlrAppPane { 54 | left:10px; 55 | top:10px; 56 | width:340px; 57 | } 58 | .mdlrAppTitle { 59 | font-size: 1.2em; 60 | display: inline-block; 61 | } 62 | .mdlrAppLinks { 63 | text-align: right; 64 | } 65 | 66 | .mdlrScrollPanel, .weighted-overlay-model-designer { 67 | height: 300px; 68 | min-height: 300px; 69 | width: 100%; 70 | overflow: auto; 71 | } 72 | .mdlrModelContainer { 73 | margin: 10px; 74 | } 75 | .mdlrLoadingPane { 76 | background-color: #FFFFFF; 77 | border: 2px solid #666666; 78 | border-radius: 5px 5px 5px 5px; 79 | top: 10px; 80 | color: #666666; 81 | left: 50%; 82 | position: absolute; 83 | width: 120px; 84 | z-index: 999; 85 | margin-left: -60px; 86 | } 87 | .mdlrLoadingMessage { 88 | background-image: url("./images/loading_circle_sm.gif"); 89 | background-position: left center; 90 | background-repeat: no-repeat; 91 | margin: 10px; 92 | padding-left: 20px; 93 | } 94 | 95 | 96 | .mdlrResultsGrid { 97 | font-size:14px; 98 | font-weight:normal; 99 | } 100 | 101 | /* dgrid overrides */ 102 | .dgrid .dgrid-scroller { 103 | position: relative; 104 | max-height: 200px; 105 | overflow: auto; 106 | } 107 | 108 | .dgrid-row-odd { 109 | background: #F0F0F0; 110 | } 111 | 112 | /* to hide columns with id and tags */ 113 | .field-id { 114 | display: none; 115 | } 116 | 117 | .field-tags { 118 | display: none; 119 | } 120 | 121 | .field-modified { 122 | width: 20%; 123 | } 124 | 125 | .field-owner { 126 | width: 20%; 127 | } 128 | 129 | .modeler-color-ramp { 130 | font-size: inherit; 131 | vertical-align: bottom; 132 | } 133 | 134 | .modeler-color-ramp .modeler-color-ramp-color { 135 | height: 20px; 136 | width: 10px; 137 | } 138 | 139 | .mdlrButtons { 140 | margin: 10px 0; 141 | } 142 | 143 | .mdlrRight { 144 | float: right; 145 | } 146 | 147 | .mdlrLeft { 148 | float: left; 149 | } 150 | 151 | .mdlrChartPane 152 | { 153 | position: absolute; 154 | bottom: 10px; 155 | left: 55%; 156 | /* appear over map, but under map pop up */ 157 | z-index: 35; 158 | background-color: rgb(80, 80, 80); 159 | background-color: rgba(100, 100, 100, 0.8); 160 | border: 0 none; 161 | -webkit-border-radius: 5px; 162 | -moz-border-radius: 5px; 163 | border-radius: 5px; 164 | } 165 | .mdlrChartMessage { 166 | color: #FFFFFF; 167 | font-size: 0.9em; 168 | font-style: italic; 169 | margin: 0 0 5px; 170 | padding: 5px 10px 3px; 171 | } 172 | .mdlrChartsHeader { 173 | line-height: 20px; 174 | padding: 5px 10px 3px; 175 | color: #fff; 176 | } 177 | .mdlrChartsHeaderIcon { 178 | cursor: pointer; 179 | float: right; 180 | height: 16px; 181 | width: 16px; 182 | } 183 | /*TODO: use bootstrap class for icon*/ 184 | .mdlrChartsHeaderIconCollapse { 185 | background: url("../../lib/bootstrap_v2/img/glyphicons-halflings-white.png") no-repeat scroll -240px -95px transparent; 186 | font-size: 0; 187 | margin-right: 5px; 188 | opacity: 0.7; 189 | } 190 | .mdlrChartsHeaderIconMinimize { 191 | background: url("images/16x16_minimize_store.png") no-repeat top center; 192 | font-size:0; 193 | margin-right: 5px; 194 | } 195 | .mdlrChartsHeaderIconRestore { 196 | background: url("images/16x16_minimize_store.png") no-repeat bottom center; 197 | font-size:0; 198 | margin-right: 5px; 199 | } 200 | .mdlrChartsHeaderIconClose { 201 | background-image: url("images/16x16_X.png"); 202 | font-size:0; 203 | } 204 | 205 | /* override dijit styles */ 206 | .claro .dijitButton .dijitButtonNode, .claro .dijitDropDownButton .dijitButtonNode, .claro .dijitComboButton .dijitButtonNode, .claro .dijitToggleButton .dijitButtonNode { 207 | background-color: #ddd; 208 | background-image: none; 209 | border: 1px solid #AAA; 210 | } 211 | .claro .dijitDialog { 212 | border: 1px solid #AAA; 213 | border-radius: 5px 5px 5px 5px; 214 | background: rgba(100, 100, 100, 0.8); 215 | } 216 | .claro .dijitDialogPaneContent { 217 | border-top: 1px solid #759DC0; 218 | margin: 0 8px 8px 8px; 219 | } 220 | .claro .dijitDialogTitleBar { 221 | background-color: transparent; 222 | background-image: none; 223 | border: 0; 224 | } 225 | .claro .dijitDialogTitle { 226 | color: #FFFFFF; 227 | } 228 | .claro .dijitDialogPaneActionBar { 229 | padding: 5px; 230 | } 231 | .claro .dijitDialogCloseIcon { 232 | background-image: url("images/16x16_X.png"); 233 | margin-top: 3px; 234 | float: right; 235 | cursor: pointer; 236 | } 237 | .claro .dijitDialogCloseIcon { 238 | background-position: inherit; 239 | } 240 | .claro .dijitTitlePane { 241 | background-color: #666; 242 | border: 2px solid; 243 | border-color: #666; 244 | border-color: rgba(100, 100, 100, 0.8); 245 | border-radius: 5px 5px 5px 5px; 246 | } 247 | .claro .dijitTitlePaneTitle, .claro .dijitTitlePaneTitleBar { 248 | background-color: #666; 249 | background-image: none; 250 | border: 0; 251 | } 252 | .claro .dijitTitlePane .dijitArrowNode { 253 | background-image: url("images/spriteArrows.png"); 254 | background-repeat: no-repeat; 255 | height: 8px; 256 | width: 7px; 257 | } 258 | .claro .dijitTitlePane .dijitTitlePaneTextNode { 259 | color: #fff; 260 | } 261 | .claro .dijitCheckedMenuItemIconChar { 262 | display: none 263 | } 264 | 265 | /* override esri styles */ 266 | .templatePicker { 267 | border: 0; 268 | border-radius: 0; 269 | } 270 | .templatePicker .grid .item { 271 | font-size: 11px; 272 | } 273 | 274 | .esriSimpleSliderTR { 275 | right: 10px; 276 | top: 10px; 277 | } 278 | .esriLegendServiceLabel, .templatePicker .grid .groupLabel { 279 | display: none; 280 | } 281 | 282 | /*feature layer controls - place above map in upper right corner*/ 283 | .mdlrFeatureLayerPaneTitlePane { 284 | position:absolute; 285 | left: 364px; 286 | top: 10px; 287 | z-index: 35; 288 | } 289 | .mdlrFeatureLayerPane .dijitToolbar { 290 | background: transparent; 291 | background-image: none; 292 | padding: 2px 0 2px 4px; 293 | text-align: center; 294 | border-bottom: 0; 295 | } 296 | .mdlrFeatureLayerPane .dijitContentPane { 297 | padding: 3px; 298 | } 299 | .mdlrFeatureLayerPane .dijitTitlePaneContentInner { 300 | padding: 0; 301 | } 302 | .mdlrLegend { 303 | max-height: 300px; 304 | overflow: auto; 305 | } 306 | 307 | /* map controls - float right above map */ 308 | .mdlrMapControls { 309 | position:absolute; 310 | right:54px; 311 | top:10px; 312 | z-index: 35; 313 | } 314 | .mdlrMapControls .dijitTitlePane { 315 | clear: right; 316 | float: right; 317 | margin-top: 6px; 318 | } 319 | .mdlrMapControls .simpleGeocoder { 320 | float: right; 321 | } 322 | .mdlrPortalControls { 323 | margin-bottom: 10px; 324 | } 325 | .mdlrAddFeatureLayerButtons { 326 | padding-top: 10px; 327 | text-align: center; 328 | } 329 | .mdlrPanelContainerContent { 330 | padding: 3px 3px 0 3px; 331 | margin: 5px; 332 | border-top: 1px solid rgb(192, 192, 192); 333 | border-top: 1px solid rgba(255, 255, 255, 0.25); 334 | text-align: center; 335 | background: #fff; 336 | -webkit-border-radius: 0 0 4px 4px; 337 | -moz-border-radius: 0 0 4px 4px; 338 | border-radius: 0 0 4px 4px; 339 | } 340 | .mdlrLoginMessage { 341 | margin: 300px auto; 342 | text-align: center; 343 | width: 600px; 344 | } 345 | -------------------------------------------------------------------------------- /src/app/geometryUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/array", 15 | 16 | "esri/geometry/Polygon" 17 | ], 18 | function( 19 | array, 20 | Polygon 21 | ) { 22 | return { 23 | createMergedPolygon: function(featuresOrGeometries, spatialReference) { 24 | var polygon = new Polygon(spatialReference); 25 | var geometry; 26 | array.forEach(featuresOrGeometries, function(featureOrGeometry) { 27 | geometry = featureOrGeometry.geometry || featureOrGeometry; 28 | array.forEach(geometry.rings, function(ring) { 29 | polygon.addRing(ring); 30 | }); 31 | }); 32 | return polygon; 33 | } 34 | }; 35 | }); -------------------------------------------------------------------------------- /src/app/nls/resources.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define({ 14 | root: { 15 | 16 | dashboard: { 17 | close: "Close", 18 | minimize: "Minimize", 19 | restore: "Restore", 20 | closeHistogramCharts: "Close Histogram Charts" 21 | } 22 | 23 | // TODO: remaining i18n 24 | 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/portal/ModelItemEditor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/array", 16 | 17 | "dojo/Evented", 18 | 19 | "dijit/_WidgetBase", 20 | "dijit/_TemplatedMixin", 21 | "dijit/_WidgetsInTemplateMixin", 22 | 23 | "dojo/text!./templates/ModelItemEditor.html", 24 | 25 | "dijit/form/Form", 26 | "dijit/form/ValidationTextBox", 27 | "dijit/form/SimpleTextarea", 28 | "dijit/form/Button", 29 | "dijit/form/Select" 30 | ], 31 | function( 32 | declare, array, Evented, 33 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, 34 | template 35 | ) { 36 | 37 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 38 | templateString: template, 39 | baseClass: "mdlrModelItemEditor", 40 | _setItemInfoAttr: function(newItemInfo) { 41 | this.itemInfo = newItemInfo; 42 | this.titleNode.set("value", this.itemInfo.title); 43 | this.descriptionNode.set("value", this.itemInfo.description); 44 | this.setSelectedCategory(this.itemInfo.tags); 45 | }, 46 | getItemInfo: function() { 47 | return this.itemInfo; 48 | }, 49 | isValid: function() { 50 | return this.formNode.isValid(); 51 | }, 52 | populateCategories: function(options) { 53 | this.categoryNode.set("options", options); 54 | if (this.categoryNode.options.length) { 55 | this.categoryNode.set("value", this.categoryNode.options[0].value); 56 | } 57 | }, 58 | setSelectedCategory: function(tags) { 59 | array.some(tags, function(tag) { 60 | var inCategories = array.some(this.categoryNode.options, function(option) { 61 | return option.value === tag; 62 | }); 63 | if (inCategories) { 64 | this.categoryNode.set("value", tag); 65 | } 66 | return inCategories; 67 | }, this); 68 | }, 69 | _onTitleChange: function() { 70 | this.itemInfo.title = this.titleNode.value; 71 | }, 72 | _onDescriptionChange: function() { 73 | this.itemInfo.description = this.descriptionNode.value; 74 | }, 75 | _onCategoryChange: function() { 76 | this.itemInfo.tags = [this.categoryNode.value]; 77 | }, 78 | _onSaveClick: function(/*e*/) { 79 | if (this.formNode.validate()) { 80 | this.emit("Save", this.getItemInfo()); 81 | } 82 | }, 83 | _onCancelClick: function() { 84 | this.emit("Cancel"); 85 | } 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /src/app/portal/ModelItemList.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define(["dojo/_base/declare", 14 | "dojo/_base/array", 15 | "dojo/dom", 16 | "dojo/on", 17 | "dojo/string", "dojo/Evented", "dijit/_WidgetBase", 18 | "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", 19 | "dojo/text!./templates/ModelItemList.html", 20 | "dojo/io-query", "dojo/io/script", 21 | "dojo/store/Memory", "dgrid/OnDemandGrid", "dgrid/Selection", 22 | "dijit/form/Select" 23 | ], 24 | function(declare, array, dom, on, 25 | string, Evented, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, template, 26 | ioQuery, script, Memory, OnDemandGrid, Selection) { 27 | 28 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 29 | templateString : template, 30 | baseClass : "landscape-model-info", 31 | result : "", 32 | postCreate: function() { 33 | this.inherited(arguments); 34 | this.loadButton.set("disabled",true); 35 | }, 36 | populateGrid: function (result) { 37 | //to get access to widget controls in the grid event handlers 38 | var _this = this; 39 | 40 | var columns = [{ 41 | field : "title", 42 | label : "Name" 43 | }, { 44 | field : "description", 45 | label : "Description" 46 | }, { 47 | field : "modified", 48 | label : "Modified" 49 | }, { 50 | field : "owner", 51 | label : "Author" 52 | }, { 53 | field : "id", 54 | label : "id" 55 | }, { 56 | field : "tags", 57 | label : "tags" 58 | }]; 59 | 60 | var modelStore = this.formatGridData(result); 61 | 62 | // need custom grid to have selection functionality 63 | var CustomGrid = declare([ OnDemandGrid, Selection ]); 64 | 65 | this.grid = new CustomGrid({ 66 | store : modelStore, 67 | columns : columns, 68 | selectionMode : "single", 69 | loadingMessage : "Loading data...", 70 | noDataMessage : "No results found." 71 | }, this.gridNode); 72 | 73 | this.grid.on("dgrid-select", function(/*e*/) { 74 | _this.loadButton.set("disabled",false); 75 | }); 76 | 77 | this.grid.on("dgrid-deselect", function(/*e*/) { 78 | _this.loadButton.set("disabled",true); 79 | }); 80 | 81 | this.grid.startup(); 82 | 83 | }, 84 | resizeGrid : function(){ 85 | this.grid.resize(); 86 | }, 87 | formatGridData: function(result) { 88 | //format data to be array of objects - so dgrid can read it 89 | var storeArray = []; 90 | for(var i = 0; i < result.results.length; i++) { 91 | 92 | var dt = new Date(result.results[i].modified).toLocaleDateString(); 93 | var obj = { 94 | "title" : result.results[i].title, 95 | "description" : result.results[i].description, 96 | "modified" : dt, 97 | "owner" : result.results[i].owner, 98 | "id" : result.results[i].id, 99 | "category" : this.getModelCategory(result.results[i].tags) 100 | }; 101 | 102 | storeArray.push(obj); 103 | } 104 | 105 | var modelStore = new Memory({ 106 | data : storeArray, 107 | idProperty : "id" 108 | }); 109 | return modelStore; 110 | }, 111 | getModelCategory: function(tags) { 112 | var modelCategory; 113 | array.some(tags, function(tag) { 114 | var inCategories = array.some(this.categoryNode.options, function(option) { 115 | return option.value === tag; 116 | }); 117 | if (inCategories) { 118 | modelCategory = tag; 119 | } 120 | return inCategories; 121 | }, this); 122 | return modelCategory; 123 | }, 124 | refreshGrid: function(result) { 125 | var store = this.formatGridData(result); 126 | this.grid.set('store',store); 127 | }, 128 | refreshCategories: function() { 129 | this.categoryNode.reset(); 130 | }, 131 | startup : function() { 132 | this.inherited(arguments); 133 | }, 134 | populateCategories: function(options) { 135 | this.categoryNode.set("options", options); 136 | this.categoryNode.options.splice(0, 0, { value: "*", label: "All Categories"}); 137 | }, 138 | _onCategoryChange : function(/*e*/){ 139 | if (this.categoryNode.value === "*"){ 140 | this.grid.setQuery({}); //reset query 141 | }else{ 142 | this.grid.setQuery({category: this.categoryNode.value}); 143 | } 144 | }, 145 | _onLoadClick: function(/*e*/) { 146 | //get selected row's id (which is agol item id) 147 | var itemID = ""; 148 | for(var id in this.grid.selection){ 149 | itemID = id; 150 | } 151 | this.emit("LoadSelectedModel", itemID); 152 | }, 153 | _onCancelClick: function(/*e*/) { 154 | this.emit("Cancel"); 155 | } 156 | }); 157 | 158 | }); -------------------------------------------------------------------------------- /src/app/portal/OAuthHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define( 14 | [ 15 | "dojo/_base/lang", 16 | "dojo/_base/json", 17 | "dojo/_base/url", 18 | "dojo/cookie", 19 | "dojo/Deferred", 20 | "dojo/io-query", 21 | "esri/IdentityManager" 22 | ], 23 | function(lang, dojoJson, Url, cookie, Deferred, ioquery, idManager) { 24 | 25 | var OAuthHelper = { 26 | 27 | portal: "http://www.arcgis.com", 28 | 29 | popupCallbackPage: window.location.protocol + "//" + 30 | window.location.host + 31 | window.location.pathname.replace(/\/[^\/]+$/, "") + 32 | "/oauth-callback.html", 33 | 34 | init: function(parameters) { 35 | /** 36 | * parameters = { 37 | * appId: "", 38 | * portal: "", // deafult is "http://www.arcgis.com" 39 | * expiration: , // in minutes 40 | * popup: 41 | * } 42 | */ 43 | 44 | lang.mixin(this, parameters); 45 | this.portalUrl = this.portal + "/sharing/rest"; 46 | 47 | // Read OAuth response from the page url fragment if available, 48 | // and register with identity manager 49 | this.checkOAuthResponse(window.location.href, true); 50 | 51 | // Read token from cookie if available, and register 52 | // with identity manager 53 | this.checkCookie(); 54 | 55 | // You don't need this if you require your users to sign-in 56 | // before using the app. This override helps trigger OAuth 57 | // flow instead of the legacy generateToken flow. 58 | this.overrideIdentityManager(); 59 | }, 60 | 61 | isSignedIn: function() { 62 | return !!idManager.findCredential(this.portalUrl); 63 | }, 64 | 65 | signIn: function() { 66 | var deferred = (this.deferred = new Deferred()); 67 | 68 | var authParameters = { 69 | client_id: this.appId, 70 | response_type: "token", 71 | expiration: this.expiration, // in minutes. Default is 30. 72 | redirect_uri: this.popup ? 73 | this.popupCallbackPage : 74 | window.location.href.replace(/#.*$/, "") 75 | }; 76 | 77 | var authUrl = this.portal.replace(/^http:/i, "https:") + 78 | "/sharing/oauth2/authorize?" + 79 | ioquery.objectToQuery(authParameters); 80 | 81 | if (this.popup) { 82 | // Internet Explorer 8 throws error if windowName 83 | // (second argument below) has hyphen 84 | window.open( 85 | authUrl, 86 | "esrioauth", 87 | "width=480,height=320,location=yes,status=yes,scrollbars=yes" 88 | ); 89 | } 90 | else { 91 | window.location = authUrl; 92 | } 93 | 94 | return deferred; 95 | }, 96 | 97 | signOut: function(noReload) { 98 | // Delete the cookie 99 | cookie("arcgis_auth", null, { 100 | expires: -1, 101 | path: "/", 102 | domain: document.domain 103 | }); 104 | if (!noReload) { 105 | window.location.reload(); 106 | } 107 | }, 108 | 109 | checkOAuthResponse: function(url, clearHash) { 110 | // This method will be called from popup callback page as well 111 | 112 | var oauthResponse = this.parseFragment(url); 113 | 114 | if (oauthResponse) { 115 | if (clearHash) { // redirection flow 116 | // Remove OAuth bits from the URL fragment 117 | window.location.hash = ""; 118 | } 119 | 120 | if (oauthResponse.error) { 121 | var error = new Error(oauthResponse.error); 122 | error.details = [ oauthResponse.error_description ]; 123 | 124 | if (this.deferred) { 125 | this.deferred.reject(error); 126 | } 127 | } 128 | else { 129 | var credential = this.registerToken(oauthResponse); 130 | 131 | // User checked "Keep me signed in" option 132 | if (oauthResponse.persist) { 133 | cookie("arcgis_auth", dojoJson.toJson(oauthResponse), { 134 | expires: new Date(oauthResponse.expires_at), 135 | path: "/", 136 | domain: document.domain 137 | }); 138 | 139 | console.log("[Cookie] Write: ", cookie("arcgis_auth")); 140 | } 141 | 142 | if (this.deferred) { 143 | this.deferred.resolve(credential); 144 | } 145 | } 146 | } 147 | }, 148 | 149 | checkCookie: function() { 150 | var ckie = cookie("arcgis_auth"); 151 | 152 | if (ckie) { 153 | console.log("[Cookie] Read: ", ckie); 154 | 155 | var oauthResponse = dojoJson.fromJson(ckie); 156 | this.registerToken(oauthResponse); 157 | } 158 | }, 159 | 160 | registerToken: function(oauthResponse) { 161 | // Register the access token with Identity Manager, so that 162 | // it can be added to all ArcGIS Online REST API requests 163 | 164 | idManager.registerToken({ 165 | server: this.portalUrl, 166 | userId: oauthResponse.username, 167 | token: oauthResponse.access_token, 168 | expires: oauthResponse.expires_at, 169 | ssl: oauthResponse.ssl 170 | }); 171 | 172 | var credential = idManager.findCredential(this.portalUrl, oauthResponse.username); 173 | 174 | console.log("Token registered with Identity Manager: ", credential); 175 | return credential; 176 | }, 177 | 178 | parseFragment: function(url) { 179 | var urlObj = new Url(url), 180 | fragment = urlObj.fragment ? ioquery.queryToObject(urlObj.fragment) : null; 181 | 182 | if (fragment) { 183 | if (fragment.access_token) { 184 | console.log("[OAuth Response]: ", fragment); 185 | 186 | // Convert from String to Number 187 | fragment.expires_in = Number(fragment.expires_in); 188 | 189 | // Calculate universal time 190 | fragment.expires_at = (new Date()).getTime() + (fragment.expires_in * 1000); 191 | 192 | fragment.ssl = (fragment.ssl === "true"); 193 | } 194 | else if (fragment.error) { 195 | console.log("[OAuth Error]: ", fragment.error, " - ", fragment.error_description); 196 | } 197 | 198 | return fragment; 199 | } 200 | }, 201 | 202 | overrideIdentityManager: function() { 203 | var signInMethod = idManager.signIn, 204 | helper = this; 205 | 206 | idManager.signIn = function(resUrl, serverInfo, options) { 207 | 208 | return (serverInfo.server.indexOf(".arcgis.com") !== -1) ? 209 | // OAuth flow 210 | helper.signIn() : 211 | // generateToken flow 212 | signInMethod.apply(this, arguments); 213 | }; 214 | } 215 | }; 216 | 217 | window.OAuthHelper = OAuthHelper; 218 | 219 | return OAuthHelper; 220 | }); 221 | -------------------------------------------------------------------------------- /src/app/portal/PortalControls.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/lang", 16 | "dojo/_base/array", 17 | "dojo/on", 18 | "dojo/Evented", 19 | "dojo/Deferred", 20 | 21 | "dijit/_WidgetBase", 22 | "dijit/_TemplatedMixin", 23 | "dijit/_WidgetsInTemplateMixin", 24 | 25 | "esri/arcgis/Portal", 26 | 27 | "./portalUtils", 28 | "./ModelItemEditor", 29 | "./ModelItemList", 30 | 31 | "dojo/text!./templates/PortalControls.html", 32 | "dojo/i18n!../nls/resources", 33 | 34 | "dijit/form/Button", 35 | "dijit/Dialog" 36 | ], function ( 37 | declare, lang, array, on, Evented, Deferred, 38 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, 39 | esriPortal, 40 | portalUtils, ModelItemEditor, ModelItemList, 41 | template, i18n 42 | ) { 43 | var supportedItemTypes = ["Web Map"]; //, "Image Service"]; 44 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 45 | 46 | templateString: template, 47 | i18n: i18n, 48 | baseClass: "mdlrPortalControls", 49 | typeKeyword: "", 50 | numberOfItems: "100", 51 | 52 | _setModelItemAttr: function(newModelItem) { 53 | if (newModelItem.type === undefined) { 54 | newModelItem.type = supportedItemTypes[0]; 55 | } 56 | if (array.some(supportedItemTypes, function(supportedType) { 57 | return newModelItem.type === supportedType; 58 | })) { 59 | this.modelItem = newModelItem; 60 | this.setTitle(this.modelItem.title, this.modelItem.id); 61 | } else { 62 | throw "Expected one of these item types: " + supportedItemTypes; 63 | } 64 | }, 65 | 66 | _setModelAttr: function(newModel) { 67 | this.model = newModel; 68 | this.saveButton.set("disabled", !this.model); 69 | }, 70 | 71 | postCreate: function() { 72 | var _this = this; 73 | this.inherited(arguments); 74 | this.own(on(this.saveButton, "Click", function(/*e*/) { 75 | _this.showSaveModelDialog(); 76 | })); 77 | }, 78 | 79 | _onLoadClick: function(/*e*/) { 80 | this.showLoadModelDialog(); 81 | }, 82 | 83 | showSaveModelDialog: function() { 84 | var _this = this; 85 | // TODO: validate that the model layer has been added to the map? 86 | if (!this.modelItemEditor) { 87 | this.modelItemEditor = new ModelItemEditor({ 88 | onSave: function(/*itemInfo*/) { 89 | // TODO: show loading... 90 | _this.modelItem = _this.modelItemEditor.itemInfo; 91 | _this.saveModelItem().then(function(response) { 92 | // TODO: hide loading... 93 | _this.loadModelItem(response.id); 94 | _this.saveDialog.hide(); 95 | }); 96 | }, 97 | onCancel: function() { 98 | _this.saveDialog.hide(); 99 | } 100 | }, this.saveModelNode); 101 | this.modelItemEditor.startup(); 102 | this.modelItemEditor.populateCategories(this.getCategoryOptions()); 103 | } 104 | if (!this.modelItem.description && this.model) { 105 | this.modelItem.description = this.createItemDescription(); 106 | } 107 | this.modelItemEditor.set("itemInfo", this.modelItem); 108 | this.saveDialog.show(); 109 | }, 110 | 111 | createItemDescription: function() { 112 | var desc = ""; 113 | array.forEach(this.model.overlayLayers, function(overlayLayer) { 114 | desc += overlayLayer.title + ": " + overlayLayer.weight + "%\n"; 115 | }); 116 | return desc; 117 | }, 118 | 119 | showLoadModelDialog: function() { 120 | var _this = this; 121 | _this.portal.signIn().then(function(/*portalUser*/) { 122 | // TODO: show loading... 123 | _this.portal.queryItems({ 124 | q : 'type: ("' + supportedItemTypes.join('" OR "') + '") AND typekeywords:"' + _this.typeKeyword + '"', 125 | num: _this.numberOfItems 126 | }).then(function(queryResults) { 127 | // TODO: hide loading... 128 | if (!_this.modelItemList) { 129 | _this.modelItemList = new ModelItemList({ 130 | onLoadSelectedModel: function(itemId) { 131 | if (itemId) { 132 | _this.loadModelItem(itemId); 133 | _this.loadDialog.hide(); 134 | } 135 | }, 136 | onCancel: function() { 137 | _this.loadDialog.hide(); 138 | } 139 | }); 140 | _this.modelItemList.populateCategories(_this.getCategoryOptions()); 141 | _this.modelItemList.populateGrid(queryResults); 142 | _this.modelItemList.placeAt(_this.queryResultsNode); 143 | _this.modelItemList.startup(); 144 | _this.modelItemList.resizeGrid(); 145 | } else { 146 | _this.modelItemList.refreshGrid(queryResults); 147 | _this.modelItemList.refreshCategories(); 148 | } 149 | }); 150 | _this.loadDialog.show(); 151 | }); 152 | }, 153 | 154 | loadModelItem: function(id) { 155 | var _this = this; 156 | return portalUtils.getItem(_this.portal, id).then(function(getItemResponse) { 157 | // var itemType = getItemResponse.item.type; 158 | // if (itemType === "Image Service") { 159 | _this.set("modelItem", getItemResponse.item); 160 | _this.emit("item-loaded", getItemResponse); 161 | return getItemResponse; 162 | // } else { 163 | // return new Deferred().reject("Expected item of type 'Image Service'"); 164 | // } 165 | }); 166 | }, 167 | 168 | // save map as web map to portal 169 | saveModelItem: function() { 170 | var _this = this; 171 | var modelItem = this.modelItem; 172 | var queryString; 173 | var itemData; 174 | var hasTypeKeyword = false; 175 | if (modelItem && this.weightedOverlayService && this.weightedOverlayService.imageServiceLayer && this.model) { 176 | // sign in to portal 177 | return this.portal.signIn().then(function(portalUser) { 178 | // verify that item has app type keyword 179 | // NOTE: this overwrites any type keywords set by ArcGIS 180 | // which could be a problem if updating, but ok for new items 181 | modelItem.typeKeywords = [ _this.typeKeyword]; 182 | // serialize tags to string for saving 183 | modelItem.tags = modelItem.tags.join(","); 184 | if (modelItem.type === "Image Service") { 185 | // set item URL to the weighted overlay service 186 | // and append unique id so portal won't complain 187 | queryString = "?uid=" + new Date().getTime(); 188 | modelItem.url = _this.weightedOverlayService.imageServiceLayer.url + queryString; 189 | // LATER: extent should really be that of the image service layer, 190 | // not the map (as set below) 191 | itemData = _this.weightedOverlayService.modelToImageServiceLayer(_this.model, { 192 | modelTitle: modelItem.title 193 | }); 194 | } else { 195 | // create a web map then 196 | // overwrite operational layers generated from them map 197 | // with ones generated from the model 198 | itemData = portalUtils.createWebMapItemData(_this.map); 199 | itemData.operationalLayers = [_this.weightedOverlayService.modelToImageServiceLayer(_this.model, { 200 | modelTitle: modelItem.title 201 | })]; 202 | } 203 | // LATER: support overwiting the current item 204 | // for now, just add a copy by deleting id if any 205 | delete(modelItem.id); 206 | delete(modelItem.item); 207 | return portalUtils.addItem(portalUser, lang.mixin(modelItem, { 208 | "text": JSON.stringify(itemData), 209 | extent: portalUtils.webMercatorExtentToItemExtent(_this.map.extent) 210 | })).then(function(addItemResults) { 211 | // LATER: update the item and remove the unique key from the URL 212 | return addItemResults; 213 | }); 214 | }); 215 | } else { 216 | return new Deferred().reject("Item info, weighted overlay service, image layer, and/or model is undefined."); 217 | } 218 | }, 219 | 220 | getCategoryOptions: function() { 221 | return array.map(this.categoryTags, function(tag) { 222 | return { value: tag, label: tag }; 223 | }); 224 | }, 225 | 226 | // set portal reference 227 | setPortalUrl: function(newPortalUrl) { 228 | this.portal = new esriPortal.Portal(newPortalUrl); 229 | }, 230 | 231 | signIn: function() { 232 | if (this.portal) { 233 | return this.portal.signIn(); 234 | } else { 235 | throw "Portal is not initialized"; 236 | } 237 | }, 238 | 239 | getTitle: function() { 240 | return this.titleNode.innerHTML; 241 | }, 242 | 243 | // Ex: 244 | // http://www.arcgis.com/home/item.html?id=d5e02a0c1f2b4ec399823fdd3c2fdebd 245 | setTitle: function(title, id) { 246 | var html = title || ""; 247 | if (id) { 248 | html = '' + html + ''; 249 | } 250 | this.titleNode.innerHTML = html; 251 | } 252 | }); 253 | }); 254 | -------------------------------------------------------------------------------- /src/app/portal/portalUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/array", 15 | "dojo/io-query", 16 | 17 | "esri/request", 18 | "esri/geometry/webMercatorUtils", 19 | "esri/arcgis/utils" 20 | ], 21 | function( 22 | array, ioQuery, 23 | esriRequest, webMercatorUtils, arcgisUtils 24 | ) { 25 | return { 26 | // add an item to a portal users's content 27 | // assumes portal user is already logged in (using identity manager) 28 | addItem: function (portalUser, content) { 29 | var requestParams = { f: "json", token: portalUser.credential.token }; 30 | var url = portalUser.portal.portalUrl + "content/users/" + portalUser.username + "/addItem"; 31 | url += "?" + ioQuery.objectToQuery(requestParams); 32 | return esriRequest({ 33 | url: url, 34 | content: content, 35 | handleAs: "json" 36 | }, { 37 | usePost: true 38 | }); 39 | }, 40 | // get item info and item data 41 | // wraps arcgisUtils.getItem function to 42 | // make it more generic for other portals 43 | getItem: function(portal, itemId) { 44 | arcgisUtils.arcgisUrl = portal.url + "/sharing/content/items"; 45 | return arcgisUtils.getItem(itemId); 46 | }, 47 | // create web map item data (JSON) from map 48 | // Example: 49 | // "itemData": { 50 | // "operationalLayers": [{ 51 | // "url": "http://ec2-54-243-84-56.compute-1.amazonaws.com/arcgis/rest/services/landscape/weightedOverlayAnalysis/ImageServer", 52 | // "id": "weightedOverlayAnalysis_9218", 53 | // "visibility": true, 54 | // "opacity": 1, 55 | // "title": "New Model", 56 | // "itemId": "938eda16a45f405a92665e5982968903" 57 | // } 58 | // ], 59 | // "baseMap": { 60 | // "baseMapLayers": [{ 61 | // "id": "defaultBasemap", 62 | // "opacity": 1, 63 | // "visibility": true, 64 | // "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer" 65 | // } 66 | // ], 67 | // "title": "Topographic" 68 | // }, 69 | // "mapOptions" : { 70 | // "extent" : { 71 | // "xmin":-12933906.537, 72 | // "ymin":3993856.886, 73 | // "xmax":-12933371.998, 74 | // "ymax":3994375.189, 75 | // "spatialReference" : { 76 | // "wkid" : 102100 77 | // } 78 | // }, 79 | // "scale" : 1234.5, 80 | // "rotation" : -45, 81 | // "spatialReference" : { 82 | // "wkid" : 102100 83 | // }, 84 | // "time" : [ 85 | // 1199145600000, 86 | // 1230768000000 87 | // ] 88 | // } 89 | // "version": "1.9" 90 | // } 91 | createWebMapItemData: function(map) { 92 | // TODO: how to get the current version? 93 | var itemData = { 94 | operationalLayers: [], 95 | "version": "1.9" 96 | }; 97 | var basemapLayerIds = map.basemapLayerIds; 98 | var baseMapLayers = []; 99 | // add operational and basemap layers 100 | array.forEach(map.layerIds, function(layerId) { 101 | // check if basemap layer 102 | var layer = map.getLayer(layerId); 103 | var layerJson = { 104 | "url": layer.url, 105 | "id": layer.id, 106 | "visibility": layer.visible, 107 | "opacity": layer.opacity 108 | }; 109 | if (basemapLayerIds && array.some(basemapLayerIds, function(basemapLayerId) { return basemapLayerId === layerId; })) { 110 | // add to basemap layers 111 | baseMapLayers.push(layerJson); 112 | } else { 113 | // set operational layer properties 114 | layerJson.title = layer.description; 115 | // set layer type (image/feature, etc) specific properites 116 | if (layer.renderingRule && layer.renderingRule.toJson) { 117 | layerJson.renderingRule = layer.renderingRule.toJson(); 118 | } 119 | // TODO: others? 120 | // esri.layers.ArcGISImageServiceLayer 121 | // "bandIds":[0], 122 | // "format":"jpgpng", 123 | // "mosaicRule": 124 | // { 125 | // "mosaicMethod" : "esriMosaicLockRaster", 126 | // "lockRasterIds":[1,3,5,6], 127 | // "ascending": true, 128 | // "mosaicOperation" : "MT_FIRST", 129 | // "where":"objected<7" 130 | // }, 131 | // "layerDefinition":{"definitionExpression":"objected<7"}, 132 | // "popupInfo":{...} 133 | // add operational layer JSON to item data 134 | itemData.operationalLayers.push(layerJson); 135 | } 136 | }); 137 | // add basemaps 138 | if (baseMapLayers.length > 0) { 139 | itemData.baseMap = { 140 | // TODO: how to get acutal title? 141 | // use baseMapLayers[0].description? 142 | title: "Basemap", 143 | baseMapLayers: baseMapLayers 144 | }; 145 | } 146 | itemData.mapOptions = { 147 | extent: map.extent.toJson() 148 | // TODO: others?? 149 | // "scale" : 1234.5, 150 | // "rotation" : -45, 151 | // "spatialReference" : { 152 | // "wkid" : 102100 153 | // }, 154 | // "time" : [ 155 | // 1199145600000, 156 | // 1230768000000 157 | // ] 158 | }; 159 | // TODO: add bookmarks? 160 | // "bookmarks": [ 161 | // { 162 | // 163 | // }, 164 | // { 165 | // 166 | // } 167 | // ], 168 | return itemData; 169 | }, 170 | // get an extent string in the format expected by portal 171 | // Syntax: extent=, , , 172 | // Example: extent=-110.05, 44.13, -110, 44.98 173 | webMercatorExtentToItemExtent: function(webMercatorExtent) { 174 | var geographicExtent = webMercatorUtils.webMercatorToGeographic(webMercatorExtent); 175 | return geographicExtent.xmin + ", " + geographicExtent.ymin + ", " + geographicExtent.xmax + ", " + geographicExtent.ymax; 176 | } 177 | }; 178 | }); 179 | -------------------------------------------------------------------------------- /src/app/portal/templates/ModelItemEditor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 | 11 |
12 | 13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 |
-------------------------------------------------------------------------------- /src/app/portal/templates/ModelItemList.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 |
7 | 8 | 9 |
-------------------------------------------------------------------------------- /src/app/portal/templates/PortalControls.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/app/reports/Chart.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dijit/_WidgetBase", 16 | "dijit/_TemplatedMixin", 17 | "dijit/_WidgetsInTemplateMixin", 18 | "dojo/text!./templates/Chart.html", 19 | "dojo/i18n!../nls/resources", 20 | // "../app/context", 21 | "dojo/_base/lang", 22 | "dojo/_base/array", 23 | "dojo/_base/Color", 24 | "dojo/topic", 25 | "dojo/dom-style", 26 | "dojo/dom-class", 27 | "dojo/dom-construct", 28 | "dojo/on", 29 | "dojo/Evented", 30 | "dojo/store/Memory", 31 | "dojo/store/Observable", 32 | "dijit/registry", 33 | "dijit/Tooltip", 34 | "dojox/charting/Chart", 35 | "dojox/charting/Theme", 36 | "dojox/charting/widget/Legend", 37 | "dojox/charting/plot2d/Pie", 38 | "dojox/charting/plot2d/ClusteredColumns", 39 | "dojox/charting/action2d/Magnify", 40 | "dojox/charting/action2d/Tooltip", 41 | "dojox/charting/action2d/MoveSlice", 42 | "dojox/charting/StoreSeries", 43 | "esri/map", 44 | "dojox/charting/axis2d/Default", 45 | "dojox/charting/plot2d/Default" 46 | ], function ( 47 | declare, WidgetBase, TemplatedMixin, WidgetsInTemplateMixin, template, i18n,// context, 48 | lang, arrayUtil, Color, topic, domStyle, domClass, domConstruct, on, Evented, Memory, Observable, registry, dijitTooltip, 49 | Chart, Theme, Legend, Pie, ClusteredColumns, Magnify, Tooltip, MoveSlice, StoreSeries) { 50 | 51 | var _this; 52 | 53 | var oThisClass = declare([WidgetBase, TemplatedMixin, WidgetsInTemplateMixin, Evented], { 54 | 55 | appContext: null, 56 | i18n: i18n, 57 | templateString: template, 58 | header: null, 59 | type: "Pie", // default chart type is "pie chart" 60 | _type: null, 61 | chart: false, 62 | legend: false, 63 | _theme: null, 64 | _strokeStyle: null, 65 | _shadow: null, 66 | data: null, 67 | _newData: null, 68 | dataStore: null, 69 | keyType: null, //only for donut chart: which field to use for calculating the percentage 70 | dataType: "area", 71 | _unit: " Acres", 72 | fPolygonLayer: null, 73 | selectedFieldName: null, 74 | 75 | postCreate: function () { 76 | this.inherited(arguments); 77 | 78 | if (this.type === "Donut") { 79 | // donut is a modification from pie chart 80 | this._type = "Pie"; 81 | } else { 82 | this._type = this.type; 83 | } 84 | // set chart title if there is one 85 | if (this.header) { 86 | this.chartTitleNode.innerHTML = this.header; 87 | } 88 | else { 89 | domStyle.set(this.chartTitleNode, "display", "none"); 90 | } 91 | // add chart type as a class name to the chart container 92 | domClass.add(this.chartContainer, this.type); 93 | // columns chart has stroke (border) 94 | if (this.type.toLowerCase() === "columns") { 95 | this._strokeStyle = { color: "#555", width: 1 }; 96 | // this._shadow = { dx: 0, dy: -1, width: 3, color: [0, 0, 0, 0.1] }; 97 | } 98 | // default theme 99 | this._theme = new dojox.charting.Theme({ 100 | colors: [ 101 | "#68C6E6", 102 | "#FA916C", 103 | "#9ADB70", 104 | "#EB490B", 105 | "#F2D780", 106 | "#E44960" 107 | ] 108 | }); 109 | this._theme.chart.fill = "transparent"; 110 | this._theme.plotarea.fill = "transparent"; 111 | 112 | //create chart 113 | this._create(this.type.toLowerCase()); 114 | }, 115 | 116 | _create: function(chartType) { 117 | this.chart = new Chart(this.chartContainerNode); 118 | this.chart.setTheme(this._theme); 119 | var plotParams = { 120 | type: this._type, 121 | labels: false, 122 | //animate: true, 123 | stroke: this._strokeStyle 124 | }; 125 | if (chartType === "donut") { 126 | plotParams.startAngle = 90; 127 | } 128 | if (chartType === "columns") { 129 | plotParams.gap = 4; 130 | plotParams.maxBarSize = 25; 131 | if (this.data) { 132 | var newWidth = 127; 133 | var count = this.data.dataset.length; 134 | if (count >= 7) { 135 | plotParams.gap = 1; 136 | newWidth = 127 + 3 * 25; 137 | } else if (count > 4 && count < 7) { 138 | newWidth = 127 + (count - 4) * 25; 139 | } 140 | this._resizeColumnsContainer(newWidth); 141 | } 142 | } 143 | this.chart.addPlot("default", plotParams); 144 | 145 | // custom configuration 146 | switch (chartType) { 147 | case "pie": // pie 148 | new MoveSlice(this.chart, "default"); 149 | break; 150 | case "donut": 151 | domStyle.set(this.donutChartValueNode, "display", "block"); 152 | new Magnify(this.chart, "default", { scale: 1.1 }); 153 | new Tooltip(this.chart, "default"); 154 | break; 155 | case "columns": // columns 156 | this.chart.addAxis("x", { type: "Invisible", fixLower: "minor", fixUpper: "minor", natural: true }) 157 | .addAxis("y", { type: "Invisible", vertical: true, fixLower: "minor", fixUpper: "minor", includeZero: true }); 158 | new Tooltip(this.chart, "default"); 159 | break; 160 | default: 161 | //default handler 162 | } 163 | 164 | // add data series 165 | this._newData = this._createData(chartType, this.data); 166 | this.dataStore = Observable(new Memory({ 167 | idProperty: "name", 168 | data: this._newData 169 | })); 170 | this.chart.addSeries("", new StoreSeries(this.dataStore, { query: {} }, lang.hitch(this, this._valueTrans))); 171 | 172 | this.chart.render(); 173 | this._connectChartEvents(); 174 | 175 | // topic.publish('/charts/areaChartAdded', this, { 176 | // type: chartType 177 | // }); 178 | if (this.dataType == "area") { 179 | this.own(topic.subscribe("/charts/area/Refreshed", lang.hitch(this, function (sender, args) { 180 | this.data = args.data; 181 | this._refresh(args.removedFieldValues, args.data); 182 | }))); 183 | } else if (this.dataType == "count") { 184 | this.own(topic.subscribe("/charts/histogram/Refreshed", lang.hitch(this, function (sender, args) { 185 | console.log(args); 186 | this.data = args.data; 187 | if (args.data) { 188 | if (args.data.dataset) { 189 | this._refresh(args.removedFieldValues, args.data); 190 | } else { 191 | this._refresh(args.removedFieldValues, null); 192 | } 193 | } 194 | this.chartTitleNode.innerHTML = args.title; 195 | }))); 196 | } 197 | 198 | console.log(this.type + "chart added"); 199 | }, 200 | 201 | _valueTrans: function (object) { 202 | // reformat the displayed the value 203 | var value = object.value; 204 | var tooltip; 205 | // if (this.dataType === "count") { 206 | // value = value > 10000 ? (value / 1000) + "K" : value; 207 | // this._unit = ""; 208 | // } else { 209 | value = value > 10000 ? (value / 1000).toFixed(2) + "K" : value.toFixed(0); 210 | //} 211 | 212 | if (this.type === "Pie") { 213 | tooltip = object.name + ": " + (object.percentage * 100).toFixed(2) + "%"; 214 | } 215 | else { 216 | tooltip = object.name + ": ~" + value + this._unit; 217 | } 218 | return { 219 | text: object.name, 220 | y: object.value, 221 | fill: object.fill, 222 | tooltip: tooltip 223 | }; 224 | }, 225 | 226 | _createData: function (chartType, data) { 227 | var dataset; 228 | if (data) { 229 | if (chartType === "columns") { 230 | dataset = arrayUtil.map(data.dataset, lang.hitch(this, function (item, index) { 231 | return { name: item.name, value: item.value, fill: data.colors[item.name] }; 232 | })); 233 | } else { 234 | var sum = 0; 235 | for (var i = 0; i < data.dataset.length; i++) { 236 | sum += data.dataset[i].value; 237 | } 238 | if (chartType === "pie") { 239 | dataset = arrayUtil.map(data.dataset, lang.hitch(this, function (item, index) { 240 | return { name: item.name, value: item.value, percentage: item.value / sum, fill: data.colors[item.name] }; 241 | })); 242 | } 243 | else { 244 | dataset = []; 245 | var itemFill; 246 | var hasKeyType = false; // whether the dataset includes the key type 247 | arrayUtil.forEach(data.dataset, lang.hitch(this, function (dataObj, index) { 248 | if (dataObj.name === this.keyType) { 249 | hasKeyType = true; 250 | if (data.colors) { 251 | itemFill = data.colors[dataObj.name]; 252 | } else { 253 | itemFill = this._theme.colors[index]; 254 | } 255 | if (sum) { 256 | var percentage = ((dataObj.value / sum) * 100).toFixed(1); 257 | this.donutChartValueNode.innerHTML = percentage + "%"; 258 | dataset.push({ name: dataObj.name, value: dataObj.value, fill: itemFill, tooltip: dataObj.name + ": " + percentage + "%" }); 259 | var remainingValue = sum - dataObj.value; 260 | dataset.push({ name: "rest", value: remainingValue, fill: "transparent", tooltip: "rest: " + (100 - percentage) + "%" }); 261 | } 262 | } 263 | })); 264 | 265 | //no key type defined: percentage equals to 0 266 | if (!hasKeyType) { 267 | this.donutChartValueNode.innerHTML = "0%"; 268 | dataset.push({ name: this.keyType, value: 0, fill: "transparent", tooltip: "" }); 269 | dataset.push({ name: "rest", value: sum, fill: "transparent", tooltip: "rest: " + 100 + "%" }); 270 | } 271 | } 272 | } 273 | } 274 | return dataset; 275 | }, 276 | 277 | _refresh: function (removedFieldValues, data) { 278 | if (data && this.type == "Columns") { 279 | //var newWidth = 127; 280 | //var count = data.dataset.length; 281 | //if (count >= 7) { 282 | // this.chart.plotParams.gap = 1; 283 | // newWidth = 127 + 3 * 25; 284 | //} else if (count > 4 && count < 7) { 285 | // newWidth = 127 + (count - 4) * 25; 286 | //} 287 | //this._resizeColumnsContainer(newWidth); 288 | } 289 | //if (this.dataType === "area") { 290 | // remove items 291 | if (data) { 292 | arrayUtil.forEach(removedFieldValues, lang.hitch(this, function (value) { 293 | if (this.type != "Donut") { 294 | this.dataStore.remove(value); 295 | } 296 | })); 297 | this._newData = this._createData(this.type.toLowerCase(), this.data); 298 | // add/update items 299 | arrayUtil.forEach(this._newData, lang.hitch(this, function (obj) { 300 | this.dataStore.put(obj); 301 | })); 302 | } else { 303 | // when donut chart has no data 304 | if (this.type == "Donut") { 305 | this.dataStore.remove(this.keyType); 306 | this.dataStore.remove("rest"); 307 | this.donutChartValueNode.innerHTML = "0%"; 308 | } 309 | arrayUtil.forEach(this._newData, lang.hitch(this, function (obj) { 310 | this.dataStore.remove(obj.name); 311 | })); 312 | 313 | } 314 | 315 | }, 316 | 317 | _resizeColumnsContainer: function (width) { 318 | width = width + "px"; 319 | domStyle.set(this.chartContainerNode, "width", width); 320 | this.chart.resize(); 321 | domStyle.set(this.chartTitleNode, "max-width", width); 322 | }, 323 | 324 | _connectChartEvents: function () { 325 | var _this = this; 326 | var pieTooltip; 327 | this.chart.connectToPlot("default",lang.hitch(this, function(evt) { 328 | // console.log(evt); 329 | var type = evt.type; 330 | if (type != "onplotreset") { 331 | // Get access to the shape and type 332 | var shape = evt.shape, run = evt.run; 333 | var selectedType = run.data[evt.index].text; 334 | // React to mouse event 335 | if (type == "onclick") { 336 | if (this.dataType == "area") { 337 | var geometries = []; 338 | var fPolygonLayer = this.fPolygonLayer; 339 | arrayUtil.forEach(fPolygonLayer.graphics, lang.hitch(this, function (graphic) { 340 | if (graphic.attributes[this.selectedFieldName] === selectedType) { 341 | geometries.push(graphic.geometry); 342 | } 343 | })); 344 | _this.emit("type-select", { 345 | type: selectedType, 346 | geometries: geometries 347 | }); 348 | 349 | //if(!shape.originalFill) { 350 | // shape.originalFill = shape.fillStyle; 351 | //} 352 | //var highlightColor = shape.originalFill; 353 | //highlightColor.a = 0.7; 354 | //// Set the fill color to pink 355 | //shape.setFill(highlightColor); 356 | } 357 | } 358 | // If it's a mouseover event 359 | else if (type == "onmouseover") { 360 | if (this.type === "Pie") { 361 | if (!pieTooltip) { 362 | pieTooltip = domConstruct.create("div", { "class": "pieTooltip" }, this.domNode); 363 | } 364 | pieTooltip.innerHTML = run.data[evt.index].tooltip; 365 | domStyle.set(pieTooltip, { display: "", top: evt.event.layerY - pieTooltip.clientHeight + "px", left: evt.event.layerX - pieTooltip.clientWidth / 2 + "px" }); 366 | } 367 | } 368 | // If it's a mouseout event 369 | else if (type == "onmouseout") { 370 | if (this.type === "Pie") { 371 | domStyle.set(pieTooltip, "display", "none"); 372 | } 373 | //// Set the fill the original fill 374 | //shape.setFill(shape.originalFill); 375 | } 376 | } 377 | 378 | })); 379 | } 380 | 381 | 382 | }); 383 | 384 | return oThisClass; 385 | }); 386 | -------------------------------------------------------------------------------- /src/app/reports/chartUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/lang", 15 | "dojo/_base/array" 16 | ], 17 | function( 18 | lang, array 19 | ) { 20 | return { 21 | // sort colormap colors by value 22 | // add no value color for 0 23 | // for each color in the colormap get: 24 | // color in chart format and 25 | // label/count as a chart series value 26 | getChartData: function(colormapDefinition, histogram) { 27 | var chartColors = {}; 28 | var chartSeries = []; 29 | var colors = lang.clone(colormapDefinition.colors); 30 | var histogramMin = Math.round(histogram.min); 31 | var histogramMax = Math.round(histogram.max) - 1; 32 | colors.sort(function(a,b) { 33 | return a.value - b.value; 34 | }); 35 | if (colors[0].value !== 0) { 36 | colors.unshift({label: "No Data", value: 0, rgb:[128,128,128]}); 37 | } 38 | array.forEach(colors, function(color) { 39 | var count; 40 | chartColors[color.label] = color.rgb; 41 | // chartColors.push(chartColor); 42 | if (color.value < histogramMin || color.value > histogramMax) { 43 | count = 0; 44 | } else { 45 | count = histogram.counts[color.value - histogramMin]; 46 | } 47 | chartSeries.push({ 48 | name: color.label, 49 | value: count 50 | }); 51 | }); 52 | return { 53 | dataset: chartSeries, 54 | colors: chartColors 55 | }; 56 | } 57 | }; 58 | }); -------------------------------------------------------------------------------- /src/app/reports/css/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/reports/css/images/add.png -------------------------------------------------------------------------------- /src/app/reports/css/images/hollow-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/reports/css/images/hollow-arrow.png -------------------------------------------------------------------------------- /src/app/reports/css/images/pie-background-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/app/reports/css/images/pie-background-small.png -------------------------------------------------------------------------------- /src/app/reports/css/reports.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | /*#mdlrDashboardContainer { 14 | bottom:2.5em; 15 | left: 50%; 16 | } 17 | */ 18 | .mdlrChartsHeaderIcon-settings { 19 | font-size: 12px; 20 | height:auto; 21 | width: auto; 22 | margin-right: 5px; 23 | } 24 | 25 | .mdlrCharts { 26 | border-top: 1px solid rgba(255, 255, 255, 0.25); 27 | list-style: none; 28 | padding: 5px 0 0; 29 | margin: 0; 30 | text-align: center; 31 | /*background: #fff;*/ 32 | -webkit-border-radius: 0 0 4px 4px; 33 | -moz-border-radius: 0 0 4px 4px; 34 | border-radius: 0 0 4px 4px; 35 | } 36 | 37 | .mdlrCharts li { 38 | display: inline-block; 39 | margin: 0 6px; 40 | padding: 4px; 41 | vertical-align: top; 42 | } 43 | /*.mdlrCharts li:not(.addNewChartButtonWrapper):hover { 44 | background: none repeat scroll 0 0 rgba(100, 100, 100, 0.8); 45 | border: 1px solid rgba(255, 255, 255, 0.15); 46 | border-radius: 4px 4px 4px 4px; 47 | box-shadow: 0 0 1px rgba(0, 0, 0, 0.2) inset; 48 | }*/ 49 | 50 | .mdlrCharts li.rootLevelChart { 51 | margin: -5px 0 0; 52 | padding: 9px 10px 4px 10px; 53 | border-radius: 0; 54 | background: rgba(0,0,0,0.2); 55 | position: relative; 56 | } 57 | /*.mdlrCharts li.chartSeperator { 58 | height: 100%; 59 | margin: 0; 60 | padding: 0; 61 | position: absolute; 62 | top: 0; 63 | width: 25px; 64 | background: url("images/hollow-arrow.png") left center no-repeat; 65 | }*/ 66 | .mdlrCharts li.rootLevelChart.lastChart { 67 | margin-right:25px; 68 | } 69 | 70 | .mdlrCharts li.rootLevelChart.lastChart:after { 71 | background: url("images/hollow-arrow.png") repeat scroll left center transparent; 72 | content: ""; 73 | height: 100%; 74 | position: absolute; 75 | right: -19px; 76 | top: 0; 77 | width: 25px; 78 | } 79 | .mdlrCharts li h4 { 80 | margin: 0 auto; 81 | text-align: center; 82 | color: #fff; 83 | font-weight: normal; 84 | max-width: 128px; 85 | font-size: 12px; 86 | } 87 | .chartContainer { 88 | width: 127px; 89 | height: 127px; 90 | /*padding:15px;*/ 91 | } 92 | 93 | .Pie .chartContainer svg, 94 | .Donut .chartContainer svg { 95 | background: url("images/pie-background-small.png") 1px 1px no-repeat; 96 | } 97 | 98 | /* to fake donut chart */ 99 | .Donut .chartContainer { 100 | position: relative 101 | } 102 | .Donut .chartContainer:after { 103 | background: none repeat scroll 0 0 #555555; 104 | border-radius: 100px 100px 100px 100px; 105 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.4) inset; 106 | content: ""; 107 | display: block; 108 | height: 70px; 109 | left: 50%; 110 | margin: -35px 0 0 -35px; 111 | position: absolute; 112 | top: 50%; 113 | width: 70px; 114 | } 115 | .donutDisplayedValue { 116 | color: #FFFFFF; 117 | display: block; 118 | font-size: 18px; 119 | left: 50%; 120 | line-height: 18px; 121 | margin-left: -30px; 122 | margin-top: -9px; 123 | position: absolute; 124 | text-align: center; 125 | text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.37); 126 | top: 50%; 127 | width: 60px; 128 | z-index: 101; 129 | } 130 | .addNewChartButtonWrapper { 131 | vertical-align: middle; 132 | } 133 | .addNewChartButton { 134 | background: url("images/add.png") center center no-repeat rgba(255,255,255,0.2); 135 | border: 2px dashed #fff; 136 | -webkit-border-radius: 5px; 137 | -moz-border-radius: 5px; 138 | border-radius: 5px; 139 | display: block; 140 | height: 100px; 141 | width: 100px; 142 | margin: 10px 0 10px 10px; 143 | cursor: pointer; 144 | transition-property: background-color, opacity; 145 | transition-duration: 0.2s; 146 | opacity:0.645; 147 | filter: alpha(opacity=70); 148 | } 149 | .addNewChartButton:hover, 150 | .addNewChartButton:active { 151 | background: rgba(255,255,255,0.4); 152 | opacity:1; 153 | filter: alpha(opacity=100); 154 | } 155 | 156 | .addNewChartButton span { 157 | display: none; 158 | } 159 | .addNewChartButton:hover span, 160 | .addNewChartButton:active span { 161 | display: block; 162 | color: #fff; 163 | font-size: 18px; 164 | text-shadow: -1px -1px 0 rgba(0,0,0,0.3); 165 | text-align:center; 166 | opacity:1; 167 | margin: 25px 0; 168 | } 169 | 170 | /*tooltip for pie */ 171 | .pieTooltip { 172 | position: absolute; 173 | z-index:300; 174 | background: rgba(255,255,255,0.75); 175 | cursor: default; 176 | padding:5px; 177 | border-radius: 4px; 178 | min-width: 120px; 179 | } 180 | -------------------------------------------------------------------------------- /src/app/reports/templates/AreaBreakdown.html: -------------------------------------------------------------------------------- 1 | 
2 |
Breakdown by Area 3 | 4 | X 5 | 6 | 7 | _ 8 | 9 | 12 |
13 |
    14 |
15 |

Approximate area by type for the ? features in the map extent.

16 |
17 | -------------------------------------------------------------------------------- /src/app/reports/templates/Chart.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 4 |
5 |
6 |

No Title

7 |
8 | -------------------------------------------------------------------------------- /src/app/templates/FeatureLayerPane.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 | 8 |
9 | 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /src/app/templates/MapControls.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
-------------------------------------------------------------------------------- /src/app/templates/ModelerPane.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |

9 |
10 |
11 |
12 |
13 |

Select Layers

14 |
15 |
16 | 17 |
18 |
19 |
20 |

Design Model

21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | 20 | 21 | Modeler 22 | 23 | 24 | 25 | 26 | 27 | 28 | 48 | 49 | 116 | 117 | 118 |
119 |
120 |
121 |
Loading...
122 |
123 |
124 |
125 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/lib/bootstrap_v2/css/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.3.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;} 11 | .clearfix:after{clear:both;} 12 | .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;} 13 | .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} 14 | .label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;} 15 | .label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} 16 | .badge{padding-left:9px;padding-right:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;} 17 | .label:empty,.badge:empty{display:none;} 18 | a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer;} 19 | .label-important,.badge-important{background-color:#b94a48;} 20 | .label-important[href],.badge-important[href]{background-color:#953b39;} 21 | .label-warning,.badge-warning{background-color:#f89406;} 22 | .label-warning[href],.badge-warning[href]{background-color:#c67605;} 23 | .label-success,.badge-success{background-color:#468847;} 24 | .label-success[href],.badge-success[href]{background-color:#356635;} 25 | .label-info,.badge-info{background-color:#3a87ad;} 26 | .label-info[href],.badge-info[href]{background-color:#2d6987;} 27 | .label-inverse,.badge-inverse{background-color:#333333;} 28 | .label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;} 29 | .btn .label,.btn .badge{position:relative;top:-1px;} 30 | .btn-mini .label,.btn-mini .badge{top:0;} 31 | [class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;} 32 | .icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");} 33 | .icon-glass{background-position:0 0;} 34 | .icon-music{background-position:-24px 0;} 35 | .icon-search{background-position:-48px 0;} 36 | .icon-envelope{background-position:-72px 0;} 37 | .icon-heart{background-position:-96px 0;} 38 | .icon-star{background-position:-120px 0;} 39 | .icon-star-empty{background-position:-144px 0;} 40 | .icon-user{background-position:-168px 0;} 41 | .icon-film{background-position:-192px 0;} 42 | .icon-th-large{background-position:-216px 0;} 43 | .icon-th{background-position:-240px 0;} 44 | .icon-th-list{background-position:-264px 0;} 45 | .icon-ok{background-position:-288px 0;} 46 | .icon-remove{background-position:-312px 0;} 47 | .icon-zoom-in{background-position:-336px 0;} 48 | .icon-zoom-out{background-position:-360px 0;} 49 | .icon-off{background-position:-384px 0;} 50 | .icon-signal{background-position:-408px 0;} 51 | .icon-cog{background-position:-432px 0;} 52 | .icon-trash{background-position:-456px 0;} 53 | .icon-home{background-position:0 -24px;} 54 | .icon-file{background-position:-24px -24px;} 55 | .icon-time{background-position:-48px -24px;} 56 | .icon-road{background-position:-72px -24px;} 57 | .icon-download-alt{background-position:-96px -24px;} 58 | .icon-download{background-position:-120px -24px;} 59 | .icon-upload{background-position:-144px -24px;} 60 | .icon-inbox{background-position:-168px -24px;} 61 | .icon-play-circle{background-position:-192px -24px;} 62 | .icon-repeat{background-position:-216px -24px;} 63 | .icon-refresh{background-position:-240px -24px;} 64 | .icon-list-alt{background-position:-264px -24px;} 65 | .icon-lock{background-position:-287px -24px;} 66 | .icon-flag{background-position:-312px -24px;} 67 | .icon-headphones{background-position:-336px -24px;} 68 | .icon-volume-off{background-position:-360px -24px;} 69 | .icon-volume-down{background-position:-384px -24px;} 70 | .icon-volume-up{background-position:-408px -24px;} 71 | .icon-qrcode{background-position:-432px -24px;} 72 | .icon-barcode{background-position:-456px -24px;} 73 | .icon-tag{background-position:0 -48px;} 74 | .icon-tags{background-position:-25px -48px;} 75 | .icon-book{background-position:-48px -48px;} 76 | .icon-bookmark{background-position:-72px -48px;} 77 | .icon-print{background-position:-96px -48px;} 78 | .icon-camera{background-position:-120px -48px;} 79 | .icon-font{background-position:-144px -48px;} 80 | .icon-bold{background-position:-167px -48px;} 81 | .icon-italic{background-position:-192px -48px;} 82 | .icon-text-height{background-position:-216px -48px;} 83 | .icon-text-width{background-position:-240px -48px;} 84 | .icon-align-left{background-position:-264px -48px;} 85 | .icon-align-center{background-position:-288px -48px;} 86 | .icon-align-right{background-position:-312px -48px;} 87 | .icon-align-justify{background-position:-336px -48px;} 88 | .icon-list{background-position:-360px -48px;} 89 | .icon-indent-left{background-position:-384px -48px;} 90 | .icon-indent-right{background-position:-408px -48px;} 91 | .icon-facetime-video{background-position:-432px -48px;} 92 | .icon-picture{background-position:-456px -48px;} 93 | .icon-pencil{background-position:0 -72px;} 94 | .icon-map-marker{background-position:-24px -72px;} 95 | .icon-adjust{background-position:-48px -72px;} 96 | .icon-tint{background-position:-72px -72px;} 97 | .icon-edit{background-position:-96px -72px;} 98 | .icon-share{background-position:-120px -72px;} 99 | .icon-check{background-position:-144px -72px;} 100 | .icon-move{background-position:-168px -72px;} 101 | .icon-step-backward{background-position:-192px -72px;} 102 | .icon-fast-backward{background-position:-216px -72px;} 103 | .icon-backward{background-position:-240px -72px;} 104 | .icon-play{background-position:-264px -72px;} 105 | .icon-pause{background-position:-288px -72px;} 106 | .icon-stop{background-position:-312px -72px;} 107 | .icon-forward{background-position:-336px -72px;} 108 | .icon-fast-forward{background-position:-360px -72px;} 109 | .icon-step-forward{background-position:-384px -72px;} 110 | .icon-eject{background-position:-408px -72px;} 111 | .icon-chevron-left{background-position:-432px -72px;} 112 | .icon-chevron-right{background-position:-456px -72px;} 113 | .icon-plus-sign{background-position:0 -96px;} 114 | .icon-minus-sign{background-position:-24px -96px;} 115 | .icon-remove-sign{background-position:-48px -96px;} 116 | .icon-ok-sign{background-position:-72px -96px;} 117 | .icon-question-sign{background-position:-96px -96px;} 118 | .icon-info-sign{background-position:-120px -96px;} 119 | .icon-screenshot{background-position:-144px -96px;} 120 | .icon-remove-circle{background-position:-168px -96px;} 121 | .icon-ok-circle{background-position:-192px -96px;} 122 | .icon-ban-circle{background-position:-216px -96px;} 123 | .icon-arrow-left{background-position:-240px -96px;} 124 | .icon-arrow-right{background-position:-264px -96px;} 125 | .icon-arrow-up{background-position:-289px -96px;} 126 | .icon-arrow-down{background-position:-312px -96px;} 127 | .icon-share-alt{background-position:-336px -96px;} 128 | .icon-resize-full{background-position:-360px -96px;} 129 | .icon-resize-small{background-position:-384px -96px;} 130 | .icon-plus{background-position:-408px -96px;} 131 | .icon-minus{background-position:-433px -96px;} 132 | .icon-asterisk{background-position:-456px -96px;} 133 | .icon-exclamation-sign{background-position:0 -120px;} 134 | .icon-gift{background-position:-24px -120px;} 135 | .icon-leaf{background-position:-48px -120px;} 136 | .icon-fire{background-position:-72px -120px;} 137 | .icon-eye-open{background-position:-96px -120px;} 138 | .icon-eye-close{background-position:-120px -120px;} 139 | .icon-warning-sign{background-position:-144px -120px;} 140 | .icon-plane{background-position:-168px -120px;} 141 | .icon-calendar{background-position:-192px -120px;} 142 | .icon-random{background-position:-216px -120px;width:16px;} 143 | .icon-comment{background-position:-240px -120px;} 144 | .icon-magnet{background-position:-264px -120px;} 145 | .icon-chevron-up{background-position:-288px -120px;} 146 | .icon-chevron-down{background-position:-313px -119px;} 147 | .icon-retweet{background-position:-336px -120px;} 148 | .icon-shopping-cart{background-position:-360px -120px;} 149 | .icon-folder-close{background-position:-384px -120px;width:16px;} 150 | .icon-folder-open{background-position:-408px -120px;width:16px;} 151 | .icon-resize-vertical{background-position:-432px -119px;} 152 | .icon-resize-horizontal{background-position:-456px -118px;} 153 | .icon-hdd{background-position:0 -144px;} 154 | .icon-bullhorn{background-position:-24px -144px;} 155 | .icon-bell{background-position:-48px -144px;} 156 | .icon-certificate{background-position:-72px -144px;} 157 | .icon-thumbs-up{background-position:-96px -144px;} 158 | .icon-thumbs-down{background-position:-120px -144px;} 159 | .icon-hand-right{background-position:-144px -144px;} 160 | .icon-hand-left{background-position:-168px -144px;} 161 | .icon-hand-up{background-position:-192px -144px;} 162 | .icon-hand-down{background-position:-216px -144px;} 163 | .icon-circle-arrow-right{background-position:-240px -144px;} 164 | .icon-circle-arrow-left{background-position:-264px -144px;} 165 | .icon-circle-arrow-up{background-position:-288px -144px;} 166 | .icon-circle-arrow-down{background-position:-312px -144px;} 167 | .icon-globe{background-position:-336px -144px;} 168 | .icon-wrench{background-position:-360px -144px;} 169 | .icon-tasks{background-position:-384px -144px;} 170 | .icon-filter{background-position:-408px -144px;} 171 | .icon-briefcase{background-position:-432px -144px;} 172 | .icon-fullscreen{background-position:-456px -144px;} 173 | .alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} 174 | .alert,.alert h4{color:#c09853;} 175 | .alert h4{margin:0;} 176 | .alert .close{position:relative;top:-2px;right:-21px;line-height:20px;} 177 | .alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;} 178 | .alert-success h4{color:#468847;} 179 | .alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;} 180 | .alert-danger h4,.alert-error h4{color:#b94a48;} 181 | .alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;} 182 | .alert-info h4{color:#3a87ad;} 183 | .alert-block{padding-top:14px;padding-bottom:14px;} 184 | .alert-block>p,.alert-block>ul{margin-bottom:0;} 185 | .alert-block p+p{margin-top:5px;} 186 | -------------------------------------------------------------------------------- /src/lib/bootstrap_v2/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/lib/bootstrap_v2/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/lib/bootstrap_v2/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/landscape-modeler-js/9969e163cd107bdcef2bbc3e02202cccf50f96a4/src/lib/bootstrap_v2/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/Colormap.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define(["dojo/_base/declare", 14 | "dojo/_base/lang", 15 | "dojo/_base/array", 16 | "dojo/on", 17 | "dojo/_base/Color", 18 | "dojo/dom-construct", 19 | "dojo/dom-style", 20 | "dijit/_WidgetBase", 21 | "dijit/_TemplatedMixin", 22 | "dijit/_WidgetsInTemplateMixin", 23 | "dojo/text!./templates/Colormap.html", 24 | "dojo/i18n!./nls/resources"], 25 | function(declare, lang, array, on, Color, domConstruct, domStyle, 26 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, 27 | template, i18n) { 28 | 29 | var oThisClass = declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { 30 | 31 | definition: null, 32 | showLabels: true, 33 | i18n: i18n, 34 | templateString: template, 35 | _setDefinitionAttr: function(newDefinition) { 36 | this.definition = newDefinition; 37 | var parentNode = this.__colorsNode; 38 | parentNode.innerHTML = ""; 39 | if (this.definition.colors) { 40 | array.forEach(this.definition.colors,lang.hitch(this,function(colorDef){ 41 | this._addColor(parentNode,colorDef); 42 | })); 43 | } 44 | }, 45 | 46 | postCreate: function() { 47 | this.inherited(arguments); 48 | if (!this.showLabels) { 49 | this.__lowNode.style.display = "none"; 50 | this.__highNode.style.display = "none"; 51 | } 52 | }, 53 | 54 | _addColor: function(parentNode,colorDef) { 55 | var sTip = null, s1 = colorDef.label, s2 = colorDef.value; 56 | if (s1) { 57 | s1 = s1.trim(); 58 | } 59 | if (s2) { 60 | s2 = (""+s2).trim(); 61 | } 62 | if (s1 && (s1.length > 0) && s2 && (s2.length > 0)) { 63 | var s = this.i18n.colorRamp.tipPattern; 64 | s = s.replace("{label}",s1); 65 | s = s.replace("{value}",s2); 66 | sTip = s; 67 | } else if (s1 && (s1.length > 0)) { 68 | sTip = s1; 69 | } else if (s2 && (s2.length > 0)) { 70 | sTip = s2; 71 | } 72 | 73 | var c = new Color(colorDef.rgb); 74 | var el = domConstruct.create("span",{className: "modeler-color-ramp-color"}); 75 | domStyle.set(el,"backgroundColor",c.toCss()); 76 | if (sTip && (sTip.length > 0)) { 77 | el.title = sTip; 78 | } 79 | parentNode.appendChild(el); 80 | } 81 | }); 82 | return oThisClass; 83 | }); -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/WeightedOverlayLayerEditor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/lang", 16 | "dojo/_base/array", 17 | "dojo/on", 18 | "dojo/dom-style", 19 | "dojo/dom-class", 20 | "dojo/number", 21 | "dojo/string", 22 | "dojo/Evented", 23 | "dojo/_base/fx", 24 | "dojo/fx", 25 | 26 | 27 | "dijit/_WidgetBase", 28 | "dijit/_TemplatedMixin", 29 | "dijit/_WidgetsInTemplateMixin", 30 | "dijit/_Container", 31 | 32 | "./containerUtils", 33 | 34 | "dojo/text!./templates/RemapRangeEditor.html", 35 | "dojo/text!./templates/WeightedOverlayLayerEditor.html", 36 | 37 | "dijit/form/Button", 38 | "dijit/form/NumberTextBox", 39 | "dijit/form/HorizontalSlider" 40 | ], 41 | function( 42 | declare, lang, array, on, domStyle, domClass, number, string, Evented, fx, coreFx, 43 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, 44 | containerUtils, 45 | remapRangeEditorTemplate, template, 46 | Button, NumberTextBox, HorizontalSlider 47 | ) { 48 | 49 | var getRangeMinMaxString = function(remapRange) { 50 | var rangeString; 51 | if (remapRange.inputMin === remapRange.inputMax) { 52 | rangeString = remapRange.inputMin + ""; 53 | } else { 54 | rangeString = remapRange.inputMin + " - " + remapRange.inputMax; 55 | } 56 | return rangeString; 57 | }; 58 | 59 | // private widget for editing an individual range 60 | var RemapRangeEditor = declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], { 61 | 62 | // properties 63 | templateString: remapRangeEditorTemplate, 64 | baseClass: "reclass-range-editor", 65 | 66 | // custom setters 67 | _setRemapRangeAttr: function(remapRange) { 68 | var inputMinMax = " (" + getRangeMinMaxString(remapRange) + ")"; 69 | this.labelNode.innerHTML = remapRange.label.substr(0,14)+(remapRange.label.length>15?'…':'') + inputMinMax; 70 | this.labelNode.title = remapRange.label + inputMinMax; 71 | this.outputValueNode.set("value", remapRange.outputValue); 72 | }, 73 | 74 | // init number text box and slider and wire up events 75 | postCreate: function() { 76 | var _this = this; 77 | this.inherited(arguments); 78 | this.outputValueNode.set("constraints", {round:0}); 79 | this.sliderObject = new HorizontalSlider({ 80 | name: "slider", 81 | value: this.outputValueNode.value, 82 | // TODO: get these defaults from properties and/or config 83 | minimum: 0, 84 | maximum: 9, 85 | intermediateChanges: true, 86 | discreteValues: 10, 87 | style: "width: 100px;" 88 | }, this.slider); 89 | this.sliderObject.startup(); 90 | this.own(on(this.sliderObject, "Change", function(value) { 91 | _this.outputValueNode.set("value", value); 92 | })); 93 | this.own(on(this.outputValueNode, "Change", function(value) { 94 | _this.remapRange.outputValue = value; 95 | _this.sliderObject.set("value", value); 96 | })); 97 | }, 98 | 99 | // set the valid range for output values 100 | setOutputRange: function(min, max) { 101 | var numTextBox = this.outputValueNode; 102 | var rangeMessage = string.substitute("Value must be between ${0} and ${1}", [min, max]); 103 | numTextBox.set("constraints", {min: min, max: max}); 104 | numTextBox.set("rangeMessage", rangeMessage); 105 | this.sliderObject.set({minimum: min, maximum: max}); 106 | } 107 | }); 108 | 109 | // private container widget for editing a set of ranges 110 | var RemapRangesEditor = declare([_WidgetBase, _TemplatedMixin, _Container, Evented], { 111 | 112 | // properties 113 | templateString: '
', 114 | minOutputValue: 0, 115 | maxOutputValue: 0, 116 | baseClass: "remap-ranges-editor", 117 | 118 | // custom setters 119 | // refresh child editor nodes based on ranges 120 | _setRemapRangesAttr: function(newRemapRanges) { 121 | var _this = this; 122 | var editor; 123 | containerUtils.removeChildren(this); 124 | array.forEach(newRemapRanges, function(remapRange) { 125 | // set label to "min - max" if not alerady set 126 | // TODO: should this be set in model? 127 | if (!remapRange.label) { 128 | remapRange.label = remapRange.inputMin + " - " + remapRange.inputMax; 129 | } 130 | editor = new RemapRangeEditor({remapRange: remapRange}); 131 | // TODO: pass as properties to constructor once refactored 132 | editor.setOutputRange(_this.minOutputValue, _this.maxOutputValue); 133 | _this.addChild(editor); 134 | }); 135 | }, 136 | 137 | // get remap ranges from editor nodes 138 | getRemapRanges: function() { 139 | var remapRanges = []; 140 | array.forEach(this.getChildren(), function(editorWidget) { 141 | remapRanges.push(editorWidget.getRemapRange()); 142 | }); 143 | return remapRanges; 144 | } 145 | }); 146 | 147 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Evented], { 148 | 149 | // properties 150 | templateString: template, 151 | baseClass: "weighted-overlay-layer-editor", 152 | 153 | // custom setters 154 | _setOverlayLayerAttr: function(newOverlayLayer) { 155 | var _this = this; 156 | this.weightNode.set("value", newOverlayLayer.weight); 157 | this.labelNode.innerHTML = newOverlayLayer.title; 158 | this.remapRangesEditor = new RemapRangesEditor({ 159 | // TODO: get min/max from colormap or config 160 | minOutputValue: 0, 161 | maxOutputValue: 9, 162 | remapRanges: newOverlayLayer.remapRanges 163 | }, _this.remapRangesEditorContainer); 164 | 165 | }, 166 | 167 | postCreate: function() { 168 | this.inherited(arguments); 169 | this.attachWeightingToggle(); 170 | }, 171 | 172 | // drop down to show remap ranges 173 | attachWeightingToggle: function() { 174 | domStyle.set(this.remapRangesEditorWrapper, "display", "none"); 175 | this.own(on(this.layerNode, "click", lang.hitch(this, function () { 176 | if (domClass.contains(this.iconNode, "icon-chevron-right")) { 177 | coreFx.wipeIn({ 178 | node: this.remapRangesEditorWrapper, 179 | onEnd: lang.hitch(this, function () { 180 | domClass.replace(this.iconNode, "icon-chevron-down", "icon-chevron-right"); 181 | }) 182 | }).play(); 183 | } else { 184 | coreFx.wipeOut ({ 185 | node: this.remapRangesEditorWrapper, 186 | onEnd: lang.hitch(this, function () { 187 | domClass.replace(this.iconNode, "icon-chevron-right", "icon-chevron-down"); 188 | }) 189 | }).play(); 190 | } 191 | }))); 192 | }, 193 | 194 | // are the layer parameters valid 195 | isValid: function() { 196 | // TODO: what about remap ranges being valid 197 | return this.weightNode.isValid(); 198 | }, 199 | 200 | // attach events 201 | _onWeightChange: function() { 202 | var value = parseInt(this.weightNode.valueNode.value, 10); 203 | value = (isNaN(value)) ? 0 : value; 204 | // TODO: make sure it's valid befor setting layer weight? 205 | this.overlayLayer.weight = value; 206 | this.emit("WeightChange", value); 207 | }, 208 | _onFocus: function() { 209 | this.weightNode.focusNode.select(); 210 | }, 211 | _onBlur: function() { 212 | var value = parseInt(this.weightNode.valueNode.value, 10); 213 | if (isNaN(value)) { 214 | this.weightNode.value = 0; 215 | this.emit("WeightChange", value); 216 | } 217 | } 218 | }); 219 | }); 220 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/WeightedOverlayModelDesigner.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([ 14 | "dojo/_base/declare", 15 | "dojo/_base/array", 16 | "dojo/on", 17 | "dojo/Evented", 18 | "dojo/dom-class", 19 | 20 | "dijit/_WidgetBase", 21 | "dijit/_TemplatedMixin", 22 | "dijit/_WidgetsInTemplateMixin", 23 | "dijit/_Container", 24 | "dijit/form/DropDownButton", 25 | "dijit/DropDownMenu", 26 | "dijit/MenuItem", 27 | 28 | "esri/layers/RasterFunction", 29 | 30 | "./containerUtils", 31 | "./WeightedOverlayLayerEditor", 32 | "./Colormap", 33 | 34 | "dojo/text!./templates/WeightedOverlayModelDesigner.html", 35 | 36 | "dijit/form/Button", 37 | "dijit/form/HorizontalSlider", 38 | "dijit/form/CheckBox" 39 | ], 40 | 41 | function( 42 | declare, array, on, Evented, domClass, 43 | _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, DropDownButton, DropDownMenu, MenuItem, 44 | RasterFunction, 45 | containerUtils, WeightedOverlayLayerEditor, Colormap, 46 | template 47 | ) { 48 | 49 | return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Evented], { 50 | 51 | // properties 52 | templateString: template, 53 | baseClass: "weighted-overlay-model-designer", 54 | _isValid: false, 55 | 56 | // init widgets 57 | buildRendering: function() { 58 | this.inherited(arguments); 59 | this.colormap = new Colormap({}, this.colorMapNode); 60 | this.ColormapSelector = new DropDownButton({ 61 | label: "Color Ramp" 62 | }, this.colormapSelectorNode); 63 | }, 64 | 65 | // init controls for model layer visibility 66 | postCreate: function() { 67 | var _this = this; 68 | var horizontalSlider = this.sliderNode; 69 | on(horizontalSlider, "change", function (/*e*/){ 70 | if (_this.weightedOverlayService && _this.weightedOverlayService.imageServiceLayer) { 71 | _this.weightedOverlayService.imageServiceLayer.setOpacity(1 - horizontalSlider.value); 72 | } 73 | }); 74 | var chkModelVisible = this.visibleModelNode; 75 | on(chkModelVisible, "Change", function (/*e*/) { 76 | if (_this.weightedOverlayService && _this.weightedOverlayService.imageServiceLayer) { 77 | _this.weightedOverlayService.imageServiceLayer.setVisibility(chkModelVisible.checked); 78 | } 79 | //set slider to enable only if chkModelVisible is checked on 80 | horizontalSlider.disabled = !chkModelVisible.checked; 81 | }); 82 | //set initial visibilty transparency 83 | horizontalSlider.set("value", 0); 84 | chkModelVisible.set('checked', false); 85 | }, 86 | 87 | // property setters 88 | _setModelAttr: function(newModel) { 89 | this.setModel(newModel); 90 | }, 91 | 92 | _setWeightedOverlayServiceAttr: function(newWeightedOverlayService) { 93 | this.setWeightedOverlayService(newWeightedOverlayService); 94 | }, 95 | 96 | // validate model on start up 97 | startup: function() { 98 | this.inherited(arguments); 99 | this.validate(); 100 | }, 101 | 102 | // set weighted overlay service 103 | // and set color map selector options 104 | setWeightedOverlayService: function(newWeightedOverlayService) { 105 | var _this = this; 106 | this.weightedOverlayService = newWeightedOverlayService; 107 | var menu = new DropDownMenu({style: "display: none;"}); 108 | array.forEach(this.weightedOverlayService.colormapDefinitions, function(colormapDefinition) { 109 | var menuItem = new MenuItem(); 110 | new Colormap({ 111 | definition: colormapDefinition 112 | }).placeAt(menuItem.containerNode); 113 | _this.own(on(menuItem, "Click", function() { 114 | if (_this.colormap && _this.colormap.definition !== colormapDefinition) { 115 | _this.colormap.set("definition", colormapDefinition); 116 | } 117 | _this.model.colormapDefinition = colormapDefinition; 118 | _this.runModel(); 119 | })); 120 | menuItem.containerNode.title = colormapDefinition.label; 121 | menu.addChild(menuItem); 122 | }); 123 | this.ColormapSelector.set("dropDown", menu); 124 | }, 125 | 126 | // validate then emit event w/ raster function 127 | _onRunClick: function() { 128 | this.runModel(); 129 | }, 130 | 131 | _onClearClick: function() { 132 | this.clearModel(); 133 | }, 134 | 135 | // load a new, empty model 136 | // hide the model layer 137 | // disable the model visibility checkbox 138 | // clear the model from the service 139 | // emit an event 140 | clearModel: function() { 141 | this.setModel(this.weightedOverlayService.createNewModel()); 142 | this.emit("model-clear", this.model); 143 | }, 144 | 145 | // replace existing layer editors with 146 | // new ones for the each layer 147 | // update colormap definition 148 | setModel: function(newModel) { 149 | var _this = this; 150 | this.model = newModel; 151 | containerUtils.removeChildren(this); 152 | if (this.model.overlayLayers && this.model.overlayLayers.length && this.model.overlayLayers.length > 0) { 153 | array.forEach(this.model.overlayLayers, function(layer) { 154 | var layerEditor = new WeightedOverlayLayerEditor({ 155 | overlayLayer: layer 156 | }); 157 | _this.own(on(layerEditor, "WeightChange", function() { 158 | _this.validate(); 159 | })); 160 | layerEditor.startup(); 161 | _this.addChild(layerEditor); 162 | }); 163 | } else { 164 | // no layers 165 | this.hideModelLayer(); 166 | this.visibleModelNode.set("disabled", true); 167 | this.weightedOverlayService.clearModel(); 168 | } 169 | this.validate(); 170 | if (this.model.colormapDefinition) { 171 | this.colormap.set("definition", this.model.colormapDefinition); 172 | } 173 | }, 174 | 175 | // sum up raster weights and show total 176 | // if total is not 100% set state to invalid 177 | // don't let user run model 178 | validate: function() { 179 | var totalWeight = 0; 180 | if (this.model && this.model.overlayLayers) { 181 | array.forEach(this.model.overlayLayers, function(layer) { 182 | totalWeight += layer.weight; 183 | }); 184 | } 185 | this.weightTotalNode.innerHTML = totalWeight; 186 | if (totalWeight === 100) { 187 | this._isValid = true; 188 | domClass.remove(this.weightTotalWrapper, "validation-error"); 189 | } else { 190 | this._isValid = false; 191 | domClass.add(this.weightTotalWrapper, "validation-error"); 192 | } 193 | this.runModelButton.set("disabled", !this._isValid); 194 | }, 195 | 196 | // validate the form 197 | // run the model 198 | // enable the model visibility checkbox 199 | // show the model layer 200 | // emit an event 201 | runModel: function(model) { 202 | // validate the model 203 | this.validate(); 204 | if (this._isValid) { 205 | // TODO: IMPORTANT! wrap in try catch, show model validation errors if any 206 | this.weightedOverlayService.runModel(this.model); 207 | this.visibleModelNode.set("disabled", false); 208 | this.showModelLayer(); 209 | this.emit("model-run", this.model); 210 | } 211 | }, 212 | 213 | // check visible checkbox and show layer 214 | showModelLayer: function() { 215 | this.visibleModelNode.set('checked', true); 216 | }, 217 | 218 | // uncheck visible checkbox and hide layer 219 | hideModelLayer: function() { 220 | this.visibleModelNode.set('checked', false); 221 | } 222 | }); 223 | }); 224 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/containerUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define([], 14 | function() { 15 | return { 16 | 17 | // remove all children from a container widget 18 | removeChildren: function(container) { 19 | var childCount; 20 | if (container && container.getChildren && container.removeChild) { 21 | childCount = container.getChildren().length; 22 | for (var i = childCount - 1; i >= 0; i--) { 23 | container.removeChild(i); 24 | } 25 | } 26 | } 27 | }; 28 | }); -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/nls/resources.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | define({ 14 | root: { 15 | 16 | colorRamp: { 17 | low: "Low", 18 | high: "High", 19 | tipPattern: "{label} ({value})" 20 | } 21 | 22 | // TODO: remaining i18n 23 | 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/resources/weighted-overlay-modeler.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Esri 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | /*custom classes*/ 14 | .group:after { 15 | content: ""; 16 | display: table; 17 | clear: both; 18 | } 19 | .clearfix:after { 20 | content: ""; 21 | display: table; 22 | clear: both; 23 | } 24 | .group .left { 25 | float: left; 26 | } 27 | .group .right { 28 | float: right; 29 | } 30 | .validation-error { 31 | background-color: #f00; 32 | color: #FFFFFF; 33 | } 34 | .weight-pct { 35 | vertical-align: top; 36 | } 37 | .weight-pct-total { 38 | padding: 5px 0; 39 | height: 26px; 40 | } 41 | .weight-pct-total-wrapper { 42 | float: right; 43 | padding: 5px; 44 | width: 45px; 45 | font-weight: bold; 46 | text-align: right; 47 | } 48 | .modeler-scroll-panel { 49 | height: 300px; 50 | min-height: 300px; 51 | width: 100%; 52 | overflow: auto; 53 | } 54 | .layer-visibility { 55 | margin-bottom: 1em; 56 | } 57 | .remap-ranges-editorPopup { 58 | background-color: #FFFFFF; 59 | } 60 | .remap-ranges-editor { 61 | margin: 0 0 0 16px; 62 | } 63 | .reclass-range-editor-ouput-value { 64 | width: 15px; 65 | text-align: right; 66 | } 67 | .weighted-overlay-layer-editor-weight { 68 | width: 30px; 69 | text-align: right; 70 | } 71 | 72 | .weighted-overlay-layer-selector { 73 | margin: 5px 0; 74 | } 75 | 76 | .weighted-overlay-layer-selector-buttons { 77 | float: right; 78 | } 79 | 80 | .reclass-range-editor-label { 81 | font-size: 0.9em; 82 | } 83 | 84 | .modeler-color-ramp { 85 | font-size: xx-small; 86 | } 87 | .modeler-color-ramp .modeler-color-ramp-colors { 88 | margin-left: 0; 89 | margin-right: 0; 90 | } 91 | .modeler-color-ramp .modeler-color-ramp-color { 92 | display:inline-block; 93 | width: 5px; 94 | height: 10px; 95 | } 96 | 97 | .modeler-transparency-slider { 98 | display: inline-block; 99 | width: 110px; 100 | } 101 | 102 | .layer-row { 103 | clear: both; 104 | margin: 5px 0; 105 | } 106 | .layer-row .title { 107 | float: left; 108 | } 109 | .layer-row .value { 110 | float: right; 111 | } 112 | .layer-ranges { 113 | clear: both; 114 | } -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/templates/Colormap.html: -------------------------------------------------------------------------------- 1 | 2 | ${i18n.colorRamp.low} 3 | 4 | ${i18n.colorRamp.high} 5 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/templates/RemapRangeEditor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/templates/WeightedOverlayLayerEditor.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 | 7 | 8 |
9 |
10 | % 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 | 20 |
-------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/templates/WeightedOverlayLayerSelector.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /src/lib/weighted-overlay-modeler/widget/templates/WeightedOverlayModelDesigner.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 |
16 | 17 |
18 | % 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/proxy.ashx: -------------------------------------------------------------------------------- 1 | <%@ WebHandler Language="C#" Class="proxy" %> 2 | /* 3 | This proxy page does not have any security checks. It is highly recommended 4 | that a user deploying this proxy page on their web server, add appropriate 5 | security checks, for example checking request path, username/password, target 6 | url, etc. 7 | */ 8 | using System; 9 | using System.Drawing; 10 | using System.IO; 11 | using System.Web; 12 | using System.Collections.Generic; 13 | using System.Text; 14 | using System.Xml.Serialization; 15 | using System.Web.Caching; 16 | 17 | /// 18 | /// Forwards requests to an ArcGIS Server REST resource. Uses information in 19 | /// the proxy.config file to determine properties of the server. 20 | /// 21 | public class proxy : IHttpHandler { 22 | 23 | public void ProcessRequest (HttpContext context) { 24 | 25 | HttpResponse response = context.Response; 26 | 27 | // Get the URL requested by the client (take the entire querystring at once 28 | // to handle the case of the URL itself containing querystring parameters) 29 | string uri = context.Request.Url.Query.Substring(1); 30 | 31 | // Get token, if applicable, and append to the request 32 | string token = getTokenFromConfigFile(uri); 33 | if (!String.IsNullOrEmpty(token)) 34 | { 35 | if (uri.Contains("?")) 36 | uri += "&token=" + token; 37 | else 38 | uri += "?token=" + token; 39 | } 40 | 41 | System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(uri); 42 | req.Method = context.Request.HttpMethod; 43 | req.ServicePoint.Expect100Continue = false; 44 | 45 | // Set body of request for POST requests 46 | if (context.Request.InputStream.Length > 0) 47 | { 48 | byte[] bytes = new byte[context.Request.InputStream.Length]; 49 | context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length); 50 | req.ContentLength = bytes.Length; 51 | 52 | string ctype = context.Request.ContentType; 53 | if (String.IsNullOrEmpty(ctype)) { 54 | req.ContentType = "application/x-www-form-urlencoded"; 55 | } 56 | else { 57 | req.ContentType = ctype; 58 | } 59 | 60 | using (Stream outputStream = req.GetRequestStream()) 61 | { 62 | outputStream.Write(bytes, 0, bytes.Length); 63 | } 64 | } 65 | 66 | // Send the request to the server 67 | System.Net.WebResponse serverResponse = null; 68 | try 69 | { 70 | serverResponse = req.GetResponse(); 71 | } 72 | catch (System.Net.WebException webExc) 73 | { 74 | response.StatusCode = 500; 75 | response.StatusDescription = webExc.Status.ToString(); 76 | response.Write(webExc.Response); 77 | response.End(); 78 | return; 79 | } 80 | 81 | // Set up the response to the client 82 | if (serverResponse != null) { 83 | response.ContentType = serverResponse.ContentType; 84 | using (Stream byteStream = serverResponse.GetResponseStream()) 85 | { 86 | 87 | // Text response 88 | if (serverResponse.ContentType.Contains("text") || 89 | serverResponse.ContentType.Contains("json")) 90 | { 91 | using (StreamReader sr = new StreamReader(byteStream)) 92 | { 93 | string strResponse = sr.ReadToEnd(); 94 | response.Write(strResponse); 95 | } 96 | } 97 | else 98 | { 99 | // Binary response (image, lyr file, other binary file) 100 | BinaryReader br = new BinaryReader(byteStream); 101 | byte[] outb = br.ReadBytes((int)serverResponse.ContentLength); 102 | br.Close(); 103 | 104 | // Tell client not to cache the image since it's dynamic 105 | response.CacheControl = "no-cache"; 106 | 107 | // Send the image to the client 108 | // (Note: if large images/files sent, could modify this to send in chunks) 109 | response.OutputStream.Write(outb, 0, outb.Length); 110 | } 111 | 112 | serverResponse.Close(); 113 | } 114 | } 115 | response.End(); 116 | } 117 | 118 | public bool IsReusable { 119 | get { 120 | return false; 121 | } 122 | } 123 | 124 | // Gets the token for a server URL from a configuration file 125 | private string getTokenFromConfigFile(string uri) 126 | { 127 | try 128 | { 129 | ProxyConfig config = ProxyConfig.GetCurrentConfig(); 130 | if (config != null) 131 | return config.GetToken(uri); 132 | else 133 | throw new ApplicationException( 134 | "Proxy.config file does not exist at application root, or is not readable."); 135 | } 136 | catch (InvalidOperationException) 137 | { 138 | // Proxy is being used for an unsupported service (proxy.config has mustMatch="true") 139 | HttpResponse response = HttpContext.Current.Response; 140 | response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden; 141 | response.End(); 142 | } 143 | catch (Exception e) 144 | { 145 | if (e is ApplicationException) 146 | throw e; 147 | 148 | // just return an empty string at this point 149 | // -- may want to throw an exception, or add to a log file 150 | } 151 | 152 | return string.Empty; 153 | } 154 | } 155 | 156 | [XmlRoot("ProxyConfig")] 157 | public class ProxyConfig 158 | { 159 | #region Static Members 160 | 161 | private static object _lockobject = new object(); 162 | 163 | public static ProxyConfig LoadProxyConfig(string fileName) 164 | { 165 | ProxyConfig config = null; 166 | 167 | lock (_lockobject) 168 | { 169 | if (System.IO.File.Exists(fileName)) 170 | { 171 | XmlSerializer reader = new XmlSerializer(typeof(ProxyConfig)); 172 | using (System.IO.StreamReader file = new System.IO.StreamReader(fileName)) 173 | { 174 | config = (ProxyConfig)reader.Deserialize(file); 175 | } 176 | } 177 | } 178 | 179 | return config; 180 | } 181 | 182 | public static ProxyConfig GetCurrentConfig() 183 | { 184 | ProxyConfig config = HttpRuntime.Cache["proxyConfig"] as ProxyConfig; 185 | if (config == null) 186 | { 187 | string fileName = GetFilename(HttpContext.Current); 188 | config = LoadProxyConfig(fileName); 189 | 190 | if (config != null) 191 | { 192 | CacheDependency dep = new CacheDependency(fileName); 193 | HttpRuntime.Cache.Insert("proxyConfig", config, dep); 194 | } 195 | } 196 | 197 | return config; 198 | } 199 | 200 | public static string GetFilename(HttpContext context) 201 | { 202 | return context.Server.MapPath("./proxy.config"); 203 | } 204 | #endregion 205 | 206 | ServerUrl[] serverUrls; 207 | bool mustMatch; 208 | 209 | [XmlArray("serverUrls")] 210 | [XmlArrayItem("serverUrl")] 211 | public ServerUrl[] ServerUrls 212 | { 213 | get { return this.serverUrls; } 214 | set { this.serverUrls = value; } 215 | } 216 | 217 | [XmlAttribute("mustMatch")] 218 | public bool MustMatch 219 | { 220 | get { return mustMatch; } 221 | set { mustMatch = value; } 222 | } 223 | 224 | public string GetToken(string uri) 225 | { 226 | foreach (ServerUrl su in serverUrls) 227 | { 228 | if (su.MatchAll && uri.StartsWith(su.Url, StringComparison.InvariantCultureIgnoreCase)) 229 | { 230 | return su.Token; 231 | } 232 | else 233 | { 234 | if (String.Compare(uri, su.Url, StringComparison.InvariantCultureIgnoreCase) == 0) 235 | return su.Token; 236 | } 237 | } 238 | 239 | if (mustMatch) 240 | throw new InvalidOperationException(); 241 | 242 | return string.Empty; 243 | } 244 | } 245 | 246 | public class ServerUrl 247 | { 248 | string url; 249 | bool matchAll; 250 | string token; 251 | 252 | [XmlAttribute("url")] 253 | public string Url 254 | { 255 | get { return url; } 256 | set { url = value; } 257 | } 258 | 259 | [XmlAttribute("matchAll")] 260 | public bool MatchAll 261 | { 262 | get { return matchAll; } 263 | set { matchAll = value; } 264 | } 265 | 266 | [XmlAttribute("token")] 267 | public string Token 268 | { 269 | get { return token; } 270 | set { token = value; } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/proxy.config: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/data/arcgis/rest/services/Landscape_Modeler/USA_Weighted_Overlay/ImageServer: -------------------------------------------------------------------------------- 1 | {"currentVersion":10.2,"serviceDescription":"Weigthed overlay service for Landscape Modeler application","name":"Landscape_Modeler/USA_Weighted_Overlay","description":"Weigthed overlay service for Landscape Modeler application","extent":{"xmin":-1.00349991098E7,"ymin":-2072988.8044999996,"xmax":3310260.8902000003,"ymax":5151761.1955,"spatialReference":{"wkid":102008,"latestWkid":102008}},"initialExtent":{"xmin":-1.00349991098E7,"ymin":-2072988.8044999996,"xmax":3310260.8902000003,"ymax":5151761.1955,"spatialReference":{"wkid":102008,"latestWkid":102008}},"fullExtent":{"xmin":-1.00349991098E7,"ymin":-2072988.8044999996,"xmax":3310260.8902000003,"ymax":5151761.1955,"spatialReference":{"wkid":102008,"latestWkid":102008}},"pixelSizeX":30,"pixelSizeY":30,"bandCount":1,"pixelType":"U8","minPixelSize":0,"maxPixelSize":0,"copyrightText":"","serviceDataType":"esriImageServiceDataTypeGeneric","minValues":[0],"maxValues":[4],"meanValues":[0.32091033502919886],"stdvValues":[0.4668947541093439],"objectIdField":"OBJECTID","fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null},{"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null},{"name":"Name","type":"esriFieldTypeString","alias":"Name","domain":null,"length":50},{"name":"MinPS","type":"esriFieldTypeDouble","alias":"MinPS","domain":null},{"name":"MaxPS","type":"esriFieldTypeDouble","alias":"MaxPS","domain":null},{"name":"LowPS","type":"esriFieldTypeDouble","alias":"LowPS","domain":null},{"name":"HighPS","type":"esriFieldTypeDouble","alias":"HighPS","domain":null},{"name":"Category","type":"esriFieldTypeInteger","alias":"Category","domain":{"type":"codedValue","name":"MosaicCatalogItemCategoryDomain","codedValues":[{"name":"Unknown","code":0},{"name":"Primary","code":1},{"name":"Overview","code":2},{"name":"Unprocessed Overview","code":3},{"name":"Partial Overview","code":4},{"name":"Uploaded","code":253},{"name":"Incomplete","code":254},{"name":"Custom","code":255}]}},{"name":"Tag","type":"esriFieldTypeString","alias":"Tag","domain":null,"length":20},{"name":"GroupName","type":"esriFieldTypeString","alias":"GroupName","domain":null,"length":50},{"name":"ProductName","type":"esriFieldTypeString","alias":"ProductName","domain":null,"length":50},{"name":"CenterX","type":"esriFieldTypeDouble","alias":"CenterX","domain":null},{"name":"CenterY","type":"esriFieldTypeDouble","alias":"CenterY","domain":null},{"name":"ZOrder","type":"esriFieldTypeInteger","alias":"ZOrder","domain":null},{"name":"Title","type":"esriFieldTypeString","alias":"Title","domain":null,"length":50},{"name":"Url","type":"esriFieldTypeString","alias":"Url","domain":null,"length":1024},{"name":"InputRanges","type":"esriFieldTypeString","alias":"InputRanges","domain":null,"length":256},{"name":"OutputValues","type":"esriFieldTypeString","alias":"OutputValues","domain":null,"length":256},{"name":"NoDataRanges","type":"esriFieldTypeString","alias":"NoDataRanges","domain":null,"length":256},{"name":"RangeLabels","type":"esriFieldTypeString","alias":"RangeLabels","domain":null,"length":256},{"name":"Shape_Length","type":"esriFieldTypeDouble","alias":"Shape_Length","domain":null},{"name":"Shape_Area","type":"esriFieldTypeDouble","alias":"Shape_Area","domain":null},{"name":"NoDataRangeLabels","type":"esriFieldTypeString","alias":"NoDataRangeLabels","domain":null,"length":1024}],"capabilities":"Image,Metadata,Catalog,Mensuration","defaultMosaicMethod":"Northwest","allowedMosaicMethods":"NorthWest,Center,LockRaster,ByAttribute,Nadir,Viewpoint,Seamline,None","sortField":"","sortValue":null,"mosaicOperator":"First","defaultCompressionQuality":75,"defaultResamplingMethod":"Nearest","maxImageHeight":4100,"maxImageWidth":15000,"maxRecordCount":1000,"maxDownloadImageCount":20,"maxDownloadSizeLimit":2048,"maxMosaicImageCount":20,"allowRasterFunction":true,"rasterFunctionInfos":[{"name":"None","description":"A No-Op Function.","help":""},{"name":"WeightedOverlay_7_0_9_histogram","description":"A raster function template.","help":""},{"name":"WeightedOverlay_7_1_9_colormap","description":"A raster function template.","help":""}],"rasterTypeInfos":[{"name":"Raster Dataset","description":"Supports all ArcGIS Raster Datasets","help":""}],"mensurationCapabilities":"Basic","hasHistograms":true,"hasColormap":false,"hasRasterAttributeTable":false,"minScale":0,"maxScale":0,"exportTilesAllowed":false,"editFieldsInfo":null,"ownershipBasedAccessControlForRasters":null,"allowComputeTiePoints":false,"useStandardizedQueries":true,"spatialReference":{"wkid":102008,"latestWkid":102008}} -------------------------------------------------------------------------------- /test/data/query.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectIdFieldName": "OBJECTID", 3 | "fields": [{ 4 | "name": "OBJECTID", 5 | "type": "esriFieldTypeOID", 6 | "alias": "OBJECTID", 7 | "domain": null 8 | }, { 9 | "name": "Name", 10 | "type": "esriFieldTypeString", 11 | "alias": "Name", 12 | "domain": null, 13 | "length": 50 14 | }, { 15 | "name": "Title", 16 | "type": "esriFieldTypeString", 17 | "alias": "Title", 18 | "domain": null, 19 | "length": 50 20 | }, { 21 | "name": "Url", 22 | "type": "esriFieldTypeString", 23 | "alias": "Url", 24 | "domain": null, 25 | "length": 1024 26 | }, { 27 | "name": "InputRanges", 28 | "type": "esriFieldTypeString", 29 | "alias": "InputRanges", 30 | "domain": null, 31 | "length": 256 32 | }, { 33 | "name": "OutputValues", 34 | "type": "esriFieldTypeString", 35 | "alias": "OutputValues", 36 | "domain": null, 37 | "length": 256 38 | }, { 39 | "name": "NoDataRanges", 40 | "type": "esriFieldTypeString", 41 | "alias": "NoDataRanges", 42 | "domain": null, 43 | "length": 256 44 | }, { 45 | "name": "RangeLabels", 46 | "type": "esriFieldTypeString", 47 | "alias": "RangeLabels", 48 | "domain": null, 49 | "length": 256 50 | }, { 51 | "name": "NoDataRangeLabels", 52 | "type": "esriFieldTypeString", 53 | "alias": "NoDataRangeLabels", 54 | "domain": null, 55 | "length": 1024 56 | }], 57 | "features": [{ 58 | "attributes": { 59 | "OBJECTID": 32, 60 | "Name": "LndOwn_BLM_US_PADUS_2011_PA", 61 | "Title": "BLM Lands", 62 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_BLM_Lands/ImageServer", 63 | "InputRanges": "1,1", 64 | "OutputValues": "5", 65 | "NoDataRanges": "0,0", 66 | "RangeLabels": "BLM Land", 67 | "NoDataRangeLabels": "Non-BLM Land" 68 | } 69 | }, { 70 | "attributes": { 71 | "OBJECTID": 33, 72 | "Name": "LndOwn_Fed_US_PADUS_2011_PA", 73 | "Title": "Federal Lands", 74 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_All_Federal_Lands/ImageServer", 75 | "InputRanges": "1,1", 76 | "OutputValues": "5", 77 | "NoDataRanges": "0,0", 78 | "RangeLabels": "Federal Land", 79 | "NoDataRangeLabels": "Non-Federal Land" 80 | } 81 | }, { 82 | "attributes": { 83 | "OBJECTID": 34, 84 | "Name": "LndOwn_FS_US_PADUS_2011_PA", 85 | "Title": "USFS Lands", 86 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_USFS_Lands/ImageServer", 87 | "InputRanges": "1,1", 88 | "OutputValues": "5", 89 | "NoDataRanges": "0,0", 90 | "RangeLabels": "USFS Land", 91 | "NoDataRangeLabels": "Non-USFS Land" 92 | } 93 | }, { 94 | "attributes": { 95 | "OBJECTID": 35, 96 | "Name": "LndOwn_FWS_US_PADUS_2011_PA", 97 | "Title": "USFWS Lands", 98 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_USFWS_Lands/ImageServer", 99 | "InputRanges": "1,1", 100 | "OutputValues": "5", 101 | "NoDataRanges": "0,0", 102 | "RangeLabels": "USFWS Lands", 103 | "NoDataRangeLabels": "Non-USFWS Lands" 104 | } 105 | }, { 106 | "attributes": { 107 | "OBJECTID": 36, 108 | "Name": "LndOwn_NPS_US_PADUS_2011_PA", 109 | "Title": "NPS Lands", 110 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_NPS_Lands/ImageServer", 111 | "InputRanges": "1,1", 112 | "OutputValues": "5", 113 | "NoDataRanges": "0,0", 114 | "RangeLabels": "NPS Lands", 115 | "NoDataRangeLabels": "Non-NPS Lands" 116 | } 117 | }, { 118 | "attributes": { 119 | "OBJECTID": 37, 120 | "Name": "LndOwn_Ntv_US_PADUS_2011_PA", 121 | "Title": "Native American Lands", 122 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Native_American_Lands/ImageServer", 123 | "InputRanges": "1,1", 124 | "OutputValues": "5", 125 | "NoDataRanges": "0,0", 126 | "RangeLabels": "Native American Lands", 127 | "NoDataRangeLabels": "Non-Native American Lands" 128 | } 129 | }, { 130 | "attributes": { 131 | "OBJECTID": 38, 132 | "Name": "LndOwn_Wild_US_PADUS_2011_PA", 133 | "Title": "Wilderness Areas", 134 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Wilderness_Areas/ImageServer", 135 | "InputRanges": "1,1", 136 | "OutputValues": "5", 137 | "NoDataRanges": "0,0", 138 | "RangeLabels": "Wilderness", 139 | "NoDataRangeLabels": "Non-Wilderness" 140 | } 141 | }, { 142 | "attributes": { 143 | "OBJECTID": 39, 144 | "Name": "Soil_MapUnit_US_SSURGO_2010_WtrTblDpthAnnMin", 145 | "Title": "Soils Water Table Depth", 146 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Soils_Water_Table_Depth/ImageServer", 147 | "InputRanges": "0,55,55,110,110,165,165,230,230,281", 148 | "OutputValues": "1,3,5,7,9", 149 | "NoDataRanges": null, 150 | "RangeLabels": "Very Shallow,Shallow,Medium,Deep,Very Deep", 151 | "NoDataRangeLabels": null 152 | } 153 | }, { 154 | "attributes": { 155 | "OBJECTID": 43, 156 | "Name": "Soil_MapUnit_US_SSURGO_2010_AvailWtrStorWA_8bit", 157 | "Title": "Soils Available Water Storage", 158 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Soils_Available_Water_Storage/ImageServer", 159 | "InputRanges": "0,20,20,35,35,70,50,70,70,85", 160 | "OutputValues": "1,3,5,7,9", 161 | "NoDataRanges": null, 162 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 163 | "NoDataRangeLabels": null 164 | } 165 | }, { 166 | "attributes": { 167 | "OBJECTID": 44, 168 | "Name": "FloodRsk_US_FEMA_2011_PA", 169 | "Title": "Flood Risk", 170 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Flood_Risk/ImageServer", 171 | "InputRanges": "1,1", 172 | "OutputValues": "5", 173 | "NoDataRanges": "0,0", 174 | "RangeLabels": "In 100-yr Flood Plain", 175 | "NoDataRangeLabels": "Not In 100-yr Flood Plain" 176 | } 177 | }, { 178 | "attributes": { 179 | "OBJECTID": 45, 180 | "Name": "LndForm_US_USGS_Class", 181 | "Title": "Land Surface Forms", 182 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Land_Surface_Forms/ImageServer", 183 | "InputRanges": "1,4,4,4,5,8,8,10,10,10", 184 | "OutputValues": "1,3,5,7,9", 185 | "NoDataRanges": null, 186 | "RangeLabels": "Plains,Escarpments,Hills,Mountains/Canyons,Drainage Channels", 187 | "NoDataRangeLabels": null 188 | } 189 | }, { 190 | "attributes": { 191 | "OBJECTID": 47, 192 | "Name": "DevelopmentRisk_US_USFS_2007", 193 | "Title": "Development Risk", 194 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Development_Risk/ImageServer", 195 | "InputRanges": "0,0,1,1,2,2,3,3,4,4", 196 | "OutputValues": "0,1,5,8,9", 197 | "NoDataRanges": null, 198 | "RangeLabels": "No Risk,Low,Moderate,High,Very High", 199 | "NoDataRangeLabels": null 200 | } 201 | }, { 202 | "attributes": { 203 | "OBJECTID": 48, 204 | "Name": "FirePotential_US_USFS_2013", 205 | "Title": "Wildland Fire Potential", 206 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Fire_Potential/ImageServer", 207 | "InputRanges": "1,1,2,2,3,3,4,4,5,5,6,6,7,7", 208 | "OutputValues": "1,3,5,7,9,0,0", 209 | "NoDataRanges": null, 210 | "RangeLabels": "Very Low,Low,Moderate,High,Very High,Non-burnable,Water", 211 | "NoDataRangeLabels": null 212 | } 213 | }, { 214 | "attributes": { 215 | "OBJECTID": 50, 216 | "Name": "InsectDiseaseRisk_US_USDA_2007", 217 | "Title": "Insect and Disease Risk", 218 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Insect_and_Disease_Risk/ImageServer", 219 | "InputRanges": "0,36,35,66,65,101", 220 | "OutputValues": "1,9,5", 221 | "NoDataRanges": null, 222 | "RangeLabels": "Low,High,Medium", 223 | "NoDataRangeLabels": null 224 | } 225 | }, { 226 | "attributes": { 227 | "OBJECTID": 51, 228 | "Name": "LndCov_US_GAP_2003", 229 | "Title": "Landcover GAP", 230 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Landcover_GAP/ImageServer", 231 | "InputRanges": "549,551,502,507,573,577,1,289,552,557,514,554,577,579,500,507,565,576,460,499,290,459", 232 | "OutputValues": "9,3,1,7,6,2,1,4,8,2,4", 233 | "NoDataRanges": null, 234 | "RangeLabels": "Ag. Veg,Aquatic Veg,Developed,Forest,Introduced Veg,Non/Sparse-Vascular Rock Veg,Open Water,Polar/Montane Veg,Recently Disturbed,Semi-Desert,Shrubland", 235 | "NoDataRangeLabels": null 236 | } 237 | }, { 238 | "attributes": { 239 | "OBJECTID": 52, 240 | "Name": "Rainfall_US_USDA_2000_Inches", 241 | "Title": "Average Rainfall Inches", 242 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Mean_Rainfall/ImageServer", 243 | "InputRanges": "2,10,10,20,20,40,40,75,75,283", 244 | "OutputValues": "1,2,3,4,5", 245 | "NoDataRanges": null, 246 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 247 | "NoDataRangeLabels": null 248 | } 249 | }, { 250 | "attributes": { 251 | "OBJECTID": 53, 252 | "Name": "Road_US_TIGER_2011_KM", 253 | "Title": "Road Density", 254 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Roads/ImageServer", 255 | "InputRanges": "0,1,1,3,3,5,5,10,10,45", 256 | "OutputValues": "9,7,5,3,1", 257 | "NoDataRanges": null, 258 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 259 | "NoDataRangeLabels": null 260 | } 261 | }, { 262 | "attributes": { 263 | "OBJECTID": 54, 264 | "Name": "Temperature_US_USDA_2000_Celsius", 265 | "Title": "Mean Annual Temperature", 266 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Mean_Temperature/ImageServer", 267 | "InputRanges": "-9,0,0,5,5,12,12,20,20,26", 268 | "OutputValues": "1,2,3,4,5", 269 | "NoDataRanges": null, 270 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 271 | "NoDataRangeLabels": null 272 | } 273 | }, { 274 | "attributes": { 275 | "OBJECTID": 55, 276 | "Name": "WildlandUrbanInterface_US_WUI_2008_PA", 277 | "Title": "Wildland-Urban Interface", 278 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Wildland_Urban_Interface/ImageServer", 279 | "InputRanges": "1,1,0,0", 280 | "OutputValues": "5,0", 281 | "NoDataRanges": "", 282 | "RangeLabels": "Interface,Noninterface", 283 | "NoDataRangeLabels": "" 284 | } 285 | }, { 286 | "attributes": { 287 | "OBJECTID": 56, 288 | "Name": "WoodyBiomass_US_USDA_2008", 289 | "Title": "Woody Biomass", 290 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/USA_Woody_Biomass/ImageServer", 291 | "InputRanges": "0,66,66,133,133,256", 292 | "OutputValues": "1,5,9", 293 | "NoDataRanges": null, 294 | "RangeLabels": "Low,Medium,High", 295 | "NoDataRangeLabels": null 296 | } 297 | }, { 298 | "attributes": { 299 | "OBJECTID": 57, 300 | "Name": "CONUS_aspect_90m", 301 | "Title": "Aspect", 302 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/Landscape_Modeler/USA_Aspect/ImageServer", 303 | "InputRanges": "-1,0,0,22.5,22.5,67.5,67.5,112.5,112.5,157.5,157.5,202.5,202.5,247.5,247.5,292.5,292.5,337.5,337.5,360.5", 304 | "OutputValues": "1,2,3,4,5,6,7,8,9,2", 305 | "NoDataRanges": null, 306 | "RangeLabels": "Flat,NNE,NE,E,SE,S,SW,W,NW,NNW", 307 | "NoDataRangeLabels": null 308 | } 309 | }, { 310 | "attributes": { 311 | "OBJECTID": 58, 312 | "Name": "CONUS_dem_90m", 313 | "Title": "Elevation", 314 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/Landscape_Modeler/USA_Elevation/ImageServer", 315 | "InputRanges": "-96,100,100,300,300,800,800,2000,2000,4412", 316 | "OutputValues": "1,2,3,4,5", 317 | "NoDataRanges": null, 318 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 319 | "NoDataRangeLabels": null 320 | } 321 | }, { 322 | "attributes": { 323 | "OBJECTID": 59, 324 | "Name": "CONUS_slope_90m", 325 | "Title": "Slope", 326 | "Url": "https://landscape3.arcgis.com/arcgis/rest/services/Landscape_Modeler/USA_Slope/ImageServer", 327 | "InputRanges": "0,3,3,8,8,16,16,26,26,82", 328 | "OutputValues": "1,2,3,4,5", 329 | "NoDataRanges": null, 330 | "RangeLabels": "Very Low,Low,Medium,High,Very High", 331 | "NoDataRangeLabels": null 332 | } 333 | }, { 334 | "attributes": { 335 | "OBJECTID": 60, 336 | "Name": "Critical_Habitat", 337 | "Title": "Critical Habitat", 338 | "Url": "https://landscape2.arcgis.com/arcgis/rest/services/USA_Critical_Habitat/ImageServer", 339 | "InputRanges": "1,1", 340 | "OutputValues": "5", 341 | "NoDataRanges": "0,0", 342 | "RangeLabels": "Critical Habitat", 343 | "NoDataRangeLabels": "Noncritical Habitat" 344 | } 345 | }] 346 | } -------------------------------------------------------------------------------- /test/data/webmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationalLayers": [{ 3 | "url": "http://ec2-54-243-84-56.compute-1.amazonaws.com/arcgis/rest/services/landscape/weightedOverlayAnalysis/ImageServer", 4 | "id": "landscape/weightedOverlayAnalysis", 5 | "visibility": true, 6 | "opacity": 1, 7 | "title": "Habitat Development Risk", 8 | "renderingRule": { 9 | "rasterFunction": "WeightedOverlay_7_1_9_colormap", 10 | "rasterFunctionArguments": { 11 | "Raster1": "$1", 12 | "Weight_Raster1": 0.25, 13 | "InputRanges_Raster1": [0, 0, 5, 5, 10, 10], 14 | "OutputValues_Raster1": [1, 9, 9], 15 | "Raster2": "$2", 16 | "Weight_Raster2": 0.25, 17 | "InputRanges_Raster2": [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], 18 | "OutputValues_Raster2": [0, 1, 5, 8, 9], 19 | "Raster3": "$32", 20 | "Weight_Raster3": 0.25, 21 | "InputRanges_Raster3": [11, 12, 12, 13, 21, 25, 31, 32, 41, 44, 52, 72, 81, 83, 90, 96], 22 | "OutputValues_Raster3": [0, 0, 1, 1, 9, 7, 3, 5], 23 | "Raster4": "$14", 24 | "Weight_Raster4": 0.25, 25 | "InputRanges_Raster4": [0, 1, 1, 3, 3, 5, 5, 10, 10, 45], 26 | "OutputValues_Raster4": [9, 7, 5, 3, 1], 27 | "Raster5": "$4", 28 | "Weight_Raster5": 0, 29 | "Raster6": "$4", 30 | "Weight_Raster6": 0, 31 | "Raster7": "$4", 32 | "Weight_Raster7": 0, 33 | "Colormap": [ 34 | [1, 38, 115, 0], 35 | [2, 86, 148, 0], 36 | [3, 39, 181, 0], 37 | [4, 197, 219, 0], 38 | [5, 255, 255, 0], 39 | [6, 255, 195, 0], 40 | [7, 250, 142, 0], 41 | [8, 242, 85, 0], 42 | [9, 230, 0, 0] 43 | ] 44 | }, 45 | "variableName": "Raster" 46 | }, 47 | "remapRangeLabels": { 48 | "Labels_Raster1": ["Non Critical", "Threatened", "Endangered"] 49 | } 50 | }], 51 | "version": "1.9", 52 | "baseMap": { 53 | "baseMapLayers": [{ 54 | "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", 55 | "id": "layer0", 56 | "visibility": true, 57 | "opacity": 1 58 | }] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/spec/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | /* jasmine */ 4 | "jasmine": true, 5 | "spyOn": true, 6 | "it": true, 7 | "console": true, 8 | "describe": true, 9 | "expect": true, 10 | "beforeEach": true, 11 | "afterEach": true, 12 | "waits": true, 13 | "waitsFor": true, 14 | "runs": true, 15 | "xit": true 16 | } 17 | } -------------------------------------------------------------------------------- /test/spec/dist.main.js: -------------------------------------------------------------------------------- 1 | var allTestFiles = []; 2 | // var TEST_REGEXP = /test.*\.js$/; 3 | var TEST_REGEXP = /.*Spec\.js$/; 4 | 5 | Object.keys(window.__karma__.files).forEach(function(file) { 6 | if (TEST_REGEXP.test(file)) { 7 | allTestFiles.push(file); 8 | } 9 | }); 10 | 11 | var dojoConfig = { 12 | packages: [ 13 | { 14 | name:"spec", 15 | location:"/base/spec" 16 | }, { 17 | name:"weighted-overlay-modeler", 18 | location:"/base/dist" 19 | }, { 20 | name: 'esri', 21 | location: 'http://js.arcgis.com/3.6/js/esri' 22 | }, { 23 | name: 'dojo', 24 | location: 'http://js.arcgis.com/3.6/js/dojo/dojo' 25 | }, { 26 | name: 'dojox', 27 | location: 'http://js.arcgis.com/3.6/js/dojo/dojox' 28 | }, { 29 | name: 'dijit', 30 | location: 'http://js.arcgis.com/3.6/js/dojo/dijit' 31 | } 32 | ], 33 | asynch: true 34 | }; 35 | 36 | 37 | /** 38 | * This function must be defined and is called back by the dojo adapter 39 | * @returns {string} a list of dojo spec/test modules to register with your testing framework 40 | */ 41 | window.__karma__.dojoStart = function(){ 42 | return allTestFiles; 43 | } -------------------------------------------------------------------------------- /test/spec/main.js: -------------------------------------------------------------------------------- 1 | var allTestFiles = []; 2 | // var TEST_REGEXP = /test.*\.js$/; 3 | var TEST_REGEXP = /.*Spec\.js$/; 4 | 5 | Object.keys(window.__karma__.files).forEach(function(file) { 6 | if (TEST_REGEXP.test(file)) { 7 | allTestFiles.push(file); 8 | } 9 | }); 10 | 11 | var dojoConfig = { 12 | packages: [ 13 | { 14 | name:"spec", 15 | location:"/base/test/spec" 16 | }, { 17 | name:"weighted-overlay-modeler", 18 | location:"/base/src/lib/weighted-overlay-modeler" 19 | }, { 20 | name: 'esri', 21 | location: 'http://js.arcgis.com/3.10/js/esri' 22 | }, { 23 | name: 'dojo', 24 | location: 'http://js.arcgis.com/3.10/js/dojo/dojo' 25 | }, { 26 | name: 'dojox', 27 | location: 'http://js.arcgis.com/3.10/js/dojo/dojox' 28 | }, { 29 | name: 'dijit', 30 | location: 'http://js.arcgis.com/3.10/js/dojo/dijit' 31 | } 32 | ], 33 | async: true 34 | }; 35 | 36 | 37 | /** 38 | * This function must be defined and is called back by the dojo adapter 39 | * @returns {string} a list of dojo spec/test modules to register with your testing framework 40 | */ 41 | window.__karma__.dojoStart = function(){ 42 | return allTestFiles; 43 | } --------------------------------------------------------------------------------