/**
* @class
* Manages multiple lucid.maps.limbs.Limb.
* This provides a slight performance gain by refreshing multiple LIMBs within a single map-moved event handler.
* This class will also create the LIMB for you, and the associated google.maps.Marker instance too.
*
* @constructor
* @param {lucid.maps.limbs.LimbMarkerFactoryOptions} options The factory options.
*/
lucid.maps.limbs.LimbMarkerFactory = function( options )
{
var markers;
var limbs;
var zoomedToAll;
// The zoom level used when the user clicks for info on a place.
// This is only used when the map is zoomed out to show all places.
// If the user has zoomed in to a custom zoom level then their custom zoom is preserved in that instance.
var selectPlaceZoomLevel;
// This initialise function is called at the end of the constructor.
function init()
{
markers = [];
limbs = new lucid.maps.limbs.LimbFactory( options.map );
zoomedToAll = false;
selectPlaceZoomLevel = (typeof options.zoomLevelWhenSelected === "undefined") ? 16 : options.zoomLevelWhenSelected;
google.maps.event.addListener( options.map, "zoom_changed", handleZoomChanged );
}
function handleZoomChanged()
{
// The map is now at a user-chosen zoom level.
zoomedToAll = false;
}
/**
* Remove all markers and LIMBs created by this manager.
*/
this.removeAll = function()
{
for (var i=0; i<markers.length; i++)
{
if (typeof options.infoBox !== "undefined")
options.infoBox.remove( markers[i] );
markers[i].setMap( null );
markers[i] = null;
}
markers = [];
limbs.destroyAll();
};
/**
* Add a new marker and a LIMB to the map.
* The map instance and label styling are set in the options passed into the manager's constructor.
*
* @param {google.maps.LatLng} position The location to be marked.
* @param {string} title The name/title of the location. Used as the title of the marker created on the map.
* @param {string} [placeReference] Optionally associated the marker with a place using the place reference.
*/
this.add = function( position, title, placeReference )
{
// TODO Take a deep copy.
var markerOptions = options.markerOptions;
markerOptions.position = position;
markerOptions.title = title;
markerOptions.map = options.map;
var marker = new google.maps.Marker( markerOptions );
if (typeof placeReference !== "undefined")
{
marker.placeReference = placeReference;
}
markers[markers.length] = marker;
var limbOptions = {};
if (typeof options.limbOptions !== "undefined")
{
lucid.maps.limbs.applyLimbOptions( limbOptions, options.limbOptions );
}
limbOptions.marker = marker;
var limb = limbs.create( limbOptions );
if (typeof options.infoBox !== "undefined")
{
options.infoBox.add( marker );
}
google.maps.event.addListener( marker, "click", createClickMarkerHandler( marker ) );
google.maps.event.addListener( limb, "click", createClickLimbHandler( marker ) );
};
function createClickMarkerHandler( marker )
{
return function()
{
if (typeof options.infoBox === "undefined")
{
options.map.setCenter( marker.getPosition() );
}
// else: The info box will appear when the marker is clicked.
// Let the map centre be centred around the info box.
if (zoomedToAll === true)
{
if (selectPlaceZoomLevel != null)
{
options.map.setZoom( selectPlaceZoomLevel );
}
// else: the zoom is turned off
}
// else: keep the map at the user-chosen zoom level
}
}
function createClickLimbHandler( marker )
{
return function()
{
// Perform the same action as if the user clicked on the marker.
google.maps.event.trigger( marker, "click" );
};
}
/**
* Zoom and pan the map so that all markers are in view.
* You can optionally specify additional locations to be included in the map view by setting LatLng in the options parameter.
*
* @param {object} zoomOptions
* @param {google.maps.LatLng[]} [zoomOptions.additionalLocations] Locations, in addition to the markers, which should be kept in view when the map view is changed.
*/
this.zoomToAll = function( zoomOptions )
{
var locations = [];
for (var i=0; i<markers.length; i++)
{
locations[locations.length] = markers[i].getPosition();
}
if (zoomOptions.additionalLocations)
{
for (var i=0; i<zoomOptions.additionalLocations.length; i++)
{
locations[locations.length] = zoomOptions.additionalLocations[i];
}
}
if (locations.length > 0)
{
var bounds = new google.maps.LatLngBounds( locations[0], locations[0] );
for (var i=0; i<locations.length; i++)
{
bounds = bounds.extend( locations[i] );
}
options.map.fitBounds( bounds );
}
zoomedToAll = true;
};
/**
* Temporarily hide the labels.
* This does not remove the LIMBs and markers, it just takes them off display.
* Call this if you hide the map element.
*/
this.hide = function()
{
limbs.hide();
// NB: The markers are part of the map and should be hidden by hiding the map element.
};
/**
* Re-display the labels after being hidden with a call to 'hide'.
*/
this.show = function()
{
limbs.show();
// NB: The markers are part of the map and should be shown by showing the map element.
};
/**
* Destroy this instance and any associated resources.
* This method should be called when the instance is no longer required.
*/
this.destroy = function()
{
this.removeAll();
};
/**
* @return {number} The total number of places being labelled (includes any labels currently being hidden).
*/
this.getNumberOfMarkers = function()
{
return markers.length;
};
init();
};
/**
* @type {object}
* @property {google.maps.Map} map The map the markers and LIMBs are to be associated with.
* @property {google.maps.MarkerOptions} markerOptions The settings for the marker shown on the map for these labels.
* The LimbMarkerFactory will ignore the position property. This will be set when the places are added with a call to addPlaces.
* @property {lucid.maps.limbs.LimbOptions} [limbOptions] The settings for the LIMB shown on the map for these labels.
* The LimbMarkerFactory will ignore the marker property. This will be set to the place's marker.
* The LimbMarkerFactory will ignore the independent property. This will always be false.
* If undefined the LIMB will take its styling from the marker.
* @property {lucid.maps.limbs.InfoBox} [infoBox] The class that displays an info box when the user clicks on the label.
* Leave undefined if no info box should appear.
* @property {number} [zoomLevelWhenSelected] The zoom level to be used when a marker is clicked.
* Set this to null if you do not want the zoom to change.
* Defaults to 16 if not defined.
*/
lucid.maps.limbs.LimbMarkerFactoryOptions = {};