Deprecated
Google has now stopped its Earth API and Maps API is a paid for API nowadays. Some capabilities have also been either removed or changed. Therefore, I had to remove all examples of VizMap applications I had created. I didn’t remove the entire topic as I thought some of the code may still be useful with some modifications.
VizMap: Javascript howTos
This relates to Data Driven Mapping applications
Firstly – I am not an expert in javaScript, but in the accelerated learning process to discover what was necessary to create this application generator, there were some things that were hard to track down. In this section I will share those with you and hope you find them useful.
The VizMap generated application
Using our example application , we will use extracts in the following pages to illustrate ‘howTos’
- Creating a tabbed Google Mapping InfoWindow
- Embedding Google Analytics in your VizMap application
- Using Google Visualization DataTables
- Using Google Maps
- Using Google Visualization DataViews
- Using Google Visualization charts and tables
- Using Google Earth
- Click events in Google Earth
- Flying around with Google Earth
- Using Twitter in Earth and Maps
Here is the complete generated code including the data
<!DOCTYPE html> <HTML> <HEAD> <STYLE type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0; } #map_canvas { height: 100%; } .mcinfo { margin: 0px; font-family: sans-serif; font-size: 0.7em; padding: 0px; background-color: #0; vertical-align: top; } .mcelementtd { vertical-align:top; align-text:left; } .mcli { display: inline; border: 1px solid Teal; padding: .2em; padding-bottom: 0px; background-color: Azure; border-top-left-radius: 5px; -moz-border-radius-topleft: 5px; border-top-right-radius: 5px; -moz-border-radius-topright: 5px; } .mctabselected { background-color: WhiteSmoke; border-bottom: 1px solid WhiteSmoke; } .mchover:hover { background-color: LightSeaGreen; } .mcselected { background-color: WhiteSmoke; margin: 0px; padding: .3em; border: 1px solid Teal; border-bottom-left-radius: 15px; -moz-border-radius-bottomleft: 15px; border-bottom-right-radius: 15px; -moz-border-radius-bottomright: 15px; border-top-right-radius: 15px; -moz-border-radius-topright: 15px; } .mccheck { width : 0.7em; height :0.7em; padding: 0px; margin: 0px; } .mcstatus { display: inline; font-family: sans-serif; font-size: 0.7em; border: 1px solid Gray; border-radius: 5px; -moz-border-radius: 5px; float: right; } .mcready { color: WhiteSmoke; background-color: Teal; } .mcbusy { color: WhiteSmoke; background-color: FireBrick; } .mctable { font-family: sans-serif; font-size: 0.7em; border: 1px solid Gray; } .mcearth { width:240px; height:180px; border: 1px solid gray; } .mctableheader { font-family: sans-serif; font-size: 0.6em; background-color: Azure; border: 1px solid Gray; } .mctweetheader { font-family: sans-serif; font-size: 1em; background-color: gainsboro; color:darkslategrey; align:left; vertical-align:top; } .mctweet { font-family: sans-serif; font-size: 1em; background-color:WhiteSmoke; color:DarkSlateGrey; align:left; vertical-align:top; } .mchide { display: none; } .mcshow { display: block; } </STYLE> <SCRIPT type='text/javascript' src='http://maps.googleapis.com/maps/api/js?sensor=false'></SCRIPT><script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script> <SCRIPT type='text/javascript' src='https://www.google.com/jsapi'></SCRIPT><SCRIPT type="text/javascript">var mcpher = new function() { // ---ramblings.mcpher.com // ---insert functions to provide data just before here // mcpherVizMap // you may use this code as you wish // but please acknowledge in code, and in postings as below // Bruce Mcpherson // ramblings.mcpher.com // Any feedback or questions - http://groups.google.com/group/excel-ramblings // --- var vMap; var providers = {'google earth':'earth','google maps':'maps'}; mcpherInitialize(); // --get the included packages and draw everything when done function mcpherInitialize() { google.load('earth', '1'); google.load('visualization', '1', {packages:['table','imagebarchart','orgchart']}); google.setOnLoadCallback(mcpherDrawEverything); } //-- prepare all the content function mcpherDrawEverything() { vMap = new mcpherVizMap(); // get the data and framework if ( vMap.populate()) { // assuming there is some data if (vMap.cJobject.length > 0) { // set up all the spots if (vMap.create()) { //maps or earth? vMap.createProvider(); //create map items for each data item vMap.createItems(); // add spots to map switch (vMap.provider) { case 'maps': vMap.spotEverything(); break; case 'earth': // will be called asynchronously when earth is ready break; default: alert ('draweverythinginsanity'); } } } else { this.statusUpdate('..nothing to do',true,'there was no data'); } } } //---Excel generated framework function mcpherGetFramework () { return ( { "framework":{ "dictionary":{ "Latitude":"number", "Longtitude":"number", "SpotID":"number", "Title":"string", "Content":"string", "Code":"string", "Country":"string", "Venue":"string", "State":"string", "City":"string", "Address":"string", "Shows":"number", "ID":"number", "Average Price":"number", "Price":"number", "Date":"date", "Artist":"string", "Artist Photo":"string", "Venue Photo":"string" }, "measures":{ "Average Price":"average", "Shows":"count" }, "control":{ "browser":"default", "provider":"google earth", "tilt":"60", "range":"500", "flyspeed":"0.9", "flyrange":"200", "flyincrement":"1", "imagewidth":"80", "placemarkscale":"3", "adjustlightfortimeofday":"yes", "twitterimagewidth":"50", "twitterradius":".5mi", "twitterlanguage":"en", "twittermaxtweets":"10" }, "tabs":{ "Detail":{ "filter":[ "SpotID" ], "chart":[ "Artist", "Average Price" ], "table":[ "Date", "City", "State", "Country", "Venue", "Artist", "Price" ], "twitter":[ "Artist" ], "image":"Venue Photo", "charttype":"barchart" }, "Artist":{ "filter":[ "Artist" ], "chart":[ "City", "Average Price" ], "table":[ "Date", "City", "State", "Country", "Venue", "Artist", "Price" ], "twitter":[ "Artist" ], "image":"Artist Photo", "charttype":"barchart" }, "Country":{ "filter":[ "Code" ], "chart":[ "Artist", "Average Price" ], "table":[ "Date", "City", "State", "Venue", "Artist", "Price" ], "twitter":[ "Country", "Artist" ], "image":"Venue Photo", "charttype":"barchart" }, "City":{ "filter":[ "Code", "State", "City" ], "chart":[ "Artist", "Average Price" ], "table":[ "Date", "Venue", "Artist", "Price" ], "twitter":[ "City", "Artist" ], "image":"Venue Photo", "charttype":"barchart" }, "Venue":{ "filter":[ "ID" ], "chart":[ "Artist", "Average Price" ], "table":[ "Date", "Artist", "Price" ], "twitter":[ "Venue" ], "image":"Venue Photo", "charttype":"barchart" }, "World":{ "chart":[ "Artist", "Shows", "Average Price" ], "table":[ "Date", "Venue", "Artist", "Price" ], "twitter":[ "Artist" ], "image":"Venue Photo", "charttype":"barchart" } }, "elements":{ "content":{ "position":"2", "show":"yes" }, "filters":{ "position":"4", "show":"no" }, "image":{ "position":"3", "show":"yes" }, "checks":{ "position":"5", "show":"yes" }, "table":{ "position":"6", "show":"yes" }, "summary":{ "position":"7", "show":"yes" }, "chart":{ "position":"8", "show":"yes" }, "title":{ "position":"1", "show":"no" }, "navigate":{ "position":"11", "show":"yes" }, "twitter":{ "position":"0", "show":"yes" } }, "spots":{ "earth marker":"Artist Photo", "maps marker":"" } } } ) ; } //---ramblings.mcpher.com //---Excel generated data function mcpherGetData () { return ( { "data":{ "cJobject":[ { "Latitude":"41.976641", "Longtitude":"-87.86369", "SpotID":"101", "Title":"Rosemount theatre", "Content":"\<b\>Rosemount theatre\</b\>\<br\>Rosemont Theater\<br\> 5400 N River Rd\<br\> Rosemont\<br\> IL 60018-5409\<br\> USA\<br\>", "Code":"US", "Country":"United States", "Venue":"Rosemount theatre", "State":"IL", "City":"Rosemont", "Address":"Rosemont Theater, 5400 N River Rd, Rosemont, IL 60018-5409, USA", "Shows":"101", "ID":"101", "Average Price":"100", "Price":"100", "Date":"11/5/2011", "Artist":"Sting", "Artist Photo":"https://ramblings.mcpher.com/images/pic243.png", "Venue Photo":"https://ramblings.mcpher.com/images/pic239.png" }, { "Latitude":"41.976641", "Longtitude":"-87.86369", "SpotID":"101", "Title":"Rosemount theatre", "Content":"\<b\>Rosemount theatre\</b\>\<br\>Rosemont Theater\<br\> 5400 N River Rd\<br\> Rosemont\<br\> IL 60018-5409\<br\> USA\<br\>", "Code":"US", "Country":"United States", "Venue":"Rosemount theatre", "State":"IL", "City":"Rosemont", "Address":"Rosemont Theater, 5400 N River Rd, Rosemont, IL 60018-5409, USA", "Shows":"101", "ID":"101", "Average Price":"90", "Price":"90", "Date":"11/13/2011", "Artist":"Paul Simon", "Artist Photo":"https://ramblings.mcpher.com/images/pic241.png", "Venue Photo":"https://ramblings.mcpher.com/images/pic239.png" }, { "Latitude":"30.029015", "Longtitude":"-90.0522531", "SpotID":"102", "Title":"UNO Lakefront Arena", "Content":"\<b\>UNO Lakefront Arena\</b\>\<br\>6801 Franklin Ave\<br\> New Orleans\<br\> LA 70122\<br\> USA\<br\>", "Code":"US", "Country":"United States", "Venue":"UNO Lakefront Arena", "State":"LA", "City":"New Orleans", "Address":"6801 Franklin Ave, New Orleans, LA 70122, USA", "Shows":"102", "ID":"102", "Average Price":"120", "Price":"120", "Date":"11/4/2011", "Artist":"Linkin Park", "Artist Photo":"https://ramblings.mcpher.com/images/pic242.png", "Venue Photo":"https://ramblings.mcpher.com/images/pic238.png" }, { "Latitude":"34.6381228", "Longtitude":"135.4253848", "SpotID":"103", "Title":"Intex", "Content":"\<b\>Intex\</b\>\<br\>Nankokita\<br\> Suminoe Ward\<br\> Osaka\<br\> Osaka Prefecture 559-0034\<br\> Japan\<br\>", "Code":"JP", "Country":"Japan", "Venue":"Intex", "State":"", "City":"Suminoe Ward", "Address":"Nankokita, Suminoe Ward, Osaka, Osaka Prefecture 559-0034, Japan", "Shows":"103", "ID":"103", "Average Price":"80", "Price":"80", "Date":"9/16/2011", "Artist":"Linkin Park", "Artist Photo":"https://ramblings.mcpher.com/images/pic242.png", "Venue Photo":"https://ramblings.mcpher.com/images/pic240.png" }, { "Latitude":"37.5198209", "Longtitude":"127.1227901", "SpotID":"104", "Title":"olympic park", "Content":"\<b\>olympic park\</b\>\<br\>Olympic Park\<br\> 88 Bangi-dong\<br\> Songpa-gu\<br\> Seoul\<br\> South Korea\<br\>", "Code":"KR", "Country":"South Korea", "Venue":"olympic park", "State":"", "City":"Seoul", "Address":"Olympic Park, 88 Bangi-dong, Songpa-gu, Seoul, South Korea", "Shows":"104", "ID":"104", "Average Price":"67", "Price":"67", "Date":"9/8/2011", "Artist":"Linkin Park", "Artist Photo":"https://ramblings.mcpher.com/images/pic242.png", "Venue Photo":"" }, { "Latitude":"13.8448335", "Longtitude":"100.3610179", "SpotID":"105", "Title":"aktiv square", "Content":"\<b\>aktiv square\</b\>\<br\>Ban Mai\<br\> Bang Yai\<br\> Nonthaburi 11140\<br\> Thailand\<br\>", "Code":"TH", "Country":"Thailand", "Venue":"aktiv square", "State":"", "City":"Bang Yai", "Address":"Ban Mai, Bang Yai, Nonthaburi 11140, Thailand", "Shows":"105", "ID":"105", "Average Price":"50", "Price":"50", "Date":"9/23/2011", "Artist":"Linkin Park", "Artist Photo":"https://ramblings.mcpher.com/images/pic242.png", "Venue Photo":"" }, { "Latitude":"55.7532225", "Longtitude":"37.6215052", "SpotID":"106", "Title":"red square", "Content":"\<b\>red square\</b\>\<br\>Red Square\<br\> Moscow\<br\> Russia\<br\>", "Code":"RU", "Country":"Russian Federation", "Venue":"red square", "State":"administrativnyy okrug Tsentral\'nyy", "City":"Moscow", "Address":"Red Square, Moscow, Russia", "Shows":"106", "ID":"106", "Average Price":"20", "Price":"20", "Date":"6/23/2011", "Artist":"Linkin Park", "Artist Photo":"https://ramblings.mcpher.com/images/pic242.png", "Venue Photo":"" }, { "Latitude":"52.3123574", "Longtitude":"4.9441509", "SpotID":"107", "Title":"heineken hall", "Content":"\<b\>heineken hall\</b\>\<br\>Arena Boulevard 590\<br\> 1101 Amsterdam Zuidoost\<br\> The Netherlands\<br\>", "Code":"NL", "Country":"The Netherlands", "Venue":"heineken hall", "State":"Government of Amsterdam", "City":"Amsterdam Zuidoost", "Address":"Arena Boulevard 590, 1101 Amsterdam Zuidoost, The Netherlands", "Shows":"107", "ID":"107", "Average Price":"40", "Price":"40", "Date":"6/23/2011", "Artist":"Toto", "Artist Photo":"https://ramblings.mcpher.com/images/pic244.png", "Venue Photo":"" } ] } } ) ; } //---ramblings.mcpher.com // Start of mcpherVizMap var mcpherVizMap = function () { this.cssBusy = 'mcstatus mcbusy'; this.cssReady = 'mcstatus mcready'; this.cssInfoWindow = 'mcinfo'; this.cssLi = 'mcli'; this.cssTab = this.cssLi + ' mchover'; this.cssTabSelected = this.cssTab + ' mctabselected'; this.cssSelected = 'mcselected'; this.cssHide = 'mchide'; this.cssShow = 'mcshow'; this.cssChart = 'mchart'; this.cssTableOptions = { alternatingRowStyle: true, showRowNumber: false, allowHtml: true, cssClassNames: { headerCell: 'mctableheader', tableCell: 'mctable' } }; }; mcpherVizMap.prototype.populate = function() { //some setup we can do while waiting for the google modules to load // get the jSon for the tabular data this.data = mcpherGetData().data; this.framework = mcpherGetFramework().framework; // these are all the fields that are known if (!(this.dictionary = this.framework.dictionary)) { alert('no data dictionary found'); return (null); } // various control information if (!(this.control = this.framework.control)) { alert('no control found'); return (null); } // various control information if (!(this.spotControl = this.framework.spots)) { alert('no spot control found'); return (null); } // the element containing the data if (!(this.cJobject = this.data.cJobject)) { alert('no data found'); return (null); } // get the names of the tabs if (!(this.cTabs = this.framework.tabs )) { alert('no tab definitions found'); return (null); } // and the measures if (!(this.cMeasures = this.framework.measures )) { alert('no measure definitions found'); return (null); } // and the elements if (!(this.cElements = this.framework.elements )) { alert('no element definitions found'); return (null); } // check we have minimum mapping fields for (var key in {'Latitude':0,'Longtitude':0,'Content':0,'Title':0,'SpotID':0}) { if (!this.dictionary[key]) { alert ('Missing required dictionary field ' + key); return (null); } } // find out if we are doing maps or earth if (this.control.provider){ this.provider = providers[this.control.provider]; } if (!this.provider){ alert('unknown provider ' + this.control.provider); return (null); } // these are the things that will be used in the summary and the chart this.measures = new Array (); for (var mkey in this.cMeasures) { this.measures.push(mkey); } // this is for positioning each of the tables & charts etc this.elements = new Array(); for (var ekey in this.cElements) { this.elements.push(ekey); } // these are the tabs that will be shown this.tabs = new Array (); for (var tkey in this.cTabs) { this.tabs.push(tkey); } return(this); }; mcpherVizMap.prototype.create = function() { this.statusDiv = document.getElementById('status_div'); this.statusUpdate('..loading data and map',true); this.tempDiv = document.body.appendChild(document.createElement('div')); this.tempDiv.className = this.cssHide; // create a set of markers this.items = new Array (this.cJobject.length); this.mTable = this.masterTable(); // get the unique spots this.spots = new Array(); for ( var i = 0 ; i < this.cJobject.length;i++) { for (var j=0; j < this.spots.length ;j++) { if (this.spots[j].spotId == this.cJobject[i].SpotID) { break; } } if (j >= this.spots.length) { this.spots.push(new mcpherSpot(this,this.spots.length,i)); } } return (this); }; mcpherVizMap.prototype.createProvider = function() { // get parameters if any var qparams = mcpherGetqparams(); var cj = this.cJobject; this.mapDiv = document.getElementById('map_canvas'); switch (this.provider) { case 'maps': var bounds = new google.maps.LatLngBounds(); // create a bounding box for ( var i = 0; i < cj.length; i++ ) { bounds.extend (new google.maps.LatLng(cj[i].Latitude, cj[i].Longtitude)); } var myOptions = { mapTypeId: google.maps.MapTypeId.ROADMAP }; if (!qparams['zoom']) { qparams['zoom'] = 2; } myOptions['zoom'] = parseInt(qparams['zoom']); // create the map and show an info window this.map = new google.maps.Map(this.mapDiv, myOptions); this.map.fitBounds(bounds); break; case 'earth': this.vEarth = new mcpherEarth(this,'google'); this.vEarth.createInstance(this.mapDiv); break; default: alert ('lost the provider'); break; } }; mcpherVizMap.prototype.spotEverything = function() { var vm=this; for ( var i = 0 ; i < vm.spots.length;i++) { vm.spots[i].createSpot (); } // add the spots to each item vm.spotItems(); vm.statusUpdate('..Good to go!',false,null); }; mcpherVizMap.prototype.gotoAnotherSpot = function (spot,newSpot) { var vm = this; if (newSpot.spotIndex != spot.spotIndex ){ vm.statusUpdate('change spot from '+ spot.title + ' to '+ newSpot.title,true,null); switch (vm.provider){ case 'maps': spot.infoWindow.close(); break; case 'earth': var ve = vm.vEarth; ve.ge.setBalloon(null); ve.showLookAt(newSpot); break; default: alert('dealwithinsanity'); break; } vm.statusUpdate('changed spot from '+ spot.title + ' to '+ newSpot.title,false,null); } }; mcpherVizMap.prototype.statusUpdate = function(s,isbusy,a) { if (this.statusDiv){ this.statusDiv.innerHTML = (new Date()).toTimeString() + '-' + s; if (isbusy) { this.statusDiv.className = this.cssBusy; } else { this.statusDiv.className = this.cssReady; } } if (a) { alert(a); } }; mcpherVizMap.prototype.isMeasure = function(key) { for ( var i= 0; i < mcpherSize(this.measures) ; i++) { if (this.measures[i] == key ) { return (true); } } return(false); }; mcpherVizMap.prototype.columnType = function(key) { return (this.dictionary[key]); }; mcpherVizMap.prototype.columnValue = function(key,value) { // given a key, set the type for a table (it will probably be string in the incoming json var x; switch ( this.columnType(key) ){ case 'number': if (!(mcpherIsNumber(value))){ alert (key + ' has invalid value ' + value + ' for type' + this.columnType(key) ); } else { x = mcpherToNumber(value); } break; case 'date': if (!(x=mcpherToDate(value))){ alert (key + ' has invalid value ' + value + ' for type' + this.columnType(key) ); } break; default: x=value; break; } return (x); }; mcpherVizMap.prototype.masterTable = function() { // create a master google viz table gTable = new google.visualization.DataTable(); for ( key in this.dictionary) { gTable.addColumn (this.columnType(key), key); } var currentCol; for (var i = 0 ; i < this.cJobject.length ; i++){ var currentRow = gTable.addRow(); for ( var key in this.cJobject[i]){ if ( (currentCol = this.columnIndex (gTable,key)) >= 0){ gTable.setCell(currentRow,currentCol,this.columnValue(key,this.cJobject[i][key])); } else { alert ("Unknown column name " + key + " at row " + currentRow + " in cJobject data"); } } } return (gTable); }; mcpherVizMap.prototype.spotItems = function () { for (var i=0; i<this.items.length;i++) { // find matching spot; var spot = null; for (var j=0; j < this.spots.length ;j++){ if (this.spots[j].spotId == this.items[i].spotId) { spot = this.spots[j]; break; } } if (spot){ this.items[i].spot = this.spots[j]; } else { alert('couldnt find spot for item ' + i); } } }; mcpherVizMap.prototype.createItems = function () { // create the infotab contents for each plottable item for (var i=0; i<this.cJobject.length;i++) { this.items[i] = new mcpherItem(); this.items[i].create (this, i); } }; mcpherVizMap.prototype.columnIndex = function (gTab,label) { for (var i=0; i < gTab.getNumberOfColumns() ;i++) { if (gTab.getColumnLabel(i) == label) { return (i); } } return (-1); }; mcpherVizMap.prototype.nextSpot = function (spot) { var vm=this; return (vm.spots[(spot.spotIndex +1) % vm.spots.length]); }; // END OF mcpherVizMap // --INFOTAB custom object to hold infowindow tab data var mcpherInfoTab = function () {this.init();}; mcpherInfoTab.prototype.init = function () { return (this); }; mcpherInfoTab.prototype.showOrNot =function () { // are we showing it? var vm = this.parent.parent; for (var key in this.infoElements) { if (this.checkBoxes[key]){ this.infoElements[key].className = this.checkBoxes[key].checked ? vm.cssShow : vm.cssHide; } } }; mcpherInfoTab.prototype.createDivElements = function (container) { // this creates the structure for div elements of the infowindow that are indexed by the framework this.divElements = new Array(); this.divContainer = container.appendChild(document.createElement('div')); var labBody = this.divContainer.appendChild( document.createElement('table')).appendChild(document.createElement('tbody')); var labTds = new Array(); // because of the rowspans and colspans its kind of tricky // r0 3 cells this.createTds(labBody,labTds, 1 , 3); // r1 2 cells this.createTds(labBody,labTds, 1 , 2); // r2-5 1 cells this.createTds(labBody,labTds, 4 , 1); // r6 2 cell this.createTds(labBody,labTds, 1 , 2); // set up colspans & row spans and assign to positions this.pushDiv(labTds , 0 ,0 , 0, 0, 1); this.pushDiv(labTds , 1 ,0 , 0, 0, 2); this.pushDiv(labTds , 1 ,1 , 2, 0, 3); this.pushDiv(labTds , 2 ,0 , 0, 0, 4); this.pushDiv(labTds , 3 ,0 , 0, 2, 5); this.pushDiv(labTds , 4 ,0 , 0, 2, 6); this.pushDiv(labTds , 5 ,0 , 0, 2, 7); this.pushDiv(labTds , 6 ,0 , 0, 2, 8); this.pushDiv(labTds , 0 ,2 , 6, 0, 9); this.pushDiv(labTds , 6 ,1 , 0, 0, 10); this.pushDiv(labTds , 0 ,1 , 0, 0, 11); }; mcpherInfoTab.prototype.createTds =function (labBody,labTds, nRows, nCols ) { for (var i=0; i < nRows ;i++){ var labTrTds = new Array(); var labTr = labBody.appendChild(document.createElement('tr')); for (var j=0; j < nCols ; j++){ labTrTds.push (labTr.appendChild(document.createElement('td'))); } labTds.push (labTrTds); } }; mcpherInfoTab.prototype.pushDiv =function (container, r ,c , rs, cs, pos ) { var cTd = container[r]; cTd.className= 'mcelementtd'; if (rs) { cTd.rowSpan = rs; } if (cs) { cTd.colSpan = cs; } this.divElements.push(cTd.appendChild(document.createElement('div'))); }; mcpherInfoTab.prototype.create =function (mm , tabIndex) { this.parent = mm; var vm = mm.parent; var ve = vm.ve; var conTab = this; this.tabIndex = tabIndex; this.tab = this.parent.ulContainer.appendChild( document.createElement('li')); this.tab.className = vm.cssTab ; mcpherAddEvent (this.tab,"click", function(){ mm.fillDiv(conTab);} ,false ,true) ; this.tab.appendChild(document.createTextNode (vm.tabs[this.tabIndex])); // create the structure of this info tab this.createDivElements(mm.container.appendChild (document.createElement('div'))); // now assign these divs to required elements var ie = this.infoElements = new Array(); for (key in vm.cElements){ var p = parseInt(vm.cElements[key].position); if (p){ if ( p<0 || p > this.divElements.length){ alert(' position ' + p + ' is not valid - refer to diagram in parameter sheet'); } else{ this.infoElements[key] = this.divElements[p -1]; } } } if (ie['title']) { ie['title'].appendChild (document.createTextNode(mm.cJobject.Title)); } // fill all that up if (ie['content']) { ie['content'].innerHTML= mm.cJobject.Content; } // add checkboxes if (ie['checks']) { this.checkBoxes = new Array(); for (key in vm.cElements) { if ( parseInt(vm.cElements[key].position)) { var cbx = document.createElement("input"); cbx.className = 'mccheck'; cbx.type = "checkbox"; cbx.id = "mcphercbx" + key; ie['checks'].appendChild( document.createTextNode('\u00a0\u00a0'+ key )); this.checkBoxes[key]= ie['checks'].appendChild(cbx); if (vm.cElements[key].show == "yes") { cbx.setAttribute("checked", "checked" ); } mcpherAddEvent (cbx,"click",function(){ conTab.showOrNot(); },false ,true) ; } } } if (ie['navigate']){ var bx = document.createElement('button'); bx.className = 'mcnextbutton'; bx.appendChild(document.createTextNode('Next Spot')); ie['navigate'].appendChild(bx); mcpherAddEvent (bx,"click",function(){ var nextSpot =vm.nextSpot(mm.spot) vm.gotoAnotherSpot(mm.spot,nextSpot); if (vm.provider=='maps') nextSpot.createInfoWindow(0); },false ,true) ; if (ve) { var bflyx = document.createElement('button'); bflyx.className = 'mcnextbutton'; bflyx.appendChild(document.createTextNode('Fly Around')); ie['navigate'].appendChild(bflyx); mcpherAddEvent (bflyx,"click",function(){ mm.spot.initFlyAround(); },false ,true) ; } } // is there an image? var imgField = vm.cTabs[vm.tabs[this.tabIndex]]['image']; if (ie['image'] && imgField && mm.cJobject[imgField]){ if ( mcpherIsImage(mm.cJobject[imgField])) { var labImage = ie['image'].appendChild(document.createElement('img')); labImage.src = mm.cJobject[imgField]; if (mcpherIsNumber(vm.control.imagewidth)){ labImage.width = mcpherToNumber(vm.control.imagewidth); } labImage.title = vm.Title; } } this.vTable = this.createTable(); // and a google viz table of qualifying matching transactions if (ie['table']){ if (this.vTable) { this.table = new google.visualization.Table(ie['table']); this.table.draw(this.vTable , mm.parent.cssTableOptions); // allow selection on this table var vizTab = this.table; var datTab = this.vTable; google.visualization.events.addListener(vizTab, 'select', function() { conTab.dealWithSelection (vizTab,datTab); }); } } // and a summary table if (ie['summary'] || ie['chart'] ){ if (this.cTable = this.chartTable()) { if(ie['summary']){ this.chtable = new google.visualization.Table(ie['summary']); this.chtable.draw(this.cTable , vm.cssTableOptions); } } } if (ie['chart']){ if (this.cTable) { var chtType = vm.cTabs[ vm.tabs[conTab.tabIndex] ]['charttype']; switch (chtType) { case 'barchart': this.chart = new google.visualization.ImageBarChart(ie['chart']); this.chart.draw(this.cTable, {title:vm.tabs[this.tabIndex], isStacked:false}); break; case 'orgchart': this.oTable = this.orgTable(); this.chart = new google.visualization.OrgChart(ie['chart']); this.chart.draw(this.oTable, {title:vm.tabs[this.tabIndex]}); // allow selection on this chart var vizTab = this.chart; var datTab = this.oTable; google.visualization.events.addListener(vizTab, 'select', function() { conTab.dealWithSelection (vizTab,datTab); }); break; case 'none': break; default: alert('unknown charttype ' + chtType); break; } } } // and which filters were selected if (ie['filters']){ this.fTable = new google.visualization.DataTable(); this.fTable.addColumn('string','Where'); this.fTable.addColumn('string','is'); for ( var key in this.filters) { var cLab = vm.mTable.getColumnLabel( this.filters[key].column) ; this.fTable.addRow ( [cLab,this.filters[key].value.toString()]); } this.filterTable = new google.visualization.Table(ie['filters']); this.filterTable.draw(this.fTable,vm.cssTableOptions); } // tweets needed - we'll do these in real time so they are up to date when selected. // but create a view table of the query items that we'll need if (ie['twitter']){ this.tTable = this.tweetTable(); } conTab.showOrNot(); return (this); }; mcpherInfoTab.prototype.viewTable = function (vType,needGroup,complain) { var mm = this.parent.parent; var tItems = mm.cTabs[ mm.tabs[this.tabIndex] ][vType]; var colIndex; if (!tItems) { if (complain)alert ('Cant find ' + vType + 'fields for ' + mm.tabs[this.tabIndex]); return (null); } else { // only include data columns referenced in tab[].table var viewColumns = new Array(); for ( var i=0 ; i < tItems.length ; i++ ){ if ( (colIndex = mm.columnIndex( mm.mTable,tItems[i])) >= 0){ viewColumns.push (colIndex); } else { alert (tItems[i] + ' is required for ' + vType + ' in tab ' + mm.tabs[this.tabIndex] + ' but was not found in the dictionary'); } } var gTable = new google.visualization.DataView(mm.mTable); //now filter rows var fItems = mm.cTabs[ mm.tabs[this.tabIndex] ]['filter']; if (fItems) { this.filters = new Array(); for (var i=0; i < fItems.length ; i++){ if ( (colIndex = mm.columnIndex( mm.mTable,fItems[i])) >= 0) { this.filters.push ({'column' : colIndex, 'value' : mm.columnValue( fItems[i],this.parent.cJobject[fItems[i]]) } ); } else{ alert (fItems[i] + ' is required filter for ' + vType + ' in tab ' + mm.tabs[this.tabIndex] + ' but was not found in the dictionary'); } } gTable.setRows (gTable.getFilteredRows(this.filters)); } if (!viewColumns.length) { alert ('there were no columns required for ' + vType + ' in tab ' + mm.tabs[this.tabIndex] ); return(null); } if (needGroup) { var grouping = new Array(); var aggregate = new Array(); for (var i=0; i < viewColumns.length ;i++ ) { var lab; if (mm.isMeasure( lab=gTable.getColumnLabel(viewColumns[i]))) { aggregate.push ( {'column': viewColumns[i], 'aggregation' : mcpherGag(mm.cMeasures[lab]), 'type' : 'number'} ); } else{ grouping.push (viewColumns[i]); } } return( google.visualization.data.group ( gTable, grouping, aggregate )) ; } else { gTable.setColumns(viewColumns); return (gTable) ; } } }; mcpherInfoTab.prototype.dealWithSelection = function (vizTab,datTab) { var row = vizTab.getSelection()[0].row; var newIndex = datTab.getTableRowIndex(row); // now we have the original item - we may need oc lose this infowindow and go somewhere else var im = this.parent; var vm = im.parent; var spot = im.spot; var markerIndex = im.markerIndex; if (newIndex != markerIndex){ switch (vm.provider){ case 'maps': spot.infoWindow.close(); break; case 'earth': break; default: alert('dealwithinsanity'); } // so we may need to close this one var newSpot =vm.items[newIndex].spot; vm.gotoAnotherSpot (spot, newSpot); newSpot.usingIndex = newIndex; newSpot.createInfoWindow(0); } }; mcpherInfoTab.prototype.createTable = function () { return (this.viewTable ('table', false,true)); }; mcpherInfoTab.prototype.chartTable = function () { return (this.viewTable ('chart',true,true)); }; mcpherInfoTab.prototype.orgTable = function () { return (this.viewTable ('chart',false,true)); }; mcpherInfoTab.prototype.tweetTable = function () { return (this.viewTable ('twitter',false,false)); }; // END OF INFOTAB // mcpherItem // custom object to hold marker plus associated data var mcpherItem = function () {this.init();}; mcpherItem.prototype.init = function () { return (this); }; mcpherItem.prototype.create = function (mm , markerIndex) { this.parent = mm; mMark=this; this.cJobject = this.parent.cJobject[markerIndex]; this.spotId= this.cJobject.SpotID; this.markerIndex = markerIndex; // this will be the info box detail this.container = this.parent.tempDiv.appendChild( document.createElement('div')); this.container.cssName = this.parent.cssHide; this.container.id ='itemcon'+this.markerIndex; // to hold the tab selectors this.tabContainer = this.container.appendChild( document.createElement('div')); this.infoTabs = new Array (this.parent.tabs.length); // making tabbed infowindow this.ulContainer = this.tabContainer; for (var i = 0; i < this.infoTabs.length;i++ ){ this.infoTabs[i] = new mcpherInfoTab(); this.infoTabs[i].create (this,i); } return this; }; mcpherItem.prototype.fillDiv =function (conTab) { var mm = this; mm.refreshTweets(conTab); // change the selected tab and load up the appropriate divs for (var i=0; i < mm.infoTabs.length ; i++) { if (conTab.tabIndex == i ) { mm.infoTabs[i].tab.className = mm.parent.cssTabSelected; mm.infoTabs[i].divContainer.className = mm.parent.cssShow + ' ' + mm.parent.cssSelected; } else { mm.infoTabs[i].tab.className = mm.parent.cssTab; mm.infoTabs[i].divContainer.className = mm.parent.cssHide; } } }; mcpherItem.prototype.refreshTweets = function (conTab) { var ie=conTab.infoElements['twitter']; var qt=conTab.tTable; var spot = this.spot; var vm = spot.parent; if (!ie)return(null); ie.innerHTML=''; var sArg = "?"; var sRest = "http://search.twitter.com/search.json"; // create the query from any terms in the twitter table var sQ =''; if (qt){ for (var i =0; i < qt.getNumberOfRows(); i++) { var sa=''; for (var j =0; j < qt.getNumberOfColumns(); j++) { var s = qt.getValue(i,j).toString(); if (s) { if(sa) sa += '+AND+'; sa += s ; } } if (sa) { if (sQ) sQ += '+OR+'; sQ += '(' + sa + ')'; } } } if (sQ){ sRest += sArg + "q=" + sQ; sArg = "&"; } // location based if (vm.control.twitterradius){ sRest += sArg + 'geocode=' + spot.lat + "," + spot.lng + "," + vm.control.twitterradius; sArg = "&"; } //language selected if (vm.control.twitterlanguage){ sRest += sArg + 'lang=' + vm.control.twitterlanguage; sArg = "&"; } //limit if (vm.control.twittermaxtweets){ sRest += sArg + 'rpp=' + vm.control.twittermaxtweets + '&page=1'; sArg = "&"; } $.getJSON(encodeURI(sRest) + '&callback=?', function(resp){ $(ie).append('<table><th class="mctweetheader" colspan=2>' + '<a href="https://twitter.com/brucemcpherson">' + 'Follow ramblings.mcpher.com @brucemcpherson</a>' + (vm.control.twitterradius ? '<br>Tweets within ' + vm.control.twitterradius + ' of ' + spot.title:'') + '</th>'); $.each(resp.results, function(i,item){ $(ie).append('<tr><td class="mctweet">' + '<img src="'+item.profile_image_url+ '" ' + (mcpherIsNumber(vm.control.twitterimagewidth) ? 'width="' + vm.control.twitterimagewidth +'"' : '') + '"</img></td>'+ '<td class="mctweet" rowspan="2">' + item.created_at+':'+item.text + '</tr><tr><td class="mctweet">@' + item.from_user + ':</td>' + '</tr>'); }); $(ie).append('</table>'); }); }; // END OF mcpherItem // mcpherSpot // custom object to hold spot plus associated data var mcpherSpot = function (mp,idx,ddx) {this.init(mp,idx,ddx);}; mcpherSpot.prototype.init = function (mp,idx,ddx) { this.parent = mp; this.spotIndex = idx; this.usingIndex = ddx; this.cJobject = mp.cJobject[ddx]; this.spotId = this.cJobject.SpotID; this.title = this.cJobject.Title; this.lat = parseFloat(this.cJobject.Latitude); this.lng = parseFloat(this.cJobject.Longtitude); return (this); }; mcpherSpot.prototype.createSpot = function () { // create a marker on the map var vm = this.parent; var spot = this; switch (vm.provider){ case 'maps': var gMarker = new google.maps.Marker({ position: new google.maps.LatLng(this.lat,this.lng), map: this.parent.map, title: this.title }); this.marker = gMarker; google.maps.event.addListener(gMarker, 'click', function(e) { mcpherClicks(e,spot,true); } ); break; case 'earth': vm.ve.plotSpot(spot); google.earth.addEventListener(spot.placemark, 'click', function(e) { mcpherClicks(e,spot,false); } ); break; default: alert('createspotinsanity'); break; } return this; }; function mcpherClicks(e,spot,plot){ if(e.preventDefault)e.preventDefault(); if(e.stopPropagation)e.stopPropagation(); if (spot.flying){ var ge = spot.parent.ve.ge; spot.stopFlyAround(); google.earth.addEventListener(ge.getView(), 'viewchangeend', mcpherFlyingClicks(spot,plot)); } else { spot.dealWithClicks(plot); } } function mcpherFlyingClicks(spot,plot){ var vm = spot.parent; var ge = vm.ve.ge; if(spot.timer) clearTimeout(spot.timer); spot.timer = setTimeout( function(){ spot.dealWithClicks(plot); }, 100); } function mcpherPlotBalloon(spot, iw ){ var vm = spot.parent; var ge = vm.ve.ge; vm.statusUpdate ('..waiting for view to settle down',true); google.earth.addEventListener(ge.getView(), 'viewchangeend', mcpherBalloon(spot,iw)); } function mcpherBalloon(spot, iw ){ var vm = spot.parent; var ge = vm.ve.ge; if(spot.timer) clearTimeout(spot.timer); spot.timer = setTimeout( function(){ ge.setBalloon(iw); vm.statusUpdate ('..information balloon set for ' + spot.title,false); }, 100); } mcpherSpot.prototype.stopFlyAround = function() { var spot = this; // clean up after flying if (spot.flying) { var vm = spot.parent; var ve = vm.ve; var ge = ve.ge; spot.flying = false; google.earth.removeEventListener(ge, 'frameend', function () { mcpherFly(spot); } ); spot.lookAt.setHeading(spot.initialLookAtHeading); spot.lookAt.setRange(parseFloat(vm.control.range)); ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT); ve.showLookAt(spot); vm.statusUpdate('..finished flying around '+spot.title,false); } return(spot.flying); } mcpherSpot.prototype.initFlyAround = function() { var spot = this; // been asked to rotate around the spot if (!spot.flying) { var vm = spot.parent; var ve = vm.ve; var ge = ve.ge; spot.flying = true; ge.setBalloon(null); spot.lookAt.setRange(parseFloat(vm.control.flyrange)); ge.getOptions().setFlyToSpeed(parseFloat(vm.control.flyspeed)); spot.initialLookAtHeading = spot.lookAt.getHeading(); google.earth.addEventListener(ge, 'frameend', function () { mcpherFly(spot); }); vm.statusUpdate('..flying around '+spot.title,true); mcpherFly(spot); } } function mcpherFly(spot){ // whats the point of this? // seems that i can't addevent listener of spot.fly directly... spot.fly(); } mcpherSpot.prototype.fly = function() { // changing the heading and re- do var spot = this; if (spot.flying){ var vm = spot.parent; var ve=vm.ve; var ge = ve.ge; var newHeading = spot.lookAt.getHeading() + parseFloat(vm.control.flyincrement); if (newHeading > 360) { newHeading -= 360; } spot.lookAt.setHeading(newHeading); ve.showLookAt(spot); } return (spot.flying); } mcpherSpot.prototype.dealWithClicks = function(showit) { var spot=this; var vm = this.parent; var doubleClickTimeout = 300; // now see if its the second click within the timeout period if (!vm.dealingWithClicks){ vm.dealingWithClicks = true; // this is single click behavior setTimeout( function() { if(vm.dealingWithClicks) { vm.dealingWithClicks = false; spot.createInfoWindow (0); }} , doubleClickTimeout); } else { // double click behavior vm.dealingWithClicks = false; vm.gotoAnotherSpot(spot,vm.nextSpot(spot)); if(showit)vm.nextSpot.createInfoWindow(0); } return(false); }; mcpherSpot.prototype.createInfoWindow = function (startTabIndex){ //default is the first tab var vm = this.parent; var spot=this; var gMap = spot.parent.map; var uItem = vm.items[spot.usingIndex]; var infoTab = uItem.infoTabs[startTabIndex]; vm.statusUpdate('..creating info for '+spot.title,true); // select the appropriate tab uItem.fillDiv(infoTab); // set the content var iDiv = document.createElement('div'); this.container = iDiv; iDiv.appendChild (uItem.container); iDiv.className = vm.cssShow + ' ' + vm.cssInfoWindow; var s= '..plotted info for '+spot.title; switch (vm.provider){ case 'maps': var iw = spot.infoWindow = new google.maps.InfoWindow (); iw.setContent (iDiv); iw.open(gMap,spot.marker); google.maps.event.addListener(iw, 'domready', function() { vm.statusUpdate(s,false,null); }); break; case 'earth': var ge = vm.vEarth.ge; var iw = spot.balloon = ge.createHtmlDivBalloon(''); iw.setFeature(spot.placemark); iw.setContentDiv(iDiv); mcpherPlotBalloon(spot, iw ); //ge.setBalloon(iw) break; default: alert ('createinfowindowinstanity'); break; } return (iw); }; // endof mcpherspot // mcpherEarth var mcpherEarth = function (vm,control) { if (control == 'google') { if (google.earth.isInstalled()){ this.vMap = vm; vMap.ve = this; this.control = control; return this; } else { alert("you need to install the google earth plug-in"); return null; } } else { alert ('unknown earth provider ' + control); return null; } }; mcpherEarth.prototype.createInstance = function (div) { var ve=this; var vm = this.vMap; ve.container = div; vm.statusUpdate('..creating google earth instance',true,null); google.earth.createInstance(ve.container, initCallback, failureCallback); }; mcpherEarth.prototype.success = function (instance) { var ve = this; var vm = ve.vMap; // the first spot var spot = vm.spots[0]; ve.ge = ge = instance; ge.getWindow().setVisibility(true); // add some layers ge.getLayerRoot().enableLayerById(ge.LAYER_ROADS, true); ge.getLayerRoot().enableLayerById(ge.LAYER_BUILDINGS, true); ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO); if (vm.control.adjustlightfortimeofday == 'yes'){ ge.getSun().setVisibility(true); ge.getOptions().setAtmosphereVisibility(true); } vm.statusUpdate('..google earth successfully created',false,null); vm.spotEverything(); // go to the first spot ve.showLookAt(spot); }; mcpherEarth.prototype.plotSpot = function (spot) { var ve = this; spot.lookAt = ve.makeLookAt(spot); spot.placemark = ve.makePlacemark (spot); return(spot); }; mcpherEarth.prototype.makeLookAt = function (spot) { var ve= this; var ge = ve.ge; var vm = this.vMap; // look at something var lookAt = ge.getView().copyAsLookAt (ge.ALTITUDE_RELATIVE_TO_GROUND); // Set the position values lookAt.setLatitude(spot.lat); lookAt.setLongitude(spot.lng); lookAt.setRange(parseFloat(vm.control.range)); lookAt.setTilt(parseFloat(vm.control.tilt)); return (lookAt); }; mcpherEarth.prototype.makePlacemark = function (spot) { //var ge = this.ge; var ge= this.ge; var vm = this.vMap; var uItem = vm.items[spot.usingIndex]; var marker = vm.spotControl['earth marker']; var placemark = ge.createPlacemark(''); placemark.setName(spot.title); // Create style map for placemark if(marker){ var image = uItem.cJobject[marker]; if (image) { if (mcpherIsImage(image)){ var icon = ge.createIcon(''); icon.setHref(image); var style = ge.createStyle(''); style.getIconStyle().setIcon(icon); // make marker bigger than usual if (vm.control.placemarkscale){ var x; if(x=mcpherToNumber(vm.control.placemarkscale)){ style.getIconStyle().setScale(x); } } placemark.setStyleSelector(style); } else { alert (image + ' is not a valid image link to use as a marker'); } } } var point = ge.createPoint(''); point.setLatitude(spot.lookAt.getLatitude()); point.setLongitude(spot.lookAt.getLongitude()); placemark.setGeometry(point); ge.getFeatures().appendChild(placemark); return(placemark); }; mcpherEarth.prototype.showLookAt = function (spot) { var lookAt = spot.lookAt; this.ge.getView().setAbstractView(lookAt); return (lookAt); }; mcpherEarth.prototype.failure = function (errorCode) { var vm = this.vMap; alert('error loading google earth' + errorCode); //vm.statusUpdate('..google earth failed',false,'error ' + errorCode); }; function initCallback (instance) { vMap.ve.success(instance); } function failureCallback (errorCode) { vMap.ve.failure(errorCode); } // END of mcpherEarth // MCPHERFUNCTIONS function mcpherAddEvent (o,e,f,b,complain){ // because IE is different if(o.addEventListener) return(o.addEventListener(e, f, b)); else if (o.attachEvent) return(o.attachEvent('on' + e, f)); else if (complain) alert ('browser doesnt support events'); return(null); } function mcpherIsNumber(x){ return (!isNaN(parseFloat(x))); } function mcpherToNumber(x) { if (mcpherIsNumber(x)) return (parseFloat(x)); else return (null); } function mcpherToDate(x) { if (mcpherIsDate(x)) return (new Date(Date.parse(x))); else return (null); } function mcpherIsImage(url) { return ((/http:\/\/.+?\.jpg|jpeg|png|gif/).test (url.toLowerCase()) ); } function mcpherSize(o) { var s=0; for (var key in o) if (o.hasOwnProperty(key))s++; return (s); } function mcpherIsDate(s){ return (!isNaN(Date.parse(s))); } function mcpherGetqparams(){ // this is just a utility to pick up command line params var qparams = new Array(); var htmlquery = window.location.search.substring(1); var htmlparams = htmlquery.split('&'); for ( var i=0; i < htmlparams.length;i++) { var k = htmlparams[i].indexOf('='); if (k > 0) { qparams[ htmlparams[i].substring(0,k) ] = decodeURI(htmlparams [i].substring(k+1)); } } return qparams; } function mcpherGag(x){ switch (x) { case 'average': return (google.visualization.data.avg); case 'count': return (google.visualization.data.count); case 'sum': return (google.visualization.data.sum); case 'min': return (google.visualization.data.min); case 'max': return (google.visualization.data.max); default: alert('Unknown google aggregation request ' + x); return (null); } } function oShow (x,o) { var s =x+'(object)'; for (var key in o) s += key + ","; s+='(array)'; for (var i=0; i < o.length; i++) s += o[i] + ','; s+='(size)' + mcpherSize(o) + '(length)' + o.length; alert (s); } }; </script></head> <body style="font-family: sans-serif;"> <b>VizMap Venues Demonstration Application Excel to Google Mapping</b> <br>For More details <a href="http://rambings.mcpher.com">Visit Excel and Google Ramblings</a> <div id="status_div" class ="mcstatus"></div> <div id="map_canvas" style="width: 100%; height: 90%"></div> </body> </html>
For help and more information join our forum, follow the blog, follow me on Twitter