This is the code to create a 'tracker' marker. The logic resides completely in the Pointer
object.
You simple instantiate a new Pointer
and pass the marker and map. The Pointer
automatically shows and hides, no work necessary on your part.
I see no licensing info but if I am not mistaken, this is based on a gmap sample I have seen in the past. What you do with it is up to you.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Marker Tracker Code</title>
<script src="http://maps.google.com/maps?file=api&amp;v=3"
type="text/javascript"></script>
<style type="text/css">
.map
{
height: 450px;
width: 500px;
}
</style>
<script type="text/javascript">
var map;
function load() {
if (GBrowserIsCompatible()) {
var latlng = new GLatLng(37.4419, -122.1419);
map = new GMap2(document.getElementById("map"));
map.setCenter(latlng, 13);
// create a marker
var marker = new GMarker(latlng);
map.addOverlay(marker);
// create a tracker for marker
var tracker = new Pointer(marker, map);
}
}
/*
Pointer creates an intelligent 'minimarker' that shows with a compass arrow pointing to
the marker passed in to the constructor when the parent marker is off screen
*/
// Creates a Pointer for the given marker and displays it on the map as needed.
function Pointer(marker, map) {
this.map_ = map;
this.marker_ = marker;
this.enabled_ = true;
this.arrowDisplayed_ = false;
this.arrow_ = null;
this.oldArrow_ = null;
this.control_ = null;
this.iconScale_ = 0.6;
this.padding_ = 65;
this.color_ = '#0000ff';
this.weight_ = 10;
this.length_ = 10
this.opacity_ = 0.6
this.updateEvent_ = 'move';
this.panEvent_ = 'click';
this.quickPanEnabled_ = true;
//replicate a different sized icon
var babyIcon = new GIcon(marker.getIcon());
babyIcon.iconSize = new GSize(
marker.getIcon().iconSize.width * this.iconScale_,
marker.getIcon().iconSize.height * this.iconScale_);
babyIcon.iconAnchor = new GPoint(
marker.getIcon().iconAnchor.x * this.iconScale_,
marker.getIcon().iconAnchor.y * this.iconScale_ / 2);
// kill the shadow
babyIcon.shadow = null;
this.babyMarker_ = new GMarker(new GPoint(0, 0), babyIcon);
//bind the update task to the event trigger
GEvent.bind(this.map_, this.updateEvent_, this, this.updateArrow_);
//update the arrow if the marker moves
GEvent.bind(this.marker_, 'changed', this, this.updateArrow_);
if (this.quickPanEnabled_) {
GEvent.bind(this.babyMarker_, this.panEvent_, this, this.panToMarker_);
}
//do an inital check
this.updateArrow_();
};
// Disables the pointer.
Pointer.prototype.disable = function() {
this.enabled_ = false;
this.updateArrow_();
};
//Enables the pointer.
Pointer.prototype.enable = function() {
this.enabled_ = true;
this.updateArrow_();
};
// Called on on the trigger event to update the arrow. Primary function is to
// check if the parent marker is in view, if not draw the tracking arrow.
Pointer.prototype.updateArrow_ = function() {
if (!this.map_.getBounds().containsLatLng(this.marker_.getLatLng()) && this.enabled_) {
this.drawArrow_();
} else if (this.arrowDisplayed_) {
this.hideArrow_();
}
};
//Draws or redraws the arrow as needed, called when the parent marker is
//not with in the map view.
Pointer.prototype.drawArrow_ = function() {
//convert to pixels
var bounds = this.map_.getBounds();
var SE = this.map_.fromLatLngToDivPixel(bounds.getSouthWest());
var NE = this.map_.fromLatLngToDivPixel(bounds.getNorthEast());
//include the padding while deciding on the arrow location
var minX = SE.x + this.padding_;
var minY = NE.y + this.padding_;
var maxX = NE.x - this.padding_;
var maxY = SE.y - this.padding_;
// find the geometric info for the marker realative to the center of the map
var center = this.map_.fromLatLngToDivPixel(this.map_.getCenter());
var loc = this.map_.fromLatLngToDivPixel(this.marker_.getLatLng());
//get the slope of the line
var m = (center.y - loc.y) / (center.x - loc.x);
var b = (center.y - m * center.x);
// end the line within the bounds
if (loc.x < maxX && loc.x > minX) {
var x = loc.x;
} else if (center.x > loc.x) {
var x = minX;
} else {
var x = maxX;
}
//calculate y and check boundaries again
var y = m * x + b;
if (y > maxY) {
y = maxY;
x = (y - b) / m;
} else if (y < minY) {
y = minY;
x = (y - b) / m;
}
// get the proper angle of the arrow
var ang = Math.atan(-m);
if (x > center.x) {
ang = ang + Math.PI;
}
// define the point of the arrow
var arrowLoc = this.map_.fromDivPixelToLatLng(new GPoint(x, y));
// left side of marker is at -1,1
var arrowLeft = this.map_.fromDivPixelToLatLng(
this.getRotatedPoint_(((-1) * this.length_), this.length_, ang, x, y));
// right side of marker is at -1,-1
var arrowRight = this.map_.fromDivPixelToLatLng(
this.getRotatedPoint_(((-1) * this.length_), ((-1) * this.length_), ang, x, y));
var center = this.map_.getCenter();
var loc = this.marker_.getLatLng();
this.oldArrow_ = this.arrow_;
this.arrow_ = new GPolyline([arrowLeft, arrowLoc, arrowRight],
this.color_, this.weight_, this.opacity_);
this.map_.addOverlay(this.arrow_);
// move the babyMarker to -1,0
this.babyMarker_.setLatLng(this.map_.fromDivPixelToLatLng(
this.getRotatedPoint_(((-2) * this.length_), 0, ang, x, y)));
if (!this.arrowDisplayed_) {
this.map_.addOverlay(this.babyMarker_);
this.arrowDisplayed_ = true;
}
if (this.oldArrow_) {
this.map_.removeOverlay(this.oldArrow_);
}
};
//Hides the arrows.
Pointer.prototype.hideArrow_ = function() {
this.map_.removeOverlay(this.babyMarker_);
if (this.arrow_) {
this.map_.removeOverlay(this.arrow_);
}
if (this.oldArrow_) {
this.map_.removeOverlay(this.oldArrow_);
}
this.arrowDisplayed_ = false;
};
//Pans the map to the parent marker.
Pointer.prototype.panToMarker_ = function() {
this.map_.panTo(this.marker_.getLatLng());
};
//This applies a counter-clockwise rotation to any point.
Pointer.prototype.getRotatedPoint_ = function(x, y, ang, xoffset, yoffset) {
var newx = y * Math.sin(ang) - x * Math.cos(ang) + xoffset;
var newy = x * Math.sin(ang) + y * Math.cos(ang) + yoffset;
var rotatedPoint = new GPoint(newx, newy);
return (rotatedPoint);
};
</script>
</head>
<body onload="load()" onunload="GUnload()">
<div>
<div id="map" class="map">
</div>
</div>
</body>
</html>