1 /**
  2 * Unit conversion table used by measureTool to convert metric units to others,
  3 * and to select appropriate units based on size of value.
  4 */
  5 madrona.measureConvTable = [
  6     [ 'metric', 
  7         [ // distance measures
  8             ['m', 1, 500 ], // switch to km at 1/2 km
  9             ['km', 0.001, null ]
 10         ],
 11         [ // area measures
 12             ['sq m', 1, 100000 ], // switch to sq km at 1/10 sq km
 13             ['sq km', 0.000001, null ]
 14         ]
 15     ],
 16     [ 'english', 
 17         [ // distance measures
 18             ['ft', 3.2808399, 0.9144 ], // switch to yards at 1 yd
 19             //['yd', 1.0936133, 804.672 ], // switch to miles at 1/2 mile
 20             ['mi', 0.000621371192, null ]
 21         ],
 22         [ // area measures
 23             ['sq ft', 10.7639104, 25899.8811 ], // switch to sq miles at 0.01 sq miles
 24             //['sq yd', 1.19599005, 1294994.06 ], // switch to sq miles at 1/2 sq mile
 25             ['sq mi', 0.000000386102159, null ]
 26         ]
 27     ],
 28     [ 'nautical', 
 29         [ // distance measures
 30             ['naut mi', 0.000539956803, null ]
 31         ],
 32         [ // area measures
 33             ['sq naut mi', 0.00000029155335, null ]
 34         ]
 35     ]
 36 ];
 37 
 38 /**
 39  * Creates a new measurement tool.
 40  *    
 41  * @constructor
 42  */
 43 madrona.measureTool = function() {
 44     this.placemark = null;
 45     this.distTarget = null;
 46     this.areaTarget = null;
 47     this.gex = null;
 48     this.area = 0.0;
 49     this.distance = 0.0;
 50     this.units = 'metric';
 51     this.area_unit = 'sq m';
 52     this.distance_unit = 'm';
 53 };
 54 
 55 madrona.measureTool.prototype.clear = function() 
 56 { 
 57     this.area = 0.0;
 58     this.distance = 0.0;
 59     
 60     if ( this.placemark )
 61     {
 62         if ( this.distTarget )
 63         {
 64             this.gex.edit.endEditLineString( this.placemark.getGeometry() );
 65             document.getElementById(this.distTarget).innerHTML = 'N/A';
 66             this.distTarget = null;
 67         }
 68         
 69         if ( this.areaTarget )
 70         { 
 71             this.gex.edit.endEditLineString( this.placemark.getGeometry().getOuterBoundary() );
 72             document.getElementById(this.areaTarget).innerHTML = 'N/A';
 73             this.areaTarget = null;
 74         }
 75     
 76         this.gex.dom.removeObject( this.placemark );
 77         this.placemark = null;
 78     }
 79 };
 80 
 81 /**
 82  * Set the measure tool's system of measurement for reporting (still uses metric internally)
 83  * @param {String} units The name of the system of measure, i.e. 'english', 'metric', 'nautical'
 84  */
 85 madrona.measureTool.prototype.setUnits = function( units ) {
 86     this.units = units;
 87     this.updateDistance();
 88 };
 89 
 90 /**
 91  * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 92  * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
 93  * @param {String} areaSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
 94  */
 95 madrona.measureTool.prototype.measureArea = function( gex, areaSpanId ) 
 96 {
 97     var self = this;
 98     this.gex = gex;
 99     
100     this.clear();
101 
102     this.areaTarget = areaSpanId;
103 
104     this.placemark = gex.dom.addPlacemark({
105         polygon: [],
106         style: {
107             line: { width: 2, color: '#ff0' },
108             poly: { color: '8000ffff' }
109         }
110     });
111     
112     var drawLineStringOptions = {
113         bounce: false,
114         drawCallback: function() {
115             self.updateDistance();
116         },
117         finishCallback: function() {
118             var editLineStringOptions = {
119                 editCallback: function() {
120                     self.updateDistance();
121                 }
122             }
123             gex.edit.editLineString( self.placemark.getGeometry().getOuterBoundary(), editLineStringOptions );
124         }
125     };
126 
127     gex.edit.drawLineString( this.placemark.getGeometry().getOuterBoundary(), drawLineStringOptions );
128 };
129 
130 /**
131  * Start accepting user input for line-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
132  * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
133  * @param {String} distSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
134  */
135 madrona.measureTool.prototype.measureDistance = function( gex, distSpanId ) 
136 {    
137     var self = this;
138     this.gex = gex;
139     
140     this.clear();
141     
142     this.distTarget = distSpanId;
143 
144     this.placemark = gex.dom.addPlacemark({
145         lineString: [],
146         style: {
147             line: { width: 2, color: '#ff0' }
148         }
149     });
150     
151     var drawLineStringOptions = {
152         bounce: false,
153         drawCallback: function() {
154             self.updateDistance();
155         },
156         finishCallback: function() {
157             var editLineStringOptions = {
158                 editCallback: function() {
159                     self.updateDistance();
160                 }
161             }
162             gex.edit.editLineString( self.placemark.getGeometry(), editLineStringOptions );
163         }
164     };
165 
166     gex.edit.drawLineString( this.placemark.getGeometry(), drawLineStringOptions );
167 };
168 
169 
170 /**
171  * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
172  * @param {String} measure_type_str 'distance' or 'area' (assumes 'distance' if not 'area')
173  * @param {Number} value The metric value to convert to the measureTool's currently set units.
174  * @return [{Number}, {String}] The converted value and its units.
175  */
176 madrona.measureTool.prototype.convertMetricValue = function( measure_type_str, value )
177 {
178     var SYSTEM = 0;
179     var DISTANCE_MAPPINGS = 1;
180     var AREA_MAPPINGS = 2;
181     
182     var UNIT = 0;
183     var CONVERSION_FACTOR = 1;
184     var RANGE_MAX = 2;
185     
186     var measure_type = DISTANCE_MAPPINGS;
187     if ( measure_type_str == 'area' )
188         measure_type = AREA_MAPPINGS;
189     
190     for ( var system_iter = 0; system_iter < madrona.measureConvTable.length; system_iter++ )
191     {
192         if ( madrona.measureConvTable[system_iter][SYSTEM] == this.units )
193         {
194             for ( var unit_iter = 0; unit_iter < madrona.measureConvTable[system_iter][measure_type].length; unit_iter++ )
195             {
196                 if ( value < madrona.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX] 
197                     || madrona.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX] == null )
198                     return [ value * 
199                             madrona.measureConvTable[system_iter][measure_type][unit_iter][CONVERSION_FACTOR],
200                         madrona.measureConvTable[system_iter][measure_type][unit_iter][UNIT] ];
201             }
202         }
203     }
204     
205     return[ 0, 'invalid units set' ];
206 };
207 
208 /**
209  * Calculate the current measurement(s) based on the state of the measureTool's placemark object.
210  * Writes values to target HTML object and stores internally.
211  */
212 madrona.measureTool.prototype.updateDistance = function() 
213 { 
214     if ( this.areaTarget ) 
215     {
216         this.area = new geo.Polygon(this.placemark.getGeometry()).area();
217         converted_val_n_unit = this.convertMetricValue( 'area', this.area );
218         
219         this.area = converted_val_n_unit[0];
220         this.area_unit = converted_val_n_unit[1];
221         
222         document.getElementById(this.areaTarget).innerHTML =
223             this.area.toFixed(2) + ' ' + this.area_unit;
224     }
225       
226     if ( this.distTarget ) 
227     {
228         this.distance = new geo.Path(this.placemark.getGeometry()).distance();
229         converted_val_n_unit = this.convertMetricValue( 'distance', this.distance );
230         
231         this.distance = converted_val_n_unit[0];
232         this.distance_unit = converted_val_n_unit[1];
233         
234         document.getElementById(this.distTarget).innerHTML =
235             this.distance.toFixed(2) + ' ' + this.distance_unit;
236     }
237 };
238