├── .gitignore ├── .jshintrc ├── Examples ├── .gitignore ├── Build │ └── amd.min.js ├── README.md ├── Source │ ├── SpirographPositionProperty.js │ ├── SpirographPositionProperty_amd-build.js │ ├── SpirographPositionProperty_amd-source.js │ ├── amd │ │ ├── main-build.js │ │ └── main-source.js │ └── mainConfig.js ├── amd_build.html ├── amd_except_cesium.html ├── amd_source.html ├── build.js ├── index.html ├── models │ ├── Cesium_Air.glb │ └── Cesium_Ground.glb ├── server.js ├── sources.html └── standalone.html ├── README.md ├── Source ├── CesiumNavigation.js ├── Core │ ├── KnockoutHammerBinding.js │ ├── KnockoutMarkdownBinding.js │ ├── Utils.js │ ├── createFragmentFromTemplate.js │ ├── loadView.js │ └── registerKnockoutBindings.js ├── Styles │ ├── Core.less │ ├── DistanceLegend.less │ ├── Floating.less │ ├── Navigation.less │ └── cesium-navigation.less ├── SvgPaths │ ├── svgCompassGyro.js │ ├── svgCompassOuterRing.js │ ├── svgCompassRotationMarker.js │ └── svgReset.js ├── ViewModels │ ├── DistanceLegendViewModel.js │ ├── NavigationControl.js │ ├── NavigationViewModel.js │ ├── ResetViewNavigationControl.js │ ├── UserInterfaceControl.js │ └── ZoomNavigationControl.js ├── copyrightHeader.js └── viewerCesiumNavigationMixin.js ├── bower.json ├── build.js ├── mainConfig.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | node_modules/* 4 | *~ 5 | bower_components/* 6 | node_modules/* 7 | 8 | # Created by https://www.gitignore.io/api/webstorm,netbeans,eclipse 9 | 10 | ### WebStorm ### 11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 12 | 13 | *.iml 14 | 15 | ## Directory-based project format: 16 | .idea/ 17 | # if you remove the above rule, at least ignore the following: 18 | 19 | # User-specific stuff: 20 | # .idea/workspace.xml 21 | # .idea/tasks.xml 22 | # .idea/dictionaries 23 | # .idea/shelf 24 | 25 | # Sensitive or high-churn files: 26 | # .idea/dataSources.ids 27 | # .idea/dataSources.xml 28 | # .idea/sqlDataSources.xml 29 | # .idea/dynamic.xml 30 | # .idea/uiDesigner.xml 31 | 32 | # Gradle: 33 | # .idea/gradle.xml 34 | # .idea/libraries 35 | 36 | # Mongo Explorer plugin: 37 | # .idea/mongoSettings.xml 38 | 39 | ## File-based project format: 40 | *.ipr 41 | *.iws 42 | 43 | ## Plugin-specific files: 44 | 45 | # IntelliJ 46 | /out/ 47 | 48 | # mpeltonen/sbt-idea plugin 49 | .idea_modules/ 50 | 51 | # JIRA plugin 52 | atlassian-ide-plugin.xml 53 | 54 | # Crashlytics plugin (for Android Studio and IntelliJ) 55 | com_crashlytics_export_strings.xml 56 | crashlytics.properties 57 | crashlytics-build.properties 58 | fabric.properties 59 | 60 | 61 | ### NetBeans ### 62 | nbproject/private/ 63 | build/ 64 | nbbuild/ 65 | dist/ 66 | nbdist/ 67 | nbactions.xml 68 | .nb-gradle/ 69 | 70 | 71 | ### Eclipse ### 72 | 73 | .metadata 74 | bin/ 75 | tmp/ 76 | *.tmp 77 | *.bak 78 | *.swp 79 | *~.nib 80 | local.properties 81 | .settings/ 82 | .loadpath 83 | 84 | # Eclipse Core 85 | .project 86 | 87 | # External tool builders 88 | .externalToolBuilders/ 89 | 90 | # Locally stored "Eclipse launch configurations" 91 | *.launch 92 | 93 | # PyDev specific (Python IDE for Eclipse) 94 | *.pydevproject 95 | 96 | # CDT-specific (C/C++ Development Tooling) 97 | .cproject 98 | 99 | # JDT-specific (Eclipse Java Development Tools) 100 | .classpath 101 | 102 | # Java annotation processor (APT) 103 | .factorypath 104 | 105 | # PDT-specific (PHP Development Tools) 106 | .buildpath 107 | 108 | # sbteclipse plugin 109 | .target 110 | 111 | # TeXlipse plugin 112 | .texlipse 113 | 114 | # STS (Spring Tool Suite) 115 | .springBeans 116 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "camelcase": false, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": true, 7 | "forin": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "noempty": false, 13 | "nonew": true, 14 | "plusplus": false, 15 | "quotmark": false, 16 | "regexp": false, 17 | "undef": true, 18 | "unused": false, 19 | "strict": true, 20 | "trailing": true, 21 | "asi": false, 22 | "boss": false, 23 | "debug": false, 24 | "eqnull": false, 25 | "esnext": false, 26 | "evil": false, 27 | "expr": false, 28 | "funcscope": false, 29 | "globalstrict": false, 30 | "iterator": false, 31 | "lastsemic": false, 32 | "laxbreak": false, 33 | "laxcomma": false, 34 | "loopfunc": false, 35 | "moz": false, 36 | "multistr": true, 37 | "proto": false, 38 | "regexdash": false, 39 | "scripturl": false, 40 | "smarttabs": false, 41 | "shadow": false, 42 | "sub": false, 43 | "supernew": false, 44 | "browser": true 45 | } -------------------------------------------------------------------------------- /Examples/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | node_modules/* 4 | *~ 5 | -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | Missing files 2 | ------------ 3 | 4 | These examples are using Cesium and RequireJS so you have to run `npm install` to get these. 5 | The AMD example needs to be built in advance. To do so run `node build.js` from the `Examples` root directory 6 | 7 | 8 | Where are the dependencies from? 9 | ------------ 10 | 11 | The root directory of the server is `Examples` but some files from the parent/main directory are needed. 12 | This is achieved by internal redirects: 13 | - `/` -> `../Source` (note that the mapping `/` -> `/` still exists) 14 | - `/dist` -> `../dist` 15 | - `/node_modules` -> `../node_modules` 16 | - `/bower_components` -> `../bower_components` 17 | - `/cesiumNavigationMainConfig` -> `../mainConfig.js` 18 | 19 | Using these redirects ensures that the examples are always running with the current build and/or sources of the main project. 20 | Furthermore it avoids redundant node modules and/or bower components 21 | 22 | 23 | Local server 24 | ------------ 25 | 26 | A local HTTP server is required to run the app. 27 | 28 | Use the node.js server. To start it change to the `Examples` root directory and run 29 | `node server.js` 30 | 31 | Then browse to `http://localhost:8080/` 32 | -------------------------------------------------------------------------------- /Examples/Source/SpirographPositionProperty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by G.Cordes on 01.06.16. 3 | */ 4 | 5 | (function (Cesium) { 6 | /** 7 | * A position property that simulates a spirograph 8 | * @constructor 9 | * 10 | * @param {Cesium.Cartographic} center The center of the spirograph 11 | * @param {Number} radiusMedian The radius of the median circle 12 | * @param {Number} radiusSubCircle the maximum distance to the median circle 13 | * @param {Number} durationMedianCircle The duration in milliseconds to orbit the median circle 14 | * @param {Number} durationSubCircle The duration in milliseconds to orbit the sub circle 15 | * @param {Cesium.Ellipsoid} [ellipsoid=Cesium.Ellipsoid.WGS84] The ellipsoid to convert cartographic to cartesian 16 | */ 17 | var SpirographPositionProperty = function (center, radiusMedian, radiusSubCircle, durationMedianCircle, durationSubCircle, ellipsoid) { 18 | this._center = center; 19 | this._radiusMedian = radiusMedian; 20 | this._radiusSubCircle = radiusSubCircle; 21 | 22 | this._durationMedianCircle = durationMedianCircle; 23 | this._durationSubCircle = durationSubCircle; 24 | 25 | if (!Cesium.defined(ellipsoid)) { 26 | ellipsoid = Cesium.Ellipsoid.WGS84; 27 | } 28 | this._ellipsoid = ellipsoid; 29 | 30 | this._definitionChanged = new Cesium.Event(); 31 | }; 32 | 33 | Cesium.defineProperties(SpirographPositionProperty.prototype, { 34 | /** 35 | * Gets a value indicating if this property is constant. A property is considered 36 | * constant if getValue always returns the same result for the current definition. 37 | * @memberof PositionProperty.prototype 38 | * 39 | * @type {Boolean} 40 | * @readonly 41 | */ 42 | isConstant: { 43 | get: function () { 44 | return this._radiusMedian == 0 && this._radiusSubCircle == 0; 45 | } 46 | }, 47 | /** 48 | * Gets the event that is raised whenever the definition of this property changes. 49 | * The definition is considered to have changed if a call to getValue would return 50 | * a different result for the same time. 51 | * @memberof PositionProperty.prototype 52 | * 53 | * @type {Event} 54 | * @readonly 55 | */ 56 | definitionChanged: { 57 | get: function () { 58 | return this._definitionChanged; 59 | } 60 | }, 61 | /** 62 | * Gets the reference frame that the position is defined in. 63 | * @memberof PositionProperty.prototype 64 | * @type {ReferenceFrame} 65 | */ 66 | referenceFrame: { 67 | get: function () { 68 | return Cesium.ReferenceFrame.FIXED; 69 | } 70 | } 71 | }); 72 | 73 | /** 74 | * Gets the value of the property at the provided time in the fixed frame. 75 | * @function 76 | * 77 | * @param {JulianDate} time The time for which to retrieve the value. 78 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 79 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 80 | */ 81 | SpirographPositionProperty.prototype.getValue = function (time, result) { 82 | return this.getValueInReferenceFrame(time, Cesium.ReferenceFrame.FIXED, result); 83 | }; 84 | 85 | var cartographicScratch = new Cesium.Cartographic(); 86 | 87 | /** 88 | * Gets the value of the property at the provided time and in the provided reference frame. 89 | * @function 90 | * 91 | * @param {JulianDate} time The time for which to retrieve the value. 92 | * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. 93 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 94 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 95 | */ 96 | SpirographPositionProperty.prototype.getValueInReferenceFrame = function (time, referenceFrame, result) { 97 | var milliseconds = Cesium.JulianDate.toDate(time).getTime(); 98 | 99 | var radius = this._radiusMedian + this._radiusSubCircle * Math.sin(2 * Math.PI * (milliseconds / this._durationSubCircle)); 100 | 101 | cartographicScratch.latitude = this._center.latitude + radius * Math.cos(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 102 | cartographicScratch.longitude = this._center.longitude + radius * Math.sin(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 103 | cartographicScratch.height = this._center.height; 104 | 105 | result = this._ellipsoid.cartographicToCartesian(cartographicScratch, result); 106 | 107 | if (referenceFrame == Cesium.ReferenceFrame.FIXED) { 108 | return result; 109 | } 110 | 111 | return Cesium.PositionProperty.convertToReferenceFrame(time, result, Cesium.ReferenceFrame.FIXED, referenceFrame, result); 112 | }; 113 | 114 | /** 115 | * Compares this property to the provided property and returns 116 | * true if they are equal, false otherwise. 117 | * @function 118 | * 119 | * @param {Property} [other] The other property. 120 | * @returns {Boolean} true if left and right are equal, false otherwise. 121 | */ 122 | SpirographPositionProperty.prototype.equals = function (other) { 123 | return other instanceof SpirographPositionProperty 124 | && this._center.equals(other._center) 125 | && this._radiusMedian == other._radiusMedian 126 | && this._radiusSubCircle == other._radiusSubCircle 127 | && this._durationMedianCircle == other._durationMedianCircle 128 | && this._durationSubCircle == other._durationSubCircle 129 | && this._ellipsoid.equals(other._ellipsoid); 130 | }; 131 | 132 | Cesium.SpirographPositionProperty = SpirographPositionProperty; 133 | 134 | })(Cesium); -------------------------------------------------------------------------------- /Examples/Source/SpirographPositionProperty_amd-build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by G.Cordes on 01.06.16. 3 | */ 4 | 5 | define([ 6 | 'Cesium' 7 | ], function (Cesium) { 8 | "use strict"; 9 | 10 | /** 11 | * A position property that simulates a spirograph 12 | * @constructor 13 | * 14 | * @param {Cesium.Cartographic} center The center of the spirograph 15 | * @param {Number} radiusMedian The radius of the median circle 16 | * @param {Number} radiusSubCircle the maximum distance to the median circle 17 | * @param {Number} durationMedianCircle The duration in milliseconds to orbit the median circle 18 | * @param {Number} durationSubCircle The duration in milliseconds to orbit the sub circle 19 | * @param {Cesium.Ellipsoid} [ellipsoid=Cesium.Ellipsoid.WGS84] The ellipsoid to convert cartographic to cartesian 20 | */ 21 | var SpirographPositionProperty = function(center, radiusMedian, radiusSubCircle, durationMedianCircle, durationSubCircle, ellipsoid) { 22 | this._center = center; 23 | this._radiusMedian = radiusMedian; 24 | this._radiusSubCircle = radiusSubCircle; 25 | 26 | this._durationMedianCircle = durationMedianCircle; 27 | this._durationSubCircle = durationSubCircle; 28 | 29 | if(!Cesium.defined(ellipsoid)) { 30 | ellipsoid = Cesium.Ellipsoid.WGS84; 31 | } 32 | this._ellipsoid = ellipsoid; 33 | 34 | this._definitionChanged = new Cesium.Event(); 35 | }; 36 | 37 | Cesium.defineProperties(SpirographPositionProperty.prototype, { 38 | /** 39 | * Gets a value indicating if this property is constant. A property is considered 40 | * constant if getValue always returns the same result for the current definition. 41 | * @memberof PositionProperty.prototype 42 | * 43 | * @type {Boolean} 44 | * @readonly 45 | */ 46 | isConstant : { 47 | get : function() { 48 | return this._radiusMedian == 0 && this._radiusSubCircle == 0; 49 | } 50 | }, 51 | /** 52 | * Gets the event that is raised whenever the definition of this property changes. 53 | * The definition is considered to have changed if a call to getValue would return 54 | * a different result for the same time. 55 | * @memberof PositionProperty.prototype 56 | * 57 | * @type {Event} 58 | * @readonly 59 | */ 60 | definitionChanged : { 61 | get : function() { 62 | return this._definitionChanged; 63 | } 64 | }, 65 | /** 66 | * Gets the reference frame that the position is defined in. 67 | * @memberof PositionProperty.prototype 68 | * @type {ReferenceFrame} 69 | */ 70 | referenceFrame : { 71 | get : function() { 72 | return Cesium.ReferenceFrame.FIXED; 73 | } 74 | } 75 | }); 76 | 77 | /** 78 | * Gets the value of the property at the provided time in the fixed frame. 79 | * @function 80 | * 81 | * @param {JulianDate} time The time for which to retrieve the value. 82 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 83 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 84 | */ 85 | SpirographPositionProperty.prototype.getValue = function(time, result) { 86 | return this.getValueInReferenceFrame(time, Cesium.ReferenceFrame.FIXED, result); 87 | }; 88 | 89 | var cartographicScratch = new Cesium.Cartographic(); 90 | 91 | /** 92 | * Gets the value of the property at the provided time and in the provided reference frame. 93 | * @function 94 | * 95 | * @param {JulianDate} time The time for which to retrieve the value. 96 | * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. 97 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 98 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 99 | */ 100 | SpirographPositionProperty.prototype.getValueInReferenceFrame = function(time, referenceFrame, result) { 101 | var milliseconds = Cesium.JulianDate.toDate(time).getTime(); 102 | 103 | var radius = this._radiusMedian + this._radiusSubCircle * Math.sin(2 * Math.PI * (milliseconds / this._durationSubCircle)); 104 | 105 | cartographicScratch.latitude = this._center.latitude + radius * Math.cos(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 106 | cartographicScratch.longitude = this._center.longitude + radius * Math.sin(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 107 | cartographicScratch.height = this._center.height; 108 | 109 | result = this._ellipsoid.cartographicToCartesian(cartographicScratch, result); 110 | 111 | if(referenceFrame == Cesium.ReferenceFrame.FIXED) { 112 | return result; 113 | } 114 | 115 | return Cesium.PositionProperty.convertToReferenceFrame(time, result, Cesium.ReferenceFrame.FIXED, referenceFrame, result); 116 | }; 117 | 118 | /** 119 | * Compares this property to the provided property and returns 120 | * true if they are equal, false otherwise. 121 | * @function 122 | * 123 | * @param {Property} [other] The other property. 124 | * @returns {Boolean} true if left and right are equal, false otherwise. 125 | */ 126 | SpirographPositionProperty.prototype.equals = function(other) { 127 | return other instanceof SpirographPositionProperty 128 | && this._center.equals(other._center) 129 | && this._radiusMedian == other._radiusMedian 130 | && this._radiusSubCircle == other._radiusSubCircle 131 | && this._durationMedianCircle == other._durationMedianCircle 132 | && this._durationSubCircle == other._durationSubCircle 133 | && this._ellipsoid.equals(other._ellipsoid); 134 | }; 135 | 136 | return SpirographPositionProperty; 137 | }); -------------------------------------------------------------------------------- /Examples/Source/SpirographPositionProperty_amd-source.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by G.Cordes on 01.06.16. 3 | */ 4 | 5 | define([ 6 | 'Cesium/Cesium' 7 | ], function (Cesium) { 8 | "use strict"; 9 | 10 | /** 11 | * A position property that simulates a spirograph 12 | * @constructor 13 | * 14 | * @param {Cesium.Cartographic} center The center of the spirograph 15 | * @param {Number} radiusMedian The radius of the median circle 16 | * @param {Number} radiusSubCircle the maximum distance to the median circle 17 | * @param {Number} durationMedianCircle The duration in milliseconds to orbit the median circle 18 | * @param {Number} durationSubCircle The duration in milliseconds to orbit the sub circle 19 | * @param {Cesium.Ellipsoid} [ellipsoid=Cesium.Ellipsoid.WGS84] The ellipsoid to convert cartographic to cartesian 20 | */ 21 | var SpirographPositionProperty = function(center, radiusMedian, radiusSubCircle, durationMedianCircle, durationSubCircle, ellipsoid) { 22 | this._center = center; 23 | this._radiusMedian = radiusMedian; 24 | this._radiusSubCircle = radiusSubCircle; 25 | 26 | this._durationMedianCircle = durationMedianCircle; 27 | this._durationSubCircle = durationSubCircle; 28 | 29 | if(!Cesium.defined(ellipsoid)) { 30 | ellipsoid = Cesium.Ellipsoid.WGS84; 31 | } 32 | this._ellipsoid = ellipsoid; 33 | 34 | this._definitionChanged = new Cesium.Event(); 35 | }; 36 | 37 | Cesium.defineProperties(SpirographPositionProperty.prototype, { 38 | /** 39 | * Gets a value indicating if this property is constant. A property is considered 40 | * constant if getValue always returns the same result for the current definition. 41 | * @memberof PositionProperty.prototype 42 | * 43 | * @type {Boolean} 44 | * @readonly 45 | */ 46 | isConstant : { 47 | get : function() { 48 | return this._radiusMedian == 0 && this._radiusSubCircle == 0; 49 | } 50 | }, 51 | /** 52 | * Gets the event that is raised whenever the definition of this property changes. 53 | * The definition is considered to have changed if a call to getValue would return 54 | * a different result for the same time. 55 | * @memberof PositionProperty.prototype 56 | * 57 | * @type {Event} 58 | * @readonly 59 | */ 60 | definitionChanged : { 61 | get : function() { 62 | return this._definitionChanged; 63 | } 64 | }, 65 | /** 66 | * Gets the reference frame that the position is defined in. 67 | * @memberof PositionProperty.prototype 68 | * @type {ReferenceFrame} 69 | */ 70 | referenceFrame : { 71 | get : function() { 72 | return Cesium.ReferenceFrame.FIXED; 73 | } 74 | } 75 | }); 76 | 77 | /** 78 | * Gets the value of the property at the provided time in the fixed frame. 79 | * @function 80 | * 81 | * @param {JulianDate} time The time for which to retrieve the value. 82 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 83 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 84 | */ 85 | SpirographPositionProperty.prototype.getValue = function(time, result) { 86 | return this.getValueInReferenceFrame(time, Cesium.ReferenceFrame.FIXED, result); 87 | }; 88 | 89 | var cartographicScratch = new Cesium.Cartographic(); 90 | 91 | /** 92 | * Gets the value of the property at the provided time and in the provided reference frame. 93 | * @function 94 | * 95 | * @param {JulianDate} time The time for which to retrieve the value. 96 | * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. 97 | * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. 98 | * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. 99 | */ 100 | SpirographPositionProperty.prototype.getValueInReferenceFrame = function(time, referenceFrame, result) { 101 | var milliseconds = Cesium.JulianDate.toDate(time).getTime(); 102 | 103 | var radius = this._radiusMedian + this._radiusSubCircle * Math.sin(2 * Math.PI * (milliseconds / this._durationSubCircle)); 104 | 105 | cartographicScratch.latitude = this._center.latitude + radius * Math.cos(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 106 | cartographicScratch.longitude = this._center.longitude + radius * Math.sin(2 * Math.PI * (milliseconds / this._durationMedianCircle)); 107 | cartographicScratch.height = this._center.height; 108 | 109 | result = this._ellipsoid.cartographicToCartesian(cartographicScratch, result); 110 | 111 | if(referenceFrame == Cesium.ReferenceFrame.FIXED) { 112 | return result; 113 | } 114 | 115 | return Cesium.PositionProperty.convertToReferenceFrame(time, result, Cesium.ReferenceFrame.FIXED, referenceFrame, result); 116 | }; 117 | 118 | /** 119 | * Compares this property to the provided property and returns 120 | * true if they are equal, false otherwise. 121 | * @function 122 | * 123 | * @param {Property} [other] The other property. 124 | * @returns {Boolean} true if left and right are equal, false otherwise. 125 | */ 126 | SpirographPositionProperty.prototype.equals = function(other) { 127 | return other instanceof SpirographPositionProperty 128 | && this._center.equals(other._center) 129 | && this._radiusMedian == other._radiusMedian 130 | && this._radiusSubCircle == other._radiusSubCircle 131 | && this._durationMedianCircle == other._durationMedianCircle 132 | && this._durationSubCircle == other._durationSubCircle 133 | && this._ellipsoid.equals(other._ellipsoid); 134 | }; 135 | 136 | return SpirographPositionProperty; 137 | }); -------------------------------------------------------------------------------- /Examples/Source/amd/main-build.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'Cesium', 3 | 'Source/SpirographPositionProperty_amd-build', 4 | 'viewerCesiumNavigationMixin' 5 | ], function( 6 | Cesium, 7 | SpirographPositionProperty, 8 | viewerCesiumNavigationMixin) { 9 | "use strict"; 10 | 11 | var cesiumViewer = new Cesium.Viewer('cesiumContainer'); 12 | 13 | // extend our view by the cesium navigation mixin 14 | cesiumViewer.extend(viewerCesiumNavigationMixin, {}); 15 | // you can access the cesium navigation by cesiumViewer.cesiumNavigation or cesiumViewer.cesiumWidget.cesiumNavigation 16 | 17 | function createSpirographEntity(url, longitude, latitude, height, radiusMedian, radiusSubCircle, 18 | durationMedianCircle, durationSubCircle) { 19 | var centerPosition = Cesium.Cartographic.fromDegrees(longitude, latitude, height); 20 | var spirographPositionProperty = new SpirographPositionProperty(centerPosition, radiusMedian, radiusSubCircle, 21 | durationMedianCircle, durationSubCircle, cesiumViewer.scene.globe.ellipsoid); 22 | 23 | cesiumViewer.entities.add({ 24 | name : url, 25 | description: 'It is supposed to have a useful desciption here
but instead there is just a placeholder to get a larger info box', 26 | position: spirographPositionProperty, 27 | orientation: new Cesium.VelocityOrientationProperty(spirographPositionProperty, cesiumViewer.scene.globe.ellipsoid), 28 | model : { 29 | uri : url, 30 | minimumPixelSize : 96 31 | } 32 | }); 33 | } 34 | 35 | createSpirographEntity('models/Cesium_Air.glb', -100, 44, 10000.0, 36 | Cesium.Math.toRadians(0.5), Cesium.Math.toRadians(2), 1e6, 2e5); 37 | createSpirographEntity('models/Cesium_Ground.glb', -122, 45, 0, 38 | Cesium.Math.toRadians(0.1), Cesium.Math.toRadians(1), 5e6, 7e5); 39 | }); -------------------------------------------------------------------------------- /Examples/Source/amd/main-source.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'Cesium/Cesium', 3 | 'Source/SpirographPositionProperty_amd-source', 4 | 'viewerCesiumNavigationMixin' 5 | ], function( 6 | Cesium, 7 | SpirographPositionProperty, 8 | viewerCesiumNavigationMixin) { 9 | "use strict"; 10 | 11 | var cesiumViewer = new Cesium.Viewer('cesiumContainer'); 12 | 13 | // extend our view by the cesium navigation mixin 14 | cesiumViewer.extend(viewerCesiumNavigationMixin, {}); 15 | // you can access the cesium navigation by cesiumViewer.cesiumNavigation or cesiumViewer.cesiumWidget.cesiumNavigation 16 | 17 | function createSpirographEntity(url, longitude, latitude, height, radiusMedian, radiusSubCircle, 18 | durationMedianCircle, durationSubCircle) { 19 | var centerPosition = Cesium.Cartographic.fromDegrees(longitude, latitude, height); 20 | var spirographPositionProperty = new SpirographPositionProperty(centerPosition, radiusMedian, radiusSubCircle, 21 | durationMedianCircle, durationSubCircle, cesiumViewer.scene.globe.ellipsoid); 22 | 23 | cesiumViewer.entities.add({ 24 | name : url, 25 | description: 'It is supposed to have a useful desciption here
but instead there is just a placeholder to get a larger info box', 26 | position: spirographPositionProperty, 27 | orientation: new Cesium.VelocityOrientationProperty(spirographPositionProperty, cesiumViewer.scene.globe.ellipsoid), 28 | model : { 29 | uri : url, 30 | minimumPixelSize : 96 31 | } 32 | }); 33 | } 34 | 35 | createSpirographEntity('models/Cesium_Air.glb', -100, 44, 10000.0, 36 | Cesium.Math.toRadians(0.5), Cesium.Math.toRadians(2), 1e6, 2e5); 37 | createSpirographEntity('models/Cesium_Ground.glb', -122, 45, 0, 38 | Cesium.Math.toRadians(0.1), Cesium.Math.toRadians(1), 5e6, 7e5); 39 | }); -------------------------------------------------------------------------------- /Examples/Source/mainConfig.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | baseUrl: '.', 3 | paths: { 4 | // IMPORTANT: this path has to be set because 5 | // viewerCesiumNavigationMixin uses 'Cesium/...' for dependencies 6 | Cesium: "../node_modules/cesium/Build/Cesium/Cesium", 7 | viewerCesiumNavigationMixin: "../dist/amd/viewerCesiumNavigationMixin" 8 | }, 9 | shim: { 10 | "Cesium": { 11 | exports: "Cesium", 12 | // deps: [ 13 | // "require-css!../../node_modules/cesium/Build/Cesium/Widgets/widgets.css" 14 | // ] 15 | } 16 | } 17 | }); -------------------------------------------------------------------------------- /Examples/amd_build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation Example (using requirejs.optimize) 5 | 6 | 21 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 44 | 45 | -------------------------------------------------------------------------------- /Examples/amd_except_cesium.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation Example (using AMD except Cesium) 5 | 6 | 7 | 8 | 14 | 20 | 21 | 22 |
23 | 61 | 62 | 71 | 72 | -------------------------------------------------------------------------------- /Examples/amd_source.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation Example (using requirejs.optimize) 5 | 6 | 17 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 40 | 41 | -------------------------------------------------------------------------------- /Examples/build.js: -------------------------------------------------------------------------------- 1 | // This only builds the sources for the example amd.html 2 | // all other examples do not need to be built. 3 | 4 | var requirejs = require('requirejs'); 5 | 6 | var config = { 7 | baseUrl: '.', 8 | optimize: 'uglify2', 9 | mainConfigFile: 'Source/mainConfig.js', 10 | name: "Source/amd/main-build", 11 | out: "Build/amd.min.js", 12 | paths: { 13 | // do not include Cesium in the build file but rather access it seperately from browser 14 | Cesium: "empty:" 15 | }, 16 | logLevel: 0 17 | }; 18 | 19 | requirejs.optimize(config); -------------------------------------------------------------------------------- /Examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation Examples 5 | 13 | 14 | 15 |

Cesium-Navigation Examples

16 |

Five examples are available where the output is always the same concerning only the user interface but they differ in how they were created.

17 |
18 |
Sources Example
19 |
Using the sources without building anything. This is also great for developing/debugging the sources since any changes are available on browser refresh.
20 |
Standalone Example
21 |
No AMD loader is needed but Cesium has to be defined as global variable.
22 |
Using AMD except for Cesium Example
23 |
Using AMD except for Cesium (so Cesium is not accessed via AMD but is a global variable). Since this mixin is accessed via AMD the AMD compatible version is used. The version automatically checks if Cesium is globally accessible and if so it does not try to load Cesium via AMD.
24 |
Asynchronous Module Definition (AMD) without requirejs.optimize Example
25 |
When using the AMD compatible version make sure to set 'Cesium' either to point to the Cesium source directory or to the built Cesium.js file.
26 |
Asynchronous Module Definition (AMD) with requirejs.optimize Example (needs to be built)
27 |
When using the AMD compatible version make sure to set 'Cesium' either to point to the Cesium source directory or to the built Cesium.js file.
28 |
29 | 30 | 39 | 40 | -------------------------------------------------------------------------------- /Examples/models/Cesium_Air.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larcius/cesium-navigation/2352771805b082f26df3a0b50a2863fc2ff4ef6e/Examples/models/Cesium_Air.glb -------------------------------------------------------------------------------- /Examples/models/Cesium_Ground.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larcius/cesium-navigation/2352771805b082f26df3a0b50a2863fc2ff4ef6e/Examples/models/Cesium_Ground.glb -------------------------------------------------------------------------------- /Examples/server.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | /*global console,require,__dirname,process*/ 4 | /*jshint es3:false*/ 5 | 6 | var express = require('express'); 7 | var compression = require('compression'); 8 | var path = require('path'); 9 | 10 | var yargs = require('yargs').options({ 11 | 'port' : { 12 | 'default' : process.env.PORT || 8080, 13 | 'description' : 'Port to listen on.' 14 | }, 15 | 'public' : { 16 | 'type' : 'boolean', 17 | 'description' : 'Run a public server that listens on all interfaces.' 18 | }, 19 | 'help' : { 20 | 'alias' : 'h', 21 | 'type' : 'boolean', 22 | 'description' : 'Show this help.' 23 | } 24 | }); 25 | var argv = yargs.argv; 26 | 27 | if (argv.help) { 28 | return yargs.showHelp(); 29 | } 30 | 31 | // eventually this mime type configuration will need to change 32 | // https://github.com/visionmedia/send/commit/d2cb54658ce65948b0ed6e5fb5de69d022bef941 33 | var mime = express.static.mime; 34 | mime.define({ 35 | 'application/json' : ['czml', 'json', 'geojson', 'topojson', 'gltf'], 36 | 'text/plain' : ['glsl'] 37 | }); 38 | 39 | var app = express(); 40 | app.use(compression()); 41 | app.use(express.static(__dirname)); 42 | // don't forget to copy necessary files when preparing the gh-pages on github since there is no redirecting 43 | app.use(express.static(path.join(__dirname, '..', 'Source'))); 44 | app.use('/cesiumNavigationMainConfig.js', express.static(path.join(__dirname, '..', 'mainConfig.js'))); 45 | app.use('/node_modules', express.static(path.join(__dirname, '..', 'node_modules'))); 46 | app.use('/Cesium', express.static(path.join(__dirname, '..', 'node_modules/cesium/Build/Cesium'))); 47 | app.use('/bower_components', express.static(path.join(__dirname, '..', 'bower_components'))); 48 | app.use('/dist', express.static(path.join(__dirname, '..', 'dist'))); 49 | 50 | var serverName = 'Cesium navigation examples server'; 51 | 52 | var server = app.listen(argv.port, argv.public ? undefined : 'localhost', function() { 53 | if (argv.public) { 54 | console.log(serverName + ' is running publicly.'); 55 | console.log('\tConnect to http://\:%d, e.g. http://localhost:%d', server.address().port, server.address().port); 56 | } else { 57 | console.log(serverName + ' is running locally.'); 58 | console.log('\tConnect to http://localhost:%d', server.address().port); 59 | } 60 | }); 61 | 62 | server.on('error', function (e) { 63 | if (e.code === 'EADDRINUSE') { 64 | console.log('Error: Port %d is already in use, select a different port.', argv.port); 65 | console.log('Example: node server.js --port %d', argv.port + 1); 66 | } else if (e.code === 'EACCES') { 67 | console.log('Error: This process does not have permission to listen on port %d.', argv.port); 68 | if (argv.port < 1024) { 69 | console.log('Try a port number higher than 1024.'); 70 | } 71 | } 72 | console.log(e); 73 | process.exit(1); 74 | }); 75 | 76 | // Maintain an array of all connected sockets 77 | var sockets = []; 78 | server.on('connection', function (socket) { 79 | // Add a newly connected socket 80 | sockets.push(socket); 81 | 82 | // Remove the socket when it closes 83 | socket.on('close', function () { 84 | sockets.splice(sockets.indexOf(socket), 1); 85 | }); 86 | }); 87 | 88 | var shutdownSever = function() { 89 | server.close(); 90 | 91 | for (var i = 0; i < sockets.length; i++) { 92 | sockets[i].destroy(); 93 | } 94 | }; 95 | 96 | server.once('close', function() { 97 | console.log(serverName + ' has stopped.'); 98 | process.exit(); 99 | }); 100 | 101 | process.once('SIGTERM', shutdownSever); 102 | process.once('SIGINT', shutdownSever); 103 | 104 | })(); 105 | 106 | -------------------------------------------------------------------------------- /Examples/sources.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation from Sources 5 | 6 | 7 | 15 | 21 | 22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/standalone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium-Navigation Example (standalone) 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 |
18 | 53 | 54 | 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cesium-navigation 2 | This is a Cesium plugin that adds to the Cesium map a user friendly compass, navigator (zoom in/out), and 3 | distance scale graphical user interface. 4 | 5 | **Why did you build it?** 6 | 7 | First of all the Cesiumjs sdk does not include a compass, navigator (zoom in/out) nor distance scale. You can use the mouse to navigate on the map but this navigation plugin offers more navigation control and capabilities to the user. 8 | Some of the capabilities are: 9 | reset the compass to point to north, reset the orbit, and reset the view to a default bound. 10 | 11 | **How did you build it?** 12 | 13 | This plugin is based on the excellent compass, navigator (zoom in/out) and distance scale from the terriajs open source library (https://github.com/TerriaJS). The navigation UI from terriajs can not be used out of the box in Cesium because Cesium uses CommonJS modules with RequireJS, and the terriajs uses commonjs and Browserify, so you can't just copy the source files into Cesium and build. My work consisted on adapting the code to work within Cesium as a plugin as follows: 14 | 15 | - Extracted the minimum required modules from terriajs. 16 | - Converted all the modules from Browserify to requirejs. 17 | - Using nodejs and the requirejs optimizer as well as almond the whole plugin is built and bundled in a single file even the CSS style 18 | - This plugin can be used as a standalone script or via an AMD loader (tested with requirejs). Even in the special case where you use AMD but not for Cesium the plugin can be easily used. 19 | 20 | **How to use it?** 21 | 22 | *When to use which edition?* 23 | 24 | There are two editions, a standalone edition and an AMD compatible edition. If you want to load the mixin via requireJS then use the AMD compatible edition. Otherwise use the standalone edition which includes almond to resolve dependencies. Below some examples are given for better understanding. 25 | 26 | - If you are loading Cesium without requirejs (i.e. you have a global variable Cesium) then use the standalone edition. This edition is also suitable if you use requirejs but not for this mixin. 27 | ```HTML 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ``` 38 | and then extend a viewer: 39 | 40 | ```JavaScript 41 | // create a viewer assuming there is a DIV element with id 'cesiumContainer' 42 | var cesiumViewer = new Cesium.Viewer('cesiumContainer'); 43 | 44 | // extend our view by the cesium navigaton mixin 45 | cesiumViewer.extend(Cesium.viewerCesiumNavigationMixin, {}); 46 | ``` 47 | 48 | or a widget: 49 | 50 | ```JavaScript 51 | // create a widget assuming there is a DIV element with id 'cesiumContainer' 52 | var cesiumWidget = new Cesium.CesiumWidget('cesiumContainer'); 53 | 54 | // extend our view by the cesium navigaton mixin 55 | Cesium.viewerCesiumNavigationMixin.mixinWidget(cesiumWidget, {}); 56 | ``` 57 | 58 | You can access the newly created instance via 59 | 60 | ``` 61 | // if using a viewer 62 | var cesiumNavigation = cesiumViewer.cesiumNavigation; 63 | 64 | // if using a widget 65 | var cesiumNavigation = cesiumWidget.cesiumNavigation; 66 | ``` 67 | 68 | Another example if your are using requirejs except for Cesium: 69 | ```HTML 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | ``` 86 | and code 87 | ```JavaScript 88 | // IMPORTANT: be sure that Cesium.js has already been loaded, e.g. by loading requirejs AFTER Cesium 89 | require(['path/to/amd/viewerCesiumNavigationMixin'], function(viewerCesiumNavigationMixin) { 90 | // like above code but now one can directly access 91 | // viewerCesiumNavigationMixin 92 | // instead of 93 | // Cesium.viewerCesiumNavigationMixin 94 | } 95 | ``` 96 | 97 | - If you are using requirejs for your entire project, including Cesium, then you have to use the AMD compatible edition. 98 | 99 | ```HTML 100 | 101 | 102 | 103 | 104 | 121 | 122 | 123 | 124 | ``` 125 | and the code 126 | ```JavaScript 127 | require([ 128 | 'Cesium/Cesium', // if Cesium points to Cesium directory 129 | 'Cesium', // if Cesium points to Cesium.js file 130 | 'path/to/amd/viewerCesiumNavigationMixin' 131 | ], function( 132 | Cesium, 133 | viewerCesiumNavigationMixin) { 134 | 135 | // like above but now you cannot access Cesium.viewerCesiumNavigationMixin 136 | // but use just viewerCesiumNavigationMixin 137 | }); 138 | ``` 139 | or if Cesium points to the Cesium directory 140 | ```JavaScript 141 | require([ 142 | 'Cesium/Core/Viewer', 143 | 'path/to/amd/viewerCesiumNavigationMixin' 144 | ], function( 145 | CesiumViewer, 146 | viewerCesiumNavigationMixin) { 147 | 148 | // like above but now you cannot access Cesium.viewerCesiumNavigationMixin 149 | // but use just viewerCesiumNavigationMixin 150 | }); 151 | ``` 152 | *Available options of the plugin* 153 | ``` 154 | defaultResetView --> option used to set a default view when resetting the map view with the reset navigation 155 | control. Values accepted are of type Cesium.Cartographic and Cesium.Rectangle. 156 | 157 | enableCompass --> option used to enable or disable the compass. Values accepted are true for enabling and false to disable. The default is true. 158 | 159 | enableZoomControls --> option used to enable or disable the zoom controls. Values accepted are true for enabling and false to disable. The default is true. 160 | 161 | enableDistanceLegend --> option used to enable or disable the distance legend. Values accepted are true for enabling and false to disable. The default is true. 162 | 163 | More options will be set in future releases of the plugin. 164 | ``` 165 | Example of using the options when loading Cesium without requirejs: 166 | ```JavaScript 167 | var options = {}; 168 | options.defaultResetView = Cesium.Rectangle.fromDegrees(71, 3, 90, 14); 169 | // Only the compass will show on the map 170 | options.enableCompass = true; 171 | options.enableZoomControls = false; 172 | options.enableDistanceLegend = false; 173 | cesiumViewer.extend(Cesium.viewerCesiumNavigationMixin, options); 174 | ``` 175 | 176 | *Others* 177 | 178 | - To destroy the navigation object and release the resources later on use the following 179 | ```JavaScript 180 | viewer.cesiumNavigation.destroy(); 181 | ``` 182 | 183 | - if there are still open questions please checkout the examples 184 | 185 | 186 | **How to build it?** 187 | 188 | - run `npm install` 189 | - run `node build.js` 190 | - The build process also copies the files to the Example folder in order to always keep them sync with your build 191 | 192 | 193 | **Developers guide** 194 | 195 | For developing/debugging you should have a look at the "Source example". That example directly uses the source files and therefore it allows you to immediatley (only a page refresh is needed) see your changes without rebuilding anything. Furthermore due to working with the sources you can easily debug the project (e.g. via the developer console of the browser or via a debugger of your IDE like Webstorm) 196 | 197 | 198 | **Is there a demo using the plugin?** 199 | 200 | This is the demo: 201 | 202 | (http://larcius.github.io/cesium-navigation/) 203 | 204 | - The compass, navigator, and distance scale will appear on the right side of te map. 205 | - This plugin was successfully tested on Cesium version 1.15. It works great with Cesium in 3D mode. Recently Larcius (https://github.com/Larcius) made a lot of improvements and fixed some issues in Columbus and 2D modes. 206 | 207 | **What about the license?** 208 | - The plugin is 100% based on open source libraries. The same license that applies to Cesiumjs and terriajs applies also to this plugin. Feel free to use it, modify it, and improve it. 209 | -------------------------------------------------------------------------------- /Source/CesiumNavigation.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/defineProperties', 5 | // 'Cesium/Core/defaultValue', 6 | 'Cesium/Core/Event', 7 | 'KnockoutES5', 8 | 'Core/registerKnockoutBindings', 9 | 'ViewModels/DistanceLegendViewModel', 10 | 'ViewModels/NavigationViewModel' 11 | ], function ( 12 | defined, 13 | defineProperties, 14 | // defaultValue, 15 | CesiumEvent, 16 | Knockout, 17 | registerKnockoutBindings, 18 | DistanceLegendViewModel, 19 | NavigationViewModel) 20 | { 21 | 'use strict'; 22 | 23 | /** 24 | * @alias CesiumNavigation 25 | * @constructor 26 | * 27 | * @param {Viewer|CesiumWidget} viewerCesiumWidget The Viewer or CesiumWidget instance 28 | */ 29 | var CesiumNavigation = function (viewerCesiumWidget) { 30 | initialize.apply(this, arguments); 31 | 32 | this._onDestroyListeners = []; 33 | }; 34 | 35 | CesiumNavigation.prototype.distanceLegendViewModel = undefined; 36 | CesiumNavigation.prototype.navigationViewModel = undefined; 37 | CesiumNavigation.prototype.navigationDiv = undefined; 38 | CesiumNavigation.prototype.distanceLegendDiv = undefined; 39 | CesiumNavigation.prototype.terria = undefined; 40 | CesiumNavigation.prototype.container = undefined; 41 | CesiumNavigation.prototype._onDestroyListeners = undefined; 42 | 43 | CesiumNavigation.prototype.destroy = function () 44 | { 45 | if (defined(this.navigationViewModel)) 46 | { 47 | this.navigationViewModel.destroy(); 48 | } 49 | if (defined(this.distanceLegendViewModel)) 50 | { 51 | this.distanceLegendViewModel.destroy(); 52 | } 53 | 54 | if (defined(this.navigationDiv)) 55 | { 56 | this.navigationDiv.parentNode.removeChild(this.navigationDiv); 57 | } 58 | delete this.navigationDiv; 59 | 60 | if (defined(this.distanceLegendDiv)) 61 | { 62 | this.distanceLegendDiv.parentNode.removeChild(this.distanceLegendDiv); 63 | } 64 | delete this.distanceLegendDiv; 65 | 66 | if (defined(this.container)) 67 | { 68 | this.container.parentNode.removeChild(this.container); 69 | } 70 | delete this.container; 71 | 72 | for (var i = 0; i < this._onDestroyListeners.length; i++) 73 | { 74 | this._onDestroyListeners[i](); 75 | } 76 | }; 77 | 78 | CesiumNavigation.prototype.addOnDestroyListener = function (callback) 79 | { 80 | if (typeof callback === "function") 81 | { 82 | this._onDestroyListeners.push(callback); 83 | } 84 | }; 85 | 86 | /** 87 | * @param {Viewer|CesiumWidget} viewerCesiumWidget The Viewer or CesiumWidget instance 88 | * @param options 89 | */ 90 | function initialize(viewerCesiumWidget, options) { 91 | if (!defined(viewerCesiumWidget)) { 92 | throw new DeveloperError('CesiumWidget or Viewer is required.'); 93 | } 94 | 95 | // options = defaultValue(options, defaultValue.EMPTY_OBJECT); 96 | 97 | var cesiumWidget = defined(viewerCesiumWidget.cesiumWidget) ? viewerCesiumWidget.cesiumWidget : viewerCesiumWidget; 98 | 99 | var container = document.createElement('div'); 100 | container.className = 'cesium-widget-cesiumNavigationContainer'; 101 | cesiumWidget.container.appendChild(container); 102 | 103 | this.terria = viewerCesiumWidget; 104 | this.terria.options = (defined(options))?options :{}; 105 | this.terria.afterWidgetChanged = new CesiumEvent(); 106 | this.terria.beforeWidgetChanged = new CesiumEvent(); 107 | this.container = container; 108 | 109 | //this.navigationDiv.setAttribute("id", "navigationDiv"); 110 | 111 | 112 | // Register custom Knockout.js bindings. If you're not using the TerriaJS user interface, you can remove this. 113 | registerKnockoutBindings(); 114 | 115 | if (!defined(this.terria.options.enableDistanceLegend) || this.terria.options.enableDistanceLegend) 116 | { 117 | this.distanceLegendDiv = document.createElement('div'); 118 | container.appendChild(this.distanceLegendDiv); 119 | this.distanceLegendDiv.setAttribute("id", "distanceLegendDiv"); 120 | this.distanceLegendViewModel = DistanceLegendViewModel.create({ 121 | container: this.distanceLegendDiv, 122 | terria: this.terria, 123 | mapElement: container, 124 | enableDistanceLegend: true 125 | }); 126 | 127 | } 128 | 129 | 130 | if ((!defined(this.terria.options.enableZoomControls) || this.terria.options.enableZoomControls) && (!defined(this.terria.options.enableCompass) || this.terria.options.enableCompass)) 131 | { 132 | this.navigationDiv = document.createElement('div'); 133 | this.navigationDiv.setAttribute("id", "navigationDiv"); 134 | container.appendChild(this.navigationDiv); 135 | // Create the navigation controls. 136 | this.navigationViewModel = NavigationViewModel.create({ 137 | container: this.navigationDiv, 138 | terria: this.terria, 139 | enableZoomControls: true, 140 | enableCompass: true 141 | }); 142 | } 143 | else if ((defined(this.terria.options.enableZoomControls) && !this.terria.options.enableZoomControls) && (!defined(this.terria.options.enableCompass) || this.terria.options.enableCompass)) 144 | { 145 | this.navigationDiv = document.createElement('div'); 146 | this.navigationDiv.setAttribute("id", "navigationDiv"); 147 | container.appendChild(this.navigationDiv); 148 | // Create the navigation controls. 149 | this.navigationViewModel = NavigationViewModel.create({ 150 | container: this.navigationDiv, 151 | terria: this.terria, 152 | enableZoomControls: false, 153 | enableCompass: true 154 | }); 155 | } 156 | else if ((!defined(this.terria.options.enableZoomControls) || this.terria.options.enableZoomControls) && (defined(this.terria.options.enableCompass) && !this.terria.options.enableCompass)) 157 | { 158 | this.navigationDiv = document.createElement('div'); 159 | this.navigationDiv.setAttribute("id", "navigationDiv"); 160 | container.appendChild(this.navigationDiv); 161 | // Create the navigation controls. 162 | this.navigationViewModel = NavigationViewModel.create({ 163 | container: this.navigationDiv, 164 | terria: this.terria, 165 | enableZoomControls: true, 166 | enableCompass: false 167 | }); 168 | } 169 | else if ((defined(this.terria.options.enableZoomControls) && !this.terria.options.enableZoomControls) && (defined(this.terria.options.enableCompass) && !this.terria.options.enableCompass)) 170 | { 171 | //this.navigationDiv.setAttribute("id", "navigationDiv"); 172 | // container.appendChild(this.navigationDiv); 173 | // Create the navigation controls. 174 | // this.navigationViewModel = NavigationViewModel.create({ 175 | // container: this.navigationDiv, 176 | // terria: this.terria, 177 | // enableZoomControls: false, 178 | // enableCompass: false 179 | // }); 180 | } 181 | 182 | } 183 | 184 | return CesiumNavigation; 185 | }); -------------------------------------------------------------------------------- /Source/Core/KnockoutHammerBinding.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'KnockoutES5', 4 | 'Hammer' 5 | ], function (Knockout, Hammer) { 6 | 'use strict'; 7 | 8 | var KnockoutHammerBinding = { 9 | register: function (Knockout) { 10 | Knockout.bindingHandlers.swipeLeft = { 11 | init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 12 | var f = Knockout.unwrap(valueAccessor()); 13 | new Hammer(element).on('swipeleft', function (e) { 14 | var viewModel = bindingContext.$data; 15 | f.apply(viewModel, arguments); 16 | }); 17 | } 18 | }; 19 | 20 | Knockout.bindingHandlers.swipeRight = { 21 | init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 22 | var f = Knockout.unwrap(valueAccessor()); 23 | new Hammer(element).on('swiperight', function (e) { 24 | var viewModel = bindingContext.$data; 25 | f.apply(viewModel, arguments); 26 | }); 27 | } 28 | }; 29 | } 30 | }; 31 | 32 | return KnockoutHammerBinding; 33 | }); 34 | -------------------------------------------------------------------------------- /Source/Core/KnockoutMarkdownBinding.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'markdown-it-sanitizer', 4 | 'markdown-it' 5 | ], function ( 6 | MarkdownItSanitizer, 7 | MarkdownIt) { 8 | 'use strict'; 9 | 10 | var htmlTagRegex = /(.|\s)*<\/html>/im; 11 | 12 | var md = new MarkdownIt({ 13 | html: true, 14 | linkify: true 15 | }); 16 | 17 | md.use(MarkdownItSanitizer, { 18 | imageClass: '', 19 | removeUnbalanced: false, 20 | removeUnknown: false 21 | }); 22 | 23 | var KnockoutMarkdownBinding = { 24 | register: function (Knockout) { 25 | Knockout.bindingHandlers.markdown = { 26 | 'init': function () { 27 | // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications) 28 | return { 'controlsDescendantBindings': true }; 29 | }, 30 | 'update': function (element, valueAccessor) { 31 | // Remove existing children of this element. 32 | while (element.firstChild) { 33 | Knockout.removeNode(element.firstChild); 34 | } 35 | 36 | var rawText = Knockout.unwrap(valueAccessor()); 37 | 38 | // If the text contains an tag, don't try to interpret it as Markdown because 39 | // we'll probably break it in the process. 40 | var html; 41 | if (htmlTagRegex.test(rawText)) { 42 | html = rawText; 43 | } else { 44 | html = md.render(rawText); 45 | } 46 | 47 | var nodes = Knockout.utils.parseHtmlFragment(html, element); 48 | element.className = element.className + ' markdown'; 49 | 50 | for (var i = 0; i < nodes.length; ++i) { 51 | var node = nodes[i]; 52 | setAnchorTargets(node); 53 | element.appendChild(node); 54 | } 55 | } 56 | }; 57 | } 58 | }; 59 | 60 | function setAnchorTargets(element) { 61 | if (element instanceof HTMLAnchorElement) { 62 | element.target = '_blank'; 63 | } 64 | 65 | if (element.childNodes && element.childNodes.length > 0) { 66 | for (var i = 0; i < element.childNodes.length; ++i) { 67 | setAnchorTargets(element.childNodes[i]); 68 | } 69 | } 70 | } 71 | 72 | return KnockoutMarkdownBinding; 73 | }); 74 | 75 | -------------------------------------------------------------------------------- /Source/Core/Utils.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/Ray', 5 | 'Cesium/Core/Cartesian3', 6 | 'Cesium/Core/Cartographic', 7 | 'Cesium/Core/ReferenceFrame', 8 | 'Cesium/Scene/SceneMode' 9 | ], function ( 10 | defined, 11 | Ray, 12 | Cartesian3, 13 | Cartographic, 14 | ReferenceFrame, 15 | SceneMode) { 16 | 'use strict'; 17 | 18 | var Utils = {}; 19 | 20 | var unprojectedScratch = new Cartographic(); 21 | var rayScratch = new Ray(); 22 | 23 | /** 24 | * gets the focus point of the camera 25 | * @param {Viewer|Widget} terria The terria 26 | * @param {boolean} inWorldCoordinates true to get the focus in world coordinates, otherwise get it in projection-specific map coordinates, in meters. 27 | * @param {Cartesian3} [result] The object in which the result will be stored. 28 | * @return {Cartesian3} The modified result parameter, a new instance if none was provided or undefined if there is no focus point. 29 | */ 30 | Utils.getCameraFocus = function (terria, inWorldCoordinates, result) { 31 | var scene = terria.scene; 32 | var camera = scene.camera; 33 | 34 | if(scene.mode == SceneMode.MORPHING) { 35 | return undefined; 36 | } 37 | 38 | if(!defined(result)) { 39 | result = new Cartesian3(); 40 | } 41 | 42 | // TODO bug when tracking: if entity moves the current position should be used and not only the one when starting orbiting/rotating 43 | // TODO bug when tracking: reset should reset to default view of tracked entity 44 | 45 | if(defined(terria.trackedEntity)) { 46 | result = terria.trackedEntity.position.getValue(terria.clock.currentTime, result); 47 | } else { 48 | rayScratch.origin = camera.positionWC; 49 | rayScratch.direction = camera.directionWC; 50 | result = scene.globe.pick(rayScratch, scene, result); 51 | } 52 | 53 | if (!defined(result)) { 54 | return undefined; 55 | } 56 | 57 | if(scene.mode == SceneMode.SCENE2D || scene.mode == SceneMode.COLUMBUS_VIEW) { 58 | result = camera.worldToCameraCoordinatesPoint(result, result); 59 | 60 | if(inWorldCoordinates) { 61 | result = scene.globe.ellipsoid.cartographicToCartesian(scene.mapProjection.unproject(result, unprojectedScratch), result); 62 | } 63 | } else { 64 | if(!inWorldCoordinates) { 65 | result = camera.worldToCameraCoordinatesPoint(result, result); 66 | } 67 | } 68 | 69 | return result; 70 | }; 71 | 72 | return Utils; 73 | }); 74 | -------------------------------------------------------------------------------- /Source/Core/createFragmentFromTemplate.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | ], function () { 4 | 'use strict'; 5 | 6 | var createFragmentFromTemplate = function (htmlString) { 7 | var holder = document.createElement('div'); 8 | holder.innerHTML = htmlString; 9 | 10 | var fragment = document.createDocumentFragment(); 11 | while (holder.firstChild) { 12 | fragment.appendChild(holder.firstChild); 13 | } 14 | 15 | return fragment; 16 | }; 17 | 18 | return createFragmentFromTemplate; 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /Source/Core/loadView.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Widgets/getElement', 4 | 'KnockoutES5', 5 | 'Core/createFragmentFromTemplate' 6 | ], function ( 7 | getElement, 8 | Knockout, 9 | createFragmentFromTemplate) { 10 | 'use strict'; 11 | 12 | var loadView = function (htmlString, container, viewModel) { 13 | container = getElement(container); 14 | 15 | var fragment = createFragmentFromTemplate(htmlString); 16 | 17 | // Sadly, fragment.childNodes doesn't have a slice function. 18 | // This code could be replaced with Array.prototype.slice.call(fragment.childNodes) 19 | // but that seems slightly error prone. 20 | var nodes = []; 21 | 22 | var i; 23 | for (i = 0; i < fragment.childNodes.length; ++i) { 24 | nodes.push(fragment.childNodes[i]); 25 | } 26 | 27 | container.appendChild(fragment); 28 | 29 | for (i = 0; i < nodes.length; ++i) { 30 | var node = nodes[i]; 31 | if (node.nodeType === 1 || node.nodeType === 8) { 32 | Knockout.applyBindings(viewModel, node); 33 | } 34 | } 35 | 36 | return nodes; 37 | }; 38 | 39 | return loadView; 40 | }); -------------------------------------------------------------------------------- /Source/Core/registerKnockoutBindings.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Widgets/SvgPathBindingHandler', 4 | 'KnockoutES5', 5 | 'Core/KnockoutMarkdownBinding', 6 | 'Core/KnockoutHammerBinding' 7 | ], function ( 8 | SvgPathBindingHandler, 9 | Knockout, 10 | KnockoutMarkdownBinding, 11 | KnockoutHammerBinding) { 12 | 'use strict'; 13 | 14 | var registerKnockoutBindings = function () { 15 | SvgPathBindingHandler.register(Knockout); 16 | KnockoutMarkdownBinding.register(Knockout); 17 | KnockoutHammerBinding.register(Knockout); 18 | 19 | Knockout.bindingHandlers.embeddedComponent = { 20 | init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 21 | var component = Knockout.unwrap(valueAccessor()); 22 | component.show(element); 23 | return { controlsDescendantBindings: true }; 24 | }, 25 | update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 26 | } 27 | }; 28 | }; 29 | 30 | return registerKnockoutBindings; 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /Source/Styles/Core.less: -------------------------------------------------------------------------------- 1 | /*html { 2 | height: 100%; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | 6 | body { 7 | height: 100%; 8 | width: 100%; 9 | margin: 0; 10 | overflow: hidden; 11 | padding: 0; 12 | background: #000; 13 | font-size: 15px; 14 | font-family: @default-font; 15 | }*/ 16 | 17 | .full-window { 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | right: 0; 22 | bottom: 0; 23 | margin: 0; 24 | overflow: hidden; 25 | padding: 0; 26 | 27 | -webkit-transition: left @explorer-panel-close-animation-length ease-out; 28 | -moz-transition: left @explorer-panel-close-animation-length ease-out; 29 | -ms-transition: left @explorer-panel-close-animation-length ease-out; 30 | -o-transition: left @explorer-panel-close-animation-length ease-out; 31 | transition: left @explorer-panel-close-animation-length ease-out; 32 | } 33 | 34 | .transparent-to-input { 35 | pointer-events: none; 36 | } 37 | 38 | .opaque-to-input { 39 | pointer-events: auto; 40 | } 41 | 42 | .clickable { 43 | cursor: pointer; 44 | } 45 | 46 | /*a { 47 | text-decoration: none; 48 | color: @highlight-color; 49 | }*/ 50 | 51 | a:hover { 52 | text-decoration: underline; 53 | } 54 | 55 | @modal-background-color: @panel-background-color; 56 | @modal-text-color: @panel-emphasized-text-color; 57 | @modal-header-background-color: rgba(0,0,0,0.2); 58 | @modal-header-text-color: @panel-emphasized-text-color; 59 | 60 | .modal-background { 61 | .opaque-to-input; 62 | position: fixed; 63 | left: 0; 64 | right: 0; 65 | top: 0; 66 | bottom: 0; 67 | background-color: rgba(0,0,0,0.5); 68 | z-index: 1000; /* required for IE9 */ 69 | } 70 | 71 | .modal { 72 | position: absolute; 73 | margin: auto; 74 | background-color: @modal-background-color; 75 | top: 0; 76 | left: 0; 77 | bottom: 0; 78 | right: 0; 79 | max-height: 100%; 80 | max-width: 100%; 81 | font-family: @default-font; 82 | color: @modal-text-color; 83 | } 84 | 85 | .modal-header { 86 | background-color: @modal-header-background-color; 87 | border-bottom: @panel-element-border; 88 | font-size: 15px; 89 | line-height: 40px; 90 | margin: 0; 91 | } 92 | 93 | .modal-header h1 { 94 | font-size: 15px; 95 | color: @modal-header-text-color; 96 | margin-left: 15px; 97 | } 98 | 99 | .modal-content { 100 | margin-left: 15px; 101 | margin-right: 15px; 102 | margin-bottom: 15px; 103 | padding-top: 15px; 104 | overflow: auto; 105 | } 106 | 107 | .modal-close-button { 108 | position: absolute; 109 | right: 15px; 110 | cursor: pointer; 111 | font-size: 18px; 112 | color: @modal-header-text-color; 113 | } 114 | 115 | 116 | #ui { 117 | // This keeps the UI above the map in IE9. 118 | z-index: 2100; 119 | } 120 | 121 | @media print { 122 | .full-window { 123 | position: initial; 124 | } 125 | } 126 | 127 | // input fields 128 | 129 | /* input[type=text] { 130 | height: 38px; 131 | background-color: #eeeeee; 132 | color: @input-text-color; 133 | font-size: 14px; 134 | } 135 | 136 | ::-webkit-input-placeholder { 137 | color: fade(@input-text-color, 75%); 138 | font-style: italic; 139 | } 140 | 141 | :-moz-placeholder { /* Firefox 18- 142 | color: fade(@input-text-color, 75%); 143 | font-style: italic; 144 | } 145 | 146 | ::-moz-placeholder { /* Firefox 19+ 147 | color: fade(@input-text-color, 75%); 148 | font-style: italic; 149 | } 150 | 151 | :-ms-input-placeholder { 152 | color: fade(@input-text-color, 75%); 153 | font-style: italic; 154 | } 155 | 156 | input:focus { 157 | outline-color: #FFFFFF; 158 | } 159 | */ 160 | 161 | /*select { 162 | display: block; 163 | background-color: @panel-form-input-background-color; 164 | color: @panel-form-input-text-color; 165 | height: 40px; 166 | border: 0; 167 | margin-top: 10px; 168 | font-size: 14px; 169 | padding-left: 5px; 170 | }*/ 171 | 172 | .markdown { 173 | img { max-width: 100% } 174 | svg { max-height: 100% } 175 | 176 | input, 177 | select, 178 | textarea, 179 | fieldset { 180 | font-family: inherit; 181 | font-size: 1rem; 182 | box-sizing: border-box; 183 | margin-top: 0; 184 | margin-bottom: 0; 185 | } 186 | 187 | label { 188 | vertical-align: middle; 189 | } 190 | 191 | h1, h2, h3, h4, h5, h6 { 192 | font-family: inherit; 193 | font-weight: bold; 194 | line-height: 1.25; 195 | margin-top: 1em; 196 | margin-bottom: .5em; 197 | } 198 | 199 | h1 { font-size: 2rem } 200 | h2 { font-size: 1.5rem } 201 | h3 { font-size: 1.25rem } 202 | h4 { font-size: 1rem } 203 | h5 { font-size: .875rem } 204 | h6 { font-size: .75rem } 205 | 206 | p { 207 | margin-top: 0; 208 | margin-bottom: 1rem; 209 | } 210 | 211 | strong { font-weight: bold } 212 | em { font-style: italic } 213 | small {font-size: 80%;} 214 | mark { color: #000; background: #ff0;} 215 | u {text-decoration: underline;} 216 | s {text-decoration: line-through;} 217 | 218 | dl, ol, ul { 219 | margin-top: 0; 220 | margin-bottom: 1rem; 221 | } 222 | 223 | ol{ 224 | list-style: decimal inside; 225 | } 226 | 227 | ul{ 228 | list-style: disc inside; 229 | } 230 | 231 | pre, code, samp { 232 | font-family: monospace; 233 | font-size: inherit; 234 | } 235 | 236 | pre { 237 | margin-top: 0; 238 | margin-bottom: 1rem; 239 | overflow-x: scroll; 240 | } 241 | 242 | a { 243 | color: #68ADFE; 244 | text-decoration: none; 245 | } 246 | 247 | a:hover { 248 | text-decoration: underline; 249 | } 250 | 251 | pre, code { 252 | background-color: transparent; 253 | border-radius: 3px; 254 | } 255 | 256 | hr { 257 | border: 0; 258 | border-bottom-style: solid; 259 | border-bottom-width: 1px; 260 | border-bottom-color: rgba(0,0,0,.125); 261 | } 262 | 263 | .left-align { text-align: left } 264 | .center { text-align: center } 265 | .right-align { text-align: right } 266 | .justify { text-align: justify } 267 | 268 | .truncate { 269 | max-width: 100%; 270 | overflow: hidden; 271 | text-overflow: ellipsis; 272 | white-space: nowrap; 273 | } 274 | 275 | ol.upper-roman {list-style-type: upper-roman;} 276 | ol.lower-alpha {list-style-type: lower-alpha;} 277 | 278 | ul.circle {list-style-type: circle;} 279 | ul.square {list-style-type: square;} 280 | 281 | .list-reset { 282 | list-style: none; 283 | padding-left: 0; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /Source/Styles/DistanceLegend.less: -------------------------------------------------------------------------------- 1 | .distance-legend { 2 | .floating-horizontal; 3 | right: 25px; 4 | bottom: 30px; 5 | height: 30px; 6 | width: 125px; 7 | border: 1px solid rgba(255,255,255,0.1); 8 | box-sizing: content-box; 9 | } 10 | 11 | .distance-legend-label { 12 | display: inline-block; 13 | font-family: @default-font; 14 | font-size: 14px; 15 | font-weight: lighter; 16 | line-height: 30px; 17 | color: @floating-text-color; 18 | width: 125px; 19 | text-align: center; 20 | } 21 | 22 | .distance-legend-scale-bar { 23 | border-left: 1px solid @floating-text-color; 24 | border-right: 1px solid @floating-text-color; 25 | border-bottom: 1px solid @floating-text-color; 26 | position: absolute; 27 | height: 10px; 28 | top: 15px; 29 | } 30 | 31 | @media print { 32 | .distance-legend { 33 | display: none; 34 | } 35 | } 36 | 37 | // Don't display the distance legend on small screens like mobile phones. 38 | @media screen and (max-width: 700px), screen and (max-height: 420px) { 39 | .distance-legend { 40 | display: none; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/Styles/Floating.less: -------------------------------------------------------------------------------- 1 | .floating { 2 | .opaque-to-input; 3 | position: absolute; 4 | border-radius: 15px; 5 | background-color: @floating-background-color; 6 | } 7 | 8 | .floating-horizontal { 9 | .floating; 10 | padding-left: 5px; 11 | padding-right: 5px; 12 | } 13 | 14 | .floating-vertical { 15 | .floating; 16 | padding-top: 5px; 17 | padding-bottom: 5px; 18 | } 19 | 20 | @media print { 21 | .floating { 22 | display: none; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Styles/Navigation.less: -------------------------------------------------------------------------------- 1 | .navigation-controls { 2 | //.floating-vertical; 3 | position: absolute; 4 | right: 30px; 5 | top: 210px; 6 | width: 30px; 7 | border: 1px solid rgba(255,255,255,0.1); 8 | font-weight: 300; 9 | // avoids selection of text with rapid zooming 10 | -webkit-touch-callout: none; 11 | -webkit-user-select: none; 12 | -khtml-user-select: none; 13 | -moz-user-select: none; 14 | -ms-user-select: none; 15 | user-select: none; 16 | } 17 | 18 | .navigation-control { 19 | .clickable; 20 | border-bottom: @floating-element-border; 21 | } 22 | 23 | .naviagation-control:active { 24 | color: #FFF; 25 | } 26 | 27 | .navigation-control-last { 28 | .navigation-control; 29 | border-bottom: 0; 30 | } 31 | 32 | .navigation-control-icon-zoom-in { 33 | position: relative; 34 | text-align: center; 35 | font-size: 20px; 36 | color: @floating-text-color; 37 | padding-bottom: 4px; 38 | } 39 | 40 | .navigation-control-icon-zoom-out { 41 | position: relative; 42 | text-align: center; 43 | font-size: 20px; 44 | color: @floating-text-color; 45 | } 46 | 47 | .navigation-control-icon-reset { 48 | position: relative; 49 | left: 10px; 50 | width: 10px; 51 | height: 10px; 52 | fill: fade(@floating-text-color, 80%); 53 | padding-top: 6px; 54 | padding-bottom: 6px; 55 | box-sizing: content-box; 56 | } 57 | 58 | 59 | @compass-diameter: 95px; 60 | @compass-ring-width: @compass-diameter * 20 / 145; 61 | @compass-gyro-diameter: @compass-diameter * 50 / 145; 62 | 63 | .compass { 64 | .opaque-to-input; 65 | position: absolute; 66 | right: 0px; 67 | top: 100px; 68 | width: @compass-diameter; 69 | height: @compass-diameter; 70 | overflow: hidden; 71 | } 72 | 73 | .compass-outer-ring { 74 | position: absolute; 75 | top: 0; 76 | width: @compass-diameter; 77 | height: @compass-diameter; 78 | fill: rgba(255,255,255,0.5); 79 | } 80 | 81 | .compass-outer-ring-background { 82 | position: absolute; 83 | top: 14px; 84 | left: 14px; 85 | width: 44px; 86 | height: 44px; 87 | border-radius: 44px; 88 | border: 12px solid @floating-background-color; 89 | box-sizing: content-box; 90 | } 91 | 92 | .compass-gyro { 93 | pointer-events: none; 94 | position: absolute; 95 | top: 0; 96 | width: @compass-diameter; 97 | height: @compass-diameter; 98 | fill: #CCC; 99 | } 100 | 101 | .compass-gyro-active { 102 | fill: @highlight-color; 103 | } 104 | 105 | .compass-gyro-background { 106 | position: absolute; 107 | top: 30px; 108 | left: 30px; 109 | width: 33px; 110 | height: 33px; 111 | border-radius: 33px; 112 | background-color: @floating-background-color; 113 | border: 1px solid rgba(255,255,255,0.2); 114 | box-sizing: content-box; 115 | } 116 | 117 | .compass-gyro-background:hover + .compass-gyro { 118 | fill: @highlight-color; 119 | } 120 | 121 | .compass-rotation-marker { 122 | position: absolute; 123 | top: 0; 124 | width: @compass-diameter; 125 | height: @compass-diameter; 126 | fill: @highlight-color; 127 | } 128 | 129 | // Don't display the nav controls on small screens like mobile phones. 130 | @media screen and (max-width: 700px), screen and (max-height: 420px) { 131 | .navigation-controls { 132 | display: none; 133 | } 134 | .compass { 135 | display: none; 136 | } 137 | } 138 | 139 | @media print { 140 | .navigation-controls { 141 | display: none; 142 | } 143 | .compass { 144 | display: none; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Source/Styles/cesium-navigation.less: -------------------------------------------------------------------------------- 1 | @import "Core"; 2 | @import "Floating"; 3 | 4 | @import "DistanceLegend"; 5 | @import "Navigation"; 6 | 7 | @default-font: 'Roboto', sans-serif; 8 | @highlight-color: #68ADFE; 9 | @left-side-panels-width: 350px; 10 | 11 | //Panels 12 | @panel-background-color: #2F353C; 13 | @panel-text-color: #CCCCCC; 14 | @panel-emphasized-text-color: #FFFFFF; 15 | @panel-selected-text-color: @highlight-color; 16 | @panel-box-shadow: 2px 2px 5px rgba(0,0,0,0.26); 17 | @panel-element-border: 1px solid rgba(100,100,100, 0.6); 18 | @panel-section-background-color: #282D32; 19 | @panel-form-input-background-color: #838689; 20 | @panel-form-input-text-color: #FFFFFF; 21 | @panel-form-button-background-color: #A1A3A6; 22 | @panel-form-button-text-color: #FFFFFF; 23 | 24 | // Explorer panel 25 | @explorer-panel-width: @left-side-panels-width; 26 | @explorer-panel-header-background-color: #757f88; 27 | @explorer-panel-inactive-tab-text-color: @panel-text-color; 28 | @explorer-panel-active-tab-text-color: @panel-emphasized-text-color; 29 | 30 | @explorer-panel-active-tab-underline-color: @highlight-color; 31 | @explorer-panel-close-animation-length: 0.25s; 32 | @explorer-panel-tab-switch-animation-length: 0.25s; 33 | @explorer-panel-badge-background-color: @highlight-color; 34 | @explorer-panel-badge-text-color: @panel-emphasized-text-color; 35 | 36 | //Floating elements 37 | 38 | @floating-background-color: rgba(47,53,60,80%); 39 | @floating-text-color: #FFFFFF; 40 | @floating-element-border: 1px solid #555555; 41 | 42 | // Input 43 | @input-background-color: #A1A3A6; 44 | @input-text-color: #666666; -------------------------------------------------------------------------------- /Source/SvgPaths/svgCompassGyro.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | ], function () { 4 | 'use strict'; 5 | 6 | return 'm 72.71875,54.375 c -0.476702,0 -0.908208,0.245402 -1.21875,0.5625 -0.310542,0.317098 -0.551189,0.701933 -0.78125,1.1875 -0.172018,0.363062 -0.319101,0.791709 -0.46875,1.25 -6.91615,1.075544 -12.313231,6.656514 -13,13.625 -0.327516,0.117495 -0.661877,0.244642 -0.9375,0.375 -0.485434,0.22959 -0.901634,0.471239 -1.21875,0.78125 -0.317116,0.310011 -0.5625,0.742111 -0.5625,1.21875 l 0.03125,0 c 0,0.476639 0.245384,0.877489 0.5625,1.1875 0.317116,0.310011 0.702066,0.58291 1.1875,0.8125 0.35554,0.168155 0.771616,0.32165 1.21875,0.46875 1.370803,6.10004 6.420817,10.834127 12.71875,11.8125 0.146999,0.447079 0.30025,0.863113 0.46875,1.21875 0.230061,0.485567 0.470708,0.870402 0.78125,1.1875 0.310542,0.317098 0.742048,0.5625 1.21875,0.5625 0.476702,0 0.876958,-0.245402 1.1875,-0.5625 0.310542,-0.317098 0.582439,-0.701933 0.8125,-1.1875 0.172018,-0.363062 0.319101,-0.791709 0.46875,-1.25 6.249045,-1.017063 11.256351,-5.7184 12.625,-11.78125 0.447134,-0.1471 0.86321,-0.300595 1.21875,-0.46875 0.485434,-0.22959 0.901633,-0.502489 1.21875,-0.8125 0.317117,-0.310011 0.5625,-0.710861 0.5625,-1.1875 l -0.03125,0 c 0,-0.476639 -0.245383,-0.908739 -0.5625,-1.21875 C 89.901633,71.846239 89.516684,71.60459 89.03125,71.375 88.755626,71.244642 88.456123,71.117495 88.125,71 87.439949,64.078341 82.072807,58.503735 75.21875,57.375 c -0.15044,-0.461669 -0.326927,-0.884711 -0.5,-1.25 -0.230061,-0.485567 -0.501958,-0.870402 -0.8125,-1.1875 -0.310542,-0.317098 -0.710798,-0.5625 -1.1875,-0.5625 z m -0.0625,1.40625 c 0.03595,-0.01283 0.05968,0 0.0625,0 0.0056,0 0.04321,-0.02233 0.1875,0.125 0.144288,0.147334 0.34336,0.447188 0.53125,0.84375 0.06385,0.134761 0.123901,0.309578 0.1875,0.46875 -0.320353,-0.01957 -0.643524,-0.0625 -0.96875,-0.0625 -0.289073,0 -0.558569,0.04702 -0.84375,0.0625 C 71.8761,57.059578 71.936151,56.884761 72,56.75 c 0.18789,-0.396562 0.355712,-0.696416 0.5,-0.84375 0.07214,-0.07367 0.120304,-0.112167 0.15625,-0.125 z m 0,2.40625 c 0.448007,0 0.906196,0.05436 1.34375,0.09375 0.177011,0.592256 0.347655,1.271044 0.5,2.03125 0.475097,2.370753 0.807525,5.463852 0.9375,8.9375 -0.906869,-0.02852 -1.834463,-0.0625 -2.78125,-0.0625 -0.92298,0 -1.802327,0.03537 -2.6875,0.0625 0.138529,-3.473648 0.493653,-6.566747 0.96875,-8.9375 0.154684,-0.771878 0.320019,-1.463985 0.5,-2.0625 0.405568,-0.03377 0.804291,-0.0625 1.21875,-0.0625 z m -2.71875,0.28125 c -0.129732,0.498888 -0.259782,0.987558 -0.375,1.5625 -0.498513,2.487595 -0.838088,5.693299 -0.96875,9.25 -3.21363,0.15162 -6.119596,0.480068 -8.40625,0.9375 -0.682394,0.136509 -1.275579,0.279657 -1.84375,0.4375 0.799068,-6.135482 5.504716,-11.036454 11.59375,-12.1875 z M 75.5,58.5 c 6.043169,1.18408 10.705093,6.052712 11.5,12.15625 -0.569435,-0.155806 -1.200273,-0.302525 -1.875,-0.4375 -2.262525,-0.452605 -5.108535,-0.783809 -8.28125,-0.9375 -0.130662,-3.556701 -0.470237,-6.762405 -0.96875,-9.25 C 75.761959,59.467174 75.626981,58.990925 75.5,58.5 z m -2.84375,12.09375 c 0.959338,0 1.895843,0.03282 2.8125,0.0625 C 75.48165,71.267751 75.5,71.871028 75.5,72.5 c 0,1.228616 -0.01449,2.438313 -0.0625,3.59375 -0.897358,0.0284 -1.811972,0.0625 -2.75,0.0625 -0.927373,0 -1.831062,-0.03473 -2.71875,-0.0625 -0.05109,-1.155437 -0.0625,-2.365134 -0.0625,-3.59375 0,-0.628972 0.01741,-1.232249 0.03125,-1.84375 0.895269,-0.02827 1.783025,-0.0625 2.71875,-0.0625 z M 68.5625,70.6875 c -0.01243,0.60601 -0.03125,1.189946 -0.03125,1.8125 0,1.22431 0.01541,2.407837 0.0625,3.5625 -3.125243,-0.150329 -5.92077,-0.471558 -8.09375,-0.90625 -0.784983,-0.157031 -1.511491,-0.316471 -2.125,-0.5 -0.107878,-0.704096 -0.1875,-1.422089 -0.1875,-2.15625 0,-0.115714 0.02849,-0.228688 0.03125,-0.34375 0.643106,-0.20284 1.389577,-0.390377 2.25,-0.5625 2.166953,-0.433487 4.97905,-0.75541 8.09375,-0.90625 z m 8.3125,0.03125 c 3.075121,0.15271 5.824455,0.446046 7.96875,0.875 0.857478,0.171534 1.630962,0.360416 2.28125,0.5625 0.0027,0.114659 0,0.228443 0,0.34375 0,0.735827 -0.07914,1.450633 -0.1875,2.15625 -0.598568,0.180148 -1.29077,0.34562 -2.0625,0.5 -2.158064,0.431708 -4.932088,0.754666 -8.03125,0.90625 0.04709,-1.154663 0.0625,-2.33819 0.0625,-3.5625 0,-0.611824 -0.01924,-1.185379 -0.03125,-1.78125 z M 57.15625,72.5625 c 0.0023,0.572772 0.06082,1.131112 0.125,1.6875 -0.125327,-0.05123 -0.266577,-0.10497 -0.375,-0.15625 -0.396499,-0.187528 -0.665288,-0.387337 -0.8125,-0.53125 -0.147212,-0.143913 -0.15625,-0.182756 -0.15625,-0.1875 0,-0.0047 -0.02221,-0.07484 0.125,-0.21875 0.147212,-0.143913 0.447251,-0.312472 0.84375,-0.5 0.07123,-0.03369 0.171867,-0.06006 0.25,-0.09375 z m 31.03125,0 c 0.08201,0.03503 0.175941,0.05872 0.25,0.09375 0.396499,0.187528 0.665288,0.356087 0.8125,0.5 0.14725,0.14391 0.15625,0.21405 0.15625,0.21875 0,0.0047 -0.009,0.04359 -0.15625,0.1875 -0.147212,0.143913 -0.416001,0.343722 -0.8125,0.53125 -0.09755,0.04613 -0.233314,0.07889 -0.34375,0.125 0.06214,-0.546289 0.09144,-1.094215 0.09375,-1.65625 z m -29.5,3.625 c 0.479308,0.123125 0.983064,0.234089 1.53125,0.34375 2.301781,0.460458 5.229421,0.787224 8.46875,0.9375 0.167006,2.84339 0.46081,5.433176 0.875,7.5 0.115218,0.574942 0.245268,1.063612 0.375,1.5625 -5.463677,-1.028179 -9.833074,-5.091831 -11.25,-10.34375 z m 27.96875,0 C 85.247546,81.408945 80.919274,85.442932 75.5,86.5 c 0.126981,-0.490925 0.261959,-0.967174 0.375,-1.53125 0.41419,-2.066824 0.707994,-4.65661 0.875,-7.5 3.204493,-0.15162 6.088346,-0.480068 8.375,-0.9375 0.548186,-0.109661 1.051942,-0.220625 1.53125,-0.34375 z M 70.0625,77.53125 c 0.865391,0.02589 1.723666,0.03125 2.625,0.03125 0.912062,0 1.782843,-0.0048 2.65625,-0.03125 -0.165173,2.736408 -0.453252,5.207651 -0.84375,7.15625 -0.152345,0.760206 -0.322989,1.438994 -0.5,2.03125 -0.437447,0.03919 -0.895856,0.0625 -1.34375,0.0625 -0.414943,0 -0.812719,-0.02881 -1.21875,-0.0625 -0.177011,-0.592256 -0.347655,-1.271044 -0.5,-2.03125 -0.390498,-1.948599 -0.700644,-4.419842 -0.875,-7.15625 z m 1.75,10.28125 c 0.284911,0.01545 0.554954,0.03125 0.84375,0.03125 0.325029,0 0.648588,-0.01171 0.96875,-0.03125 -0.05999,0.148763 -0.127309,0.31046 -0.1875,0.4375 -0.18789,0.396562 -0.386962,0.696416 -0.53125,0.84375 -0.144288,0.147334 -0.181857,0.125 -0.1875,0.125 -0.0056,0 -0.07446,0.02233 -0.21875,-0.125 C 72.355712,88.946416 72.18789,88.646562 72,88.25 71.939809,88.12296 71.872486,87.961263 71.8125,87.8125 z'; 7 | }); -------------------------------------------------------------------------------- /Source/SvgPaths/svgCompassOuterRing.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | ], function () { 4 | 'use strict'; 5 | 6 | return 'm 66.5625,0 0,15.15625 3.71875,0 0,-10.40625 5.5,10.40625 4.375,0 0,-15.15625 -3.71875,0 0,10.40625 L 70.9375,0 66.5625,0 z M 72.5,20.21875 c -28.867432,0 -52.28125,23.407738 -52.28125,52.28125 0,28.87351 23.413818,52.3125 52.28125,52.3125 28.86743,0 52.28125,-23.43899 52.28125,-52.3125 0,-28.873512 -23.41382,-52.28125 -52.28125,-52.28125 z m 0,1.75 c 13.842515,0 26.368948,5.558092 35.5,14.5625 l -11.03125,11 0.625,0.625 11.03125,-11 c 8.9199,9.108762 14.4375,21.579143 14.4375,35.34375 0,13.764606 -5.5176,26.22729 -14.4375,35.34375 l -11.03125,-11 -0.625,0.625 11.03125,11 c -9.130866,9.01087 -21.658601,14.59375 -35.5,14.59375 -13.801622,0 -26.321058,-5.53481 -35.4375,-14.5 l 11.125,-11.09375 c 6.277989,6.12179 14.857796,9.90625 24.3125,9.90625 19.241896,0 34.875,-15.629154 34.875,-34.875 0,-19.245847 -15.633104,-34.84375 -34.875,-34.84375 -9.454704,0 -18.034511,3.760884 -24.3125,9.875 L 37.0625,36.4375 C 46.179178,27.478444 58.696991,21.96875 72.5,21.96875 z m -0.875,0.84375 0,13.9375 1.75,0 0,-13.9375 -1.75,0 z M 36.46875,37.0625 47.5625,48.15625 C 41.429794,54.436565 37.65625,63.027539 37.65625,72.5 c 0,9.472461 3.773544,18.055746 9.90625,24.34375 L 36.46875,107.9375 c -8.96721,-9.1247 -14.5,-21.624886 -14.5,-35.4375 0,-13.812615 5.53279,-26.320526 14.5,-35.4375 z M 72.5,39.40625 c 18.297686,0 33.125,14.791695 33.125,33.09375 0,18.302054 -14.827314,33.125 -33.125,33.125 -18.297687,0 -33.09375,-14.822946 -33.09375,-33.125 0,-18.302056 14.796063,-33.09375 33.09375,-33.09375 z M 22.84375,71.625 l 0,1.75 13.96875,0 0,-1.75 -13.96875,0 z m 85.5625,0 0,1.75 14,0 0,-1.75 -14,0 z M 71.75,108.25 l 0,13.9375 1.71875,0 0,-13.9375 -1.71875,0 z'; 7 | }); -------------------------------------------------------------------------------- /Source/SvgPaths/svgCompassRotationMarker.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | ], function () { 4 | 'use strict'; 5 | 6 | return 'M 72.46875,22.03125 C 59.505873,22.050338 46.521615,27.004287 36.6875,36.875 L 47.84375,47.96875 C 61.521556,34.240041 83.442603,34.227389 97.125,47.90625 l 11.125,-11.125 C 98.401629,26.935424 85.431627,22.012162 72.46875,22.03125 z'; 7 | }); -------------------------------------------------------------------------------- /Source/SvgPaths/svgReset.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | ], function () { 4 | 'use strict'; 5 | 6 | return 'M 7.5,0 C 3.375,0 0,3.375 0,7.5 0,11.625 3.375,15 7.5,15 c 3.46875,0 6.375,-2.4375 7.21875,-5.625 l -1.96875,0 C 12,11.53125 9.9375,13.125 7.5,13.125 4.40625,13.125 1.875,10.59375 1.875,7.5 1.875,4.40625 4.40625,1.875 7.5,1.875 c 1.59375,0 2.90625,0.65625 3.9375,1.6875 l -3,3 6.5625,0 L 15,0 12.75,2.25 C 11.4375,0.84375 9.5625,0 7.5,0 z'; 7 | }); -------------------------------------------------------------------------------- /Source/ViewModels/DistanceLegendViewModel.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/DeveloperError', 5 | 'Cesium/Core/EllipsoidGeodesic', 6 | 'Cesium/Core/Cartesian2', 7 | 'Cesium/Core/getTimestamp', 8 | 'Cesium/Core/EventHelper', 9 | 'KnockoutES5', 10 | 'Core/loadView', 11 | 'leaflet' 12 | ], function ( 13 | defined, 14 | DeveloperError, 15 | EllipsoidGeodesic, 16 | Cartesian2, 17 | getTimestamp, 18 | EventHelper, 19 | Knockout, 20 | loadView, 21 | leaflet) { 22 | 'use strict'; 23 | 24 | var DistanceLegendViewModel = function (options) { 25 | if (!defined(options) || !defined(options.terria)) { 26 | throw new DeveloperError('options.terria is required.'); 27 | } 28 | 29 | this.terria = options.terria; 30 | this._removeSubscription = undefined; 31 | this._lastLegendUpdate = undefined; 32 | this.eventHelper = new EventHelper(); 33 | 34 | this.distanceLabel = undefined; 35 | this.barWidth = undefined; 36 | 37 | this.enableDistanceLegend = (defined(options.enableDistanceLegend))?options.enableDistanceLegend:true; 38 | 39 | Knockout.track(this, ['distanceLabel', 'barWidth']); 40 | 41 | this.eventHelper.add(this.terria.afterWidgetChanged, function () { 42 | if (defined(this._removeSubscription)) { 43 | this._removeSubscription(); 44 | this._removeSubscription = undefined; 45 | } 46 | }, this); 47 | // this.terria.beforeWidgetChanged.addEventListener(function () { 48 | // if (defined(this._removeSubscription)) { 49 | // this._removeSubscription(); 50 | // this._removeSubscription = undefined; 51 | // } 52 | // }, this); 53 | 54 | var that = this; 55 | 56 | function addUpdateSubscription() { 57 | if (defined(that.terria)) { 58 | var scene = that.terria.scene; 59 | that._removeSubscription = scene.postRender.addEventListener(function () { 60 | updateDistanceLegendCesium(this, scene); 61 | }, that); 62 | } else if (defined(that.terria.leaflet)) { 63 | var map = that.terria.leaflet.map; 64 | 65 | var potentialChangeCallback = function potentialChangeCallback() { 66 | updateDistanceLegendLeaflet(that, map); 67 | }; 68 | 69 | that._removeSubscription = function () { 70 | map.off('zoomend', potentialChangeCallback); 71 | map.off('moveend', potentialChangeCallback); 72 | }; 73 | 74 | map.on('zoomend', potentialChangeCallback); 75 | map.on('moveend', potentialChangeCallback); 76 | 77 | updateDistanceLegendLeaflet(that, map); 78 | } 79 | } 80 | 81 | addUpdateSubscription(); 82 | this.eventHelper.add(this.terria.afterWidgetChanged, function () { 83 | addUpdateSubscription(); 84 | }, this); 85 | //this.terria.afterWidgetChanged.addEventListener(function() { 86 | // addUpdateSubscription(); 87 | // }, this); 88 | }; 89 | 90 | 91 | DistanceLegendViewModel.prototype.destroy = function () { 92 | 93 | this.eventHelper.removeAll(); 94 | }; 95 | 96 | DistanceLegendViewModel.prototype.show = function (container) { 97 | var testing ; 98 | if ( this.enableDistanceLegend) 99 | { 100 | testing = '
' + 101 | '
' + 102 | '
' + 103 | '
'; 104 | } 105 | else 106 | { 107 | testing = ''; 111 | } 112 | loadView(testing, container, this); 113 | // loadView(distanceLegendTemplate, container, this); 114 | //loadView(require('fs').readFileSync(__dirname + '/../Views/DistanceLegend.html', 'utf8'), container, this); 115 | }; 116 | 117 | DistanceLegendViewModel.create = function (options) { 118 | var result = new DistanceLegendViewModel(options); 119 | result.show(options.container); 120 | return result; 121 | }; 122 | 123 | var geodesic = new EllipsoidGeodesic(); 124 | 125 | var distances = [ 126 | 1, 2, 3, 5, 127 | 10, 20, 30, 50, 128 | 100, 200, 300, 500, 129 | 1000, 2000, 3000, 5000, 130 | 10000, 20000, 30000, 50000, 131 | 100000, 200000, 300000, 500000, 132 | 1000000, 2000000, 3000000, 5000000, 133 | 10000000, 20000000, 30000000, 50000000]; 134 | 135 | function updateDistanceLegendCesium(viewModel, scene) { 136 | if (!viewModel.enableDistanceLegend) 137 | { 138 | viewModel.barWidth = undefined; 139 | viewModel.distanceLabel = undefined; 140 | return; 141 | } 142 | var now = getTimestamp(); 143 | if (now < viewModel._lastLegendUpdate + 250) { 144 | return; 145 | } 146 | 147 | viewModel._lastLegendUpdate = now; 148 | 149 | // Find the distance between two pixels at the bottom center of the screen. 150 | var width = scene.canvas.clientWidth; 151 | var height = scene.canvas.clientHeight; 152 | 153 | var left = scene.camera.getPickRay(new Cartesian2((width / 2) | 0, height - 1)); 154 | var right = scene.camera.getPickRay(new Cartesian2(1 + (width / 2) | 0, height - 1)); 155 | 156 | var globe = scene.globe; 157 | var leftPosition = globe.pick(left, scene); 158 | var rightPosition = globe.pick(right, scene); 159 | 160 | if (!defined(leftPosition) || !defined(rightPosition)) { 161 | viewModel.barWidth = undefined; 162 | viewModel.distanceLabel = undefined; 163 | return; 164 | } 165 | 166 | var leftCartographic = globe.ellipsoid.cartesianToCartographic(leftPosition); 167 | var rightCartographic = globe.ellipsoid.cartesianToCartographic(rightPosition); 168 | 169 | geodesic.setEndPoints(leftCartographic, rightCartographic); 170 | var pixelDistance = geodesic.surfaceDistance; 171 | 172 | // Find the first distance that makes the scale bar less than 100 pixels. 173 | var maxBarWidth = 100; 174 | var distance; 175 | for (var i = distances.length - 1; !defined(distance) && i >= 0; --i) { 176 | if (distances[i] / pixelDistance < maxBarWidth) { 177 | distance = distances[i]; 178 | } 179 | } 180 | 181 | if (defined(distance)) { 182 | var label; 183 | if (distance >= 1000) { 184 | label = (distance / 1000).toString() + ' km'; 185 | } else { 186 | label = distance.toString() + ' m'; 187 | } 188 | 189 | viewModel.barWidth = (distance / pixelDistance) | 0; 190 | viewModel.distanceLabel = label; 191 | } else { 192 | viewModel.barWidth = undefined; 193 | viewModel.distanceLabel = undefined; 194 | } 195 | } 196 | 197 | function updateDistanceLegendLeaflet(viewModel, map) { 198 | var halfHeight = map.getSize().y / 2; 199 | var maxPixelWidth = 100; 200 | var maxMeters = map.containerPointToLatLng([0, halfHeight]).distanceTo( 201 | map.containerPointToLatLng([maxPixelWidth, halfHeight])); 202 | 203 | var meters = leaflet.control.scale()._getRoundNum(maxMeters); 204 | var label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; 205 | 206 | viewModel.barWidth = (meters / maxMeters) * maxPixelWidth; 207 | viewModel.distanceLabel = label; 208 | } 209 | 210 | return DistanceLegendViewModel; 211 | }); 212 | -------------------------------------------------------------------------------- /Source/ViewModels/NavigationControl.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'ViewModels/UserInterfaceControl' 4 | ], function ( 5 | UserInterfaceControl) { 6 | 'use strict'; 7 | 8 | /** 9 | * The view-model for a control in the navigation control tool bar 10 | * 11 | * @alias NavigationControl 12 | * @constructor 13 | * @abstract 14 | * 15 | * @param {Terria} terria The Terria instance. 16 | */ 17 | var NavigationControl = function (terria) { 18 | UserInterfaceControl.apply(this, arguments); 19 | }; 20 | 21 | NavigationControl.prototype = Object.create(UserInterfaceControl.prototype); 22 | 23 | return NavigationControl; 24 | }); 25 | -------------------------------------------------------------------------------- /Source/ViewModels/NavigationViewModel.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/Math', 5 | 'Cesium/Core/getTimestamp', 6 | 'Cesium/Core/EventHelper', 7 | 'Cesium/Core/Transforms', 8 | 'Cesium/Scene/SceneMode', 9 | 'Cesium/Core/Cartesian2', 10 | 'Cesium/Core/Cartesian3', 11 | 'Cesium/Core/Matrix4', 12 | 'Cesium/Core/BoundingSphere', 13 | 'Cesium/Core/HeadingPitchRange', 14 | 'KnockoutES5', 15 | 'Core/loadView', 16 | 'ViewModels/ResetViewNavigationControl', 17 | 'ViewModels/ZoomNavigationControl', 18 | 'SvgPaths/svgCompassOuterRing', 19 | 'SvgPaths/svgCompassGyro', 20 | 'SvgPaths/svgCompassRotationMarker', 21 | 'Core/Utils' 22 | ], function ( 23 | defined, 24 | CesiumMath, 25 | getTimestamp, 26 | EventHelper, 27 | Transforms, 28 | SceneMode, 29 | Cartesian2, 30 | Cartesian3, 31 | Matrix4, 32 | BoundingSphere, 33 | HeadingPitchRange, 34 | Knockout, 35 | loadView, 36 | ResetViewNavigationControl, 37 | ZoomNavigationControl, 38 | svgCompassOuterRing, 39 | svgCompassGyro, 40 | svgCompassRotationMarker, 41 | Utils) 42 | { 43 | 'use strict'; 44 | 45 | var NavigationViewModel = function (options) 46 | { 47 | 48 | this.terria = options.terria; 49 | this.eventHelper = new EventHelper(); 50 | this.enableZoomControls = (defined(options.enableZoomControls))?options.enableZoomControls:true; 51 | this.enableCompass = (defined(options.enableCompass))?options.enableCompass:true; 52 | 53 | // if (this.showZoomControls) 54 | // { 55 | this.controls = options.controls; 56 | if (!defined(this.controls)) 57 | { 58 | this.controls = [ 59 | new ZoomNavigationControl(this.terria, true), 60 | new ResetViewNavigationControl(this.terria), 61 | new ZoomNavigationControl(this.terria, false) 62 | ]; 63 | } 64 | //} 65 | 66 | this.svgCompassOuterRing = svgCompassOuterRing; 67 | this.svgCompassGyro = svgCompassGyro; 68 | this.svgCompassRotationMarker = svgCompassRotationMarker; 69 | 70 | this.showCompass = defined(this.terria) && this.enableCompass; 71 | this.heading = this.showCompass ? this.terria.scene.camera.heading : 0.0; 72 | 73 | this.isOrbiting = false; 74 | this.orbitCursorAngle = 0; 75 | this.orbitCursorOpacity = 0.0; 76 | this.orbitLastTimestamp = 0; 77 | this.orbitFrame = undefined; 78 | this.orbitIsLook = false; 79 | this.orbitMouseMoveFunction = undefined; 80 | this.orbitMouseUpFunction = undefined; 81 | 82 | this.isRotating = false; 83 | this.rotateInitialCursorAngle = undefined; 84 | this.rotateFrame = undefined; 85 | this.rotateIsLook = false; 86 | this.rotateMouseMoveFunction = undefined; 87 | this.rotateMouseUpFunction = undefined; 88 | 89 | this._unsubcribeFromPostRender = undefined; 90 | 91 | Knockout.track(this, ['controls', 'showCompass', 'heading', 'isOrbiting', 'orbitCursorAngle', 'isRotating']); 92 | 93 | var that = this; 94 | 95 | function widgetChange() 96 | { 97 | if (defined(that.terria)) 98 | { 99 | if (that._unsubcribeFromPostRender) 100 | { 101 | that._unsubcribeFromPostRender(); 102 | that._unsubcribeFromPostRender = undefined; 103 | } 104 | 105 | that.showCompass = true && that.enableCompass; 106 | 107 | that._unsubcribeFromPostRender = that.terria.scene.postRender.addEventListener(function () 108 | { 109 | that.heading = that.terria.scene.camera.heading; 110 | }); 111 | } 112 | else 113 | { 114 | if (that._unsubcribeFromPostRender) 115 | { 116 | that._unsubcribeFromPostRender(); 117 | that._unsubcribeFromPostRender = undefined; 118 | } 119 | that.showCompass = false; 120 | } 121 | } 122 | 123 | this.eventHelper.add(this.terria.afterWidgetChanged, widgetChange, this); 124 | //this.terria.afterWidgetChanged.addEventListener(widgetChange); 125 | 126 | widgetChange(); 127 | }; 128 | 129 | 130 | NavigationViewModel.prototype.destroy = function () 131 | { 132 | 133 | this.eventHelper.removeAll(); 134 | 135 | //loadView(require('fs').readFileSync(baseURLEmpCesium + 'js-lib/terrajs/lib/Views/Navigation.html', 'utf8'), container, this); 136 | 137 | }; 138 | 139 | NavigationViewModel.prototype.show = function (container) 140 | { 141 | var testing; 142 | if (this.enableZoomControls && this.enableCompass) 143 | { 144 | testing = '
' + 148 | '
' + 149 | '
' + 150 | '
' + 151 | '
' + 152 | '
' + 153 | '
' + 154 | ''; 166 | } 167 | else if (!this.enableZoomControls && this.enableCompass) 168 | { 169 | testing = '
' + 173 | '
' + 174 | '
' + 175 | '
' + 176 | '
' + 177 | '
' + 178 | '
' + 179 | ''; 191 | } 192 | else if (this.enableZoomControls && !this.enableCompass) 193 | { 194 | testing = '' + 204 | ''; 216 | } 217 | else if (!this.enableZoomControls && !this.enableCompass) 218 | { 219 | testing = '' + 229 | ''; 241 | } 242 | loadView(testing, container, this); 243 | // loadView(navigatorTemplate, container, this); 244 | //loadView(require('fs').readFileSync(baseURLEmpCesium + 'js-lib/terrajs/lib/Views/Navigation.html', 'utf8'), container, this); 245 | 246 | }; 247 | 248 | /** 249 | * Adds a control to this toolbar. 250 | * @param {NavControl} control The control to add. 251 | */ 252 | NavigationViewModel.prototype.add = function (control) 253 | { 254 | this.controls.push(control); 255 | }; 256 | 257 | /** 258 | * Removes a control from this toolbar. 259 | * @param {NavControl} control The control to remove. 260 | */ 261 | NavigationViewModel.prototype.remove = function (control) 262 | { 263 | this.controls.remove(control); 264 | }; 265 | 266 | /** 267 | * Checks if the control given is the last control in the control array. 268 | * @param {NavControl} control The control to remove. 269 | */ 270 | NavigationViewModel.prototype.isLastControl = function (control) 271 | { 272 | return (control === this.controls[this.controls.length - 1]); 273 | }; 274 | 275 | var vectorScratch = new Cartesian2(); 276 | 277 | NavigationViewModel.prototype.handleMouseDown = function (viewModel, e) 278 | { 279 | var scene = this.terria.scene; 280 | if (scene.mode === SceneMode.MORPHING) 281 | { 282 | return true; 283 | } 284 | 285 | var compassElement = e.currentTarget; 286 | var compassRectangle = e.currentTarget.getBoundingClientRect(); 287 | var maxDistance = compassRectangle.width / 2.0; 288 | var center = new Cartesian2((compassRectangle.right - compassRectangle.left) / 2.0, (compassRectangle.bottom - compassRectangle.top) / 2.0); 289 | var clickLocation = new Cartesian2(e.clientX - compassRectangle.left, e.clientY - compassRectangle.top); 290 | var vector = Cartesian2.subtract(clickLocation, center, vectorScratch); 291 | var distanceFromCenter = Cartesian2.magnitude(vector); 292 | 293 | var distanceFraction = distanceFromCenter / maxDistance; 294 | 295 | var nominalTotalRadius = 145; 296 | var norminalGyroRadius = 50; 297 | 298 | if (distanceFraction < norminalGyroRadius / nominalTotalRadius) 299 | { 300 | orbit(this, compassElement, vector); 301 | // return false; 302 | } 303 | else if (distanceFraction < 1.0) 304 | { 305 | rotate(this, compassElement, vector); 306 | // return false; 307 | } 308 | else 309 | { 310 | return true; 311 | } 312 | }; 313 | 314 | var oldTransformScratch = new Matrix4(); 315 | var newTransformScratch = new Matrix4(); 316 | var centerScratch = new Cartesian3(); 317 | 318 | NavigationViewModel.prototype.handleDoubleClick = function (viewModel, e) 319 | { 320 | var scene = viewModel.terria.scene; 321 | var camera = scene.camera; 322 | 323 | var sscc = scene.screenSpaceCameraController; 324 | 325 | if (scene.mode == SceneMode.MORPHING || !sscc.enableInputs) { 326 | return true; 327 | } 328 | if (scene.mode == SceneMode.COLUMBUS_VIEW && !sscc.enableTranslate) { 329 | return; 330 | } 331 | if(scene.mode == SceneMode.SCENE3D || scene.mode == SceneMode.COLUMBUS_VIEW) { 332 | if (!sscc.enableLook) { 333 | return; 334 | } 335 | 336 | if(scene.mode == SceneMode.SCENE3D) { 337 | if(!sscc.enableRotate) { 338 | return 339 | } 340 | } 341 | } 342 | 343 | var center = Utils.getCameraFocus(viewModel.terria, true, centerScratch); 344 | 345 | if (!defined(center)) 346 | { 347 | // Globe is barely visible, so reset to home view. 348 | 349 | this.controls[1].resetView(); 350 | return; 351 | } 352 | 353 | var cameraPosition = scene.globe.ellipsoid.cartographicToCartesian(camera.positionCartographic, new Cartesian3()); 354 | 355 | var surfaceNormal = scene.globe.ellipsoid.geodeticSurfaceNormal(center); 356 | 357 | var focusBoundingSphere = new BoundingSphere(center, 0); 358 | 359 | camera.flyToBoundingSphere(focusBoundingSphere, { 360 | offset: new HeadingPitchRange(0, 361 | // do not use camera.pitch since the pitch at the center/target is required 362 | CesiumMath.PI_OVER_TWO - Cartesian3.angleBetween( 363 | surfaceNormal, 364 | camera.directionWC 365 | ), 366 | // distanceToBoundingSphere returns wrong values when in 2D or Columbus view so do not use 367 | // camera.distanceToBoundingSphere(focusBoundingSphere) 368 | // instead calculate distance manually 369 | Cartesian3.distance(cameraPosition, center) 370 | ), 371 | duration: 1.5 372 | }); 373 | }; 374 | 375 | NavigationViewModel.create = function (options) 376 | { 377 | //options.enableZoomControls = this.enableZoomControls; 378 | //options.enableCompass = this.enableCompass; 379 | var result = new NavigationViewModel(options); 380 | result.show(options.container); 381 | return result; 382 | }; 383 | 384 | function orbit(viewModel, compassElement, cursorVector){ 385 | var scene = viewModel.terria.scene; 386 | 387 | 388 | var sscc = scene.screenSpaceCameraController; 389 | 390 | // do not orbit if it is disabled 391 | if(scene.mode == SceneMode.MORPHING || !sscc.enableInputs) { 392 | return; 393 | } 394 | 395 | switch(scene.mode) { 396 | case SceneMode.COLUMBUS_VIEW: 397 | if(sscc.enableLook) { 398 | break; 399 | } 400 | 401 | if (!sscc.enableTranslate || !sscc.enableTilt) { 402 | return; 403 | } 404 | break; 405 | case SceneMode.SCENE3D: 406 | if(sscc.enableLook) { 407 | break; 408 | } 409 | 410 | if (!sscc.enableTilt || !sscc.enableRotate) { 411 | return; 412 | } 413 | break; 414 | case SceneMode.SCENE2D: 415 | if (!sscc.enableTranslate) { 416 | return; 417 | } 418 | break; 419 | } 420 | 421 | // Remove existing event handlers, if any. 422 | document.removeEventListener('mousemove', viewModel.orbitMouseMoveFunction, false); 423 | document.removeEventListener('mouseup', viewModel.orbitMouseUpFunction, false); 424 | 425 | if (defined(viewModel.orbitTickFunction)) 426 | { 427 | viewModel.terria.clock.onTick.removeEventListener(viewModel.orbitTickFunction); 428 | } 429 | 430 | viewModel.orbitMouseMoveFunction = undefined; 431 | viewModel.orbitMouseUpFunction = undefined; 432 | viewModel.orbitTickFunction = undefined; 433 | 434 | viewModel.isOrbiting = true; 435 | viewModel.orbitLastTimestamp = getTimestamp(); 436 | 437 | var camera = scene.camera; 438 | 439 | if (defined(viewModel.terria.trackedEntity)) { 440 | // when tracking an entity simply use that reference frame 441 | viewModel.orbitFrame = undefined; 442 | viewModel.orbitIsLook = false; 443 | } else { 444 | var center = Utils.getCameraFocus(viewModel.terria, true, centerScratch); 445 | 446 | if (!defined(center)) 447 | { 448 | viewModel.orbitFrame = Transforms.eastNorthUpToFixedFrame(camera.positionWC, scene.globe.ellipsoid, newTransformScratch); 449 | viewModel.orbitIsLook = true; 450 | } 451 | else 452 | { 453 | viewModel.orbitFrame = Transforms.eastNorthUpToFixedFrame(center, scene.globe.ellipsoid, newTransformScratch); 454 | viewModel.orbitIsLook = false; 455 | } 456 | } 457 | 458 | viewModel.orbitTickFunction = function (e) 459 | { 460 | var timestamp = getTimestamp(); 461 | var deltaT = timestamp - viewModel.orbitLastTimestamp; 462 | var rate = (viewModel.orbitCursorOpacity - 0.5) * 2.5 / 1000; 463 | var distance = deltaT * rate; 464 | 465 | var angle = viewModel.orbitCursorAngle + CesiumMath.PI_OVER_TWO; 466 | var x = Math.cos(angle) * distance; 467 | var y = Math.sin(angle) * distance; 468 | 469 | var oldTransform; 470 | 471 | if (defined(viewModel.orbitFrame)) { 472 | oldTransform = Matrix4.clone(camera.transform, oldTransformScratch); 473 | 474 | camera.lookAtTransform(viewModel.orbitFrame); 475 | } 476 | 477 | // do not look up/down or rotate in 2D mode 478 | if (scene.mode == SceneMode.SCENE2D) 479 | { 480 | camera.move(new Cartesian3(x, y, 0), Math.max(scene.canvas.clientWidth, scene.canvas.clientHeight) / 100 * camera.positionCartographic.height * distance); 481 | } 482 | else 483 | { 484 | if (viewModel.orbitIsLook) 485 | { 486 | camera.look(Cartesian3.UNIT_Z, -x); 487 | camera.look(camera.right, -y); 488 | } 489 | else 490 | { 491 | camera.rotateLeft(x); 492 | camera.rotateUp(y); 493 | } 494 | } 495 | 496 | if (defined(viewModel.orbitFrame)) { 497 | camera.lookAtTransform(oldTransform); 498 | } 499 | 500 | // viewModel.terria.cesium.notifyRepaintRequired(); 501 | 502 | viewModel.orbitLastTimestamp = timestamp; 503 | }; 504 | 505 | function updateAngleAndOpacity(vector, compassWidth) 506 | { 507 | var angle = Math.atan2(-vector.y, vector.x); 508 | viewModel.orbitCursorAngle = CesiumMath.zeroToTwoPi(angle - CesiumMath.PI_OVER_TWO); 509 | 510 | var distance = Cartesian2.magnitude(vector); 511 | var maxDistance = compassWidth / 2.0; 512 | var distanceFraction = Math.min(distance / maxDistance, 1.0); 513 | var easedOpacity = 0.5 * distanceFraction * distanceFraction + 0.5; 514 | viewModel.orbitCursorOpacity = easedOpacity; 515 | 516 | //viewModel.terria.cesium.notifyRepaintRequired(); 517 | } 518 | 519 | viewModel.orbitMouseMoveFunction = function (e) 520 | { 521 | var compassRectangle = compassElement.getBoundingClientRect(); 522 | var center = new Cartesian2((compassRectangle.right - compassRectangle.left) / 2.0, (compassRectangle.bottom - compassRectangle.top) / 2.0); 523 | var clickLocation = new Cartesian2(e.clientX - compassRectangle.left, e.clientY - compassRectangle.top); 524 | var vector = Cartesian2.subtract(clickLocation, center, vectorScratch); 525 | updateAngleAndOpacity(vector, compassRectangle.width); 526 | }; 527 | 528 | viewModel.orbitMouseUpFunction = function (e) 529 | { 530 | // TODO: if mouse didn't move, reset view to looking down, north is up? 531 | 532 | viewModel.isOrbiting = false; 533 | document.removeEventListener('mousemove', viewModel.orbitMouseMoveFunction, false); 534 | document.removeEventListener('mouseup', viewModel.orbitMouseUpFunction, false); 535 | 536 | if (defined(viewModel.orbitTickFunction)) 537 | { 538 | viewModel.terria.clock.onTick.removeEventListener(viewModel.orbitTickFunction); 539 | } 540 | 541 | viewModel.orbitMouseMoveFunction = undefined; 542 | viewModel.orbitMouseUpFunction = undefined; 543 | viewModel.orbitTickFunction = undefined; 544 | }; 545 | 546 | document.addEventListener('mousemove', viewModel.orbitMouseMoveFunction, false); 547 | document.addEventListener('mouseup', viewModel.orbitMouseUpFunction, false); 548 | viewModel.terria.clock.onTick.addEventListener(viewModel.orbitTickFunction); 549 | 550 | updateAngleAndOpacity(cursorVector, compassElement.getBoundingClientRect().width); 551 | } 552 | 553 | function rotate(viewModel, compassElement, cursorVector) 554 | { 555 | var scene = viewModel.terria.scene; 556 | var camera = scene.camera; 557 | 558 | var sscc = scene.screenSpaceCameraController; 559 | // do not rotate in 2D mode or if rotating is disabled 560 | if (scene.mode == SceneMode.MORPHING || scene.mode == SceneMode.SCENE2D || !sscc.enableInputs) { 561 | return; 562 | } 563 | if(!sscc.enableLook && (scene.mode == SceneMode.COLUMBUS_VIEW || (scene.mode == SceneMode.SCENE3D && !sscc.enableRotate))) { 564 | return; 565 | } 566 | 567 | // Remove existing event handlers, if any. 568 | document.removeEventListener('mousemove', viewModel.rotateMouseMoveFunction, false); 569 | document.removeEventListener('mouseup', viewModel.rotateMouseUpFunction, false); 570 | 571 | viewModel.rotateMouseMoveFunction = undefined; 572 | viewModel.rotateMouseUpFunction = undefined; 573 | 574 | viewModel.isRotating = true; 575 | viewModel.rotateInitialCursorAngle = Math.atan2(-cursorVector.y, cursorVector.x); 576 | 577 | if(defined(viewModel.terria.trackedEntity)) { 578 | // when tracking an entity simply use that reference frame 579 | viewModel.rotateFrame = undefined; 580 | viewModel.rotateIsLook = false; 581 | } else { 582 | var viewCenter = Utils.getCameraFocus(viewModel.terria, true, centerScratch); 583 | 584 | if (!defined(viewCenter) || (scene.mode == SceneMode.COLUMBUS_VIEW && !sscc.enableLook && !sscc.enableTranslate)) 585 | { 586 | viewModel.rotateFrame = Transforms.eastNorthUpToFixedFrame(camera.positionWC, scene.globe.ellipsoid, newTransformScratch); 587 | viewModel.rotateIsLook = true; 588 | } 589 | else 590 | { 591 | viewModel.rotateFrame = Transforms.eastNorthUpToFixedFrame(viewCenter, scene.globe.ellipsoid, newTransformScratch); 592 | viewModel.rotateIsLook = false; 593 | } 594 | } 595 | 596 | var oldTransform; 597 | if(defined(viewModel.rotateFrame)) { 598 | oldTransform = Matrix4.clone(camera.transform, oldTransformScratch); 599 | camera.lookAtTransform(viewModel.rotateFrame); 600 | } 601 | 602 | viewModel.rotateInitialCameraAngle = -camera.heading; 603 | 604 | if(defined(viewModel.rotateFrame)) { 605 | camera.lookAtTransform(oldTransform); 606 | } 607 | 608 | viewModel.rotateMouseMoveFunction = function (e) 609 | { 610 | var compassRectangle = compassElement.getBoundingClientRect(); 611 | var center = new Cartesian2((compassRectangle.right - compassRectangle.left) / 2.0, (compassRectangle.bottom - compassRectangle.top) / 2.0); 612 | var clickLocation = new Cartesian2(e.clientX - compassRectangle.left, e.clientY - compassRectangle.top); 613 | var vector = Cartesian2.subtract(clickLocation, center, vectorScratch); 614 | var angle = Math.atan2(-vector.y, vector.x); 615 | 616 | var angleDifference = angle - viewModel.rotateInitialCursorAngle; 617 | var newCameraAngle = CesiumMath.zeroToTwoPi(viewModel.rotateInitialCameraAngle - angleDifference); 618 | 619 | var camera = viewModel.terria.scene.camera; 620 | 621 | var oldTransform; 622 | if(defined(viewModel.rotateFrame)) { 623 | oldTransform = Matrix4.clone(camera.transform, oldTransformScratch); 624 | camera.lookAtTransform(viewModel.rotateFrame); 625 | } 626 | 627 | var currentCameraAngle = -camera.heading; 628 | camera.rotateRight(newCameraAngle - currentCameraAngle); 629 | 630 | if(defined(viewModel.rotateFrame)) { 631 | camera.lookAtTransform(oldTransform); 632 | } 633 | 634 | // viewModel.terria.cesium.notifyRepaintRequired(); 635 | }; 636 | 637 | viewModel.rotateMouseUpFunction = function (e) 638 | { 639 | viewModel.isRotating = false; 640 | document.removeEventListener('mousemove', viewModel.rotateMouseMoveFunction, false); 641 | document.removeEventListener('mouseup', viewModel.rotateMouseUpFunction, false); 642 | 643 | viewModel.rotateMouseMoveFunction = undefined; 644 | viewModel.rotateMouseUpFunction = undefined; 645 | }; 646 | 647 | document.addEventListener('mousemove', viewModel.rotateMouseMoveFunction, false); 648 | document.addEventListener('mouseup', viewModel.rotateMouseUpFunction, false); 649 | } 650 | 651 | return NavigationViewModel; 652 | }); 653 | -------------------------------------------------------------------------------- /Source/ViewModels/ResetViewNavigationControl.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Scene/Camera', 5 | 'Cesium/Core/Rectangle', 6 | 'Cesium/Core/Cartographic', 7 | 'ViewModels/NavigationControl', 8 | 'SvgPaths/svgReset' 9 | ], function ( 10 | defined, 11 | Camera, 12 | Rectangle, 13 | Cartographic, 14 | NavigationControl, 15 | svgReset) { 16 | 'use strict'; 17 | 18 | /** 19 | * The model for a zoom in control in the navigation control tool bar 20 | * 21 | * @alias ResetViewNavigationControl 22 | * @constructor 23 | * @abstract 24 | * 25 | * @param {Terria} terria The Terria instance. 26 | */ 27 | var ResetViewNavigationControl = function (terria) { 28 | NavigationControl.apply(this, arguments); 29 | 30 | /** 31 | * Gets or sets the name of the control which is set as the control's title. 32 | * This property is observable. 33 | * @type {String} 34 | */ 35 | this.name = 'Reset View'; 36 | 37 | /** 38 | * Gets or sets the svg icon of the control. This property is observable. 39 | * @type {Object} 40 | */ 41 | this.svgIcon = svgReset; 42 | 43 | /** 44 | * Gets or sets the height of the svg icon. This property is observable. 45 | * @type {Integer} 46 | */ 47 | this.svgHeight = 15; 48 | 49 | /** 50 | * Gets or sets the width of the svg icon. This property is observable. 51 | * @type {Integer} 52 | */ 53 | this.svgWidth = 15; 54 | 55 | /** 56 | * Gets or sets the CSS class of the control. This property is observable. 57 | * @type {String} 58 | */ 59 | this.cssClass = "navigation-control-icon-reset"; 60 | 61 | }; 62 | 63 | ResetViewNavigationControl.prototype = Object.create(NavigationControl.prototype); 64 | 65 | ResetViewNavigationControl.prototype.resetView = function () { 66 | //this.terria.analytics.logEvent('navigation', 'click', 'reset'); 67 | 68 | var scene = this.terria.scene; 69 | 70 | var sscc = scene.screenSpaceCameraController; 71 | if (!sscc.enableInputs) { 72 | return; 73 | } 74 | 75 | this.isActive = true; 76 | 77 | var camera = scene.camera; 78 | 79 | if (defined(this.terria.trackedEntity)) { 80 | // when tracking do not reset to default view but to default view of tracked entity 81 | var trackedEntity = this.terria.trackedEntity; 82 | this.terria.trackedEntity = undefined; 83 | this.terria.trackedEntity = trackedEntity; 84 | } else { 85 | // reset to a default position or view defined in the options 86 | if (this.terria.options.defaultResetView) { 87 | if (this.terria.options.defaultResetView && this.terria.options.defaultResetView instanceof Cartographic) { 88 | camera.flyTo({ 89 | destination: scene.globe.ellipsoid.cartographicToCartesian(this.terria.options.defaultResetView) 90 | }); 91 | } else if (this.terria.options.defaultResetView && this.terria.options.defaultResetView instanceof Rectangle) { 92 | try { 93 | Rectangle.validate(this.terria.options.defaultResetView); 94 | camera.flyTo({ 95 | destination: this.terria.options.defaultResetView 96 | }); 97 | } catch (e) { 98 | console.log("Cesium-navigation/ResetViewNavigationControl: options.defaultResetView Cesium rectangle is invalid!"); 99 | } 100 | } 101 | } 102 | else if (typeof camera.flyHome === "function") { 103 | camera.flyHome(1); 104 | } else { 105 | camera.flyTo({'destination': Camera.DEFAULT_VIEW_RECTANGLE, 'duration': 1}); 106 | } 107 | } 108 | this.isActive = false; 109 | }; 110 | 111 | /** 112 | * When implemented in a derived class, performs an action when the user clicks 113 | * on this control 114 | * @abstract 115 | * @protected 116 | */ 117 | ResetViewNavigationControl.prototype.activate = function () { 118 | this.resetView(); 119 | }; 120 | 121 | return ResetViewNavigationControl; 122 | }); 123 | -------------------------------------------------------------------------------- /Source/ViewModels/UserInterfaceControl.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/defineProperties', 5 | 'Cesium/Core/DeveloperError', 6 | 'KnockoutES5' 7 | ], function ( 8 | defined, 9 | defineProperties, 10 | DeveloperError, 11 | Knockout) { 12 | 'use strict'; 13 | 14 | /** 15 | * The view-model for a control in the user interface 16 | * 17 | * @alias UserInterfaceControl 18 | * @constructor 19 | * @abstract 20 | * 21 | * @param {Terria} terria The Terria instance. 22 | */ 23 | var UserInterfaceControl = function (terria) { 24 | 25 | if (!defined(terria)) { 26 | throw new DeveloperError('terria is required'); 27 | } 28 | 29 | this._terria = terria; 30 | 31 | /** 32 | * Gets or sets the name of the control which is set as the controls title. 33 | * This property is observable. 34 | * @type {String} 35 | */ 36 | this.name = 'Unnamed Control'; 37 | 38 | /** 39 | * Gets or sets the text to be displayed in the UI control. 40 | * This property is observable. 41 | * @type {String} 42 | */ 43 | this.text = undefined; 44 | 45 | /** 46 | * Gets or sets the svg icon of the control. This property is observable. 47 | * @type {Object} 48 | */ 49 | this.svgIcon = undefined; 50 | 51 | /** 52 | * Gets or sets the height of the svg icon. This property is observable. 53 | * @type {Integer} 54 | */ 55 | this.svgHeight = undefined; 56 | 57 | /** 58 | * Gets or sets the width of the svg icon. This property is observable. 59 | * @type {Integer} 60 | */ 61 | this.svgWidth = undefined; 62 | 63 | /** 64 | * Gets or sets the CSS class of the control. This property is observable. 65 | * @type {String} 66 | */ 67 | this.cssClass = undefined; 68 | 69 | /** 70 | * Gets or sets the property describing whether or not the control is in the active state. 71 | * This property is observable. 72 | * @type {Boolean} 73 | */ 74 | this.isActive = false; 75 | 76 | Knockout.track(this, ['name', 'svgIcon', 'svgHeight', 'svgWidth', 'cssClass', 'isActive']); 77 | }; 78 | 79 | defineProperties(UserInterfaceControl.prototype, { 80 | /** 81 | * Gets the Terria instance. 82 | * @memberOf UserInterfaceControl.prototype 83 | * @type {Terria} 84 | */ 85 | terria: { 86 | get: function () { 87 | return this._terria; 88 | } 89 | }, 90 | /** 91 | * Gets a value indicating whether this button has text associated with it. 92 | * @type {Object} 93 | */ 94 | hasText: { 95 | get: function () { 96 | return defined(this.text) && typeof this.text === 'string'; 97 | } 98 | } 99 | 100 | }); 101 | 102 | /** 103 | * When implemented in a derived class, performs an action when the user clicks 104 | * on this control. 105 | * @abstract 106 | * @protected 107 | */ 108 | UserInterfaceControl.prototype.activate = function () { 109 | throw new DeveloperError('activate must be implemented in the derived class.'); 110 | }; 111 | 112 | return UserInterfaceControl; 113 | }); 114 | -------------------------------------------------------------------------------- /Source/ViewModels/ZoomNavigationControl.js: -------------------------------------------------------------------------------- 1 | /*global require*/ 2 | define([ 3 | 'Cesium/Core/defined', 4 | 'Cesium/Core/Ray', 5 | 'Cesium/Core/IntersectionTests', 6 | 'Cesium/Core/Cartesian3', 7 | 'Cesium/Scene/SceneMode', 8 | 'ViewModels/NavigationControl', 9 | 'Core/Utils' 10 | ], function ( 11 | defined, 12 | Ray, 13 | IntersectionTests, 14 | Cartesian3, 15 | SceneMode, 16 | NavigationControl, 17 | Utils) { 18 | 'use strict'; 19 | 20 | /** 21 | * The model for a zoom in control in the navigation control tool bar 22 | * 23 | * @alias ZoomOutNavigationControl 24 | * @constructor 25 | * @abstract 26 | * 27 | * @param {Terria} terria The Terria instance. 28 | * @param {boolean} zoomIn is used for zooming in (true) or out (false) 29 | */ 30 | var ZoomNavigationControl = function (terria, zoomIn) { 31 | NavigationControl.apply(this, arguments); 32 | 33 | /** 34 | * Gets or sets the name of the control which is set as the control's title. 35 | * This property is observable. 36 | * @type {String} 37 | */ 38 | this.name = 'Zoom ' + (zoomIn ? 'In' : 'Out'); 39 | 40 | /** 41 | * Gets or sets the text to be displayed in the nav control. Controls that 42 | * have text do not display the svgIcon. 43 | * This property is observable. 44 | * @type {String} 45 | */ 46 | this.text = zoomIn ? '+' : '-'; 47 | 48 | /** 49 | * Gets or sets the CSS class of the control. This property is observable. 50 | * @type {String} 51 | */ 52 | this.cssClass = 'navigation-control-icon-zoom-' + (zoomIn ? 'in' : 'out'); 53 | 54 | this.relativeAmount = 2; 55 | 56 | if (zoomIn) { 57 | // this ensures that zooming in is the inverse of zooming out and vice versa 58 | // e.g. the camera position remains when zooming in and out 59 | this.relativeAmount = 1 / this.relativeAmount; 60 | } 61 | }; 62 | 63 | ZoomNavigationControl.prototype.relativeAmount = 1; 64 | 65 | ZoomNavigationControl.prototype = Object.create(NavigationControl.prototype); 66 | 67 | /** 68 | * When implemented in a derived class, performs an action when the user clicks 69 | * on this control 70 | * @abstract 71 | * @protected 72 | */ 73 | ZoomNavigationControl.prototype.activate = function () { 74 | this.zoom(this.relativeAmount); 75 | }; 76 | 77 | var cartesian3Scratch = new Cartesian3(); 78 | 79 | ZoomNavigationControl.prototype.zoom = function (relativeAmount) { 80 | // this.terria.analytics.logEvent('navigation', 'click', 'zoomIn'); 81 | 82 | this.isActive = true; 83 | 84 | if (defined(this.terria)) { 85 | var scene = this.terria.scene; 86 | 87 | var sscc = scene.screenSpaceCameraController; 88 | // do not zoom if it is disabled 89 | if (!sscc.enableInputs || !sscc.enableZoom) { 90 | return; 91 | } 92 | // TODO 93 | // if(scene.mode == SceneMode.COLUMBUS_VIEW && !sscc.enableTranslate) { 94 | // return; 95 | // } 96 | 97 | var camera = scene.camera; 98 | var orientation; 99 | 100 | switch (scene.mode) { 101 | case SceneMode.MORPHING: 102 | break; 103 | case SceneMode.SCENE2D: 104 | camera.zoomIn(camera.positionCartographic.height * (1 - this.relativeAmount)); 105 | break; 106 | default: 107 | var focus; 108 | 109 | if(defined(this.terria.trackedEntity)) { 110 | focus = new Cartesian3(); 111 | } else { 112 | focus = Utils.getCameraFocus(this.terria, false); 113 | } 114 | 115 | if (!defined(focus)) { 116 | // Camera direction is not pointing at the globe, so use the ellipsoid horizon point as 117 | // the focal point. 118 | var ray = new Ray(camera.worldToCameraCoordinatesPoint(scene.globe.ellipsoid.cartographicToCartesian(camera.positionCartographic)), camera.directionWC); 119 | focus = IntersectionTests.grazingAltitudeLocation(ray, scene.globe.ellipsoid); 120 | 121 | orientation = { 122 | heading: camera.heading, 123 | pitch: camera.pitch, 124 | roll: camera.roll 125 | }; 126 | } else { 127 | orientation = { 128 | direction: camera.direction, 129 | up: camera.up 130 | }; 131 | } 132 | 133 | var direction = Cartesian3.subtract(camera.position, focus, cartesian3Scratch); 134 | var movementVector = Cartesian3.multiplyByScalar(direction, relativeAmount, direction); 135 | var endPosition = Cartesian3.add(focus, movementVector, focus); 136 | 137 | if (defined(this.terria.trackedEntity) || scene.mode == SceneMode.COLUMBUS_VIEW) { 138 | // sometimes flyTo does not work (jumps to wrong position) so just set the position without any animation 139 | // do not use flyTo when tracking an entity because during animatiuon the position of the entity may change 140 | camera.position = endPosition; 141 | } else { 142 | camera.flyTo({ 143 | destination: endPosition, 144 | orientation: orientation, 145 | duration: 0.5, 146 | convert: false 147 | }); 148 | } 149 | } 150 | } 151 | 152 | // this.terria.notifyRepaintRequired(); 153 | this.isActive = false; 154 | }; 155 | 156 | return ZoomNavigationControl; 157 | }); 158 | -------------------------------------------------------------------------------- /Source/copyrightHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cesium Navigation - https://github.com/alberto-acevedo/cesium-navigation 3 | * 4 | * The plugin is 100% based on open source libraries. The same license that applies to Cesiumjs and terriajs applies also to this plugin. Feel free to use it, modify it, and improve it. 5 | */ -------------------------------------------------------------------------------- /Source/viewerCesiumNavigationMixin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Larcius on 18.02.16. 3 | */ 4 | /*global define*/ 5 | define([ 6 | 'Cesium/Core/defined', 7 | 'Cesium/Core/defineProperties', 8 | 'Cesium/Core/DeveloperError', 9 | 'CesiumNavigation', 10 | 'require-less/less!Styles/cesium-navigation' 11 | ], function( 12 | defined, 13 | defineProperties, 14 | DeveloperError, 15 | CesiumNavigation) { 16 | 'use strict'; 17 | 18 | /** 19 | * A mixin which adds the Compass/Navigation widget to the Viewer widget. 20 | * Rather than being called directly, this function is normally passed as 21 | * a parameter to {@link Viewer#extend}, as shown in the example below. 22 | * @exports viewerCesiumNavigationMixin 23 | * 24 | * @param {Viewer} viewer The viewer instance. 25 | * @param {{}} options The options. 26 | * 27 | * @exception {DeveloperError} viewer is required. 28 | * 29 | * @demo {@link http://localhost:8080/index.html|run local server with examples} 30 | * 31 | * @example 32 | * var viewer = new Cesium.Viewer('cesiumContainer'); 33 | * viewer.extend(viewerCesiumNavigationMixin); 34 | */ 35 | function viewerCesiumNavigationMixin(viewer, options) { 36 | if (!defined(viewer)) { 37 | throw new DeveloperError('viewer is required.'); 38 | } 39 | 40 | var cesiumNavigation = init(viewer, options); 41 | 42 | cesiumNavigation.addOnDestroyListener((function (viewer) { 43 | return function () { 44 | delete viewer.cesiumNavigation; 45 | }; 46 | })(viewer)); 47 | 48 | defineProperties(viewer, { 49 | cesiumNavigation: { 50 | configurable: true, 51 | get: function () { 52 | return viewer.cesiumWidget.cesiumNavigation; 53 | } 54 | } 55 | }); 56 | } 57 | 58 | /** 59 | * 60 | * @param {CesiumWidget} cesiumWidget The cesium widget instance. 61 | * @param {{}} options The options. 62 | */ 63 | viewerCesiumNavigationMixin.mixinWidget = function (cesiumWidget, options) { 64 | return init.apply(undefined, arguments); 65 | }; 66 | 67 | /** 68 | * @param {Viewer|CesiumWidget} viewerCesiumWidget The Viewer or CesiumWidget instance 69 | * @param {{}} options the options 70 | */ 71 | var init = function (viewerCesiumWidget, options) { 72 | var cesiumNavigation = new CesiumNavigation(viewerCesiumWidget, options); 73 | 74 | var cesiumWidget = defined(viewerCesiumWidget.cesiumWidget) ? viewerCesiumWidget.cesiumWidget : viewerCesiumWidget; 75 | 76 | defineProperties(cesiumWidget, { 77 | cesiumNavigation: { 78 | configurable: true, 79 | get: function () { 80 | return cesiumNavigation; 81 | } 82 | } 83 | }); 84 | 85 | cesiumNavigation.addOnDestroyListener((function (cesiumWidget) { 86 | return function () { 87 | delete cesiumWidget.cesiumNavigation; 88 | }; 89 | })(cesiumWidget)); 90 | 91 | return cesiumNavigation; 92 | }; 93 | 94 | return viewerCesiumNavigationMixin; 95 | }); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cesium-navigation", 3 | "dependencies": { 4 | "almond": "^0.3.2", 5 | "hammerjs": "^2.0.8", 6 | "knockout": "^3.4.0", 7 | "knockout-es5": "^0.4.4", 8 | "leaflet": "^0.7.7", 9 | "markdown-it-sanitizer": "^0.4.1", 10 | "markdown-it": "^7.0.0", 11 | "require-css": "^0.1.8", 12 | "require-less": "^0.1.5" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | /*jshint node:true*/ 4 | 5 | var sourceDir = 'Source'; 6 | var buildDir = 'dist', 7 | standaloneSubDir = 'standalone', 8 | amdSubDir = 'amd', 9 | buildName = 'viewerCesiumNavigationMixin'; 10 | var examplesDir = 'Examples'; 11 | 12 | var requirejs = require('requirejs'); 13 | 14 | var path = require('path'); 15 | var fs = require('fs-extra'); 16 | 17 | var nodeMinify = require('node-minify'); 18 | 19 | var minify = function (fileIn, callback) { 20 | var fileOut = path.join(path.dirname(fileIn), path.basename(fileIn, path.extname(fileIn)) + '.min' + path.extname(fileIn)); 21 | 22 | new nodeMinify.minify({ 23 | type: 'uglifyjs', 24 | fileIn: fileIn, 25 | fileOut: fileOut, 26 | callback: function (err) { 27 | if (err) { 28 | console.log(err); 29 | return; 30 | } 31 | 32 | callback(fileOut); 33 | } 34 | }); 35 | }; 36 | 37 | var shimsGlobal = {}, 38 | shimsBuild = {}; 39 | var licenseComments = []; 40 | 41 | var findAllCesiumReferences = function (absPath) { 42 | if (fs.lstatSync(absPath).isDirectory()) { 43 | var files = fs.readdirSync(absPath); 44 | 45 | files.forEach(function (subpath) { 46 | findAllCesiumReferences(path.join(absPath, subpath)); 47 | }); 48 | return; 49 | } else if (!fs.lstatSync(absPath).isFile()) { 50 | return; 51 | } 52 | 53 | var contents = fs.readFileSync(absPath).toString(); 54 | 55 | if (/\.js$/.test(absPath)) { 56 | // Search for Cesium modules and add shim 57 | // modules that pull from the Cesium global 58 | 59 | var cesiumRequireRegex = /['"](Cesium\/\w*\/(\w*))['"]/g; 60 | var match; 61 | while ((match = cesiumRequireRegex.exec(contents)) !== null) { 62 | if (!(match[1] in shimsGlobal)) { 63 | shimsGlobal[match[1]] = 'define(\'' + match[1] + '\', function() { return Cesium[\'' + match[2] + '\']; });'; 64 | } 65 | if (!(match[1] in shimsBuild)) { 66 | shimsBuild[match[1]] = 'define(\'' + match[1] + '\', [\'Cesium\'], function(Cesium) { return Cesium[\'' + match[2] + '\']; });'; 67 | } 68 | } 69 | } else if (/\.glsl$/.test(absPath)) { 70 | var newContents = []; 71 | 72 | contents = contents.replace(/\r\n/gm, '\n'); 73 | 74 | var licenseComments = contents.match(/\/\*\*(?:[^*\/]|\*(?!\/)|\n)*?@license(?:.|\n)*?\*\//gm); 75 | if (licenseComments !== null) { 76 | licenseComments = licenseComments.concat(licenseComments); 77 | } 78 | 79 | // Remove comments. Code ported from 80 | // https://github.com/apache/ant/blob/master/src/main/org/apache/tools/ant/filters/StripJavaComments.java 81 | for (var i = 0; i < contents.length; ++i) { 82 | var c = contents.charAt(i); 83 | if (c === '/') { 84 | c = contents.charAt(++i); 85 | if (c === '/') { 86 | while (c !== '\r' && c !== '\n' && i < contents.length) { 87 | c = contents.charAt(++i); 88 | } 89 | } else if (c === '*') { 90 | while (i < contents.length) { 91 | c = contents.charAt(++i); 92 | if (c === '*') { 93 | c = contents.charAt(++i); 94 | while (c === '*') { 95 | c = contents.charAt(++i); 96 | } 97 | if (c === '/') { 98 | c = contents.charAt(++i); 99 | break; 100 | } 101 | } 102 | } 103 | } else { 104 | --i; 105 | c = '/'; 106 | } 107 | } 108 | newContents.push(c); 109 | } 110 | 111 | newContents = newContents.join(''); 112 | newContents = newContents.replace(/\s+$/gm, '').replace(/^\s+/gm, '').replace(/\n+/gm, '\n'); 113 | } 114 | }; 115 | 116 | findAllCesiumReferences(sourceDir); 117 | 118 | shimsGlobal = Object.keys(shimsGlobal).map(function (key) { 119 | return shimsGlobal[key]; 120 | }).join('\n'); 121 | shimsBuild = Object.keys(shimsBuild).map(function (key) { 122 | return shimsBuild[key]; 123 | }).join('\n'); 124 | 125 | var copyrightHeader = fs.readFileSync(sourceDir + '/copyrightHeader.js').toString(); 126 | 127 | 128 | // <-- build standalone edition 129 | var rjsBasicConfig = { 130 | mainConfigFile: 'mainConfig.js', 131 | wrap: { 132 | start: copyrightHeader + '\n' + 133 | "(function (root, factory) {\n" + 134 | " 'use strict';\n" + 135 | " /*jshint sub:true*/\n\n" + 136 | " if (typeof define === 'function' && define.amd) {\n" + 137 | " if(require.specified('Cesium/Cesium')) {\n" + 138 | " define(['Cesium/Cesium'], factory);\n" + 139 | " } else if(require.specified('Cesium')) {\n" + 140 | " define(['Cesium'], factory);\n" + 141 | " } else {\n" + 142 | " define([], factory);\n" + 143 | " }\n" + 144 | " } else {\n" + 145 | " factory();\n" + 146 | " }\n" + 147 | "}(typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : this, function (C) {\n\n" + 148 | " if (typeof C === 'object' && C !== null) {\n" + 149 | " Cesium = C;\n" + 150 | " }\n\n" + 151 | "// <-- actual code\n\n\n", 152 | end: "\n\n" + 153 | "// actual code -->\n\n" + 154 | " /*global define,require,self,Cesium*/\n" + 155 | " " + licenseComments.join('\n ') + "\n" + 156 | shimsGlobal + "\n" + 157 | " \n" + 158 | " var mixin = require('viewerCesiumNavigationMixin');\n" + 159 | " if (typeof Cesium === 'object' && Cesium !== null) {\n" + 160 | " Cesium['" + buildName + "'] = mixin;\n" + 161 | " }\n\n" + 162 | " return mixin;" + 163 | "}));" 164 | }, 165 | name: 'almond', 166 | include: ['viewerCesiumNavigationMixin'], 167 | logLevel: 0 168 | }; 169 | 170 | var rjsConfig = JSON.parse(JSON.stringify(rjsBasicConfig)); 171 | rjsConfig.optimize = 'none'; 172 | rjsConfig.out = path.join(buildDir, standaloneSubDir, buildName + '.js'); 173 | 174 | requirejs.optimize(rjsConfig, function (buildResponse) { 175 | console.log('Built standalone edition ' + rjsConfig.out + ' successfully.'); 176 | 177 | minify(rjsConfig.out, function (minFile) { 178 | console.log('Generated minified ' + minFile); 179 | }); 180 | }); 181 | // --> 182 | 183 | 184 | // <-- build amd compatible edition 185 | var rjsAMDBasicConfig = { 186 | mainConfigFile: 'mainConfig.js', 187 | name: 'viewerCesiumNavigationMixin', 188 | wrap: { 189 | start: copyrightHeader + '\n\n', 190 | end: '\n\n\n'+ 191 | "/*global define,require*/\n" + 192 | "if(!require.specified('Cesium/Cesium')) {\n" + 193 | " if(typeof Cesium === 'object' && Cesium !== null) {\n" + 194 | shimsGlobal + "\n"+ 195 | " } else {\n" + 196 | shimsBuild + "\n"+ 197 | " }\n" + 198 | "}\n\n" + 199 | 'define([\'viewerCesiumNavigationMixin\'], function(viewerCesiumNavigationMixin) {\n' + 200 | ' return viewerCesiumNavigationMixin;\n' + 201 | '});' 202 | }, 203 | logLevel: 0 204 | }; 205 | 206 | var rjsAMDConfig = JSON.parse(JSON.stringify(rjsAMDBasicConfig)); 207 | rjsAMDConfig.optimize = 'none'; 208 | rjsAMDConfig.out = path.join(buildDir, amdSubDir, buildName + '.js'); 209 | requirejs.optimize(rjsAMDConfig, function (buildResponse) { 210 | console.log('Built AMD compatible edition ' + rjsAMDConfig.out + ' successfully.'); 211 | 212 | minify(rjsAMDConfig.out, function (minFile) { 213 | console.log('Generated minified ' + minFile); 214 | }); 215 | }); 216 | // --> 217 | })(); -------------------------------------------------------------------------------- /mainConfig.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | useStrict: true, 3 | inlineText: true, 4 | // stubModules : ['text'], 5 | baseUrl: 'Source', 6 | skipModuleInsertion: false, 7 | paths: { 8 | 'require-less': '../bower_components/require-less', 9 | 10 | 'almond': '../bower_components/almond/almond', 11 | 12 | 'KnockoutES5': '../bower_components/knockout-es5/dist/knockout-es5.min', 13 | 'knockout': '../bower_components/knockout/dist/knockout', 14 | 'Hammer': '../bower_components/hammerjs/hammer.min', 15 | 'leaflet': '../bower_components/leaflet/dist/leaflet', 16 | 'markdown-it': '../bower_components/markdown-it/dist/markdown-it.min', 17 | 'markdown-it-sanitizer': '../bower_components/markdown-it-sanitizer/dist/markdown-it-sanitizer.min', 18 | 19 | 'Cesium': 'empty:' 20 | // 'text' : 'ThirdParty/requirejs-2.1.22/text' 21 | }, 22 | onBuildWrite: function (moduleName, path, contents) { 23 | // replace all require-less calls to dummy ones because they are only needed for the optimization 24 | return contents.replace(/('|")require-less\/less.*?\1/g, '$1dummy/require-less/less/dummy$1'); 25 | }, 26 | // those are only needed during optimization where an css is generated, so no need to pack them but only the wrapped output 27 | excludeShallow: ['require-less/less', 'require-less/normalize', 'require-less/lessc', 'require-less/less-builder', 'require-less/lessc-server'] 28 | }); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cesium-navigation", 3 | "version": "1.1.3", 4 | "description": "Cesium plugin that adds a compass, navigator, and distance scale to the map", 5 | "main": "viewerCesiumNavigationMixin", 6 | "dependencies": { 7 | "bower": "*" 8 | }, 9 | "devDependencies": { 10 | "fs-extra": "^0.26.5", 11 | "csso": "^1.5.4", 12 | "less": "^1.7.5", 13 | "node-minify": "^1.3.7", 14 | "express": "~4.9.x", 15 | "compression": "1.0.8", 16 | "yargs": "1.2.6", 17 | "requirejs": "^2.1.22", 18 | "cesium": "^1.20.0" 19 | }, 20 | "scripts": { 21 | "test": "echo \"Error: no test specified\" && exit 1", 22 | "postinstall": "bower install" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/alberto-acevedo/cesium-navigation.git" 27 | }, 28 | "keywords": [ 29 | "cesium", 30 | "navigation", 31 | "compass", 32 | "navigator", 33 | "distance", 34 | "scale" 35 | ], 36 | "author": "alberto acevedo", 37 | "license": "Apache-2.0", 38 | "bugs": { 39 | "url": "https://github.com/alberto-acevedo/cesium-navigation/issues" 40 | }, 41 | "homepage": "https://github.com/alberto-acevedo/cesium-navigation#readme" 42 | } 43 | --------------------------------------------------------------------------------