1 if (Garmin == undefined) var Garmin = {};
  2 /** Copyright � 2007 Garmin Ltd. or its subsidiaries.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the 'License')
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *    http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an 'AS IS' BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  * 
 16  * @fileoverview Garmin.MapController Overlays tracks and waypoint data on Google maps.
 17  * 
 18  * @author Jason Holmes jason.holmes.at.garmin.com
 19  * @author Bobby Yang bobby.yang.at.garmin.com
 20  * @version 1.0
 21  */
 22 /**
 23  * Accepts Garmin.Series objects and draws them on a Google Map.
 24  * 
 25  * @class Garmin.MapController
 26  * @constructor 
 27  * @param (String) mapString id of element to place map in
 28  */
 29 Garmin.MapController = function(mapString){}; //just here for jsdoc
 30 Garmin.MapController = Class.create();
 31 Garmin.MapController.prototype = {
 32 
 33     initialize: function(mapString) {
 34         this.mapElement = $(mapString);
 35         this.usePositionMarker = true;
 36         
 37         this.polylines = new Array();
 38         this.markers = new Array();
 39         this.tracks = new Array();
 40         this.markerIndex = 0;
 41 
 42         this.timeToCheck = false;
 43         try {
 44 	        this.map = new GMap2( $(mapString) );
 45 	        this.map.addControl(new GSmallMapControl());
 46 			this.map.addControl(new GMapTypeControl());
 47         	new GKeyboardHandler(this.map);
 48         } catch (e) {
 49         	alert("WARNING: application will not function properly with missing Google script element or invalid Google map key.  Error: "+e);
 50         }
 51         window.onUnload = "GUnload()";
 52     },
 53 
 54     /** Set the center point and zoom level of the map.
 55      * @param (Number) Latitude of the center point
 56      * @param (Number) Longitude of the center point
 57      * @param (Number) Zoom level
 58      */
 59     centerAndScale: function(lat, lon, scale) {
 60     	scale = (scale == null ? 13 : scale);
 61         this.map.setCenter(new GLatLng(lat, lon), scale);
 62     },
 63     
 64     /** Draw track on map.
 65      * @param (Garmin.Track) The track to draw
 66      * @param (String) Color in RGB Hex format, default: "#ff0000"
 67      */    
 68     drawTrack: function(series, color) {
 69         color = (color == null ? "#ff0000" : color);
 70         
 71         // create a smaller version of the whole track
 72         // create 300 points or so ...
 73 	    // Problem is that Google Maps dies when you hit near 500 points, so we have to
 74 	    // ensure that we create fewer than that for the track.
 75         var drawAt = Math.ceil(series.getSamplesLength()/300);
 76         var drawnPoints = new Array();
 77 
 78         try {
 79         	// create up to 300 points
 80 			for(var h = 0; h < series.getSamplesLength(); h += drawAt) {
 81 				drawnPoints.push(this.createNearestValidLocationPoint(series, h, -1));
 82 		    }
 83 		    // create the end point
 84            	drawnPoints.push(this.createNearestValidLocationPoint(series, series.getSamplesLength()-1, -1));
 85         } catch( e ) {
 86             alert("GoogleMapControl.drawTrack: " + e.message);
 87         }	    
 88         
 89         if (drawnPoints.length > 0) {
 90 	        //draw the new smaller version
 91 	        var polyline = new GPolyline(drawnPoints, color, 2, .8)
 92 			try {
 93 				this.centerAndScale(drawnPoints[0].lat(), drawnPoints[0].lng());			
 94 	        	this.map.addOverlay( polyline );
 95 		        this.addStartFinishMarkers(series);
 96 		        this.bounds = this.findAZoomLevel(drawnPoints);
 97 		        this.setOnBounds( this.bounds );
 98 			} catch(e){ alert("GoogleMapControl.drawTrack, IE error on map.addOverlay("+polyline+") err: "+e); }
 99         }
100     },
101 
102 	/**Creates a GLatLng for the sample in the series closest to the index with a valid location (lat and lon).
103 	 * @param series - The series to search through.
104 	 * @param index - The index to start searching from.
105 	 * @param incDirection - The direction to travel for the search.
106 	 * @return A GLatLng of the nearest valid location sample found.
107 	 */
108 	createNearestValidLocationPoint: function(series, index, incDirection) {
109     	var sample = series.findNearestValidLocationSample(index, -1);
110     	if (sample != null) {
111     		var sampleLat = sample.getMeasurement(Garmin.Sample.MEASUREMENT_KEYS.latitude).getValue();
112     		var sampleLon = sample.getMeasurement(Garmin.Sample.MEASUREMENT_KEYS.longitude).getValue();
113     		return new GLatLng(sampleLat, sampleLon);    		
114     	} else {
115 			throw new Error("No valid location point in series.");
116     	}
117 	},
118 
119     /** Draw waypoint on map.
120      * @param (Garmin.Series) series containing a waypoint to add to the map
121      */
122     drawWaypoint: function(series) {
123     	var sample = series.getSample(0);
124         this.centerAndScale(sample.getLatitude(), sample.getLongitude(), 15);    	
125         this.addMarker(sample.getLatitude(), sample.getLongitude());
126     },
127 
128     /** Calculates minimum bounding box for an set of points.
129      * @param (Array) The array of points to find a zoom level for
130      */
131     findAZoomLevel: function(points) {
132         var bounds = new GLatLngBounds(points[0], points[0]);
133         
134         for(var i=1; i<points.length-1; i+=3) {
135             bounds.extend(points[i]);
136         }
137         
138         return bounds;
139     },
140     
141 	/** Check the new dimensions of the map, and determine the bounds of the tracks
142 	 * Then set the map to zoom to that bound level
143    	 * @private
144 	 */
145     sizeAndSetOnBounds: function() {
146         this.map.checkResize();
147         this.setOnBounds( this.bounds );
148     },
149 
150     /** Set the bounding box on the map.
151      * @param (GLatLngBounds) bounding box for the
152      */
153     setOnBounds: function(bounds) {
154         this.map.setCenter( this.bounds.getCenter(), this.map.getBoundsZoomLevel(this.bounds) );
155     },
156     
157     /** Add an icon to a point.
158      * @param {Number} latitude of marker
159      * @param {Number} longitude of marker
160      */
161     addMarker: function(latitude, longitude) {
162     	this.addMarkerWithIcon(latitude, longitude, Garmin.MapIcons.getRedIcon());
163     },
164 
165     /** Adds a marker to the point with the icon specified
166      * @param {Number} latitude of marker
167      * @param {Number} longitude of marker
168      * @param (GIcon) icon to add at the point
169      */
170     addMarkerWithIcon: function(latitude, longitude, icon) {
171         var gMarker = new GMarker(new GLatLng(latitude, longitude), icon);
172         this.markers.push(gMarker);
173         this.map.addOverlay(gMarker);
174     },
175 
176     /** Add start and finish markers to a track
177      * @param (Garmin.Series) The series to add markers to
178      */
179     addStartFinishMarkers: function(series) {
180     	var firstSample = series.getFirstValidLocationSample();
181     	var lastSample = series.getLastValidLocationSample();
182         this.addMarkerWithIcon(firstSample.getLatitude(), firstSample.getLongitude(), Garmin.MapIcons.getGreenIcon());
183         this.addMarkerWithIcon(lastSample.getLatitude(), lastSample.getLongitude(), Garmin.MapIcons.getRedIcon());
184     },
185 
186     /** String representation of map.
187      * @return (String)
188      */
189     toString: function() {
190         return "Google Based Map Controller, managing " + this.tracks.length + " track(s)";
191     }
192 };
193 
194 /** Icons used to mark waypoints and POIs on Google maps.  
195  * 
196  * @class Garmin.MapIcons
197  * @constructor 
198  */
199 Garmin.MapIcons = function(){}; //just here for jsdoc
200 Garmin.MapIcons = {
201     getRedIcon: function() {
202         var icon = new GIcon();
203         icon.image = "http://trail.motionbased.com/trail/site/images/marker_red.png";
204         return Garmin.MapIcons._applyShadowAndStuff(icon);
205     },
206     
207     getGreenIcon: function() {
208         var icon = new GIcon();
209         icon.image = "http://trail.motionbased.com/trail/site/images/marker_green.png";
210         return Garmin.MapIcons._applyShadowAndStuff(icon);
211     },
212     
213     getBaseIcon: function() {
214     	var baseIcon = new GIcon();
215 		baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
216 		baseIcon.iconSize = new GSize(20, 34);
217 		baseIcon.shadowSize = new GSize(37, 34);
218 		baseIcon.iconAnchor = new GPoint(9, 34);
219 		baseIcon.infoWindowAnchor = new GPoint(9, 2);
220 		baseIcon.infoShadowAnchor = new GPoint(18, 25);
221         return baseIcon;
222     },
223     
224     _applyShadowAndStuff: function(icon) {
225         icon.iconSize = new GSize(12, 20);
226         icon.shadow = "http://trail.motionbased.com/trail/site/images/marker_shadow.png";
227         icon.shadowSize = new GSize(22, 20);
228         icon.iconAnchor = new GPoint(6, 20);
229         icon.infoWindowAnchor = new GPoint(5, 1);
230         return icon;
231     }
232 }