Advertisement
Abdulyar

GoogleMapsV3.js

Jul 4th, 2012
2,321
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ----------------------------------------------------------------------------
  2. // Copyright 2007-2012, GeoTelematic Solutions, Inc.
  3. // All rights reserved
  4. // ----------------------------------------------------------------------------
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. // ----------------------------------------------------------------------------
  19. // Required funtions defined by this module:
  20. //   new JSMap(String mapID)
  21. //   JSClearLayers()
  22. //   JSSetCenter(JSMapPoint center [, int zoom])
  23. //   JSDrawPushpins(JSMapPushpin pushPin[], int recenterMode, int replay)
  24. //   JSDrawPOI(JSMapPushpin pushPin[])
  25. //   JSDrawRoute(JSMapPoint points[], String color)
  26. //   JSDrawShape(String type, double radius, JSMapPoint points[], String color, boolean zoomTo)
  27. //   JSDrawGeozone(int type, double radius, JSMapPoint points[], String color, int primaryIndex)
  28. //   JSShowPushpin(JSMapPushpin pushPin, boolean center)
  29. //   JSPauseReplay(int replay)
  30. //   JSUnload()
  31. // ----------------------------------------------------------------------------
  32. // Change History:
  33. //  2008/07/08  Martin D. Flynn
  34. //     -Initial release
  35. //  2008/08/08  Martin D. Flynn
  36. //     -Added support for Geozones
  37. //  2008/09/01  Martin D. Flynn
  38. //     -Added replay and geozone recenter support
  39. //  2009/08/23  Martin D. Flynn
  40. //     -Added color argument to JSDrawRoute
  41. //     -Added option for drawing multiple points per device on fleet map
  42. //  2009/09/23  Martin D. Flynn
  43. //     -Added support for displaying multipoint geozones (single point at a time)
  44. //  2009/11/01  Juan Carlos Argueta
  45. //     -Added route-arrows (see ROUTE_LINE_ARROWS)
  46. //  2010/11/29  Martin D. Flynn
  47. //     -Removed pushpins from non-editable polygon geozones
  48. //  2011/01/28  Martin D. Flynn
  49. //     -Apply minimum zoom (point range) when updating a single point on the map.
  50. //     -Added support for setting map type from "map.view" property.
  51. //  2011/07/15  Martin D. Flynn
  52. //     -Cloned from "GoogleMapV2.js", updated for Google Maps API v3
  53. //     -Improved Geozone editing: point-radius, polygon, corridor.
  54. //  2012/??/??  Martin D. Flynn
  55. //     -Remove shapes (ie. Geozones) in JSClearLayers
  56. // ----------------------------------------------------------------------------
  57.  
  58. var DRAG_NONE               = 0;
  59. var DRAG_RULER              = 1;
  60. var DRAG_GEOZONE_CENTER     = 2;
  61. var DRAG_GEOZONE_RADIUS     = 3;
  62.  
  63. // ----------------------------------------------------------------------------
  64. // ----------------------------------------------------------------------------
  65. // google.maps.Marker
  66.  
  67. /* set HTML content for pushpin popup */
  68. google.maps.Marker.prototype.setInfoWindowHTML = function(html)
  69. {
  70.     if (html) {
  71.         this.infoWindow = new google.maps.InfoWindow({
  72.             content: html
  73.         });
  74.     } else {
  75.         this.infoWindow = null;
  76.     }
  77.     this.infoWindowOpen = false;
  78. };
  79.  
  80. /* open pushpin popup */
  81. google.maps.Marker.prototype.openPushpinPopup = function()
  82. {
  83.     if (this.infoWindow && !this.infoWindowOpen && this.getMap()) {
  84.         this.infoWindow.open(this.getMap(), this);
  85.         this.infoWindowOpen = true;
  86.     }
  87. };
  88.  
  89. /* close pushpin popup */
  90. google.maps.Marker.prototype.closePushpinPopup = function()
  91. {
  92.     if (this.infoWindow && this.infoWindowOpen && this.getMap()) {
  93.         this.infoWindow.close();
  94.         this.infoWindowOpen = false;
  95.     }
  96. };
  97.  
  98. // ----------------------------------------------------------------------------
  99. // ----------------------------------------------------------------------------
  100. // google.maps.Map
  101.  
  102. /* init pushpin vars */
  103. google.maps.Map.prototype.initGTS = function()
  104. {
  105.  
  106.     /* pushpin markers */
  107.     this.pushpinMarkers = [];
  108.     this.routeMarkers   = [];
  109.     this.activePopup    = null;
  110.  
  111.     /* MapTypeId */
  112.     try {
  113.         if ((DEFAULT_VIEW == "aerial") || (DEFAULT_VIEW == "satellite")) {
  114.             this.setMapTypeId(google.maps.MapTypeId.SATELLITE);
  115.         } else
  116.         if (DEFAULT_VIEW == "hybrid") {
  117.             this.setMapTypeId(google.maps.MapTypeId.HYBRID);
  118.         } else
  119.         if (DEFAULT_VIEW == "terrain") {
  120.             this.setMapTypeId(google.maps.MapTypeId.TERRAIN);
  121.         } else {
  122.             this.setMapTypeId(google.maps.MapTypeId.ROADMAP); // "normal", "road"
  123.         }
  124.     } catch (e) {
  125.         // ignore (this catch may not be necessary here)
  126.     }
  127.  
  128.     /* key up/down listeners */
  129.     var self = this;
  130.     this.rulerDragEnabled = false;
  131.     this.keyDownListener = google.maps.event.addDomListener(document, 'keydown', function(event) {
  132.         event = event || window.event;
  133.         self.rulerDragEnabled = event.ctrlKey? true : false;
  134.         if (!self.rulerDragEnabled) {
  135.             switch (event.keyCode) {
  136.                 case 16: // shiftKey
  137.                     break;
  138.                 case 17: // ctrlKey
  139.                     self.rulerDragEnabled = true;
  140.                     break;
  141.                 case 18: // altKey
  142.                     break;
  143.             }
  144.         }
  145.     });
  146.     this.keyUpListener = google.maps.event.addDomListener(document, 'keyup', function(event) {
  147.         event = event || window.event;
  148.         self.rulerDragEnabled = false;
  149.     });
  150.  
  151. };
  152.  
  153. // ----
  154.  
  155. /* display the specified pushpin popup */
  156. google.maps.Map.prototype.showPushpinPopup = function(pp/*JSMapPushpin*/)
  157. {
  158.     this.hidePushpinPopups();
  159.     if (pp && pp.marker) {
  160.         pp.marker.openPushpinPopup();
  161.         this.activePopup = pp;
  162.         jsmHighlightDetailRow(pp.rcdNdx, true);
  163.     }
  164. };
  165.  
  166. /* hide all visible popups */
  167. google.maps.Map.prototype.hidePushpinPopups = function()
  168. {
  169.     for (var i = 0; i < this.pushpinMarkers.length; i++) {
  170.         this.pushpinMarkers[i].marker.closePushpinPopup();
  171.     }
  172.     if (this.activePopup) {
  173.         jsmHighlightDetailRow(this.activePopup.rcdNdx, false);
  174.         this.activePopup = null;
  175.     }
  176. };
  177.  
  178. /* add pushpin to map */
  179. google.maps.Map.prototype.addPushpinMarker = function(pp/*JSMapPushpin*/)
  180. {
  181.  
  182.     /* accuracy radius */
  183.     if (pp.accRadius) {
  184.         pp.accRadius.setMap(this);
  185.     }
  186.  
  187.     /* pushpins */
  188.     if (pp.bgMarker) {
  189.         pp.bgMarker.setMap(this);
  190.     }
  191.     pp.marker.setMap(this);
  192.  
  193.     /* save */
  194.     this.pushpinMarkers.push(pp);
  195.  
  196.     /* info-balloon 'click' listener */
  197.     var self = this;
  198.     google.maps.event.addListener(pp.marker, 'click', function(event) {
  199.         self.showPushpinPopup(pp);
  200.     });
  201.    
  202. };
  203.  
  204. /* remove all pushpins from map */
  205. google.maps.Map.prototype.removePushpinMarkers = function()
  206. {
  207.     for (var i = 0; i < this.pushpinMarkers.length; i++) {
  208.         var pp = this.pushpinMarkers[i]; // JSMapPushpin
  209.         if (pp.accRadius) {
  210.             pp.accRadius.setMap(null);
  211.         }
  212.         if (pp.bgMarker) {
  213.             pp.bgMarker.setMap(null);
  214.         }
  215.         pp.marker.setMap(null);
  216.     }
  217.     this.pushpinMarkers = [];
  218. }
  219.  
  220. // ----
  221.  
  222. /* add route marker */
  223. google.maps.Map.prototype.addRouteMarker = function(marker)
  224. {
  225.     this.routeMarkers.push(marker);
  226. };
  227.  
  228. /* remove route markers */
  229. google.maps.Map.prototype.removeRouteMarkers = function()
  230. {
  231.     for (var i = 0; i < this.routeMarkers.length; i++) {
  232.         this.routeMarkers[i].setMap(null);
  233.     }
  234.     this.routeMarkers = [];
  235. }
  236.  
  237. // ----------------------------------------------------------------------------
  238. // ----------------------------------------------------------------------------
  239.  
  240. /**
  241. *** Create GLatLng(...)
  242. **/
  243. function jsNewGLatLng(lat, lon)
  244. {
  245.     return new google.maps.LatLng(lat, lon);
  246. };
  247.  
  248. /**
  249. *** Create GLatLngBounds()
  250. **/
  251. function jsNewGLatLngBounds()
  252. {
  253.     return new google.maps.LatLngBounds();
  254. };
  255.  
  256. /**
  257. *** Create GSize()
  258. **/
  259. function jsNewGSize(W, H)
  260. {
  261.     return new google.maps.Size(W, H);
  262. };
  263.  
  264. /**
  265. *** Create GPoint()
  266. **/
  267. function jsNewGPoint(X, Y)
  268. {
  269.     return new google.maps.Point(X, Y);
  270. };
  271.  
  272. /**
  273. *** Create GPolyline()
  274. **/
  275. function jsNewGPolyline(latLonList,
  276.     borderColor, borderWidth, borderOpacity)
  277. {
  278.     return new google.maps.Polyline({
  279.         path:          latLonList,
  280.         strokeColor:   borderColor,
  281.         strokeWeight:  borderWidth,
  282.         strokeOpacity: borderOpacity
  283.     });
  284. };
  285.  
  286. /**
  287. *** Create GPolygon()
  288. **/
  289. function jsNewGPolygon(latLonList/*GLatLng[]*/,
  290.     borderColor, borderWidth, borderOpacity,
  291.     fillColor, fillOpacity)
  292. {
  293.     return new google.maps.Polygon({
  294.         paths:         latLonList,
  295.         strokeColor:   borderColor,
  296.         strokeWeight:  borderWidth,
  297.         strokeOpacity: borderOpacity,
  298.         fillColor:     fillColor,
  299.         fillOpacity:   fillOpacity
  300.     });
  301. };
  302.  
  303. /**
  304. *** Create GCircle()
  305. **/
  306. function jsNewGCircle(
  307.     latLonCenter/*GLatLng*/, radiusM,
  308.     borderColor, borderWidth, borderOpacity,
  309.     fillColor, fillOpacity)
  310. {
  311.     try {
  312.         var cp = {
  313.             center:        latLonCenter,
  314.             radius:        radiusM,
  315.             strokeColor:   borderColor,
  316.             strokeWeight:  borderWidth,
  317.             strokeOpacity: borderOpacity,
  318.             fillColor:     fillColor,
  319.             fillOpacity:   fillOpacity,
  320.             clickable:     true
  321.         };
  322.         //alert("jsNewGCircle: center="+cp.center+", radius="+cp.radius+", strokeColor="+cp.strokeColor);
  323.         return new google.maps.Circle(cp);
  324.     } catch (e) {
  325.         alert("Error creating circle: " + e);
  326.         return null;
  327.     }
  328. };
  329.  
  330. /**
  331. *** Create Pushpin Marker()
  332. **/
  333. function jsNewImageMarker(
  334.     point/*GLatLng*/,
  335.     image, iconSize, iconAnchor,
  336.     shadow, shadowSize,
  337.     infoWindowAnchor,
  338.     draggable)
  339. {
  340.     var iconImage = new google.maps.MarkerImage(
  341.         image,                          // imageURL
  342.         iconSize,                       // iconSize
  343.         new google.maps.Point(0,0),     // iconOrigin
  344.         iconAnchor);                    // iconAnchor
  345.     var marker = new google.maps.Marker({
  346.         clickable: true,
  347.         dragable: draggable,
  348.         raiseOnDrag: false,
  349.         flat: false,
  350.         optimized: false,
  351.         position: point,
  352.         icon: iconImage,
  353.       //shadow: shadowImage,
  354.       //shape: shape,
  355.         title: "" //,
  356.       //zIndex: beach[3]
  357.         });
  358.     if (shadow && shadowSize) {
  359.         var shadowImage = new google.maps.MarkerImage(
  360.             shadow,                         // shadowURL
  361.             shadowSize,                     // shadowSize
  362.             new google.maps.Point(0,0),     // shadowOrigin
  363.             iconAnchor);                    // shadowAnchor
  364.         marker.setShadow(shadowImage);
  365.     }
  366.     return marker;
  367. };
  368.  
  369. // ----------------------------------------------------------------------------
  370.  
  371. /**
  372. *** JSMap constructor
  373. **/
  374. function JSMap(element)
  375. {
  376.     //if (navigator.platform.match(/linux|bsd/i)) { _mSvgEnabled = _mSvgForced = true; }
  377.  
  378.     /* map */
  379.     this.gmapGoogleMap = new google.maps.Map(element, {
  380.         //zoom: 8,
  381.         //center: jsNewGLatLng(<lat>, <lon>),
  382.         //mapTypeId: google.maps.MapTypeId.ROADMAP,  // SATELLITE, HYBRID, TERRAIN
  383.         draggable: true,
  384.         draggableCursor: "auto",
  385.         draggingCursor: "move" ,
  386.         disableDoubleClickZoom: true,
  387.         overviewMapControl: true
  388.     });
  389.     this.gmapGoogleMap.initGTS();
  390.  
  391.     /* general variable definition */
  392.     this.jsmapBounds = null;
  393.     var self = this;
  394.    
  395.     /* set crosshair cursor */
  396.     element.style.cursor = "crosshair"; // may not be effective
  397.  
  398.     /* replay vars */
  399.     this.replayTimer = null;
  400.     this.replayIndex = 0;
  401.     this.replayInterval = (REPLAY_INTERVAL < 100)? 100 : REPLAY_INTERVAL;
  402.     this.replayInProgress = false;
  403.     this.replayPushpins = [];
  404.  
  405.     /* zone vars */
  406.     this.geozoneIndex  = -1;
  407.  
  408.     /* drawn shapes */
  409.     this.drawShapes = [];
  410.  
  411.     /* 'mousemove' to update latitude/longitude */
  412.     var locDisp = document.getElementById(ID_LAT_LON_DISPLAY);
  413.     if (locDisp != null) {
  414.         google.maps.event.addListener(this.gmapGoogleMap, "mousemove", function(event) {
  415.             if (event && event.latLng) {
  416.                 var point = event.latLng;
  417.                 jsmSetLatLonDisplay(point.lat(), point.lng());
  418.                 element.style.cursor = "crosshair";
  419.             }
  420.         });
  421.         jsmSetLatLonDisplay(0,0);
  422.     }
  423.  
  424.     /* "click" */
  425.     google.maps.event.addListener(this.gmapGoogleMap, "click", function(event) {
  426.         if (!event || !event.latLng) { return; }
  427.         var point = event.latLng;
  428.         var LL = new JSMapPoint(point.lat(), point.lng());
  429.         if (jsvGeozoneMode && jsvZoneEditable) {
  430.             // recenter geozone
  431.             if (jsvZoneType == ZONE_POINT_RADIUS) {
  432.                 var radiusM = zoneMapGetRadius(false);
  433.                 var foundZoneNdx = -1;
  434.                 for (var x = 0; x < jsvZoneList.length; x++) {
  435.                     var pt = jsvZoneList[x];
  436.                     if (geoIsValid(pt.lat,pt.lon)) {
  437.                         if (geoDistanceMeters(pt.lat, pt.lon, LL.lat, LL.lon) <= radiusM) {
  438.                             foundZoneNdx = x;
  439.                             break;
  440.                         }
  441.                     }
  442.                 }
  443.                 if (foundZoneNdx >= 0) { // inside an existing zone
  444.                     // skip
  445.                 } else {
  446.                     jsmSetPointZoneValue(LL.lat, LL.lon, radiusM);
  447.                     mapProviderParseZones(jsvZoneList);
  448.                 }
  449.             } else
  450.             if (jsvZoneType == ZONE_POLYGON) {
  451.                 var count = 0; // count number of valid points
  452.                 for (var x = 0; x < jsvZoneList.length; x++) {
  453.                     var pt = jsvZoneList[x];
  454.                     if (geoIsValid(pt.lat,pt.lon)) { count++; }
  455.                 }
  456.                 if (count == 0) {
  457.                     var radiusM = 450;  // no valid points - create default polygon
  458.                     var crLat   = geoRadians(point.lat());  // radians
  459.                     var crLon   = geoRadians(point.lng());  // radians
  460.                     for (x = 0; x < jsvZoneList.length; x++) {
  461.                         var deg   = x * (360.0 / jsvZoneList.length);
  462.                         var radM  = radiusM / EARTH_RADIUS_METERS;
  463.                         if ((deg == 0.0) || ((deg > 170.0) && (deg<  190.0))) { radM *= 0.8; }
  464.                         var xrad  = geoRadians(deg); // radians
  465.                         var rrLat = Math.asin(Math.sin(crLat) * Math.cos(radM) + Math.cos(crLat) * Math.sin(radM) * Math.cos(xrad));
  466.                         var rrLon = crLon + Math.atan2(Math.sin(xrad) * Math.sin(radM) * Math.cos(crLat), Math.cos(radM)-Math.sin(crLat) * Math.sin(rrLat));
  467.                         _jsmSetPointZoneValue(x, geoDegrees(rrLat), geoDegrees(rrLon), 0);
  468.                     }
  469.                 } else
  470.                 if (true) {
  471.                     // just move the selected location
  472.                     jsmSetPointZoneValue(LL.lat, LL.lon, 0);
  473.                 } else {
  474.                     // move valid points to new location
  475.                     var bounds = jsNewGLatLngBounds();
  476.                     for (var x = 0; x < jsvZoneList.length; x++) {
  477.                         var pt = jsvZoneList[x];
  478.                         if (geoIsValid(pt.lat,pt.lon)) {
  479.                             bounds.extend(jsNewGLatLng(pt.lat, pt.lon));
  480.                         }
  481.                     }
  482.                     var center   = bounds.getCenter(); // GLatLng
  483.                     var deltaLat = point.lat() - center.lat();
  484.                     var deltaLon = point.lng() - center.lng();
  485.                     for (var x = 0; x < jsvZoneList.length; x++) {
  486.                         var pt = jsvZoneList[x];
  487.                         if (geoIsValid(pt.lat,pt.lon)) {
  488.                             _jsmSetPointZoneValue(x, (pt.lat + deltaLat), (pt.lon + deltaLon), 0);
  489.                         }
  490.                     }
  491.                 }
  492.                 // parse points
  493.                 mapProviderParseZones(jsvZoneList);
  494.             } else
  495.             if (jsvZoneType == ZONE_SWEPT_POINT_RADIUS) {
  496.                 var radiusM = zoneMapGetRadius(false);
  497.                 jsmSetPointZoneValue(LL.lat, LL.lon, radiusM);
  498.                 mapProviderParseZones(jsvZoneList);
  499.             }
  500.         }
  501.     });
  502.  
  503.     /* right-click-drag to display 'ruler' */
  504.     this.dragRulerLatLon = null;
  505.     this.rulerOverlay = null;
  506.     var distDisp = document.getElementById(ID_DISTANCE_DISPLAY);
  507.     if (distDisp != null) {
  508.         /* */
  509.         google.maps.event.addListener(this.gmapGoogleMap, 'mousedown', function(event) { // "mousedown", "dragstart", "dragend"
  510.             // "event.ctrlKey" is not available
  511.             if (event && event.latLng && self.gmapGoogleMap.rulerDragEnabled) {
  512.                 // clear existing
  513.                 if (self.rulerOverlay != null) { // existing ruler
  514.                     self.rulerOverlay.setMap(null);
  515.                     self.rulerOverlay = null;
  516.                 }
  517.                 self.dragRulerLatLon = null;
  518.                 // disable map dragging
  519.                 //self.gmapGoogleMap.setOptions({ draggable: false });
  520.                 // enable ruler
  521.                 var LL = event.latLng; // GLatLng
  522.                 var CC = new JSMapPoint(LL.lat(),LL.lng());
  523.                 var ruler = [];
  524.                 ruler.push(jsNewGLatLng(CC.lat,CC.lon));
  525.                 ruler.push(jsNewGLatLng(LL.lat(),LL.lng())); // copy
  526.                 self.rulerOverlay = jsNewGPolyline(ruler, '#FF6422', 2, 1.0); // google.maps.Polyline
  527.                 self.rulerOverlay.setMap(self.gmapGoogleMap);
  528.                 google.maps.event.addListenerOnce(self.rulerOverlay, 'mouseup', function(event) {
  529.                     // clear existing
  530.                     if (self.rulerOverlay != null) { // existing ruler
  531.                         self.rulerOverlay.setMap(null);
  532.                         self.rulerOverlay = null;
  533.                     }
  534.                     self.dragRulerLatLon = null;
  535.                     // re-enable dragging
  536.                     //self.gmapGoogleMap.setOptions({ draggable: true });
  537.                 });
  538.                 self.dragRulerLatLon = CC;
  539.                 jsmSetDistanceDisplay(0);
  540.             }
  541.         });
  542.         google.maps.event.addListener(this.gmapGoogleMap, 'mousemove', function(event) {
  543.             if (event && event.latLng && self.dragRulerLatLon && self.rulerOverlay) {
  544.                 var CC = self.dragRulerLatLon;  // JSMapPoint
  545.                 var LL = event.latLng;          // GLatLng
  546.                 var ruler = [];
  547.                 ruler.push(jsNewGLatLng(CC.lat,CC.lon));
  548.                 ruler.push(jsNewGLatLng(LL.lat(),LL.lng())); // copy
  549.                 self.rulerOverlay.setPath(ruler);
  550.                 jsmSetDistanceDisplay(geoDistanceMeters(CC.lat, CC.lon, LL.lat(), LL.lng()));
  551.             }
  552.         });
  553.         /* */
  554.         /*
  555.         google.maps.event.addListener(this.gmapGoogleMap, 'mouseup', function(event) {
  556.             // is never called
  557.             alert("MouseUp ...");
  558.             if (self.dragRulerLatLon) {
  559.                 if (self.rulerOverlay != null) { // existing ruler
  560.                     self.rulerOverlay.setMap(null);
  561.                     self.rulerOverlay = null;
  562.                 }
  563.                 self.dragRulerLatLon = null;
  564.             }
  565.         });
  566.         */
  567.     }
  568.  
  569. };
  570.  
  571. // ----------------------------------------------------------------------------
  572.  
  573. /**
  574. *** Unload/release resources
  575. **/
  576. JSMap.prototype.JSUnload = function()
  577. {
  578.     //GUnload();
  579. };
  580.  
  581. // ----------------------------------------------------------------------------
  582.  
  583. /**
  584. *** Clear all pushpins and drawn lines
  585. **/
  586. JSMap.prototype.JSClearLayers = function()
  587. {
  588.  
  589.     /* clear all pushpins and route */
  590.     this.gmapGoogleMap.removePushpinMarkers();
  591.     this.gmapGoogleMap.removeRouteMarkers();
  592.  
  593.     /* remove shapes */
  594.     this._removeShapes();
  595.  
  596.     /* reset state */
  597.     this._clearReplay();
  598.     this.jsmapBounds = jsNewGLatLngBounds();
  599.  
  600.     /* redraw shapes? */
  601.     /* (not sure why shapes are being redrawn here - redrawing these shapes causes
  602.     ** multiple geozones to be displayed on top of each other (darkening the image)
  603.     if (this.drawShapes) {
  604.         for (var s = 0; s < this.drawShapes.length; s++) {
  605.             var shape = this.drawShapes[s];
  606.             shape.setMap(this.gmapGoogleMap);
  607.         }
  608.     }
  609.     */
  610.  
  611. };
  612.  
  613. // ----------------------------------------------------------------------------
  614.  
  615. /**
  616. *** Pause/Resume replay
  617. **/
  618. JSMap.prototype.JSPauseReplay = function(replay)
  619. {
  620.     /* stop replay? */
  621.     if (!replay || (replay <= 0) || !this.replayInProgress) {
  622.         // stopping replay
  623.         this._clearReplay();
  624.         return REPLAY_STOPPED;
  625.     } else {
  626.         // replay currently in progress
  627.         if (this.replayTimer == null) {
  628.             // replay is "PAUSED" ... resuming replay
  629.             this.gmapGoogleMap.hidePushpinPopups();
  630.             jsmHighlightDetailRow(-1, false);
  631.             this._startReplayTimer(replay, 100);
  632.             return REPLAY_RUNNING;
  633.         } else {
  634.             // replaying "RUNNING" ... pausing replay
  635.             this._stopReplayTimer();
  636.             return REPLAY_PAUSED;
  637.         }
  638.     }
  639. };
  640.  
  641. /**
  642. *** Start the replay timer
  643. **/
  644. JSMap.prototype._startReplayTimer = function(replay, interval)
  645. {
  646.     if (this.replayInProgress) {
  647.         this.replayTimer = setTimeout("jsmap._replayPushpins("+replay+")", interval);
  648.     }
  649.     jsmSetReplayState(REPLAY_RUNNING);
  650. };
  651.  
  652. /**
  653. *** Stop the current replay timer
  654. **/
  655. JSMap.prototype._stopReplayTimer = function()
  656. {
  657.     if (this.replayTimer != null) {
  658.         clearTimeout(this.replayTimer);
  659.         this.replayTimer = null;
  660.     }
  661.     jsmSetReplayState(this.replayInProgress? REPLAY_PAUSED : REPLAY_STOPPED);
  662. };
  663.  
  664. /**
  665. *** Clear any current replay in process
  666. **/
  667. JSMap.prototype._clearReplay = function()
  668. {
  669.     this.replayPushpins = [];
  670.     this.replayInProgress = false;
  671.     this._stopReplayTimer();
  672.     this.replayIndex = 0;
  673.     jsmHighlightDetailRow(-1, false);
  674. };
  675.  
  676. /**
  677. *** Gets the current replay state
  678. **/
  679. JSMap.prototype._getReplayState = function()
  680. {
  681.     if (this.replayInProgress) {
  682.         if (this.replayTimer == null) {
  683.             return REPLAY_PAUSED;
  684.         } else {
  685.             return REPLAY_RUNNING;
  686.         }
  687.     } else {
  688.         return REPLAY_STOPPED;
  689.     }
  690. };
  691.  
  692. // ----------------------------------------------------------------------------
  693.  
  694. /**
  695. *** Sets the center of the map
  696. **/
  697. JSMap.prototype.JSSetCenter = function(center/*JSMapPoint*/, zoom)
  698. {
  699.     var gpt = jsNewGLatLng(center.lat, center.lon);
  700.     if (!zoom || (zoom == 0)) {
  701.         this.gmapGoogleMap.setCenter(gpt);
  702.     } else
  703.     if (zoom > 0) {
  704.         this.gmapGoogleMap.setCenter(gpt);
  705.         this.gmapGoogleMap.setZoom(zoom);
  706.     } else {
  707.         this.gmapGoogleMap.setCenter(gpt);
  708.         var gb = jsNewGLatLngBounds();
  709.         gb.extend(gpt);
  710.         this.gmapGoogleMap.fitBounds(gb);
  711.         //var zoom = this.gmapGoogleMap.getZoom();
  712.     }
  713. };
  714.  
  715. /**
  716. *** Sets the center of the map
  717. **/
  718. JSMap.prototype.JSSetCenter_G = function(center/*GLatLng*/, zoom)
  719. {
  720.     if (zoom) {
  721.         this.gmapGoogleMap.setCenter(center);
  722.         this.gmapGoogleMap.setZoom(zoom);
  723.     } else {
  724.         this.gmapGoogleMap.setCenter(center);
  725.     }
  726. };
  727.  
  728. // ----------------------------------------------------------------------------
  729.  
  730. /**
  731. *** Draw the specified pushpins on the map
  732. *** @param pushPins  An array of JSMapPushpin objects
  733. *** @param recenter  True to cause the map to re-center on the drawn pushpins
  734. **/
  735. JSMap.prototype.JSDrawPushpins = function(pushPins, recenterMode, replay)
  736. {
  737.  
  738.     /* clear replay (may be redundant, but repeated just to make sure) */
  739.     this._clearReplay();
  740.  
  741.     /* make sure we have a bounding box instance */
  742.     if (!this.jsmapBounds || (this.jsmapBounds == null)) {
  743.         this.jsmapBounds = jsNewGLatLngBounds();
  744.     } else {
  745.         //alert("jsmapBounds already defined ...");
  746.     }
  747.  
  748.     /* drawn pushpins */
  749.     var drawPushpins = [];
  750.  
  751.     /* recenter map on points */
  752.     var pointCount = 0;
  753.     if ((pushPins != null) && (pushPins.length > 0)) {
  754.         // extend bounding box around displayed pushpins
  755.         for (var i = 0; i < pushPins.length; i++) {
  756.             var pp = pushPins[i]; // JSMapPushpin
  757.             if (pp.show && geoIsValid(pp.lat,pp.lon)) {
  758.                 pointCount++;
  759.                 this.jsmapBounds.extend(jsNewGLatLng(pp.lat, pp.lon));
  760.                 drawPushpins.push(pp); // JSMapPushpin
  761.             }
  762.         }
  763.         // MinimumMapBounds: make sure points span a minimum distance top to bottom
  764.         var rangeRadiusM = 400; // TODO: should make this a configurable options
  765.         var cenPt = this.jsmapBounds.getCenter();
  766.         var topPt = geoRadiusPoint(cenPt.lat(), cenPt.lng(), rangeRadiusM,   0.0); // top
  767.         this.jsmapBounds.extend(jsNewGLatLng(topPt.lat,topPt.lon));
  768.         var botPt = geoRadiusPoint(cenPt.lat(), cenPt.lng(), rangeRadiusM, 180.0); // bottom
  769.         this.jsmapBounds.extend(jsNewGLatLng(botPt.lat,botPt.lon));
  770.     }
  771.     if (recenterMode > 0) {
  772.         try {
  773.             if (pointCount <= 0) {
  774.                 this.JSSetCenter(DEFAULT_CENTER, DEFAULT_ZOOM);
  775.             } else
  776.             if (recenterMode == RECENTER_LAST) { // center on last point
  777.                 var pp = drawPushpins[drawPushpins.length - 1];
  778.                 this.JSSetCenter(new JSMapPoint(pp.lat, pp.lon));
  779.             } else
  780.             if (recenterMode == RECENTER_PAN) { // pan to last point
  781.                 var pp = drawPushpins[drawPushpins.length - 1];
  782.                 this.JSSetCenter(new JSMapPoint(pp.lat, pp.lon));
  783.             } else {
  784.                 var centerPt = this.jsmapBounds.getCenter(); // GLatLng
  785.                 //var zoomFactor = this.gmapGoogleMap.getBoundsZoomLevel(this.jsmapBounds); // TODO:
  786.                 this.gmapGoogleMap.fitBounds(this.jsmapBounds);
  787.                 var zoomFactor = this.gmapGoogleMap.getZoom();
  788.                 this.JSSetCenter_G(centerPt, zoomFactor);
  789.             }
  790.         } catch (e) {
  791.             //alert("Error: [JSDrawPushpins] " + e);
  792.             return;
  793.         }
  794.     }
  795.     if (pointCount <= 0) {
  796.         return;
  797.     }
  798.  
  799.     /* replay pushpins? */
  800.     if (replay && (replay >= 1)) {
  801.         this.replayIndex = 0;
  802.         this.replayInProgress = true;
  803.         this.replayPushpins = drawPushpins;
  804.         this._startReplayTimer(replay, 100);
  805.         return;
  806.     }
  807.  
  808.     /* draw pushpins now */
  809.     var pushpinErr = null;
  810.     for (var i = 0; i < drawPushpins.length; i++) {
  811.         var pp = drawPushpins[i]; // JSMapPushpin
  812.         try {
  813.             this._addPushpin(pp); // JSMapPushpin
  814.         } catch (e) {
  815.             if (pushpinErr == null) { pushpinErr = e; }
  816.         }
  817.     }
  818.     if (pushpinErr != null) {
  819.         alert("Error: adding pushpins:\n" + pushpinErr);
  820.     }
  821.  
  822. };
  823.  
  824. /**
  825. *** Draw the specified PointsOfInterest pushpins on the map
  826. *** @param pushPins  An array of JSMapPushpin objects
  827. **/
  828. JSMap.prototype.JSDrawPOI = function(pushPins)
  829. {
  830.  
  831.     /* draw pushpins now */
  832.     if ((pushPins != null) && (pushPins.length > 0)) {
  833.         var pushpinErr = null;
  834.         for (var i = 0; i < pushPins.length; i++) {
  835.             var pp = pushPins[i]; // JSMapPushpin
  836.             if ((pp.lat == 0.0) && (pp.lon == 0.0)) {
  837.                 continue;
  838.             }
  839.             try {
  840.                 this._addPushpin(pp); // JSMapPushpin
  841.             } catch (e) {
  842.                 if (pushpinErr == null) { pushpinErr = e; }
  843.             }
  844.         }
  845.         if (pushpinErr != null) {
  846.             alert("Error: adding pushpins:\n" + pushpinErr);
  847.         }
  848.     }
  849.  
  850. };
  851.  
  852. /**
  853. *** Adds a single pushpin to the map
  854. *** @param pp  The JSMapPushpin object to add to the map
  855. **/
  856. JSMap.prototype._addPushpin = function(pp) // JSMapPushpin
  857. {
  858.     try {
  859.         var self = this;
  860.  
  861.         /* point pt */
  862.         var gPT = jsNewGLatLng(pp.lat, pp.lon); // GLatLng
  863.  
  864.         /* accuracy radius */
  865.         var accRadM = pp.accRadM;
  866.         if (accRadM > 30) {
  867.             // point-radius shape
  868.             var color  = pp.isCellLoc? "#FF5555" : "#5555FF";
  869.             var circle = jsNewGCircle(gPT, accRadM, color, 1, 0.8, color, 0.1);
  870.             pp.accRadius = circle;
  871.         } else {
  872.             pp.accRadius = null;
  873.         }
  874.  
  875.         /* background marker */
  876.         if (pp.bgUrl) {
  877.             var bgMarker = jsNewImageMarker(
  878.                 gPT,                                                                // GLatLng
  879.                 pp.bgUrl,                                                           // image
  880.                 pp.bgSize?   jsNewGSize(pp.bgSize[0],pp.bgSize[1])      : null,     // iconSize
  881.                 pp.bgOffset? jsNewGPoint(pp.bgOffset[0],pp.bgOffset[1]) : null,     // iconAnchor
  882.                 null,                                                               // shadow
  883.                 null,                                                               // shadowSize
  884.                 jsNewGPoint(5, 1),                                                  // infoWindowAnchor
  885.                 false);                                                             // draggable
  886.             bgMarker.setInfoWindowHTML(null);
  887.             pp.bgMarker = bgMarker;
  888.         } else {
  889.             pp.bgMarker = null;
  890.         }
  891.  
  892.         /* pushpin marker */
  893.         pp.marker = jsNewImageMarker(
  894.             gPT,                                                                       // GLatLng
  895.             pp.iconUrl,                                                                // image
  896.             pp.iconSize?    jsNewGSize(pp.iconSize[0],pp.iconSize[1])        : null,   // iconSize
  897.             pp.iconHotspot? jsNewGPoint(pp.iconHotspot[0],pp.iconHotspot[1]) : null,   // iconAnchor
  898.             pp.shadowUrl,                                                              // shadow
  899.             pp.shadowSize?  jsNewGSize(pp.shadowSize[0],pp.shadowSize[1])    : null,   // shadowSize
  900.             jsNewGPoint(5, 1),                                                         // infoWindowAnchor
  901.             false);                                                                    // draggable
  902.         pp.marker.setInfoWindowHTML(pp.getHTML());
  903.         this.gmapGoogleMap.addPushpinMarker(pp);
  904.  
  905.     } catch(e) {
  906.         //alert("AddPushpin ERROR: " + e);
  907.     }
  908. };
  909.  
  910. /**
  911. *** Replays the list of pushpins on the map
  912. *** @param pp  The JSMapPushpin object to add to the map
  913. **/
  914. JSMap.prototype._replayPushpins = function(replay)
  915. {
  916.  
  917.     /* advance to next valid point */
  918.     while (true) {
  919.         if (this.replayIndex >= this.replayPushpins.length) {
  920.             this._clearReplay();
  921.             jsmHighlightDetailRow(-1, false);
  922.             return; // stop
  923.         }
  924.         var pp = this.replayPushpins[this.replayIndex]; // JSMapPushpin
  925.         if (geoIsValid(pp.lat,pp.lon)) {
  926.             break; // valid point
  927.         }
  928.         this.replayIndex++;
  929.     }
  930.  
  931.     /* add pushpin */
  932.     try {
  933.         var lastNdx = this.replayIndex - 1;
  934.         var pp = this.replayPushpins[this.replayIndex++]; // JSMapPushpin
  935.         pp.hoverPopup = true;
  936.         if (REPLAY_SINGLE && (lastNdx >= 0)) {
  937.             var lastPP = this.replayPushpins[lastNdx]; // JSMapPushpin
  938.             if (lastPP.marker) {
  939.                 //this.gmapGoogleMap.removeOverlay(lastPP.marker);
  940.                 lastPP.marker.setMap(null);
  941.             }
  942.             if (lastPP.bgMarker) {
  943.                 //this.gmapGoogleMap.removeOverlay(lastPP.bgMarker);
  944.                 lastPP.bgMarker.setMap(null);
  945.             }
  946.         }
  947.         this._addPushpin(pp);
  948.         if (replay && (replay >= 2)) {
  949.             this.gmapGoogleMap.showPushpinPopup(pp);
  950.         } else {
  951.             jsmHighlightDetailRow(pp.rcdNdx, true);
  952.         }
  953.         this._startReplayTimer(replay, this.replayInterval);
  954.     } catch (e) {
  955.         alert("Replay error: " + e);
  956.     }
  957.  
  958. };
  959.  
  960. // ----------------------------------------------------------------------------
  961.  
  962. /**
  963. *** This method should cause the info-bubble popup for the specified pushpin to display
  964. *** @param pushPin   The JSMapPushpin object which popup its info-bubble
  965. **/
  966. JSMap.prototype.JSShowPushpin = function(pp, center)
  967. {
  968.     if (pp) {
  969.         if (center) {
  970.             this.JSSetCenter(new JSMapPoint(pp.lat, pp.lon));
  971.         }
  972.         this.gmapGoogleMap.showPushpinPopup(pp);
  973.     }
  974. };
  975.  
  976. // ----------------------------------------------------------------------------
  977.  
  978. /**
  979. *** Draws a line between the specified points on the map.
  980. *** @param points   An array of JSMapPoint objects
  981. **/
  982. JSMap.prototype.JSDrawRoute = function(points, color)
  983. {
  984.  
  985.     /* remove existing route */
  986.     this.gmapGoogleMap.removeRouteMarkers();
  987.    
  988.     /* no route? */
  989.     if (color == "none") {
  990.         return;
  991.     }
  992.  
  993.     /* draw new route */
  994.     var LL = []; // GLatLng[]
  995.     for (var i = 0; i < points.length; i++) {
  996.         var pt = jsNewGLatLng(points[i].lat, points[i].lon);
  997.         LL.push(pt);
  998.     }
  999.     var polyLine = jsNewGPolyline(LL, color, 2, 1.0);
  1000.     polyLine.setMap(this.gmapGoogleMap); // this.gmapGoogleMap.addOverlay(polyLine); // "#003399"
  1001.     this.gmapGoogleMap.addRouteMarker(polyLine);
  1002.    
  1003.     /* draw mid arrows */
  1004.     if (ROUTE_LINE_ARROWS) {
  1005.         this.midArrows(LL);
  1006.     }
  1007.    
  1008. };
  1009.  
  1010. //  [Juan Carlos Argueta] returns the bearing in degrees between two points.
  1011. JSMap.prototype.bearing = function(from, to) {
  1012.     // ----- Returns the bearing in degrees between two points. -----
  1013.     // ----- North = 0, East = 90, South = 180, West = 270.
  1014.     // ----- var degreesPerRadian = 180.0 / Math.PI;
  1015.  
  1016.     // ----- Convert to radians.
  1017.     var lat1 = from.latRadians();
  1018.     var lon1 = from.lngRadians();
  1019.     var lat2 = to.latRadians();
  1020.     var lon2 = to.lngRadians();
  1021.  
  1022.     // -----Compute the angle.
  1023.     var angle = - Math.atan2( Math.sin( lon1 - lon2 ) * Math.cos( lat2 ), Math.cos( lat1 ) * Math.sin( lat2 ) - Math.sin( lat1 ) * Math.cos( lat2 ) * Math.cos( lon1 - lon2 ) );
  1024.     if (angle < 0.0) { angle  += Math.PI * 2.0; }
  1025.  
  1026.     // ----- And convert result to degrees.
  1027.     angle = angle * (180.0 / Math.PI);
  1028.     angle = angle.toFixed(1);
  1029.  
  1030.     return angle;
  1031. };
  1032.        
  1033. // [Juan Carlos Argueta]  A function to create the arrow head at the end of the polyline ===
  1034. //  http://www.google.com/intl/en_ALL/mapfiles/dir_0.png
  1035. //  http://www.google.com/intl/en_ALL/mapfiles/dir_3.png
  1036. //  http://www.google.com/intl/en_ALL/mapfiles/dir_6.png
  1037. //  ...
  1038. JSMap.prototype.arrowHead = function(points) {    // GLatLng[]
  1039.     // ----- obtain the bearing between the last two points
  1040.     if (!points || (points.length < 2)) { return; }
  1041.     var p1 = points[points.length-1];
  1042.     var p2 = points[points.length-2];
  1043.     // ----- round heading to a multiple of 3 and cast out 120s
  1044.     var dir = this.bearing(p2,p1);
  1045.     dir = Math.round(dir/3) * 3;
  1046.     while (dir >= 120) { dir -= 120; }
  1047.     // ----- use the corresponding triangle marker
  1048.     var arrowMarker = jsNewImageMarker(
  1049.         p1,                                                             // GLatLng
  1050.         "http://www.google.com/intl/en_ALL/mapfiles/dir_"+dir+".png",   // image
  1051.         jsNewGSize(14,14),                                              // iconSize
  1052.         jsNewGPoint(7,7),                                               // iconAnchor
  1053.         null,                                                           // shadow
  1054.         jsNewGSize(1,1),                                                // shadowSize
  1055.         jsNewGPoint(0,0),                                               // infoWindowAnchor
  1056.         false                                                           // draggable
  1057.         );
  1058.     // ----- add arrow marker
  1059.     arrowMarker.setMap(this.gmapGoogleMap);  // this.gmapGoogleMap.addOverlay(arrowMarker);
  1060. }
  1061.      
  1062. // [Juan Carlos Argueta]  A function to put arrow heads at intermediate points
  1063. JSMap.prototype.midArrows = function(points) {  // GLatLng[]     
  1064.     if (!points || (points.length < 2)) { return; }
  1065.     for (var i = 1; i < points.length - 1; i++) {  
  1066.         var p1 = points[i-1];
  1067.         var p2 = points[i+1];
  1068.         // ----- round it to a multiple of 3 and cast out 120s
  1069.         var dir = this.bearing(p1,p2);
  1070.         dir = Math.round(dir/3) * 3;
  1071.         while (dir >= 120) { dir -= 120; }
  1072.         // ----- use the corresponding triangle marker
  1073.         var arrowMarker = jsNewImageMarker(
  1074.             points[i],                                                      // GLatLng
  1075.             "http://www.google.com/intl/en_ALL/mapfiles/dir_"+dir+".png",   // image
  1076.             jsNewGSize(14,14),                                              // iconSize
  1077.             jsNewGPoint(7,7),                                               // iconAnchor
  1078.             null,                                                           // shadow
  1079.             jsNewGSize(1,1),                                                // shadowSize
  1080.             jsNewGPoint(0,0),                                               // infoWindowAnchor
  1081.             false                                                           // draggable
  1082.             );
  1083.         // ----- add arrow marker
  1084.         arrowMarker.setMap(this.gmapGoogleMap);  // this.gmapGoogleMap.addOverlay(arrowMarker);
  1085.         this.gmapGoogleMap.addRouteMarker(arrowMarker);
  1086.     }
  1087. }
  1088.  
  1089. // ----------------------------------------------------------------------------
  1090.  
  1091. /**
  1092. *** Remove previously drawn shapes
  1093. **/
  1094. JSMap.prototype._removeShapes = function()
  1095. {
  1096.     if (this.drawShapes) {
  1097.         for (var s = 0; s < this.drawShapes.length; s++) {
  1098.             this.drawShapes[s].setMap(null);
  1099.         }
  1100.     }
  1101.     this.drawShapes = [];
  1102. };
  1103.  
  1104. /**
  1105. *** Draws a Shape on the map at the specified location
  1106. *** @param type     The Geozone shape type ("line", "circle", "rectangle", "polygon", "center")
  1107. *** @param radiusM  The circle radius, in meters
  1108. *** @param points   An array of points (JSMapPoint[])
  1109. *** @param zoomTo   rue to zoom to drawn shape
  1110. *** @return True if shape was drawn, false otherwise
  1111. **/
  1112. JSMap.prototype.JSDrawShape = function(type, radiusM, verticePts, color, zoomTo)
  1113. {
  1114.  
  1115.     /* no type? */
  1116.     if (!type || (type == "") || (type == "!")) {
  1117.         //alert("Removing shapes only ...");
  1118.         this._removeShapes();
  1119.         return false;
  1120.     }
  1121.  
  1122.     /* clear existing shapes? */
  1123.     if (type.startsWith("!")) {
  1124.         this._removeShapes();
  1125.         type = type.substr(1);
  1126.     }
  1127.  
  1128.     /* no geopoints? */
  1129.     if (!verticePts || (verticePts.length == 0)) {
  1130.         alert("No points in shape! ...");
  1131.         return false;
  1132.     }
  1133.  
  1134.     /* color */
  1135.     if (!color || (color == "")) {
  1136.         color = "#0000FF";
  1137.     }
  1138.  
  1139.     /* zoom bounds */
  1140.     var mapBounds = zoomTo? jsNewGLatLngBounds() : null;
  1141.  
  1142.     /* draw shape */
  1143.     var didDrawShape = false;
  1144.     if (type == "circle") { // ZONE_POINT_RADIUS
  1145.  
  1146.         for (var p = 0; p < verticePts.length; p++) {
  1147.             var jPT = verticePts[p]; // JSMapPoint
  1148.             var gPT = jsNewGLatLng(jPT.lat,jPT.lon); // GLatLng
  1149.             //alert("Drawing circle: " + jPT.lat+"/"+jPT.lon +" rad=("+radiusM + ") color="+color);
  1150.  
  1151.             /* draw circle */
  1152.             var circle = jsNewGCircle(gPT, radiusM, color, 2, 0.9, color, 0.1);
  1153.             if (circle != null) {
  1154.                 circle.setMap(this.gmapGoogleMap); // this.gmapGoogleMap.addOverlay(crPoly);
  1155.                 this.drawShapes.push(circle);
  1156.                 didDrawShape = true;
  1157.             }
  1158.  
  1159.             /* map bounds */
  1160.             if (mapBounds) {
  1161.                 var pt000 = geoRadiusPoint(jPT.lat, jPT.lon, radiusM,   0.0);
  1162.                 mapBounds.extend(jsNewGLatLng(pt000.lat,pt000.lon));
  1163.                 var pt090 = geoRadiusPoint(jPT.lat, jPT.lon, radiusM,  90.0);
  1164.                 mapBounds.extend(jsNewGLatLng(pt090.lat,pt090.lon));
  1165.                 var pt180 = geoRadiusPoint(jPT.lat, jPT.lon, radiusM, 180.0);
  1166.                 mapBounds.extend(jsNewGLatLng(pt180.lat,pt180.lon));
  1167.                 var pt270 = geoRadiusPoint(jPT.lat, jPT.lon, radiusM, 270.0);
  1168.                 mapBounds.extend(jsNewGLatLng(pt270.lat,pt270.lon));
  1169.             }
  1170.            
  1171.         }
  1172.  
  1173.     } else
  1174.     if (type == "rectangle") { // ZONE_BOUNDED_RECT
  1175.        
  1176.         if (verticePts.length >= 2) {
  1177.  
  1178.             /* create rectangle */
  1179.             var vp0   = verticePts[0];
  1180.             var vp1   = verticePts[1];
  1181.             var TL    = jsNewGLatLng(((vp0.lat>vp1.lat)?vp0.lat:vp1.lat),((vp0.lon<vp1.lon)?vp0.lon:vp1.lon));
  1182.             var TR    = jsNewGLatLng(((vp0.lat>vp1.lat)?vp0.lat:vp1.lat),((vp0.lon>vp1.lon)?vp0.lon:vp1.lon));
  1183.             var BL    = jsNewGLatLng(((vp0.lat<vp1.lat)?vp0.lat:vp1.lat),((vp0.lon<vp1.lon)?vp0.lon:vp1.lon));
  1184.             var BR    = jsNewGLatLng(((vp0.lat<vp1.lat)?vp0.lat:vp1.lat),((vp0.lon>vp1.lon)?vp0.lon:vp1.lon));
  1185.             var crPts = [ TL, TR, BR, BL, TL ];
  1186.    
  1187.             /* draw rectangle */
  1188.             var crPoly = jsNewGPolygon(crPts, color, 2, 0.9, color, 0.1);
  1189.             crPoly.setMap(this.gmapGoogleMap); // this.gmapGoogleMap.addOverlay(crPoly);
  1190.             this.drawShapes.push(crPoly);
  1191.             didDrawShape = true;
  1192.  
  1193.             /* map bounds */
  1194.             if (mapBounds) {
  1195.                 for (var b = 0; b < crPts.length; b++) { mapBounds.extend(crPts[b]); }
  1196.             }
  1197.  
  1198.         }
  1199.  
  1200.     } else
  1201.     if (type == "polygon") { // ZONE_POLYGON
  1202.        
  1203.         if (verticePts.length >= 3) {
  1204.  
  1205.             /* accumulate polygon vertices */
  1206.             var crPts = [];
  1207.             for (var p = 0; p < verticePts.length; p++) {
  1208.                 var gPT = jsNewGLatLng(verticePts[p].lat, verticePts[p].lon);
  1209.                 crPts.push(gPT);
  1210.                 if (mapBounds) { mapBounds.extend(gPT); }
  1211.             }
  1212.             crPts.push(crPts[0]); // close polygon
  1213.  
  1214.             /* draw polygon */
  1215.             var crPoly = jsNewGPolygon(crPts, color, 2, 0.9, color, 0.1);
  1216.             crPoly.setMap(this.gmapGoogleMap); // this.gmapGoogleMap.addOverlay(crPoly);
  1217.             this.drawShapes.push(crPoly);
  1218.             didDrawShape = true;
  1219.  
  1220.         }
  1221.  
  1222.     } else
  1223.     if (type == "corridor") { // ZONE_SWEPT_POINT_RADIUS
  1224.  
  1225.         // TODO:
  1226.        
  1227.     } else
  1228.     if (type == "center") {
  1229.  
  1230.         if (mapBounds) {
  1231.             for (var p = 0; p < verticePts.length; p++) {
  1232.                 var gPT = jsNewGLatLng(verticePts[p].lat, verticePts[p].lon);
  1233.                 mapBounds.extend(gPT);
  1234.             }
  1235.             didDrawShape = true;
  1236.         }
  1237.  
  1238.     } else {
  1239.  
  1240.         alert("Unrecognized shape type: " + type);
  1241.  
  1242.     }
  1243.  
  1244.     /* center on shape */
  1245.     if (didDrawShape && zoomTo && mapBounds) {
  1246.         var centerPt = mapBounds.getCenter(); // GLatLng
  1247.         this.gmapGoogleMap.fitBounds(mapBounds);
  1248.         var zoomFactor = this.gmapGoogleMap.getZoom();
  1249.         this.JSSetCenter_G(centerPt, zoomFactor);
  1250.     }
  1251.  
  1252.     /* shape not supported */
  1253.     return didDrawShape;
  1254.  
  1255. };
  1256.  
  1257. // ----------------------------------------------------------------------------
  1258.  
  1259. var GlobalGeozoneList = [];
  1260.  
  1261. /**
  1262. *** Draws a Geozone on the map at the specified location
  1263. *** @param type     The Geozone type
  1264. *** @param radiusM  The circle radius, in meters
  1265. *** @param points   An array of points (JSMapPoint[])
  1266. *** @param primNdx  Index of point on which to center
  1267. *** @return An object representing the Circle.
  1268. **/
  1269. JSMap.prototype.JSDrawGeozone = function(type, radiusM, points, color, primNdx)
  1270. {
  1271.     // type:
  1272.     //   0 - ZONE_POINT_RADIUS
  1273.     //   1 - ZONE_BOUNDED_RECT
  1274.     //   2 - ZONE_SWEPT_POINT_RADIUS
  1275.     //   3 - ZONE_POLYGON
  1276.     // (type ZONE_POINT_RADIUS may only be currently supported)
  1277.  
  1278.     /* Geozone mode */
  1279.     jsvGeozoneMode = true;
  1280.  
  1281.     /* remove old geozones */
  1282.     for (var i = 0; i < GlobalGeozoneList.length; i++) {
  1283.         GlobalGeozoneList[i].remove();
  1284.     }
  1285.     GlobalGeozoneList = [];
  1286.  
  1287.     /* no points? */
  1288.     if ((points == null) || (points.length <= 0)) {
  1289.         return null;
  1290.     }
  1291.     this.geozoneIndex  = ((primNdx >= 0) && (primNdx < points.length))? primNdx : 0;
  1292.  
  1293.     /* draw geozone */
  1294.     var maxLat = -90.0;
  1295.     var minLat =  90.0;
  1296.     var pointCount = 0;
  1297.     var mapBounds  = jsNewGLatLngBounds();
  1298.     if (type == ZONE_POINT_RADIUS) {
  1299.  
  1300.         /* adjust radius */
  1301.         if (isNaN(radiusM))              { radiusM = 5000; }
  1302.         if (radiusM > MAX_ZONE_RADIUS_M) { radiusM = MAX_ZONE_RADIUS_M; }
  1303.         if (radiusM < MIN_ZONE_RADIUS_M) { radiusM = MIN_ZONE_RADIUS_M; }
  1304.         jsvZoneRadiusMeters = radiusM;
  1305.  
  1306.         /* draw points */
  1307.         for (var i = 0; i < points.length; i++) {
  1308.             var pt = points[i];
  1309.             if (geoIsValid(pt.lat,pt.lon)) {
  1310.                 var isPrimary = (i == this.geozoneIndex);
  1311.                 var zColor = isPrimary? jsvZoneColor : "#55AA55";
  1312.                 var prg = new PointRadiusGeozone(this.gmapGoogleMap, i,
  1313.                     pt.lat, pt.lon, radiusM, zColor, (jsvZoneEditable && isPrimary));
  1314.                 mapBounds.extend(jsNewGLatLng(pt.lat,pt.lon));
  1315.                 mapBounds.extend(prg.calcRadiusPoint(  0.0)); // North
  1316.                 mapBounds.extend(prg.calcRadiusPoint(180.0)); // South
  1317.                 GlobalGeozoneList.push(prg);
  1318.                 pointCount++;
  1319.             }
  1320.         }
  1321.  
  1322.         /* adjust minimum map bounds */
  1323.         // TODO:
  1324.  
  1325.         /* center on geozone */
  1326.         /*
  1327.         var centerPt = mapBounds.getCenter(); // GLatLng
  1328.         this.gmapGoogleMap.fitBounds(mapBounds);
  1329.         var zoomFactor = this.gmapGoogleMap.getZoom();
  1330.         this.JSSetCenter_G(centerPt, zoomFactor);
  1331.         */
  1332.  
  1333.     } else
  1334.     if (type == ZONE_POLYGON) {
  1335.  
  1336.         /* draw points */
  1337.         // "radiusM" is not used
  1338.         //radiusM = 500; // may be used later for setting minimum map bounds
  1339.         var prg = new PolygonGeozone(this.gmapGoogleMap, points, jsvZoneColor, jsvZoneEditable)
  1340.         for (var i = 0; i < prg.verticeMarkers.length; i++) {
  1341.             var vm  = prg.verticeMarkers[i];
  1342.             var vpt = vm.getPosition(); // GLatLng
  1343.             if (geoIsValid(vpt.lat(),vpt.lng())) {
  1344.                 mapBounds.extend(vm.getPosition());
  1345.                 pointCount++;
  1346.             }
  1347.         }
  1348.         GlobalGeozoneList.push(prg);
  1349.  
  1350.     } else
  1351.     if (type == ZONE_SWEPT_POINT_RADIUS) {
  1352.  
  1353.         var zoneNdx = ((primNdx >= 0) && (primNdx < points.length))? primNdx : 0;
  1354.         var zoneCenter = points[zoneNdx]; // JSMapPoint
  1355.         if (isNaN(radiusM))              { radiusM = 1000; }
  1356.         if (radiusM > MAX_ZONE_RADIUS_M) { radiusM = MAX_ZONE_RADIUS_M; }
  1357.         if (radiusM < MIN_ZONE_RADIUS_M) { radiusM = MIN_ZONE_RADIUS_M; }
  1358.         jsvZoneRadiusMeters = radiusM;
  1359.  
  1360.         /* draw points */
  1361.         var prg = new CorridorGeozone(this.gmapGoogleMap, points, radiusM, jsvZoneColor, jsvZoneEditable);
  1362.         for (var i = 0; i < prg.verticeMarkers.length; i++) {
  1363.             var vm = prg.verticeMarkers[i];
  1364.             if (vm.isVisible) { // point-radius vertice
  1365.                 var vpt = vm.getPosition(); // GLatLng
  1366.                 if (vpt.lat() < minLat) { minLat = vpt.lat(); }
  1367.                 if (vpt.lat() > maxLat) { maxLat = vpt.lat(); }
  1368.                 var pt000 = geoRadiusPoint(vpt.lat(), vpt.lng(), radiusM,   0.0);
  1369.                 mapBounds.extend(jsNewGLatLng(pt000.lat,pt000.lon));
  1370.                 var pt090 = geoRadiusPoint(vpt.lat(), vpt.lng(), radiusM,  90.0);
  1371.                 mapBounds.extend(jsNewGLatLng(pt090.lat,pt090.lon));
  1372.                 var pt180 = geoRadiusPoint(vpt.lat(), vpt.lng(), radiusM, 180.0);
  1373.                 mapBounds.extend(jsNewGLatLng(pt180.lat,pt180.lon));
  1374.                 var pt270 = geoRadiusPoint(vpt.lat(), vpt.lng(), radiusM, 270.0);
  1375.                 mapBounds.extend(jsNewGLatLng(pt270.lat,pt270.lon));
  1376.                 pointCount++;
  1377.             }
  1378.         }
  1379.         GlobalGeozoneList.push(prg);
  1380.  
  1381.         /* center on geozone */
  1382.         //var centerPt = mapBounds.getCenter(); // GLatLng
  1383.         ////var zoomFactor = this.gmapGoogleMap.getBoundsZoomLevel(mapBounds);
  1384.         //this.gmapGoogleMap.fitBounds(mapBounds);
  1385.         //var zoomFactor = this.gmapGoogleMap.getZoom();
  1386.         //this.JSSetCenter_G(centerPt, zoomFactor);
  1387.  
  1388.     } else {
  1389.  
  1390.         alert("Geozone type not supported: " + type);
  1391.        
  1392.     }
  1393.  
  1394.     /* center on geozone */
  1395.     if (pointCount > 0) {
  1396.  
  1397.         // MinimumMapBounds: make sure points span a minimum distance top to bottom
  1398.         if (maxLat >= minLat) {
  1399.             var rangeRadiusM = radiusM * 3;
  1400.             var cenPt = mapBounds.getCenter();
  1401.             var topPt = geoRadiusPoint(maxLat, cenPt.lng(), rangeRadiusM,   0.0); // top
  1402.             mapBounds.extend(jsNewGLatLng(topPt.lat,topPt.lon));
  1403.             var botPt = geoRadiusPoint(minLat, cenPt.lng(), rangeRadiusM, 180.0); // bottom
  1404.             mapBounds.extend(jsNewGLatLng(botPt.lat,botPt.lon));
  1405.         }
  1406.  
  1407.         /* center on points */
  1408.         var centerPt = mapBounds.getCenter(); // GLatLng
  1409.         //var zoomFactor = this.gmapGoogleMap.getBoundsZoomLevel(mapBounds);
  1410.         this.gmapGoogleMap.fitBounds(mapBounds);
  1411.         var zoomFactor = this.gmapGoogleMap.getZoom();
  1412.         this.JSSetCenter_G(centerPt, zoomFactor);
  1413.  
  1414.     } else {
  1415.  
  1416.         /* default center/zoom */
  1417.         this.JSSetCenter(DEFAULT_CENTER, DEFAULT_ZOOM);
  1418.  
  1419.     }
  1420.  
  1421.     return null;
  1422. };
  1423.  
  1424. // ----------------------------------------------------------------------------
  1425. // ----------------------------------------------------------------------------
  1426.  
  1427. var pointRadiusRadiusHeading = 60.0;
  1428.  
  1429. /* this draws a single point-circle geozone */
  1430. function PointRadiusGeozone(gMap, zNdx,
  1431.     lat, lon, radiusM, color, editable)
  1432. {
  1433.     var self = this;
  1434.  
  1435.     /* circle attributes */
  1436.     this.googleMap        = gMap;
  1437.     this.zoneIndex        = zNdx;
  1438.     this.radiusMeters     = (radiusM <= MAX_ZONE_RADIUS_M)? Math.round(radiusM) : MAX_ZONE_RADIUS_M;
  1439.     this.radiusPoint      = null;
  1440.     this.centerMarker     = null;
  1441.     this.radiusMarker     = null;
  1442.     this.circlePolygon    = null;
  1443.     this.shapeColor       = (color && (color != ""))? color : "#0000FF";
  1444.     this.centerPoint      = jsNewGLatLng(lat, lon); // GLatLng
  1445.  
  1446.     /* center Icon/marker */
  1447.     if (editable) {
  1448.        
  1449.         /* create draggable center marker */
  1450.         this.centerMarker = jsNewImageMarker(
  1451.             this.centerPoint,                                               // GLatLng
  1452.             "http://labs.google.com/ridefinder/images/mm_20_blue.png",      // image
  1453.             jsNewGSize(12,20),                                              // iconSize
  1454.             jsNewGPoint(6,20),                                              // iconAnchor
  1455.             "http://labs.google.com/ridefinder/images/mm_20_shadow.png",    // shadow
  1456.             jsNewGSize(22,20),                                              // shadowSize
  1457.             null,                                                           // infoWindowAnchor
  1458.             true                                                            // draggable
  1459.             );
  1460.         this.centerMarker.setMap(this.googleMap);
  1461.         this.centerMarker.setDraggable(true);
  1462.         google.maps.event.addListener(this.centerMarker, "dragend", function(event) {
  1463.             var oldCP = self.centerPoint; // GLatLng
  1464.             var oldRP = self.radiusMarker.getPosition(); // GLatLng
  1465.             var newCP = self.centerMarker.getPosition(); // GLatLng
  1466.             var newRP = self.calcRadiusPoint(geoHeading(oldCP.lat(),oldCP.lng(),oldRP.lat(),oldRP.lng()));
  1467.             self.centerPoint = newCP;  // GLatLng
  1468.             self.radiusMarker.setPosition(newRP);
  1469.             self.drawCircle();
  1470.             jsmSetPointZoneValue(newCP.lat(), newCP.lng(), self.radiusMeters);
  1471.         });
  1472.    
  1473.         /* radius Icon/Marker */
  1474.         this.radiusPoint      = this.calcRadiusPoint(/*Global*/pointRadiusRadiusHeading); // GLatLng
  1475.         this.radiusMarker     = jsNewImageMarker(
  1476.             this.radiusPoint,                                               // GLatLng
  1477.             "http://labs.google.com/ridefinder/images/mm_20_gray.png",      // image
  1478.             jsNewGSize(12,20),                                              // iconSize
  1479.             jsNewGPoint(6,20),                                              // iconAnchor
  1480.             "http://labs.google.com/ridefinder/images/mm_20_shadow.png",    // shadow
  1481.             jsNewGSize(22,20),                                              // shadowSize
  1482.             null,                                                           // infoWindowAnchor
  1483.             true                                                            // draggable
  1484.             );
  1485.         this.radiusMarker.setMap(this.googleMap); // this.googleMap.addOverlay(this.radiusMarker);
  1486.         this.radiusMarker.setDraggable(true); // enableDragging();
  1487.         google.maps.event.addListener(this.radiusMarker, "dragend", function(event) {
  1488.             var oldCP = self.centerMarker.getPosition();
  1489.             var newRP = self.radiusMarker.getPosition();
  1490.             var radM  = Math.round(geoDistanceMeters(oldCP.lat(),oldCP.lng(),newRP.lat(),newRP.lng()));
  1491.             self.radiusMeters = radM;
  1492.             pointRadiusRadiusHeading = geoHeading(oldCP.lat(),oldCP.lng(),newRP.lat(),newRP.lng());
  1493.             if (self.radiusMeters < MIN_ZONE_RADIUS_M) {
  1494.                 self.radiusMeters = MIN_ZONE_RADIUS_M;
  1495.                 newRP = self.calcRadiusPoint(pointRadiusRadiusHeading);
  1496.                 self.radiusMarker.setPosition(newRP);
  1497.             } else
  1498.             if (self.radiusMeters > MAX_ZONE_RADIUS_M) {
  1499.                 self.radiusMeters = MAX_ZONE_RADIUS_M;
  1500.                 newRP = self.calcRadiusPoint(pointRadiusRadiusHeading);
  1501.                 self.radiusMarker.setPosition(newRP); // .setPoint(newRP);
  1502.             }
  1503.             //self.drawCircle();  <-- redrawn during zoneReset below
  1504.             jsmSetPointZoneValue(oldCP.lat(), oldCP.lng(), self.radiusMeters);
  1505.             // need to redraw other point-radius zones as well
  1506.             _zoneReset();
  1507.         });
  1508.  
  1509.     } else {
  1510.        
  1511.         this.centerMarker = null;
  1512.        
  1513.     }
  1514.  
  1515.     /* draw circle */
  1516.     this.drawCircle();
  1517.  
  1518. };
  1519.  
  1520. PointRadiusGeozone.prototype.type = function()
  1521. {
  1522.     return ZONE_POINT_RADIUS;
  1523. };
  1524.  
  1525. PointRadiusGeozone.prototype.calcRadiusPoint = function(heading)
  1526. {
  1527.     var cpt = (this.centerMarker != null)? this.centerMarker.getPosition() : this.centerPoint; // GLatLng
  1528.     var rp  = geoRadiusPoint(cpt.lat(), cpt.lng(), this.radiusMeters, heading);
  1529.     return jsNewGLatLng(rp.lat,  rp.lon);
  1530. };
  1531.  
  1532. PointRadiusGeozone.prototype.drawCircle = function()
  1533. {
  1534.  
  1535.     /* remove old circle */
  1536.     if (this.circlePolygon != null) {
  1537.         this.circlePolygon.setMap(null);
  1538.     }
  1539.  
  1540.     /* draw circle */
  1541.     var color    = this.shapeColor;
  1542.     var circPoly = jsNewGCircle(
  1543.         this.centerPoint, this.radiusMeters,
  1544.         color, 2, 0.9, color, 0.1);
  1545.     circPoly.setMap(this.googleMap);
  1546.     this.circlePolygon = circPoly;
  1547.  
  1548.     /* "click" */
  1549.     var self = this;
  1550.     google.maps.event.addListener(this.circlePolygon, "click", function(event) {
  1551.         if (!event || !event.latLng) { return; }
  1552.         var point = event.latLng;
  1553.         zoneMapSetIndex(self.zoneIndex,false);
  1554.         _zoneReset();
  1555.     });
  1556.  
  1557. };
  1558.  
  1559. //PointRadiusGeozone.prototype.getCenter = function()
  1560. //{
  1561. //    return this.centerPoint; // GLatLng
  1562. //};
  1563.  
  1564. PointRadiusGeozone.prototype.getRadiusMeters = function()
  1565. {
  1566.     return this.radiusMeters;
  1567. };
  1568.  
  1569. PointRadiusGeozone.prototype.remove = function()
  1570. {
  1571.     if (this.radiusMarker != null) {
  1572.         this.radiusMarker.setMap(null);
  1573.     }
  1574.     if (this.centerMarker != null) {
  1575.         this.centerMarker.setMap(null);
  1576.     }
  1577.     if (this.circlePolygon != null) {
  1578.         this.circlePolygon.setMap(null);
  1579.     }
  1580. };
  1581.  
  1582. // ----------------------------------------------------------------------------
  1583. // ----------------------------------------------------------------------------
  1584.  
  1585. function PolygonGeozone(gMap, points/*JSMapPoint[]*/, color, edit)
  1586. {
  1587.     var self = this;
  1588.  
  1589.     /* polygon attributes */
  1590.     this.googleMap        = gMap;
  1591.     this.verticeMarkers   = [];
  1592.     this.centerMarker     = null;
  1593.     this.shapeColor       = (color && (color != ""))? color : "#0000FF";
  1594.     this.polyBounds       = null;
  1595.     this.polygon          = null;
  1596.     this.editable         = edit;
  1597.  
  1598.     /* create vertices */
  1599.     var count  = 0;
  1600.     var bounds = jsNewGLatLngBounds();
  1601.     for (var i = 0; i < points.length; i++) {
  1602.         var p = points[i]; // JSMapPoint
  1603.         if (!geoIsValid(p.lat,p.lon)) { continue; }
  1604.  
  1605.         /* vertice Icon/marker */
  1606.         var vertPoint = jsNewGLatLng(p.lat, p.lon);
  1607.         var vertPushpin = (count == 0)?
  1608.             "http://labs.google.com/ridefinder/images/mm_20_green.png" :
  1609.             "http://labs.google.com/ridefinder/images/mm_20_blue.png";
  1610.         var vertMarker = jsNewImageMarker(
  1611.             vertPoint,                                                      // GLatLng
  1612.             vertPushpin,                                                    // image
  1613.             jsNewGSize(12,20),                                              // iconSize
  1614.             jsNewGPoint(6,20),                                              // iconAnchor
  1615.             "http://labs.google.com/ridefinder/images/mm_20_shadow.png",    // shadow
  1616.             jsNewGSize(22,20),                                              // shadowSize
  1617.             null,                                                           // infoWindowAnchor
  1618.             this.editable                                                   // draggable
  1619.             );
  1620.         vertMarker.pointIndex = i;
  1621.         vertMarker.isEditable = this.editable;
  1622.         this.verticeMarkers.push(vertMarker);
  1623.         bounds.extend(vertPoint);
  1624.         if (geoIsValid(p.lat,p.lon)) {
  1625.             vertMarker.setMap(this.googleMap);
  1626.             vertMarker.isVisible = true;     // polygon vertice
  1627.             vertMarker.isValid   = true;
  1628.             count++;
  1629.         } else {
  1630.             vertMarker.isVisible = false;    // polygon vertice
  1631.             vertMarker.isValid   = false;
  1632.         }
  1633.  
  1634.     }
  1635.  
  1636.     /* editable? */
  1637.     if (this.editable) {
  1638.  
  1639.         /* enable vertice dragging */
  1640.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1641.             this._polygonVerticeDrag(this.verticeMarkers[i], i);
  1642.         }
  1643.  
  1644.         /* center point */
  1645.         var center = bounds.getCenter();
  1646.         this.centerMarker = jsNewImageMarker(
  1647.             center,                                                         // GLatLng
  1648.             "http://labs.google.com/ridefinder/images/mm_20_red.png",       // image
  1649.             jsNewGSize(12,20),                                              // iconSize
  1650.             jsNewGPoint(6,20),                                              // iconAnchor
  1651.             "http://labs.google.com/ridefinder/images/mm_20_shadow.png",    // shadow
  1652.             jsNewGSize(22,20),                                              // shadowSize
  1653.             null,                                                           // infoWindowAnchor
  1654.             true                                                            // draggable
  1655.             );
  1656.         this.centerMarker.lastPoint = center;
  1657.         this.centerMarker.setDraggable(true);
  1658.         google.maps.event.addListener(this.centerMarker, "dragend", function(event) {
  1659.             var thisPoint = self.centerMarker.getPosition(); // GLatLng
  1660.             var lastPoint = self.centerMarker.lastPoint;  // GLatLng
  1661.             var deltaLat  = thisPoint.lat() - lastPoint.lat();
  1662.             var deltaLon  = thisPoint.lng() - lastPoint.lng();
  1663.             for (var i = 0; i < self.verticeMarkers.length; i++) {
  1664.                 var vm  = self.verticeMarkers[i]; // G
  1665.                 var vpt = vm.getPosition();
  1666.                 if (geoIsValid(vpt.lat(),vpt.lng())) {
  1667.                     var npt = jsNewGLatLng(vpt.lat() + deltaLat, vpt.lng() + deltaLon);
  1668.                     vm.setPosition(npt);
  1669.                     _jsmSetPointZoneValue(vm.pointIndex, npt.lat(), npt.lng(), 0);
  1670.                 } else {
  1671.                     _jsmSetPointZoneValue(vm.pointIndex, 0.0, 0.0, 0);
  1672.                 }
  1673.             }
  1674.             self.centerMarker.lastPoint = thisPoint;
  1675.             self.drawPolygon();
  1676.         });
  1677.         if (count > 0) {
  1678.             this.centerMarker.setMap(this.googleMap);
  1679.             this.centerMarker.isVisible = true;     // polygon center
  1680.         } else {
  1681.             this.centerMarker.isVisible = false;    // polygon center
  1682.         }
  1683.        
  1684.     }
  1685.  
  1686.     /* draw polygon */
  1687.     this.drawPolygon();
  1688.  
  1689. };
  1690.  
  1691. PolygonGeozone.prototype._polygonVerticeDrag = function(marker, iNdx)
  1692. {
  1693.     // due to JavaScript closure rules, this must be in a separate function
  1694.     var self = this;
  1695.     var vertMarker = marker;
  1696.     vertMarker.setDraggable(true);
  1697.     google.maps.event.addListener(vertMarker, "dragend", function(event) {
  1698.         var vNdx  = vertMarker.pointIndex;
  1699.         var point = vertMarker.getPosition();
  1700.         zoneMapSetIndex(vNdx, false);
  1701.         _jsmSetPointZoneValue(vNdx, point.lat(), point.lng(), 0);
  1702.         self.drawPolygon();
  1703.         self.polyBounds = jsNewGLatLngBounds();
  1704.         for (var x = 0; x < self.verticeMarkers.length; x++) {
  1705.             var vpt = self.verticeMarkers[x].getPosition();
  1706.             if (geoIsValid(vpt.lat(),vpt.lng())) {
  1707.                 self.polyBounds.extend(vpt);
  1708.             }
  1709.         }
  1710.         var polyCenter = self.polyBounds.getCenter(); // GLatLng
  1711.         self.centerMarker.setPosition(polyCenter);
  1712.         self.centerMarker.lastPoint = polyCenter;
  1713.     });
  1714. }
  1715.  
  1716. PolygonGeozone.prototype.type = function()
  1717. {
  1718.     return ZONE_POLYGON;
  1719. };
  1720.  
  1721. PolygonGeozone.prototype.drawPolygon = function()
  1722. {
  1723.  
  1724.     /* points */
  1725.     var points = []; // GLatLng[]
  1726.     var bounds = jsNewGLatLngBounds();
  1727.     if (this.verticeMarkers.length > 0) {
  1728.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1729.             var vm  = this.verticeMarkers[i];
  1730.             var vpt = vm.getPosition();
  1731.             if (vm.isVisible) { // polygon vertice
  1732.                 vm.setMap(null);
  1733.                 vm.isVisible = false;  // polygon vertice
  1734.             }
  1735.             if (geoIsValid(vpt.lat(),vpt.lng())) {
  1736.                 if (vm.isEditable) {
  1737.                     vm.setMap(this.googleMap);
  1738.                     vm.isVisible = true;  // polygon vertice
  1739.                 }
  1740.                 points.push(vpt);  // GLatLng
  1741.                 bounds.extend(vpt);
  1742.             }
  1743.         }
  1744.         if (points.length > 0) {
  1745.             points.push(points[0]); // GLatLng close polygon
  1746.         }
  1747.     }
  1748.  
  1749.     /* center marker */
  1750.     if (this.centerMarker != null) {
  1751.         var center = bounds.getCenter();
  1752.         if (points.length > 0) {
  1753.             this.centerMarker.setPosition(center);
  1754.             this.centerMarker.lastPoint = center;
  1755.             if (!this.centerMarker.isVisible) {  // polygon center
  1756.                 this.centerMarker.setMap(this.googleMap);
  1757.                 this.centerMarker.isVisible = true; // polygon center
  1758.             }
  1759.         } else {
  1760.             if (this.centerMarker.isVisible) { // polygon center
  1761.                 this.centerMarker.setMap(null);
  1762.                 this.centerMarker.isVisible = false; // polygon center
  1763.             }
  1764.         }
  1765.     }
  1766.  
  1767.     /* draw polygon */
  1768.     if (points.length >= 3) {
  1769.         if (this.polygon == null) {
  1770.             var color = this.shapeColor;
  1771.             this.polygon = jsNewGPolygon(points, color, 2, 0.9, color, 0.1);
  1772.         } else {
  1773.             this.polygon.setPath(points);
  1774.         }
  1775.         this.polygon.setMap(this.googleMap);
  1776.     } else
  1777.     if (this.polygon != null) {
  1778.         this.polygon.setMap(null);
  1779.         this.polygon = null;
  1780.     }
  1781.  
  1782. };
  1783.  
  1784. PolygonGeozone.prototype.remove = function()
  1785. {
  1786.     if (this.centerMarker != null) { // polygon center
  1787.         this.centerMarker.setMap(null);
  1788.         this.centerMarker.isVisible = false;
  1789.     }
  1790.     if (this.verticeMarkers != null) {
  1791.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1792.             this.verticeMarkers[i].setMap(null);
  1793.             this.verticeMarkers[i].isVisible = false;
  1794.         }
  1795.     }
  1796.     if (this.polygon != null) {
  1797.         this.polygon.setMap(null);
  1798.     }
  1799. };
  1800.  
  1801. //PointRadiusGeozone.prototype.getCenter = function()
  1802. //{
  1803. //    return this.centerMarker.getPosition();  // GLatLng
  1804. //};
  1805.  
  1806. // ----------------------------------------------------------------------------
  1807. // ----------------------------------------------------------------------------
  1808.  
  1809. function CorridorGeozone(gMap, points, radiusM, color, editable)
  1810. {
  1811.     var self = this;
  1812.  
  1813.     /* circle attributes */
  1814.     this.googleMap        = gMap;
  1815.     this.radiusMeters     = (radiusM <= MAX_ZONE_RADIUS_M)? Math.round(radiusM) : MAX_ZONE_RADIUS_M;
  1816.     this.verticeMarkers   = []; // GMarker[]
  1817.     this.shapeColor       = (color && (color != ""))? color : "#0000FF";
  1818.     this.corridor         = [];
  1819.     this.corrBounds       = null;
  1820.  
  1821.     /* create vertices */
  1822.     var count  = 0;
  1823.     var bounds = jsNewGLatLngBounds();
  1824.     for (var i = 0; i < points.length; i++) {
  1825.         var p = points[i]; // JSMapPoint
  1826.         if (!geoIsValid(p.lat,p.lon)) { continue; }
  1827.  
  1828.         /* vertice Icon/marker */
  1829.         var vertPoint       = jsNewGLatLng(p.lat, p.lon);
  1830.         var vertMarker      = jsNewImageMarker(
  1831.             vertPoint,                                                      // GLatLng
  1832.             "http://labs.google.com/ridefinder/images/mm_20_blue.png",      // image
  1833.             jsNewGSize(12,20),                                              // iconSize
  1834.             jsNewGPoint(6,20),                                              // iconAnchor
  1835.             "http://labs.google.com/ridefinder/images/mm_20_shadow.png",    // shadow
  1836.             jsNewGSize(22,20),                                              // shadowSize
  1837.             null,                                                           // infoWindowAnchor
  1838.             editable                                                        // draggable
  1839.             );
  1840.         vertMarker.pointIndex = i;
  1841.         this.verticeMarkers.push(vertMarker); // GMarker
  1842.         bounds.extend(vertPoint);
  1843.         if (geoIsValid(p.lat,p.lon)) {
  1844.             vertMarker.setMap(this.googleMap); // this.googleMap.addOverlay(vertMarker);
  1845.             vertMarker.isVisible = true; // corridor vertice
  1846.             vertMarker.isValid   = true;
  1847.             count++;
  1848.         } else {
  1849.             vertMarker.isVisible = false; // corridor vertice
  1850.             vertMarker.isValid   = false;
  1851.         }
  1852.  
  1853.     }
  1854.  
  1855.     /* editable? */
  1856.     if (editable) {
  1857.  
  1858.         /* enable vertice dragging */
  1859.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1860.             this._corridorVerticeDrag(this.verticeMarkers[i], i);
  1861.         }
  1862.        
  1863.     }
  1864.    
  1865.     /* draw corridor */
  1866.     this.drawCorridor();
  1867.  
  1868. };
  1869.  
  1870. CorridorGeozone.prototype.type = function()
  1871. {
  1872.     return ZONE_SWEPT_POINT_RADIUS;
  1873. };
  1874.  
  1875. CorridorGeozone.prototype._corridorVerticeDrag = function(marker, iNdx)
  1876. {
  1877.     var self = this;
  1878.     var vertMarker = marker;
  1879.     vertMarker.setDraggable(true);
  1880.     google.maps.event.addListener(vertMarker, "dragend", function(event) {
  1881.         var vNdx  = vertMarker.pointIndex;
  1882.         var point = vertMarker.getPosition();  // GLatLng
  1883.         zoneMapSetIndex(vNdx, false);
  1884.         _jsmSetPointZoneValue(vNdx, point.lat(), point.lng(), self.radiusMeters);
  1885.         self.drawCorridor();
  1886.         //self.corrBounds = jsNewGLatLngBounds();
  1887.         //for (var i = 0; i < self.verticeMarkers.length; i++) {
  1888.         //    var vpt = self.verticeMarkers[i].getPosition();
  1889.         //    if (geoIsValid(vpt.lat(),vpt.lng())) {
  1890.         //        self.corrBounds.extend(vpt);
  1891.         //    }
  1892.         //}
  1893.         // --
  1894.         //self.centerMarker.setPosition(self.corrBounds.getCenter()); // .setPoint(self.corrBounds.getCenter());
  1895.         //self.centerMarker.lastPoint = self.corrBounds.getCenter();
  1896.     });
  1897. };
  1898.  
  1899. CorridorGeozone.prototype.drawCorridor = function()
  1900. {
  1901.  
  1902.     /* remove old corridor */
  1903.     if (this.corridor != null) {
  1904.         for (var i = 0; i < this.corridor.length; i++) {
  1905.             this.corridor[i].setMap(null);
  1906.         };
  1907.     }
  1908.     this.corridor = [];
  1909.  
  1910.     /* vertices */
  1911.     var points = [];
  1912.     var bounds = jsNewGLatLngBounds();
  1913.     if (this.verticeMarkers.length > 0) {
  1914.         var lastPT = null;
  1915.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1916.             var vm     = this.verticeMarkers[i];
  1917.             var vPT    = vm.getPosition(); // vertice GLatLng
  1918.             var radM   = this.radiusMeters;
  1919.  
  1920.             /* draw vertice circle */
  1921.             var circle = jsNewGCircle(vPT, radM, this.shapeColor, 1, 0.9, this.shapeColor, 0.1);
  1922.             this.corridor.push(circle);
  1923.             circle.setMap(this.googleMap); // this.googleMap.addOverlay(circlePoly);
  1924.             bounds.extend(vPT);
  1925.            
  1926.             /* draw connecting corridor */
  1927.             if (lastPT != null) {
  1928.                 var ptA = lastPT; // GLatLng
  1929.                 var ptB = vPT;    // GLatLng
  1930.                 var hAB = geoHeading(ptA.lat(), ptA.lng(), ptB.lat(), ptB.lng()) - 90.0; // perpendicular
  1931.                 var rp1 = geoRadiusPoint(ptA.lat(), ptA.lng(), radM, hAB        ); // JSMapPoint
  1932.                 var rp2 = geoRadiusPoint(ptB.lat(), ptB.lng(), radM, hAB        ); // JSMapPoint
  1933.                 var rp3 = geoRadiusPoint(ptB.lat(), ptB.lng(), radM, hAB + 180.0); // JSMapPoint
  1934.                 var rp4 = geoRadiusPoint(ptA.lat(), ptA.lng(), radM, hAB + 180.0); // JSMapPoint
  1935.                 var rectPts = [];
  1936.                 rectPts.push(jsNewGLatLng(rp1.lat,rp1.lon));
  1937.                 rectPts.push(jsNewGLatLng(rp2.lat,rp2.lon));
  1938.                 rectPts.push(jsNewGLatLng(rp3.lat,rp3.lon));
  1939.                 rectPts.push(jsNewGLatLng(rp4.lat,rp4.lon));
  1940.                 rectPts.push(jsNewGLatLng(rp1.lat,rp1.lon));
  1941.                 var rectPoly = jsNewGPolygon(rectPts, this.shapeColor, 1, 0.9, this.shapeColor, 0.1);
  1942.                 this.corridor.push(rectPoly);
  1943.                 rectPoly.setMap(this.googleMap); // this.googleMap.addOverlay(rectPoly);
  1944.             }
  1945.             lastPT = vPT; // GLatLng
  1946.            
  1947.         }
  1948.     }
  1949.  
  1950. };
  1951.  
  1952. CorridorGeozone.prototype.remove = function()
  1953. {
  1954.     if (this.verticeMarkers != null) {
  1955.         for (var i = 0; i < this.verticeMarkers.length; i++) {
  1956.             if (this.verticeMarkers[i].isVisible) { // corridor vertice
  1957.                 this.verticeMarkers[i].setMap(null);
  1958.                 this.verticeMarkers[i].isVisible = false;
  1959.             }
  1960.         }
  1961.     }
  1962.     if (this.corridor != null) {
  1963.         for (var i = 0; i < this.corridor.length; i++) {
  1964.             this.corridor[i].setMap(null);
  1965.         }
  1966.     }
  1967. };
  1968.  
  1969. // ----------------------------------------------------------------------------
  1970. // ----------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement