/**
* ContentONE Maps Wrapper
*
* Wraps common mapping functionality.
*
* @version 1.1
* @date March 2, 2011
* @copyright (c) World Web Management Services (http://www.worldwebms.com/)
*/
(function($) {
/**
* Create C1 jQuery extension.
*/
if( !$.fn.c1 ) {
$.fn.c1 = function( name, options ) {
if( this.c1[name] )
return this.c1[name].call( this, options );
return this;
}
}
/**
* Create C1 Maps jQuery extension.
*/
$.fn.c1.maps = function( options ) {
var t = this;
// Ensure the map APIs have been loaded and we can use them
if( !window.google.maps )
return this;
// If a marker should be shown
if( options.showMarker ) {
var id = options.showMarker;
var options = t.data( 'options' );
if( options && options._map && options.markers[id] ) {
google.maps.event.trigger( options.markers[id]._marker, 'click' );
options._map.setCenter( options.markers[id]._marker.getPosition() );
options._map.setZoom( 13 );
if( $.fn.scrollIntoView )
t.scrollIntoView( { 'animation': 500, 'offsetTop': -20 } );
}
return;
// If an address should be geocoded
} else if( options.geocode ) {
// Create the geocoder
if( $.fn.c1.maps._geocoder == null )
$.fn.c1.maps._geocoder = new google.maps.Geocoder();
// Geocode the address
$.fn.c1.maps._geocoder.geocode( { 'address': options.address }, function( results, status ) {
if( status == google.maps.GeocoderStatus.OK ) {
options.geocode.call(
options,
{
'address': options.address,
'lat': results[0].geometry.location.lat(),
'lng': results[0].geometry.location.lng()
}
);
} else {
alert( 'Unable to find the address due to the following reason: ' + status );
}
} );
return false;
}
// Setup the default options
options = $.extend( {
// Initial view settings
'origin' : {'lat': -25.335448, 'lng': 135.745076, 'zoom': 4},
'type' : 'roadmap',
// Controls
'controls' : {'default': true},
// Markers
'markers' : [],
// Layers
'layers' : {
'poi.business': 'off'
},
// Info window settings
'directions': false,
'info': true
}, options );
// Convert the directions to the jquery object
if( options.directions == false )
options.directions = null;
else if( options.directions == true )
options.directions = {'container': null};
else
options.directions = {'container': $(options.directions).hide()};
// Initialize the map
t.addClass( 'loaded' ).addClass( 'c1-maps' );
// Check for markers from other sources for backwards compatibilty
if( options.markers.length == 0 ) {
// Lat/lng coordinates provided as map arguments
if( options.lat || options.lng ) {
options.origin.lat = options.lat;
options.origin.lng = options.lng;
// Single marker provided
} else if( options.marker ) {
options.markers.push( options.marker );
}
}
// If the initial view settings need to be determined
var lat = options.origin.lat;
var lng = options.origin.lng;
if( options.origin.zoom == 'auto' ) {
options.zoom = 'auto';
options.origin.zoom = 4;
}
var zoom = options.zoom || options.origin.zoom;
// If the origin has an icon then add another marker
if( options.origin.icon ) {
if( options.origin.icon == 'default' )
options.origin.icon = 'http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png';
options.markers.push( options.origin );
}
// If there is only one marker then change the origin to the marker
if( options.markers.length == 1 ) {
lat = options.markers[0].lat;
lng = options.markers[0].lng;
if( options.markers[0].zoom )
zoom = options.markers[0].zoom;
}
// Handle zoom levels
var markerBounds = false;
var mapBounds = false;
// Show all markers
if( zoom === 'auto' ) {
markerBounds = new google.maps.LatLngBounds();
mapBounds = new google.maps.LatLngBounds();
// Show full custom map
} else if( zoom === 'fill' ) {
mapBounds = new google.maps.LatLngBounds();
// Show all of Australia
} else if( zoom === 'au' ) {
mapBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-11, 113),
new google.maps.LatLng(-44, 154)
);
}
// Create the map
var mapOptions = { zoom: mapBounds ? 15 : zoom };
if( markerBounds == false )
mapOptions.center = new google.maps.LatLng( lat, lng );
switch( options.type ) {
case 'hybrid':
mapOptions.mapTypeId = google.maps.MapTypeId.HYBRID;
break;
default:
mapOptions.mapTypeId = google.maps.MapTypeId.ROADMAP;
break;
}
// Change background
if( options.background )
mapOptions.backgroundColor = options.background;
// Specify a custom map
if( options.type == 'custom' ) {
// Custom maps are generated from a single image.
// Markers and points within the map are pixel-based.
// These points are converted to the relevant lat/lng coordinate
// when displaying in the map.
// Default values
options.map = $.extend( {
'tileSize': 256,
'minZoom': 2,
'maxZoom': 4
}, options.map );
// Define a custom projection that can display the map starting
// from (0,0) and show complete map in bottom right corner.
function CustomMapProjection() {
this.origin = new google.maps.Point(0, 0);
this.perLonDegree = this.mapSize.width / 360;
this.latRange = this.mapSize.height / 4;
}
CustomMapProjection.prototype.fromLatLngToPoint = function(latLng) {
var origin = this.origin;
var x = origin.x + this.perLonDegree * latLng.lng();
var latRadians = latLng.lat() * (Math.PI / 180);
var y = origin.y - this.latRange * Math.sin(latRadians);
return new google.maps.Point(x, y);
}
CustomMapProjection.prototype.fromPointToLatLng = function(point, noWrap) {
var y = point.y;
var x = point.x;
if (y < 0)
y = 0;
if (y >= this.mapSize.height)
y = this.mapSize.height;
var origin = this.origin;
var lng = (x - origin.x) / this.perLonDegree;
var latRadians = Math.asin((origin.y - y) / this.latRange);
var lat = latRadians / (Math.PI / 180);
return new google.maps.LatLng(lat, lng, noWrap);
}
// Utility functions that can convert a pixel coordinate to
// a lat/lng coordinate and vice versa.
CustomMapProjection.prototype.fromLatLngToPixel = function(latLng) {
var worldCoordinate = this.fromLatLngToPoint(latLng);
var zoom = 1 << this.maxZoom;
return new google.maps.Point(Math.round(worldCoordinate.x * zoom), Math.round(worldCoordinate.y * zoom));
}
CustomMapProjection.prototype.fromPixelToLatLng = function(point) {
var zoom = 1 << this.maxZoom;
var worldCoordinate = new google.maps.Point(point.x / zoom, point.y / zoom);
return this.fromPointToLatLng(worldCoordinate);
}
// Determine the dimensions of the map to display
// Give some padding to the latitude height as generally lats finish around 85 degrees.
CustomMapProjection.prototype.mapSize = new google.maps.Size(options.map.image.w, options.map.image.h * 1.1);
CustomMapProjection.prototype.tileSize = new google.maps.Size(options.map.tileSize, options.map.tileSize);
CustomMapProjection.prototype.maxZoom = options.map.maxZoom;
// Create the custom map type
function CustomMapType() { }
CustomMapType.prototype.projection = options.map._projection = new CustomMapProjection();
CustomMapType.prototype.tileSize = CustomMapProjection.prototype.tileSize;
CustomMapType.prototype.maxZoom = CustomMapProjection.prototype.maxZoom;
if (options.map.minZoom)
CustomMapType.prototype.minZoom = options.map.minZoom;
CustomMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
// Determine how many tiles are required to display the image
var rZoom = this.maxZoom - zoom;
var factor = (rZoom > 0 ? Math.pow(2, rZoom) : 1) * this.tileSize.width;
var numTilesX = Math.ceil(options.map.image.w / factor);
var numTilesY = Math.ceil(options.map.image.h / factor);
// If the tile is outside of the source image then display an empty div
// as there is no need for an image to be created.
if (coord.y < 0 || coord.y >= numTilesY || coord.x < 0 || coord.x >= numTilesX) {
var div = ownerDocument.createElement('div');
div.style.width = this.tileSize.width + 'px';
div.style.height = this.tileSize.height + 'px';
div.style.backgroundColor = 'transparent';
return div;
}
// Determine the url to generate the tile from
var image = ownerDocument.createElement('img');
image.src = options.map.image.src.replace( /(-tile-\d+x\d+)(.\w+)$/, '$1-x' + coord.x + 'y' + coord.y + 'z' + (options.map.maxZoom - zoom) + '$2' );
image.width = this.tileSize.width;
image.height = this.tileSize.height;
return image;
}
// Disable common controls in map interface
mapOptions.streetViewControl = false;
mapOptions.mapTypeControl = false;
options.directions = false;
}
// Process layers
var mapStyles = [];
$.each( options.layers, function( name, layerConfig ) {
if (layerConfig === 'off') {
mapStyles.push( { featureType: name, stylers: [{ visibility: 'off'}] } );
}
} );
if (mapStyles.length > 0) {
mapOptions.styles = mapStyles;
}
// Create the map
options._map = new google.maps.Map( this.get( 0 ), mapOptions );
// Start the custom map
if( options.type == 'custom' ) {
options._map.mapTypes.set('custom', new CustomMapType());
options._map.setMapTypeId('custom');
// Add grid overlay debugging tools
if( options.map.debug ) {
function CoordMapType(tileSize) { this.tileSize = tileSize; }
CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
// Determine pixel coordinate
var projection = options._map.getProjection();
var numTiles = 1 << zoom;
var rZoom = options.map.maxZoom - zoom;
var factor = 1 << rZoom;
var pixelCoordinate = new google.maps.Point(
coord.x * this.tileSize.width * factor,
coord.y * this.tileSize.height * factor
);
var worldCoordinate = new google.maps.Point(
coord.x * this.tileSize.width / numTiles,
coord.y * this.tileSize.height / numTiles
);
var latLng = projection.fromPointToLatLng(worldCoordinate);
var div = ownerDocument.createElement('div');
div.innerHTML =
'tile: ' + coord + '
' +
'pixel: (' + pixelCoordinate.x + ',' + pixelCoordinate.y + ')
' +
'lat/lng: (' + latLng.lat().toFixed(3) + ',' + latLng.lng().toFixed(3) + ')';
div.style.width = this.tileSize.width + 'px';
div.style.height = this.tileSize.height + 'px';
div.style.borderStyle = 'solid';
div.style.borderWidth = '1px';
div.style.borderColor = '#aaa';
return div;
}
options._map.overlayMapTypes.insertAt(0, new CoordMapType(new google.maps.Size(options.map.tileSize, options.map.tileSize)));
}
// Adjust bounds if full map should be shown by extending the bounds
// to north-west and south-east corners.
if( zoom === 'fill' ) {
mapBounds.extend(options.map._projection.fromPixelToLatLng(new google.maps.Point(0, 0)));
mapBounds.extend(options.map._projection.fromPixelToLatLng(new google.maps.Point(options.map.image.w, options.map.image.h)));
}
}
// Create delivery service wrapper
if( options.directions ) {
if( options.directions === true )
options.directions = {};
options.directions._service = new google.maps.DirectionsService();
options.directions._render = new google.maps.DirectionsRenderer();
options.directions._render.setMap( options._map );
}
// Returns a lat lng object from a given set of coordinates
function getLatLng( x, y ) {
if( options.map && options.map.unit == 'px' )
return options.map._projection.fromPixelToLatLng(new google.maps.Point(x, y));
return new google.maps.LatLng(y, x);
}
// Add markers
function createMarker( pos ) {
var details = options.markers[pos];
// Setup the marker
var config = {
'position': getLatLng(details.lng, details.lat),
'map': options._map,
'title': details.name ? details.name : ''
};
// Create ordered icons if required
if( !details.icon && options.icon ) {
// Create ordered icons if required
if( options.icon == 'count' ) {
var a = 'A';
if( pos <= 25 )
details.icon = 'http://www.google.com/mapfiles/marker' + String.fromCharCode(a.charCodeAt(0) + pos) + '.png';
else
details.icon = 'http://www.google.com/mapfiles/marker.png';
// URL based icon
} else if( options.icon.indexOf( '/' ) >= 0 ) {
details.icon = options.icon;
}
}
// Add the icon
if( details.icon ) {
config.icon = details.icon;
if( details.legend )
$(details.legend).html( '' );
}
// If draggable
if( details.drop )
config.draggable = true;
// Create the marker
var marker = new google.maps.Marker(config);
if( options.info )
google.maps.event.addListener(marker, 'click', function() {
openInfoWindow( pos );
});
// Add drop event
if( details.drop )
google.maps.event.addListener(marker, 'dragend', details.drop);
// Adjust the bounds
if( markerBounds )
markerBounds.extend( config.position );
// Add link from list to open window
if( details.list )
$(details.list).click( function( event ) {
openInfoWindow( pos, null, true );
} ).css( 'cursor', 'pointer' );
return marker;
}
$.each( options.markers, function( i ) {
this._marker = createMarker( i );
} );
// If all markers should be displayed
if( markerBounds )
mapBounds = markerBounds;
// Show a specific area within the map
if( mapBounds ) {
var zoomChangeListener = google.maps.event.addListener( options._map, 'zoom_changed', function() {
var zoomChangeBoundsListener = google.maps.event.addListener( options._map, 'bounds_changed', function( event ) {
if( this.getZoom() > 16 )
this.setZoom( 16 );
google.maps.event.removeListener( zoomChangeBoundsListener );
} );
google.maps.event.removeListener( zoomChangeListener );
} );
options._map.fitBounds( mapBounds );
}
// Creates the content for the window
function getInfoWindowHtml( pos ) {
var details = options.markers[pos];
// Build the HTML
var html = details.html || details.info;
if( html == null ) {
html = ( details.name ? ( '' + details.name + '
' ) : '' ) +
( details.address ? ( details.address.replace( "\n", '
' ) ) : '' );
}
// Start the container
html = '