/** * @name cartodb-gmapsv3 for Google Maps V3 API * @version 0.2 [December 14, 2011] * @author: xavijam@gmail.com * @fileoverview Author: xavijam@gmail.com
Licence: * Licensed under MIT * license.
This library lets you use CartoDB with google * maps v3. * */ /** * @name google * @class The fundamental namespace for Google APIs */ /** * @name google.maps * @class The fundamental namespace for Google Maps V3 API */ /* * - Map style of cartodb * - Infowindow of cartodb * - Tiles style of cartodb */ // Namespace var CartoDB = CartoDB || {}; (function($) { if (typeof(google.maps.CartoDBLayer) === "undefined") { /** * @params {} * map_canvas - Gmapsv3 canvas id (necesary for showing the infowindow) * map - Your gmapsv3 map * user_name - CartoDB user name * table_name CartoDB table name * query If you want to apply any sql sentence to the table... * tile_style - If you want to add other style to the layer * map_style - If you want to see the map styles created on cartodb (opcional - default = false) * infowindow - If you want to see infowindows when click in a geometry (opcional - default = false) * auto_bound - Let cartodb auto-bound-zoom in the map (opcional - default = false) * debug - Do you want to debug the library? Set to true */ google.maps.CartoDBLayer = function (params) { this.params = params; this.params.feature = params.infowindow; if (this.params.map_style) setCartoDBMapStyle(this.params); // Map style? ok, let's style. if (this.params.auto_bound) autoBound(this.params); // Bounds? CartoDB does it. if (this.params.infowindow) { addWaxCartoDBTiles(this.params); } else { addSimpleCartoDBTiles(this.params); // Always add cartodb tiles, simple or with wax. } this.params.visible = true; this.params.active = true; // Zoom to cartodb geometries function autoBound(params) { // Zoom to your geometries // If the table is private you can't auto zoom without being authenticated if (!params.map_key) { $.ajax({ url:'http://'+params.user_name+'.cartodb.com/api/v2/sql/?q='+escape('select ST_Extent(the_geom) from '+ params.table_name), dataType: 'jsonp', timeout: 2000, callbackParameter: 'callback', success: function(result) { if (result.rows[0].st_extent!=null) { var coordinates = result.rows[0].st_extent.replace('BOX(','').replace(')','').split(','); var coor1 = coordinates[0].split(' '); var coor2 = coordinates[1].split(' '); var bounds = new google.maps.LatLngBounds(); // Check bounds if (coor1[0] > 180 || coor1[0] < -180 || coor1[1] > 90 || coor1[1] < -90 || coor2[0] > 180 || coor2[0] < -180 || coor2[1] > 90 || coor2[1] < -90) { coor1[0] = '-30'; coor1[1] = '-50'; coor2[0] = '110'; coor2[1] = '80'; } bounds.extend(new google.maps.LatLng(coor1[1],coor1[0])); bounds.extend(new google.maps.LatLng(coor2[1],coor2[0])); params.map.fitBounds(bounds); } }, error: function(e,msg) { params.debug && console.debug('Error setting table bounds: ' + msg); } }); } } // Set the map styles of your cartodb table/map function setCartoDBMapStyle(params) { $.ajax({ url: 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/map_metadata?'+ 'map_key=' + (params.map_key || ''), dataType: 'jsonp', timeout: 2000, callbackParameter: 'callback', success: function(result) { var map_style = $.parseJSON(result.map_metadata); if (!map_style || map_style.google_maps_base_type=="roadmap") { params.map.setOptions({mapTypeId: google.maps.MapTypeId.ROADMAP}); } else if (map_style.google_maps_base_type=="satellite") { params.map.setOptions({mapTypeId: google.maps.MapTypeId.SATELLITE}); } else if (map_style.google_maps_base_type=="terrain") { params.map.setOptions({mapTypeId: google.maps.MapTypeId.TERRAIN}); } else { var mapStyles = [ { stylers: [ { saturation: -65 }, { gamma: 1.52 } ] },{ featureType: "administrative", stylers: [ { saturation: -95 }, { gamma: 2.26 } ] },{ featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "administrative.locality", stylers: [ { visibility: "off" } ] },{ featureType: "road", stylers: [ { visibility: "simplified" }, { saturation: -99 }, { gamma: 2.22 } ] },{ featureType: "poi", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "road.arterial", stylers: [ { visibility: "off" } ] },{ featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "transit", stylers: [ { visibility: "off" } ] },{ featureType: "road", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "poi", stylers: [ { saturation: -55 } ] } ]; map_style.google_maps_customization_style = mapStyles; params.map.setOptions({mapTypeId: google.maps.MapTypeId.ROADMAP}); } // Custom tiles if (!map_style) { map_style = {google_maps_customization_style: []}; } params.map.setOptions({styles: map_style.google_maps_customization_style}); }, error: function(e, msg) { params.debug && console.debug('Error setting map style: ' + msg); } }); } // Add cartodb tiles to the map function addSimpleCartoDBTiles(params) { // Add the cartodb tiles var cartodb_layer = { getTileUrl: function(coord, zoom) { return 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/'+zoom+'/'+coord.x+'/'+coord.y+'.png?sql='+params.query; }, tileSize: new google.maps.Size(256, 256), name: params.query, description: false }; params.layer = new google.maps.ImageMapType(cartodb_layer); params.map.overlayMapTypes.insertAt(0,params.layer); } // Add cartodb tiles to the map function addWaxCartoDBTiles(params) { // interaction placeholder var currentCartoDbId; params.tilejson = generateTileJson(params); params.infowindow = new CartoDB.Infowindow(params); params.cache_buster = 0; params.waxOptions = { callbacks: { out: function(){ params.map.setOptions({draggableCursor: 'default'}); }, over: function(feature, div, opt3, evt){ params.map.setOptions({draggableCursor: 'pointer'}); }, click: function(feature, div, opt3, evt){ // If there are more than one cartodb layer, close all possible infowindows params.infowindow.hideAll(); params.infowindow.open(feature,evt.latLng); } }, clickAction: 'full' }; params.layer = new wax.g.connector(params.tilejson); params.map.overlayMapTypes.insertAt(0,params.layer); params.interaction = wax.g.interaction(params.map, params.tilejson, params.waxOptions); } // Refresh wax interaction function refreshWax(params,sql) { if (params.infowindow) { params.cache_buster++; params.query = sql; params.tilejson = generateTileJson(params); // Remove old wax removeOldLayer(params.map,params.layer); // Setup new wax params.tilejson.grids = wax.util.addUrlData(params.tilejson.grids_base, 'cache_buster=' + params.cache_buster); // Add map tiles params.layer = new wax.g.connector(params.tilejson); params.map.overlayMapTypes.insertAt(0,params.layer); // Add interaction params.interaction.remove(); params.interaction = wax.g.interaction(params.map, params.tilejson, params.waxOptions); } } // Refresh tiles function refreshTiles(params,sql) { // If you are not using interaction on the tiles... let's update your tiles if (!params.infowindow) { // First remove previous cartodb - tiles. removeOldLayer(params.map,params.layer); // Then add the cartodb tiles params.query = sql; var cartodb_layer = { getTileUrl: function(coord, zoom) { return 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/'+zoom+'/'+coord.x+'/'+coord.y+'.png?sql='+params.query; }, tileSize: new google.maps.Size(256, 256), name: params.query, description: false }; params.layer = new google.maps.ImageMapType(cartodb_layer); params.map.overlayMapTypes.insertAt(0,params.layer); } } function generateTileJson(params) { var core_url = 'http://' + params.user_name + '.cartodb.com'; var base_url = core_url + '/tiles/' + params.table_name + '/{z}/{x}/{y}'; var tile_url = base_url + '.png?cache_buster=0'; var grid_url = base_url + '.grid.json'; // SQL? if (params.query) { var query = 'sql=' + params.query; tile_url = wax.util.addUrlData(tile_url, query); grid_url = wax.util.addUrlData(grid_url, query); } // Map key ? if (params.map_key) { var map_key = 'map_key=' + params.map_key; tile_url = wax.util.addUrlData(tile_url,map_key); grid_url = wax.util.addUrlData(grid_url,map_key); } // Tiles style ? if (params.tile_style) { var style = 'style=' + encodeURIComponent(params.tile_style); tile_url = wax.util.addUrlData(tile_url,style); grid_url = wax.util.addUrlData(grid_url,style); } // Build up the tileJSON // TODO: make a blankImage a real 'empty tile' image return { blankImage: 'blank_tile.png', tilejson: '1.0.0', scheme: 'xyz', tiles: [tile_url], grids: [grid_url], tiles_base: tile_url, grids_base: grid_url, name: params.query, description: true, formatter: function(options, data) { currentCartoDbId = data.cartodb_id; return data.cartodb_id; }, cache_buster: function(){ return params.cache_buster; } }; } // Remove old cartodb layer added (wax or imagemaptype) function removeOldLayer(map,layer) { if (layer) { var pos = -1; map.overlayMapTypes.forEach(function(map_type,i){ if (layer == map_type && map_type.name == layer.name && map_type.description == layer.description) { pos = i; } }); if (pos!=-1) map.overlayMapTypes.removeAt(pos); layer = null; } } // Update tiles & interactivity layer; google.maps.CartoDBLayer.prototype.update = function(sql) { // Hide the infowindow if (this.params.infowindow) this.params.infowindow.hide(); // Refresh wax refreshWax(this.params,sql); // Refresh tiles refreshTiles(this.params,sql); this.params.active = true; this.params.visible = true; }; // Destroy layers from the map google.maps.CartoDBLayer.prototype.destroy = function() { // First remove previous cartodb - tiles. removeOldLayer(this.params.map,this.params.layer); if (this.params.infowindow) { // Remove wax interaction this.params.interaction.remove(); this.params.infowindow.hide(); } this.params.active = false; }; // Hide layers from the map google.maps.CartoDBLayer.prototype.hide = function() { this.destroy(); this.params.visible = false; }; // Show layers from the map google.maps.CartoDBLayer.prototype.show = function() { if (!this.params.visible || !this.params.active) { this.update(this.params.query); this.params.visible = true; } }; // CartoDB layer visible? google.maps.CartoDBLayer.prototype.isVisible = function() { return this.params.visible; }; }; } /** * CartoDB.Infowindow * @xavijam **/ CartoDB.Infowindow = function (params) { this.latlng_ = new google.maps.LatLng(0,0); this.feature_; this.map_ = params.map; this.columns_; this.offsetHorizontal_ = -107; this.width_ = 214; this.setMap(params.map); this.params_ = params; }; CartoDB.Infowindow.prototype = new google.maps.OverlayView(); CartoDB.Infowindow.prototype.draw = function() { var me = this; var div = this.div_; if (!div) { div = this.div_ = document.createElement('DIV'); div.className = "cartodb_infowindow"; div.innerHTML = 'x'+ '
'+ '
'+ '
'+ '
'+ '
'+ ''+ '
'; $(div).find('a.close').click(function(ev){ ev.preventDefault(); ev.stopPropagation(); me.hide(); }); google.maps.event.addDomListener(div,'click',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;}); google.maps.event.addDomListener(div,'dblclick',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;}); google.maps.event.addDomListener(div,'mousedown',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;}); google.maps.event.addDomListener(div,'mouseup',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;}); google.maps.event.addDomListener(div,'mousewheel',function(ev){ev.stopPropagation();}); google.maps.event.addDomListener(div,'DOMMouseScroll',function(ev){ev.stopPropagation();}); var panes = this.getPanes(); panes.floatPane.appendChild(div); div.style.opacity = 0; } var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_); if (pixPosition) { div.style.width = this.width_ + 'px'; div.style.left = (pixPosition.x - 49) + 'px'; var actual_height = - $(div).height(); div.style.top = (pixPosition.y + actual_height + 5) + 'px'; } }; CartoDB.Infowindow.prototype.setPosition = function() { if (this.div_) { var div = this.div_; var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_); if (pixPosition) { div.style.width = this.width_ + 'px'; div.style.left = (pixPosition.x - 49) + 'px'; var actual_height = - $(div).height(); div.style.top = (pixPosition.y + actual_height + 10) + 'px'; } this.show(); } }; CartoDB.Infowindow.prototype.open = function(feature,latlng){ var that = this , infowindow_sql = 'SELECT * FROM ' + this.params_.table_name + ' WHERE cartodb_id=' + feature; that.feature_ = feature; // If the table is private, you can't run any api methods if (this.params_.feature!=true) { infowindow_sql = encodeURIComponent(this.params_.feature.replace('{{feature}}',feature)); } $.ajax({ url:'http://'+ this.params_.user_name +'.cartodb.com/api/v2/sql/?q='+infowindow_sql, dataType: 'jsonp', timeout: 2000, callbackParameter: 'callback', success: function(result) { positionateInfowindow(result.rows[0],latlng); }, error: function(e,msg) { that.params_.debug && console.debug('Error retrieving infowindow variables: ' + msg); } }); function positionateInfowindow(variables,center) { if (that.div_) { var div = that.div_; // Get latlng position that.latlng_ = latlng; // Remove the unnecessary html $('div.cartodb_infowindow div.outer_top div.top').html(''); $('div.cartodb_infowindow div.outer_top div.bottom label').html(''); // List all the new variables for (p in variables) { if (p!='cartodb_id' && p!='cdb_centre' && p!='the_geom_webmercator') { $('div.cartodb_infowindow div.outer_top div.top').append('

'+(variables[p] || 'empty')+'

'); } } // Show cartodb_id? if (variables['cartodb_id']) { $('div.cartodb_infowindow div.bottom label').html('id: '+feature+''); } that.moveMaptoOpen(); that.setPosition(); } } }; CartoDB.Infowindow.prototype.hide = function() { if (this.div_) { var div = this.div_; $(div).animate({ top: '+=' + 10 + 'px', opacity: 0}, 100, 'swing', function () { div.style.visibility = "hidden"; } ); } }; CartoDB.Infowindow.prototype.show = function() { if (this.div_) { var div = this.div_; div.style.opacity = 0; div.style.visibility = "visible"; $(div).animate({ top: '-=' + 10 + 'px', opacity: 1}, 250 ); } }; CartoDB.Infowindow.prototype.hideAll = function() { $('div.cartodb_infowindow').css('visibility','hidden'); }; CartoDB.Infowindow.prototype.isVisible = function(marker_id) { if (this.div_) { var div = this.div_; if (div.style.visibility == 'visible' && this.feature_!=null) { return true; } else { return false; } } else { return false; } }; CartoDB.Infowindow.prototype.transformGeoJSON = function(str) { var json = $.parseJSON(str); return new google.maps.LatLng(json.coordinates[1],json.coordinates[0]); }; CartoDB.Infowindow.prototype.moveMaptoOpen = function() { var left = 0; var top = 0; var div = this.div_; var pixPosition = this.getProjection().fromLatLngToContainerPixel(this.latlng_); if ((pixPosition.x + this.offsetHorizontal_) < 0) { left = (pixPosition.x + this.offsetHorizontal_ - 20); } if ((pixPosition.x + 180) >= ($('#'+this.params_.map_canvas).width())) { left = (pixPosition.x + 180 - $('#'+this.params_.map_canvas).width()); } if ((pixPosition.y - $(div).height()) < 0) { top = (pixPosition.y - $(div).height() - 30); } this.map_.panBy(left,top); }; })(jQuery);