getting spreadsheet data to chart data array - html

Although I found similar posts elsewhere, I still cannot solve my issue.
I want to load locations on a html sidebar page on google spreadsheet, but the only example I find are hard-coded locations.
Here is an example, on HTML page (I removed API Key): this one works for me.
<body>
<div class="container">
<div id="map_div" style="width: 500px; height: 500px"></div>
</div> <!-- CLOSE CONTAINER -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "xxxx"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart(arrayToData) {
var data = new google.visualization.arrayToDataTable(
[["Lat", "Long","Nom"],
[45.7660889, 4.794056299999999, "Person1"],
[45.8167227, 4.8341048, "Person2"],
[45.7796433, 4.8037871, "Person3"],
[45.7780849, 4.921768399999999, "Person4"]]
);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}//function drawChart() {
</script>
</body>
And I would like to have something looking like that, where data locations are not hard-coded but comes from spreadsheet data :
google.charts.setOnLoadCallback(drawChart);
var ss = SpreadsheetApp.openByUrl("xxxx");
var datatable = ss.getRange("listNamesAdresses");
function drawChart(arrayToData) {
var dataToArray=document.getElementById("listNamesAdresses");
var data = new google.visualization.arrayToDataTable(
dataToArray
);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}//function drawChart() {
I'm aware this is not correct, but I tried many combinations, still cannot solve it. Your help is welcome !
Here is a sharable example of what I made :
Link to a copy of my Map Test
I adapted it from my spreadsheet but went out of my quota for my API key, so I could'nt test it yet. I hope this will be fine !
Many thanks in advance
EDIT 2 :
I followed ziganotschka's suggestions (thank you very much for your time) : I couldn't apply the HtmlCreateOutputFromFil("index.html") so I stuck to my code for displaying a sidepage Html. For the rest of it : I now have a map (first victory!).
But, it says : "no data points to show".
I checked on values return by getAddresses function, seems OK. For getting easier on it, I changed the function to an easier one : getGeoCodesAndNames. This one returns, as it says, geocode latitude, longitude, and name.
Here are my new code sample and link to the new version of the spreadsheet :
Gs-code :
function getGeoCodesAndNames(){
//get addresses and names list
var namesAddresses=ss.getRange("ListNamesAddresses");
var a_values=namesAddresses.getValues();
Logger.log(a_values);
/* returns
[[Lat, Lon, Name],
[45.7660889, 4.79405629999999, person1],
[45.8167227, 4.8341048, person2],
[45.7796433, 4.8037871, person3],
[45.7780849, 4.921768399999999, person4]]
*/
return a_values;
}//function getGeoCodesAndNames(){
function testMap2(){
var template = HtmlService.createTemplateFromFile("carte2");
var html = template.evaluate();
html.setTitle("Display Map 2").setHeight(550).setWidth(550);
SpreadsheetApp.getUi().showModalDialog(html, "Locations")
}//function testCarte1(){
and HTML code :
<script type="text/javascript">
window.onload = google.script.run.withSuccessHandler(onSuccess).getGeoCodesAndNames();
function onSuccess (arrayToData){
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "AIzaSyC4WPcWGMZRoqSAfZ0F4RzvWtN6Jy7hmdE"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable(arrayToData);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}// function drawChart() {
}//function onSuccess (arrayToData){
</script>
And here is the link to the new spreadsheet version :
TestMap2
Do I need to publish it as a web app if I just want to have a side page ? On previous projects I had, I could add datas from a side-page to a spreadsheet without it. In the oppposite ways, can you confirm I need to do it ? I tried, did not change anything on my current result : maybe I made something wrong.
Many thanks again for your help !
EDIT 3 :
I finally got it : My geocode/address function were not returning a proper format for coordinates, because of two things :
1) I'm using French typing, ie dot are replaced with commas in numbers
2) I had to add one more """ symbol at beginning and ending of each string part in the array.
Here is the correct function (might be improved, but..does the job):
function getGeoCodesAndNames(){
//get addresses and names list
var namesAddresses=ss.getRange("ListNamesAddresses");
var a_values=namesAddresses.getValues();
for (var i=0;i<a_values.length;i++){
for (var j=0;j<a_values[0].length;j++){
var value=a_values[i][j];
if (typeof value == "string"){
a_values[i][j]="\"" + value + "\"";
}
}
}
return a_values;
}//function getGeoCodesAndNames(){
Many thanks to the people who helped me !

To combine using the Visualization API in Javascript with retrieving spreadsheet data with Apps Script - you need to use a WebApp
Use google.script.run to pass data from the serverside to clientside
Sample:
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index.html");
}
function getDatafromSheet(){
var ss = SpreadsheetApp.openByUrl("xxxx");
// getNamedRanges()[0] works if you have only one named range
var datatable = ss.getNamedRanges()[0].getRange().getValues();
return datatable;
}
index.html
<body>
<div class="container">
<div id="map_div" style="width: 500px; height: 500px"></div>
</div> <!-- CLOSE CONTAINER -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
window.onload = google.script.run.withSuccessHandler(onSuccess).getDatafromSheet();
function onSuccess (arrayToData){
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "AIzaSyDCKzjezYeUDd2ugtFnzokCIpV1YpLmmEc"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.arrayToDataTable(arrayToData);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}
}
</script>
</body>

Related

Reusable Google doc Picker in google scripts - Picker Callback

Docu References:
Drive file picker v3
G Suite Dialogs: File Open Dialog
SO References:
Access data in Google App Script from spread sheet modal html form
How do I handle the call back using multiple Google File Picker
What to achieve?
In a Google Sheets script, I would like to define a Files Picker that returns the data of picked up files, provided that thereon, from another part of the scripts, the caller can receive that data.
Problem:
The file picker is launched as an html Modal dialog. After searching for a while, the only solution to get the data from the script that launched the picker is from the html script code:
set the callaback of the picker to a specific function: picker.setCallback(my_callback)
or use google.script.run.my_callback (i.e. from button Done for instance)
... provided that my_callback function defined in your script gets the data.
The problem with the above is that you cannot use the same picker for multiple purposes, because:
my_callback is fixed in the html script
my_callback cannot know for what purpose the picker was initially called (i.e. should it get the content?, should it give the information to some unknown caller?).
Once it gets the data, my_callback does not know what to do with it... unless my_callback is tied to only 1 caller; which does not seem correct, as that would require to have multiple html definitions for the picker, once per each reason you may invoke it, so it can call back to the proper function.
Any ideas?
global variables in scripts get re-initialized and cannot use PropertiesService to store values other than String (so no way to store the final picker_callback through a global var).
google.script.run does not offer calls by giving the name of the server-side function as String (reference) (which discards having a function to generate the picker_dialog.html by changing the callback function).
Sample Code
code.gs
function ui() {
return SpreadsheetApp.getUi();
}
function onOpen() {
ui().createMenu('ecoPortal Tools')
.addItem('Read a file', 'itemReadFile')
.addItem('Edit a file', 'itemEditFile')
.addToUi();
}
function itemReadFile() {
pickFile(readFile)
}
function itemEditFile() {
pickFile(editFile)
}
function readFile(data) {
/* do some stuff */
}
function editFile(data) {
/* do some stuff */
}
picker.gs:
function pickFile(callback) {
var html = HtmlService.createHtmlOutputFromFile('picker_dialog.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
// concept (discarded):
callback_set('picker', callback)
ui().showModalDialog(html, 'Select a file');
}
function getOAuthToken() {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}
// picker callback hub
function pickerCallback(data) {
var callback = callback_get('picker');
callback_set('picker', null);
if (callback) callback.call(data);
}
picker_dialog.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<script>
var DEVELOPER_KEY = '___PICKER_API_KEY_____';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
// currently selected files data
var files_data = null;
/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
function getOAuthToken() {
console.log("going to call get auth token :)");
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}
function createPicker(token) {
console.log("pickerApiLoadded", pickerApiLoaded);
console.log("token", token);
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.DOCS)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin)
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
}
}
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
files_data = data;
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
'</a><br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
function closeIt() {
google.script.host.close();
}
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeIt).pickerCallback(files_data);
}
</script>
</head>
<body>
<div>
<button onclick='getOAuthToken()'>Select a file</button>
<p id='result'></p>
<button onclick='returnSelectedFilesData()'>Done</button>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
picker.setCallback(my_callback)
Picker callback is different from:
or use google.script.run.my_callback
The former calls a function on the frontend html while the latter calls a function in the server.
my_callback cannot know for what purpose the picker was initially called
You can send a argument to the server:
google.script.run.my_callback("readFile");
On the server side(code.gs),
fuction my_callback(command){
if(command === "readFile") Logger.log("Picker called me to readFile");
}
google.script.run does not offer calls by giving the name of the server-side function as String
Not true. Dot is used to access members of a object. You can use bracket notation to access a member as a string:
google.script.run["my_callback"]();
EDITED BY Q.ASKER:
In your case, to pass the files_data to the server side:
google.script.run.withSuccessHandler(closeIt)[my_callback](files_data);
Now, for my_callback (String variable) to be set from server side, you need to push it using templates:
function pickFile(str_callback) {
var htmlTpl = HtmlService.createTemplateFromFile('picker_dialog.html');
// push variables
htmlTpl.str_callback = str_callback;
var htmlOut = htmlTpl.evaluate()
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
ui().showModalDialog(htmlOut, 'Select a file');
}
The two unique changes that you need to make to your picker_dialog.html:
add printing scriptlet to set my_callback (<?= ... ?>)
use the google.script.run as mentioned
var my_callback = <?= str_callback? str_callback : 'defaultPickerCallbackToServer' ?>;
/* ... omitted code ... */
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeDialog)[my_callback](files_data);
}
Now, when you call pickFile to open the frontend picker, you are able to set a different server callback that will receive the data with the file(s) chosen by the user.

google.script.run to reload google chart in HTML-service page

Just like I would inject new html on a div using google.script.run on a page without reloading, I have spent countless hours trying to do the same but with a google-visualization pie chart, I want to call as many times as needed this google.run.script through a callback.
This is the button with multiple callbacks:
<button onclick="google.script.run.withSuccessHandler(refresh).report(user());">Generate report</button>
OK, this is what user do:
<script>
function user() {
var user = $("input[type=radio]:checked").val();
return user;
}
</script>
Report() simply returns a new array of data for a chart, it will change depending on user.
Finally this is my chart script.
<script>
google.load("visualization", "1", {packages:["corechart"], "callback": refresh});
google.setOnLoadCallback(refresh);
function refresh(e) {
var data = google.visualization.arrayToDataTable(e);
var options = {
title: 'Chart',
is3D: true,
pieSliceText: 'label',
};
var chart = new google.visualization.PieChart(document.getElementById('piechart_3d'));
chart.draw(data, options);
}
</script>
My script works as intended all the way, except the end where I can't get it to update the chart everytime I click the button. Which loads new array data for the chart. I have simplified the code just for this post.
I managed to make it work as intended by parsing the callback value with JSON.parse(e), very hidden for me to figure it out.

Pass Object to Templated HTML

I have an object I wold like to pass to a templated HTML file. If I use a simple variable, all is fine, as these work:
on the server side I have the following at the top of code.gs:
//Define this here globally so it is accessed in the HTML Template
var passedFestival = ' ';
var passedSID = ' ';
var passedRID = 'FALSE';
in the HTML file I have:
<script>
var passedFestival = '<?!= passedFestival ?>';
var passedSID = '<?!= passedSID ?>';
var passedRID = '<?!= passedRID ?>';
</script>
I can use these in later code and get the appropriate value. I want to also use an Object, which is the result of reading a couple rows from the spreadsheet, and basically results in the following outside any function, same as the other variables, above:
var testformOptions = [];
testformOptions['currencies'] = [{"currency":"CAD", "default":false, "defaultOption":"Interac", "paymentOptions":"Cheque|Credit Card|Interac|PayPal", "sheetrow":2}, {"currency":"USD", "default":true, "defaultOption":"PopMoney", "paymentOptions":"Cheque|Credit Card|PayPal|PopMoney", "sheetrow":3}];
testformOptions['meals'] = {"use":true, "required":false, "default":"Omnivore", "values":"Omnivore|Vegan"};
in the HTML file I can reference the object's individual values:
if ('<?!= testformOptions.currencyOptions.use ?>') {
$("#currencies").show();
}
But I would rather copy the object over completely and reference it as part of the client side data. I have tried a few things, most of which I understand why they didn't work, but I thought this would:
var formOptions = jQuery.extend(true, {}, <?!= testformOptions?>;
I have tried saving the data to a variable as json, but that didn't work since I have single quotes and other special characters in my final object.
Is there any way to get an object passed into an object on the client side outside using google.script.run to pass it after loading? I am reading the spreadsheet data as part of the initial doGet so I figured it may be faster to use templated HTML and pass the object.
EDIT
Thanks to the reply, I have working code. The final sample reads as follows. Some items are left out to allow focusing on the important parts.
Code.gs:
var passedSID = ' ';
var passedRID = 'FALSE';
function doGet(passed) {
if(passed.parameter.rid && passed.parameter.msid){
// A registration ID and Spreadsheet ID were passed so this is to edit an existing registration
passedSID = passed.parameter.msid;
passedRID = passed.parameter.rid;
var registrationValues = getRegistrationValues(passedSID, passedRID);
}
else if(passed.parameter.msid){
// A Spreadsheet ID was passed so this is to complete a new registration
passedSID = passed.parameter.msid;
}
//get the form options from the appropriate spreadsheet file
//getFormOptions() is from Tutorial: Simple Mail Merge
//https://developers.google.com/apps-script/articles/mail_merge
testformOptions = getFormOptions(passedSID);
//Create the HTML template
var template = HtmlService.createTemplateFromFile('Index');
template.data = JSON.stringify(testformOptions);
// Build and return HTML in IFRAME sandbox mode.
return template.evaluate()
.setTitle('Registration Form').setWidth(620).setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function include(filename) {
return HtmlService.createTemplateFromFile(filename).evaluate()
.getContent();
}
Index.html:
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').getContent(); ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:100" rel="stylesheet">
<html>
<body>
<div id="mybody">
<form>
<!-- Boring form html -->
</form>
</div>
</body>
</html>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
var formOptions = $.extend(true, {}, <?!= data ?>);
</script>
<!-- Use a templated HTML printing scriptlet to import JavaScript. -->
<?!= HtmlService.createHtmlOutputFromFile('jquery_cloneform_js').getContent(); ?>
<?!= include('JavaScript'); ?>
And in JavaScript.html:
<script>
var passedSID = '<?!= passedSID ?>';
var passedRID = '<?!= passedRID ?>';
// use onload to call initialization after the document loads
window.onload = setForm;
function setForm(){
var optionArray = [];
var defaultOption = '';
if (formOptions.currencyOptions.use) {
$("#currencies").show();
//Set the options for the currency
var options = formOptions.currencyOptions.values;
defaultOption = formOptions.currencyOptions.defaultOption;
optionArray = options.split("|");
setSelectOptions('regcurrency', optionArray, defaultOption);
var options = formOptions.currencies[1].paymentOptions;
defaultOption = formOptions.currencies[1].defaultOption;
optionArray = options.split("|");
setSelectOptions('paymentMethod', optionArray, defaultOption);
}
}
How about a following answer?
Modification points :
var testformOptions = [];
testformOptions['currencies'] = [{"currency":"CAD", "default":false, "defaultOption":"Interac", "paymentOptions":"Cheque|Credit Card|Interac|PayPal", "sheetrow":2}, {"currency":"USD", "default":true, "defaultOption":"PopMoney", "paymentOptions":"Cheque|Credit Card|PayPal|PopMoney", "sheetrow":3}];
testformOptions['meals'] = {"use":true, "required":false, "default":"Omnivore", "values":"Omnivore|Vegan"};
About the above script, testformOptions is defined as an array. So testformOptions['currencies'] and testformOptions['meals'] cannot be imported. So please modify from var testformOptions = []; to var testformOptions = {};.
When it passes the object, please use JSON.stringify().
The scripts reflected above modifications are as follows.
code.gs :
function doGet() {
var testformOptions = {};
testformOptions['currencies'] = [{"currency":"CAD", "default":false, "defaultOption":"Interac", "paymentOptions":"Cheque|Credit Card|Interac|PayPal", "sheetrow":2}, {"currency":"USD", "default":true, "defaultOption":"PopMoney", "paymentOptions":"Cheque|Credit Card|PayPal|PopMoney", "sheetrow":3}];
testformOptions['meals'] = {"use":true, "required":false, "default":"Omnivore", "values":"Omnivore|Vegan"};
var t = HtmlService.createTemplateFromFile('index');
t.data = JSON.stringify(testformOptions);
return t.evaluate();
}
index.html
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<p id="disp"></p>
<script>
var data = $.extend(true, {}, <?!= data ?>);
$('#disp').text(JSON.stringify(data));
</script>
Result :
{
"currencies": [
{
"currency": "CAD",
"default": false,
"defaultOption": "Interac",
"paymentOptions": "Cheque|Credit Card|Interac|PayPal",
"sheetrow": 2
},
{
"currency": "USD",
"default": true,
"defaultOption": "PopMoney",
"paymentOptions": "Cheque|Credit Card|PayPal|PopMoney",
"sheetrow": 3
}
],
"meals": {
"use": true,
"required": false,
"default": "Omnivore",
"values": "Omnivore|Vegan"
}
}
If I misunderstand your question, I'm sorry.

Pass array from server side function using google script and html

I have an html page that will be served to a google sheet app to be used as a UI. I would like to access an array from a server side function within the html file. I am having trouble accessing a returned array. Here is what I have:
in html file:
<div id="id1">
Starting 1
</div>
<div id= "id2">
Starting 2
</div>
<script type="text/javascript">
document.getElementById("id1").innerHTML = "A change";
</script>
<script type="text/javascript">
function onSuccess(numUnread) {
alert('You have ' + numUnread[0]
+ ' unread messages in your Gmail inbox.');
document.getElementById("id2").innerHTML = numUnread[0];
}
google.script.run.withSuccessHandler(onSuccess)
.getPermits();
</script>
In code.gs:
function getPermits()
{
var permits = [];
for(var i = 0; i < 10; i++)
{
permits.push('Element ' + i);
}
return permits;
}
Right now I am just trying to figure out why the div with id = "id2"
does not get changed to the first element from the passed array. Instead, it is not changed. Also, there is no alert. If I change the return of the gePermits() function to a string, both the div and the alert work as I would expect.
Thanks in advance!
Some types are not passed trough HTMLService, but you can always STRINGFY and PARSE it, try:
return JSON.stringify(permits);
and in the html:
function onSuccess(numUnread) {
numUnread = JSON.parse(numUnread);

How to change Bing Map code to Google Map code

Good day,
Does anyone have an idea of how to change a bing map to google map api code. I have found a very useful code on line that is coded using bing maps and would like to change it to work with google maps. if found the code here: http://blogs.msdn.com/b/crm/archive/2011/01/19/custom-charting-capabilities-in-microsoft-dynamics-crm-2011.aspx
Code below:
<html>
<head>
<title>Accounts on Bing Maps</title>
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="ClientGlobalContext.js.aspx"></script>
<script type="text/javascript">
var map;
// Function to construct key-value pairs from a query string.
function getParametersFromQuery(query) {
var parametersDictionary = new Array();
var parameters = query.split('&');
for (var i = 0; i < parameters.length; i++) {
var keyAndValue = parameters[i].split('=');
parametersDictionary[unescape(keyAndValue[0])] = unescape(keyAndValue[1]);
}
return parametersDictionary;
}
// Function that makes a GET request to the CRM REST end-point, and invokes a callback with the results.
function retrieveFromCrmRestApi(url, callback) {
$.ajax({
type: "GET",
url: GetGlobalContext().getServerUrl() + "/XRMServices/2011/OrganizationData.svc" + url,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
callback(data.d);
}
});
}
// Function that retrieves the corresponding CRM chart, and invokes the callback when successful.
function loadChartFromCrm(callback) {
var parameters = getParametersFromQuery(window.location.search.substring(1));
parameters = getParametersFromQuery(parameters["data"]);
var id = parameters["visid"].substr(1, 36);
var type = parameters["vistype"];
var url = (type == "1111" ? "/SavedQueryVisualizationSet" : "/UserQueryVisualizationSet")
+ "(guid'" + id + "')?$select=DataDescription,PresentationDescription";
retrieveFromCrmRestApi(url, callback);
}
var locations = new Array();
function plotAccountLocations(accounts) {
if (accounts.length > 0) {
var account = accounts.pop();
var address = account.Address1_City + ', ' + account.Address1_Country;
map.Find(null, address, null, null, 0, 1, false, false, false, false,
function (shapeLayer, results, places, moreResults, error) {
if (null != places && places.length > 0) {
var place = places[0];
var newShape = new VEShape(VEShapeType.Pushpin, place.LatLong);
newShape.SetTitle(account.Name);
newShape.SetDescription(address);
locations.push(newShape);
}
// When we have found (or not found) the current account,
// recursively call the same function to find the next one.
plotAccountLocations(accounts);
});
}
else {
var shapeLayer = new VEShapeLayer();
map.AddShapeLayer(shapeLayer);
shapeLayer.AddShape(locations);
}
}
function loadAccountsFromCrm(dataDescription) {
var url = "/AccountSet?$select=Address1_Country,Address1_City,Name";
if (null != dataDescription) {
// Filter accounts based on country specified in data description.
url += "&$filter=Address1_Country eq '" + dataDescription + "'";
}
retrieveFromCrmRestApi(url,
function (data) {
var results = data["results"];
var accounts = new Array();
for (resultKey in results) {
accounts.push(results[resultKey]);
}
// Once accounts are retrieved from CRM Server, plot their locations on map.
plotAccountLocations(accounts);
}
);
}
function getMap(presentationDescription) {
// Set center and zoom defaults.
var center = null;
var zoom = 4;
if (null != presentationDescription) {
// Calculate map-center and zoom from the presentation description.
var arguments = presentationDescription.split(',');
if (arguments.length > 1) {
center = new VELatLong(arguments[0], arguments[1]);
}
if (arguments.length > 2) {
zoom = arguments[2];
}
}
map = new VEMap("map");
map.LoadMap(center, zoom, VEMapStyle.Road, true, VEMapMode.Mode2D, false, 0);
window.onresize = function (event) { map.Resize(document.body.clientWidth, document.body.clientHeight); };
window.onresize(null);
}
function loadMap() {
// First, get the chart object from CRM Server.
loadChartFromCrm(
function (chart) {
// Once we have retrieved the chart, format the map based on the chart's presentation description.
getMap(chart.PresentationDescription);
// Get Accounts from CRM Server based on the chart's data description, and plot them on the map.
loadAccountsFromCrm(chart.DataDescription);
}
);
}
</script>
</head>
<body onload="loadMap()">
<div id="map"></div>
</body>
</html>
There is no easy out of the box solution for what you are looking for.
Google API has many differences. For example, the Google API leaves keeping track of map children to the programmer (you). There is no means of looping through the map controls like there is with the Bing API. This means that your solution for saving the map content and re-displaying it will be a little different.
Although, since both API's are done via javascript, all you need to do is convert your functionality according to their documentation;
Geocoding Sample
Simple Map Initialization
Adding Shapes
Adding Markers (pushpin)