/**
* @class
* A location strategy that uses the corner or an edge-centre-point of the target's bounding box.
*
* This location strategy takes the bounding box of the target shape
* and determines which corner or edge of the bounding box is nearest the map viewport.
*
* The point location of the 2D shape is taken to be either the closest corner or
* a point along the nearest edge of the bounding box.
*
* @constructor
* @param {lucid.maps.limbs.location.CentreLocationStrategyOptions} options The options controlling the location calculation.
*/
lucid.maps.limbs.location.BoundsEdgeLocationStrategy = function( options )
{
lucid.maps.limbs.location.LocationStrategy.apply( this, [ options ] );
this.computeMarkerLocation = function( target, mapBounds )
{
// Markers are a special case.
// Don't use the bounds. Simply use the position of the marker.
return target.getPosition();
};
this.computePolylineLocation = function( target, mapBounds )
{
var targetBounds = lucid.maps.geometry.computeBounds( target );
return nearestCornerOrEdgePoint( targetBounds, mapBounds );
};
this.computePolygonLocation = function( target, mapBounds )
{
var targetBounds = lucid.maps.geometry.computeBounds( target );
return nearestCornerOrEdgePoint( targetBounds, mapBounds );
};
this.computeRectangleLocation = function( target, mapBounds )
{
return nearestCornerOrEdgePoint( target.getBounds(), mapBounds );
};
this.computeCircleLocation = function( target, mapBounds )
{
return nearestCornerOrEdgePoint( target.getBounds(), mapBounds );
};
/**
* Determine in which of eight zones (N, NE, E, SE, S, SW, W, NW) the target object lies.
* Then calculate the nearest corner point or point along the nearest edge to the map view.
*
* When the target lies beyond the corner of the map (NE, NW, SE, SW)
* then the corner of the target's bounding box is used as the 'nearest' point.
*
* When the target lies beyond the side of the map (N, S, E, W)
* then a point on the closest edge of bounding box is used as the 'nearest' point.
*
* @param {google.maps.LatLngBounds} targetBounds The bounds of the target object.
* @param {google.maps.LatLngBounds} mapBounds The bounds of the current map view.
* @return {google.maps.LatLng} The point on the target bounds that is closest to the map bounds.
*/
function nearestCornerOrEdgePoint( targetBounds, mapBounds )
{
var targetLiesNorth = (targetBounds.getSouthWest().lat() > mapBounds.getNorthEast().lat());
var targetLiesSouth = (targetBounds.getNorthEast().lat() < mapBounds.getSouthWest().lat());
var targetLiesEast = (targetBounds.getSouthWest().lng() > mapBounds.getNorthEast().lng());
var targetLiesWest = (targetBounds.getNorthEast().lng() < mapBounds.getSouthWest().lng());
if (targetLiesNorth && targetLiesEast)
{
// South west corner.
return targetBounds.getSouthWest();
}
if (targetLiesNorth && targetLiesWest)
{
// South east corner.
return new google.maps.LatLng( targetBounds.getSouthWest().lat(), targetBounds.getNorthEast().lng() );
}
if (targetLiesSouth && targetLiesEast)
{
// North west corner.
return new google.maps.LatLng( targetBounds.getNorthEast().lat(), targetBounds.getSouthWest().lng() );
}
if (targetLiesSouth && targetLiesWest)
{
// North east corner.
return targetBounds.getNorthEast();
}
if (targetLiesNorth)
{
// Centre of the southern edge.
return new google.maps.LatLng( targetBounds.getSouthWest().lat(), computeHorizontalEdgeXCoordinate( targetBounds, mapBounds ) );
}
if (targetLiesSouth)
{
// Centre of the northern edge.
return new google.maps.LatLng( targetBounds.getNorthEast().lat(), computeHorizontalEdgeXCoordinate( targetBounds, mapBounds ) );
}
if (targetLiesEast)
{
// Centre of the western edge.
return new google.maps.LatLng( computeVerticalEdgeYCoordinate( targetBounds, mapBounds ), targetBounds.getSouthWest().lng() );
}
if (targetLiesWest)
{
// Centre of the eastern edge.
return new google.maps.LatLng( computeVerticalEdgeYCoordinate( targetBounds, mapBounds ), targetBounds.getNorthEast().lng() );
}
// Finally, if the shape lies in none of the directions then it must be in the map view.
return mapBounds.getCenter();
}
/* Implementation Note
*
* Using the centre of an edge causes the LIMB to jump when the target moves from a 'side zone' into a 'corner zone'.
* This is because the centre of the closest edge (used when the target is in a 'side zone') is much further away than
* the corner point (used when the target is in a 'corner zone').
* As the target transitions between the two zones the LIMB jumps backwards.
*
* To avoid this we don't use the centre of the edge.
* We take the section of the edge that overlaps the edge of the map. We then use the centre point of this section
* of the bounding box's edge.
* As the bounding box moves towards a corner of the map the edge that overlaps shrinks until all that remains is the corner point
* of the bounding box. This ensures a smooth tansition into the 'corner zone' where the corner point is used as the target location.
*/
/**
* Determine an 'appropriate' point along the horizontal edge of the target bounds.
*
* @param {google.maps.LatLngBounds} targetBounds The bounds of the target object.
* @param {google.maps.LatLngBounds} mapBounds The bounds of the current map view.
* @return {number} The x-coordinate along the horizontal edge of the target bounds that is closest to the map bounds.
*/
function computeHorizontalEdgeXCoordinate( targetBounds, mapBounds )
{
var mapWestCoord = mapBounds.getSouthWest().lng();
var mapEastCoord = mapBounds.getNorthEast().lng();
var targetWestCoord = targetBounds.getSouthWest().lng();
var targetEastCoord = targetBounds.getNorthEast().lng();
var overlappingWestCoord = (mapWestCoord > targetWestCoord) ? mapWestCoord : targetWestCoord;
var overlappingEastCoord = (mapEastCoord < targetEastCoord) ? mapEastCoord : targetEastCoord;
return (overlappingWestCoord + overlappingEastCoord) / 2;
}
/**
* Determine an 'appropriate' point along the vertical edge of the target bounds.
*
* @param {google.maps.LatLngBounds} targetBounds The bounds of the target object.
* @param {google.maps.LatLngBounds} mapBounds The bounds of the current map view.
* @return {number} The y-coordinate along the vertical edge of the target bounds that is closest to the map bounds.
*/
function computeVerticalEdgeYCoordinate( targetBounds, mapBounds )
{
var mapSouthCoord = mapBounds.getSouthWest().lat();
var mapNorthCoord = mapBounds.getNorthEast().lat();
var targetSouthCoord = targetBounds.getSouthWest().lat();
var targetNorthCoord = targetBounds.getNorthEast().lat();
var overlappingSouthCoord = (mapSouthCoord > targetSouthCoord) ? mapSouthCoord : targetSouthCoord;
var overlappingNorthCoord = (mapNorthCoord < targetNorthCoord) ? mapNorthCoord : targetNorthCoord;
return (overlappingSouthCoord + overlappingNorthCoord) / 2;
}
};
/**
* @type {object}
* @augments lucid.maps.limbs.location.LocationStrategyOptions
*/
lucid.maps.limbs.location.CentreLocationStrategyOptions = {};